2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 University of Szeged
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #ifndef MacroAssemblerARMv7_h
28 #define MacroAssemblerARMv7_h
32 #include "ARMv7Assembler.h"
33 #include "AbstractMacroAssembler.h"
37 class MacroAssemblerARMv7
: public AbstractMacroAssembler
<ARMv7Assembler
> {
38 // FIXME: switch dataTempRegister & addressTempRegister, or possibly use r7?
39 // - dTR is likely used more than aTR, and we'll get better instruction
40 // encoding if it's in the low 8 registers.
41 static const RegisterID dataTempRegister
= ARMRegisters::ip
;
42 static const RegisterID addressTempRegister
= ARMRegisters::r3
;
44 static const ARMRegisters::FPDoubleRegisterID fpTempRegister
= ARMRegisters::d7
;
45 inline ARMRegisters::FPSingleRegisterID
fpTempRegisterAsSingle() { return ARMRegisters::asSingle(fpTempRegister
); }
62 explicit ArmAddress(RegisterID base
, int32_t offset
= 0)
69 explicit ArmAddress(RegisterID base
, RegisterID index
, Scale scale
= TimesOne
)
79 typedef ARMRegisters::FPDoubleRegisterID FPRegisterID
;
81 static const Scale ScalePtr
= TimesFour
;
84 Equal
= ARMv7Assembler::ConditionEQ
,
85 NotEqual
= ARMv7Assembler::ConditionNE
,
86 Above
= ARMv7Assembler::ConditionHI
,
87 AboveOrEqual
= ARMv7Assembler::ConditionHS
,
88 Below
= ARMv7Assembler::ConditionLO
,
89 BelowOrEqual
= ARMv7Assembler::ConditionLS
,
90 GreaterThan
= ARMv7Assembler::ConditionGT
,
91 GreaterThanOrEqual
= ARMv7Assembler::ConditionGE
,
92 LessThan
= ARMv7Assembler::ConditionLT
,
93 LessThanOrEqual
= ARMv7Assembler::ConditionLE
,
94 Overflow
= ARMv7Assembler::ConditionVS
,
95 Signed
= ARMv7Assembler::ConditionMI
,
96 Zero
= ARMv7Assembler::ConditionEQ
,
97 NonZero
= ARMv7Assembler::ConditionNE
99 enum DoubleCondition
{
100 // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
101 DoubleEqual
= ARMv7Assembler::ConditionEQ
,
102 DoubleNotEqual
= ARMv7Assembler::ConditionVC
, // Not the right flag! check for this & handle differently.
103 DoubleGreaterThan
= ARMv7Assembler::ConditionGT
,
104 DoubleGreaterThanOrEqual
= ARMv7Assembler::ConditionGE
,
105 DoubleLessThan
= ARMv7Assembler::ConditionLO
,
106 DoubleLessThanOrEqual
= ARMv7Assembler::ConditionLS
,
107 // If either operand is NaN, these conditions always evaluate to true.
108 DoubleEqualOrUnordered
= ARMv7Assembler::ConditionVS
, // Not the right flag! check for this & handle differently.
109 DoubleNotEqualOrUnordered
= ARMv7Assembler::ConditionNE
,
110 DoubleGreaterThanOrUnordered
= ARMv7Assembler::ConditionHI
,
111 DoubleGreaterThanOrEqualOrUnordered
= ARMv7Assembler::ConditionHS
,
112 DoubleLessThanOrUnordered
= ARMv7Assembler::ConditionLT
,
113 DoubleLessThanOrEqualOrUnordered
= ARMv7Assembler::ConditionLE
,
116 static const RegisterID stackPointerRegister
= ARMRegisters::sp
;
117 static const RegisterID linkRegister
= ARMRegisters::lr
;
119 // Integer arithmetic operations:
121 // Operations are typically two operand - operation(source, srcDst)
122 // For many operations the source may be an Imm32, the srcDst operand
123 // may often be a memory location (explictly described using an Address
126 void add32(RegisterID src
, RegisterID dest
)
128 m_assembler
.add(dest
, dest
, src
);
131 void add32(Imm32 imm
, RegisterID dest
)
133 add32(imm
, dest
, dest
);
136 void add32(Imm32 imm
, RegisterID src
, RegisterID dest
)
138 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12OrEncodedImm(imm
.m_value
);
139 if (armImm
.isValid())
140 m_assembler
.add(dest
, src
, armImm
);
142 move(imm
, dataTempRegister
);
143 m_assembler
.add(dest
, src
, dataTempRegister
);
147 void add32(Imm32 imm
, Address address
)
149 load32(address
, dataTempRegister
);
151 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12OrEncodedImm(imm
.m_value
);
152 if (armImm
.isValid())
153 m_assembler
.add(dataTempRegister
, dataTempRegister
, armImm
);
155 // Hrrrm, since dataTempRegister holds the data loaded,
156 // use addressTempRegister to hold the immediate.
157 move(imm
, addressTempRegister
);
158 m_assembler
.add(dataTempRegister
, dataTempRegister
, addressTempRegister
);
161 store32(dataTempRegister
, address
);
164 void add32(Address src
, RegisterID dest
)
166 load32(src
, dataTempRegister
);
167 add32(dataTempRegister
, dest
);
170 void add32(Imm32 imm
, AbsoluteAddress address
)
172 load32(address
.m_ptr
, dataTempRegister
);
174 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12OrEncodedImm(imm
.m_value
);
175 if (armImm
.isValid())
176 m_assembler
.add(dataTempRegister
, dataTempRegister
, armImm
);
178 // Hrrrm, since dataTempRegister holds the data loaded,
179 // use addressTempRegister to hold the immediate.
180 move(imm
, addressTempRegister
);
181 m_assembler
.add(dataTempRegister
, dataTempRegister
, addressTempRegister
);
184 store32(dataTempRegister
, address
.m_ptr
);
187 void and32(RegisterID src
, RegisterID dest
)
189 m_assembler
.ARM_and(dest
, dest
, src
);
192 void and32(Imm32 imm
, RegisterID dest
)
194 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(imm
.m_value
);
195 if (armImm
.isValid())
196 m_assembler
.ARM_and(dest
, dest
, armImm
);
198 move(imm
, dataTempRegister
);
199 m_assembler
.ARM_and(dest
, dest
, dataTempRegister
);
203 void lshift32(RegisterID shift_amount
, RegisterID dest
)
205 // Clamp the shift to the range 0..31
206 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(0x1f);
207 ASSERT(armImm
.isValid());
208 m_assembler
.ARM_and(dataTempRegister
, shift_amount
, armImm
);
210 m_assembler
.lsl(dest
, dest
, dataTempRegister
);
213 void lshift32(Imm32 imm
, RegisterID dest
)
215 m_assembler
.lsl(dest
, dest
, imm
.m_value
& 0x1f);
218 void mul32(RegisterID src
, RegisterID dest
)
220 m_assembler
.smull(dest
, dataTempRegister
, dest
, src
);
223 void mul32(Imm32 imm
, RegisterID src
, RegisterID dest
)
225 move(imm
, dataTempRegister
);
226 m_assembler
.smull(dest
, dataTempRegister
, src
, dataTempRegister
);
229 void neg32(RegisterID srcDest
)
231 m_assembler
.neg(srcDest
, srcDest
);
234 void not32(RegisterID srcDest
)
236 m_assembler
.mvn(srcDest
, srcDest
);
239 void or32(RegisterID src
, RegisterID dest
)
241 m_assembler
.orr(dest
, dest
, src
);
244 void or32(Imm32 imm
, RegisterID dest
)
246 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(imm
.m_value
);
247 if (armImm
.isValid())
248 m_assembler
.orr(dest
, dest
, armImm
);
250 move(imm
, dataTempRegister
);
251 m_assembler
.orr(dest
, dest
, dataTempRegister
);
255 void rshift32(RegisterID shift_amount
, RegisterID dest
)
257 // Clamp the shift to the range 0..31
258 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(0x1f);
259 ASSERT(armImm
.isValid());
260 m_assembler
.ARM_and(dataTempRegister
, shift_amount
, armImm
);
262 m_assembler
.asr(dest
, dest
, dataTempRegister
);
265 void rshift32(Imm32 imm
, RegisterID dest
)
267 m_assembler
.asr(dest
, dest
, imm
.m_value
& 0x1f);
270 void urshift32(RegisterID shift_amount
, RegisterID dest
)
272 // Clamp the shift to the range 0..31
273 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(0x1f);
274 ASSERT(armImm
.isValid());
275 m_assembler
.ARM_and(dataTempRegister
, shift_amount
, armImm
);
277 m_assembler
.lsr(dest
, dest
, dataTempRegister
);
280 void urshift32(Imm32 imm
, RegisterID dest
)
282 m_assembler
.lsr(dest
, dest
, imm
.m_value
& 0x1f);
285 void sub32(RegisterID src
, RegisterID dest
)
287 m_assembler
.sub(dest
, dest
, src
);
290 void sub32(Imm32 imm
, RegisterID dest
)
292 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12OrEncodedImm(imm
.m_value
);
293 if (armImm
.isValid())
294 m_assembler
.sub(dest
, dest
, armImm
);
296 move(imm
, dataTempRegister
);
297 m_assembler
.sub(dest
, dest
, dataTempRegister
);
301 void sub32(Imm32 imm
, Address address
)
303 load32(address
, dataTempRegister
);
305 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12OrEncodedImm(imm
.m_value
);
306 if (armImm
.isValid())
307 m_assembler
.sub(dataTempRegister
, dataTempRegister
, armImm
);
309 // Hrrrm, since dataTempRegister holds the data loaded,
310 // use addressTempRegister to hold the immediate.
311 move(imm
, addressTempRegister
);
312 m_assembler
.sub(dataTempRegister
, dataTempRegister
, addressTempRegister
);
315 store32(dataTempRegister
, address
);
318 void sub32(Address src
, RegisterID dest
)
320 load32(src
, dataTempRegister
);
321 sub32(dataTempRegister
, dest
);
324 void sub32(Imm32 imm
, AbsoluteAddress address
)
326 load32(address
.m_ptr
, dataTempRegister
);
328 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12OrEncodedImm(imm
.m_value
);
329 if (armImm
.isValid())
330 m_assembler
.sub(dataTempRegister
, dataTempRegister
, armImm
);
332 // Hrrrm, since dataTempRegister holds the data loaded,
333 // use addressTempRegister to hold the immediate.
334 move(imm
, addressTempRegister
);
335 m_assembler
.sub(dataTempRegister
, dataTempRegister
, addressTempRegister
);
338 store32(dataTempRegister
, address
.m_ptr
);
341 void xor32(RegisterID src
, RegisterID dest
)
343 m_assembler
.eor(dest
, dest
, src
);
346 void xor32(Imm32 imm
, RegisterID dest
)
348 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(imm
.m_value
);
349 if (armImm
.isValid())
350 m_assembler
.eor(dest
, dest
, armImm
);
352 move(imm
, dataTempRegister
);
353 m_assembler
.eor(dest
, dest
, dataTempRegister
);
358 // Memory access operations:
360 // Loads are of the form load(address, destination) and stores of the form
361 // store(source, address). The source for a store may be an Imm32. Address
362 // operand objects to loads and store will be implicitly constructed if a
363 // register is passed.
366 void load32(ArmAddress address
, RegisterID dest
)
368 if (address
.type
== ArmAddress::HasIndex
)
369 m_assembler
.ldr(dest
, address
.base
, address
.u
.index
, address
.u
.scale
);
370 else if (address
.u
.offset
>= 0) {
371 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12(address
.u
.offset
);
372 ASSERT(armImm
.isValid());
373 m_assembler
.ldr(dest
, address
.base
, armImm
);
375 ASSERT(address
.u
.offset
>= -255);
376 m_assembler
.ldr(dest
, address
.base
, address
.u
.offset
, true, false);
380 void load16(ArmAddress address
, RegisterID dest
)
382 if (address
.type
== ArmAddress::HasIndex
)
383 m_assembler
.ldrh(dest
, address
.base
, address
.u
.index
, address
.u
.scale
);
384 else if (address
.u
.offset
>= 0) {
385 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12(address
.u
.offset
);
386 ASSERT(armImm
.isValid());
387 m_assembler
.ldrh(dest
, address
.base
, armImm
);
389 ASSERT(address
.u
.offset
>= -255);
390 m_assembler
.ldrh(dest
, address
.base
, address
.u
.offset
, true, false);
394 void load8(ArmAddress address
, RegisterID dest
)
396 if (address
.type
== ArmAddress::HasIndex
)
397 m_assembler
.ldrb(dest
, address
.base
, address
.u
.index
, address
.u
.scale
);
398 else if (address
.u
.offset
>= 0) {
399 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12(address
.u
.offset
);
400 ASSERT(armImm
.isValid());
401 m_assembler
.ldrb(dest
, address
.base
, armImm
);
403 ASSERT(address
.u
.offset
>= -255);
404 m_assembler
.ldrb(dest
, address
.base
, address
.u
.offset
, true, false);
408 void store32(RegisterID src
, ArmAddress address
)
410 if (address
.type
== ArmAddress::HasIndex
)
411 m_assembler
.str(src
, address
.base
, address
.u
.index
, address
.u
.scale
);
412 else if (address
.u
.offset
>= 0) {
413 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12(address
.u
.offset
);
414 ASSERT(armImm
.isValid());
415 m_assembler
.str(src
, address
.base
, armImm
);
417 ASSERT(address
.u
.offset
>= -255);
418 m_assembler
.str(src
, address
.base
, address
.u
.offset
, true, false);
423 void load32(ImplicitAddress address
, RegisterID dest
)
425 load32(setupArmAddress(address
), dest
);
428 void load32(BaseIndex address
, RegisterID dest
)
430 load32(setupArmAddress(address
), dest
);
433 void load32WithUnalignedHalfWords(BaseIndex address
, RegisterID dest
)
435 load32(setupArmAddress(address
), dest
);
438 void load32(void* address
, RegisterID dest
)
440 move(ImmPtr(address
), addressTempRegister
);
441 m_assembler
.ldr(dest
, addressTempRegister
, ARMThumbImmediate::makeUInt16(0));
444 void load8(ImplicitAddress address
, RegisterID dest
)
446 load8(setupArmAddress(address
), dest
);
449 DataLabel32
load32WithAddressOffsetPatch(Address address
, RegisterID dest
)
451 DataLabel32 label
= moveWithPatch(Imm32(address
.offset
), dataTempRegister
);
452 load32(ArmAddress(address
.base
, dataTempRegister
), dest
);
456 Label
loadPtrWithPatchToLEA(Address address
, RegisterID dest
)
459 moveFixedWidthEncoding(Imm32(address
.offset
), dataTempRegister
);
460 load32(ArmAddress(address
.base
, dataTempRegister
), dest
);
464 void load16(BaseIndex address
, RegisterID dest
)
466 m_assembler
.ldrh(dest
, makeBaseIndexBase(address
), address
.index
, address
.scale
);
469 void load16(ImplicitAddress address
, RegisterID dest
)
471 ARMThumbImmediate armImm
= ARMThumbImmediate::makeUInt12(address
.offset
);
472 if (armImm
.isValid())
473 m_assembler
.ldrh(dest
, address
.base
, armImm
);
475 move(Imm32(address
.offset
), dataTempRegister
);
476 m_assembler
.ldrh(dest
, address
.base
, dataTempRegister
);
480 DataLabel32
store32WithAddressOffsetPatch(RegisterID src
, Address address
)
482 DataLabel32 label
= moveWithPatch(Imm32(address
.offset
), dataTempRegister
);
483 store32(src
, ArmAddress(address
.base
, dataTempRegister
));
487 void store32(RegisterID src
, ImplicitAddress address
)
489 store32(src
, setupArmAddress(address
));
492 void store32(RegisterID src
, BaseIndex address
)
494 store32(src
, setupArmAddress(address
));
497 void store32(Imm32 imm
, ImplicitAddress address
)
499 move(imm
, dataTempRegister
);
500 store32(dataTempRegister
, setupArmAddress(address
));
503 void store32(RegisterID src
, void* address
)
505 move(ImmPtr(address
), addressTempRegister
);
506 m_assembler
.str(src
, addressTempRegister
, ARMThumbImmediate::makeUInt16(0));
509 void store32(Imm32 imm
, void* address
)
511 move(imm
, dataTempRegister
);
512 store32(dataTempRegister
, address
);
516 // Floating-point operations:
518 bool supportsFloatingPoint() const { return true; }
519 // On x86(_64) the MacroAssembler provides an interface to truncate a double to an integer.
520 // If a value is not representable as an integer, and possibly for some values that are,
521 // (on x86 INT_MIN, since this is indistinguishable from results for out-of-range/NaN input)
522 // a branch will be taken. It is not clear whether this interface will be well suited to
523 // other platforms. On ARMv7 the hardware truncation operation produces multiple possible
524 // failure values (saturates to INT_MIN & INT_MAX, NaN reulsts in a value of 0). This is a
525 // temporary solution while we work out what this interface should be. Either we need to
526 // decide to make this interface work on all platforms, rework the interface to make it more
527 // generic, or decide that the MacroAssembler cannot practically be used to abstracted these
528 // operations, and make clients go directly to the m_assembler to plant truncation instructions.
530 bool supportsFloatingPointTruncate() const { return false; }
532 bool supportsFloatingPointSqrt() const
537 void loadDouble(ImplicitAddress address
, FPRegisterID dest
)
539 RegisterID base
= address
.base
;
540 int32_t offset
= address
.offset
;
542 // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2.
543 if ((offset
& 3) || (offset
> (255 * 4)) || (offset
< -(255 * 4))) {
544 add32(Imm32(offset
), base
, addressTempRegister
);
545 base
= addressTempRegister
;
549 m_assembler
.vldr(dest
, base
, offset
);
552 void loadDouble(const void* address
, FPRegisterID dest
)
554 move(ImmPtr(address
), addressTempRegister
);
555 m_assembler
.vldr(dest
, addressTempRegister
, 0);
558 void storeDouble(FPRegisterID src
, ImplicitAddress address
)
560 RegisterID base
= address
.base
;
561 int32_t offset
= address
.offset
;
563 // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2.
564 if ((offset
& 3) || (offset
> (255 * 4)) || (offset
< -(255 * 4))) {
565 add32(Imm32(offset
), base
, addressTempRegister
);
566 base
= addressTempRegister
;
570 m_assembler
.vstr(src
, base
, offset
);
573 void addDouble(FPRegisterID src
, FPRegisterID dest
)
575 m_assembler
.vadd_F64(dest
, dest
, src
);
578 void addDouble(Address src
, FPRegisterID dest
)
580 loadDouble(src
, fpTempRegister
);
581 addDouble(fpTempRegister
, dest
);
584 void divDouble(FPRegisterID src
, FPRegisterID dest
)
586 m_assembler
.vdiv_F64(dest
, dest
, src
);
589 void subDouble(FPRegisterID src
, FPRegisterID dest
)
591 m_assembler
.vsub_F64(dest
, dest
, src
);
594 void subDouble(Address src
, FPRegisterID dest
)
596 loadDouble(src
, fpTempRegister
);
597 subDouble(fpTempRegister
, dest
);
600 void mulDouble(FPRegisterID src
, FPRegisterID dest
)
602 m_assembler
.vmul_F64(dest
, dest
, src
);
605 void mulDouble(Address src
, FPRegisterID dest
)
607 loadDouble(src
, fpTempRegister
);
608 mulDouble(fpTempRegister
, dest
);
611 void sqrtDouble(FPRegisterID
, FPRegisterID
)
613 ASSERT_NOT_REACHED();
616 void convertInt32ToDouble(RegisterID src
, FPRegisterID dest
)
618 m_assembler
.vmov(fpTempRegisterAsSingle(), src
);
619 m_assembler
.vcvt_F64_S32(dest
, fpTempRegisterAsSingle());
622 void convertInt32ToDouble(Address address
, FPRegisterID dest
)
624 // Fixme: load directly into the fpr!
625 load32(address
, dataTempRegister
);
626 m_assembler
.vmov(fpTempRegisterAsSingle(), dataTempRegister
);
627 m_assembler
.vcvt_F64_S32(dest
, fpTempRegisterAsSingle());
630 void convertInt32ToDouble(AbsoluteAddress address
, FPRegisterID dest
)
632 // Fixme: load directly into the fpr!
633 load32(address
.m_ptr
, dataTempRegister
);
634 m_assembler
.vmov(fpTempRegisterAsSingle(), dataTempRegister
);
635 m_assembler
.vcvt_F64_S32(dest
, fpTempRegisterAsSingle());
638 Jump
branchDouble(DoubleCondition cond
, FPRegisterID left
, FPRegisterID right
)
640 m_assembler
.vcmp_F64(left
, right
);
643 if (cond
== DoubleNotEqual
) {
644 // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump.
645 Jump unordered
= makeBranch(ARMv7Assembler::ConditionVS
);
646 Jump result
= makeBranch(ARMv7Assembler::ConditionNE
);
647 unordered
.link(this);
650 if (cond
== DoubleEqualOrUnordered
) {
651 Jump unordered
= makeBranch(ARMv7Assembler::ConditionVS
);
652 Jump notEqual
= makeBranch(ARMv7Assembler::ConditionNE
);
653 unordered
.link(this);
654 // We get here if either unordered, or equal.
655 Jump result
= makeJump();
659 return makeBranch(cond
);
662 Jump
branchTruncateDoubleToInt32(FPRegisterID
, RegisterID
)
664 ASSERT_NOT_REACHED();
668 // Convert 'src' to an integer, and places the resulting 'dest'.
669 // If the result is not representable as a 32 bit value, branch.
670 // May also branch for some values that are representable in 32 bits
671 // (specifically, in this case, 0).
672 void branchConvertDoubleToInt32(FPRegisterID src
, RegisterID dest
, JumpList
& failureCases
, FPRegisterID
)
674 m_assembler
.vcvtr_S32_F64(fpTempRegisterAsSingle(), src
);
675 m_assembler
.vmov(dest
, fpTempRegisterAsSingle());
677 // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump.
678 m_assembler
.vcvt_F64_S32(fpTempRegister
, fpTempRegisterAsSingle());
679 failureCases
.append(branchDouble(DoubleNotEqualOrUnordered
, src
, fpTempRegister
));
681 // If the result is zero, it might have been -0.0, and the double comparison won't catch this!
682 failureCases
.append(branchTest32(Zero
, dest
));
685 void zeroDouble(FPRegisterID dest
)
687 m_assembler
.vmov_F64_0(dest
);
690 // Stack manipulation operations:
692 // The ABI is assumed to provide a stack abstraction to memory,
693 // containing machine word sized units of data. Push and pop
694 // operations add and remove a single register sized unit of data
695 // to or from the stack. Peek and poke operations read or write
696 // values on the stack, without moving the current stack position.
698 void pop(RegisterID dest
)
700 // store postindexed with writeback
701 m_assembler
.ldr(dest
, ARMRegisters::sp
, sizeof(void*), false, true);
704 void push(RegisterID src
)
706 // store preindexed with writeback
707 m_assembler
.str(src
, ARMRegisters::sp
, -sizeof(void*), true, true);
710 void push(Address address
)
712 load32(address
, dataTempRegister
);
713 push(dataTempRegister
);
718 move(imm
, dataTempRegister
);
719 push(dataTempRegister
);
722 // Register move operations:
724 // Move values in registers.
726 void move(Imm32 imm
, RegisterID dest
)
728 uint32_t value
= imm
.m_value
;
731 moveFixedWidthEncoding(imm
, dest
);
733 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(value
);
735 if (armImm
.isValid())
736 m_assembler
.mov(dest
, armImm
);
737 else if ((armImm
= ARMThumbImmediate::makeEncodedImm(~value
)).isValid())
738 m_assembler
.mvn(dest
, armImm
);
740 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(value
));
741 if (value
& 0xffff0000)
742 m_assembler
.movt(dest
, ARMThumbImmediate::makeUInt16(value
>> 16));
747 void move(RegisterID src
, RegisterID dest
)
749 m_assembler
.mov(dest
, src
);
752 void move(ImmPtr imm
, RegisterID dest
)
754 move(Imm32(imm
), dest
);
757 void swap(RegisterID reg1
, RegisterID reg2
)
759 move(reg1
, dataTempRegister
);
761 move(dataTempRegister
, reg2
);
764 void signExtend32ToPtr(RegisterID src
, RegisterID dest
)
770 void zeroExtend32ToPtr(RegisterID src
, RegisterID dest
)
777 // Forwards / external control flow operations:
779 // This set of jump and conditional branch operations return a Jump
780 // object which may linked at a later point, allow forwards jump,
781 // or jumps that will require external linkage (after the code has been
784 // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge
785 // respecitvely, for unsigned comparisons the names b, a, be, and ae are
786 // used (representing the names 'below' and 'above').
788 // Operands to the comparision are provided in the expected order, e.g.
789 // jle32(reg1, Imm32(5)) will branch if the value held in reg1, when
790 // treated as a signed 32bit value, is less than or equal to 5.
792 // jz and jnz test whether the first operand is equal to zero, and take
793 // an optional second operand of a mask under which to perform the test.
796 // Should we be using TEQ for equal/not-equal?
797 void compare32(RegisterID left
, Imm32 right
)
799 int32_t imm
= right
.m_value
;
801 m_assembler
.tst(left
, left
);
803 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(imm
);
804 if (armImm
.isValid())
805 m_assembler
.cmp(left
, armImm
);
806 if ((armImm
= ARMThumbImmediate::makeEncodedImm(-imm
)).isValid())
807 m_assembler
.cmn(left
, armImm
);
809 move(Imm32(imm
), dataTempRegister
);
810 m_assembler
.cmp(left
, dataTempRegister
);
815 void test32(RegisterID reg
, Imm32 mask
)
817 int32_t imm
= mask
.m_value
;
820 m_assembler
.tst(reg
, reg
);
822 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(imm
);
823 if (armImm
.isValid())
824 m_assembler
.tst(reg
, armImm
);
826 move(mask
, dataTempRegister
);
827 m_assembler
.tst(reg
, dataTempRegister
);
833 Jump
branch32(Condition cond
, RegisterID left
, RegisterID right
)
835 m_assembler
.cmp(left
, right
);
836 return Jump(makeBranch(cond
));
839 Jump
branch32(Condition cond
, RegisterID left
, Imm32 right
)
841 compare32(left
, right
);
842 return Jump(makeBranch(cond
));
845 Jump
branch32(Condition cond
, RegisterID left
, Address right
)
847 load32(right
, dataTempRegister
);
848 return branch32(cond
, left
, dataTempRegister
);
851 Jump
branch32(Condition cond
, Address left
, RegisterID right
)
853 load32(left
, dataTempRegister
);
854 return branch32(cond
, dataTempRegister
, right
);
857 Jump
branch32(Condition cond
, Address left
, Imm32 right
)
859 // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
860 load32(left
, addressTempRegister
);
861 return branch32(cond
, addressTempRegister
, right
);
864 Jump
branch32(Condition cond
, BaseIndex left
, Imm32 right
)
866 // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
867 load32(left
, addressTempRegister
);
868 return branch32(cond
, addressTempRegister
, right
);
871 Jump
branch32WithUnalignedHalfWords(Condition cond
, BaseIndex left
, Imm32 right
)
873 // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
874 load32WithUnalignedHalfWords(left
, addressTempRegister
);
875 return branch32(cond
, addressTempRegister
, right
);
878 Jump
branch32(Condition cond
, AbsoluteAddress left
, RegisterID right
)
880 load32(left
.m_ptr
, dataTempRegister
);
881 return branch32(cond
, dataTempRegister
, right
);
884 Jump
branch32(Condition cond
, AbsoluteAddress left
, Imm32 right
)
886 // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
887 load32(left
.m_ptr
, addressTempRegister
);
888 return branch32(cond
, addressTempRegister
, right
);
891 Jump
branch16(Condition cond
, BaseIndex left
, RegisterID right
)
893 load16(left
, dataTempRegister
);
894 m_assembler
.lsl(addressTempRegister
, right
, 16);
895 m_assembler
.lsl(dataTempRegister
, dataTempRegister
, 16);
896 return branch32(cond
, dataTempRegister
, addressTempRegister
);
899 Jump
branch16(Condition cond
, BaseIndex left
, Imm32 right
)
901 // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
902 load16(left
, addressTempRegister
);
903 m_assembler
.lsl(addressTempRegister
, addressTempRegister
, 16);
904 return branch32(cond
, addressTempRegister
, Imm32(right
.m_value
<< 16));
907 Jump
branch8(Condition cond
, RegisterID left
, Imm32 right
)
909 compare32(left
, right
);
910 return Jump(makeBranch(cond
));
913 Jump
branch8(Condition cond
, Address left
, Imm32 right
)
915 // use addressTempRegister incase the branch8 we call uses dataTempRegister. :-/
916 load8(left
, addressTempRegister
);
917 return branch8(cond
, addressTempRegister
, right
);
920 Jump
branchTest32(Condition cond
, RegisterID reg
, RegisterID mask
)
922 ASSERT((cond
== Zero
) || (cond
== NonZero
));
923 m_assembler
.tst(reg
, mask
);
924 return Jump(makeBranch(cond
));
927 Jump
branchTest32(Condition cond
, RegisterID reg
, Imm32 mask
= Imm32(-1))
929 ASSERT((cond
== Zero
) || (cond
== NonZero
));
931 return Jump(makeBranch(cond
));
934 Jump
branchTest32(Condition cond
, Address address
, Imm32 mask
= Imm32(-1))
936 ASSERT((cond
== Zero
) || (cond
== NonZero
));
937 // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/
938 load32(address
, addressTempRegister
);
939 return branchTest32(cond
, addressTempRegister
, mask
);
942 Jump
branchTest32(Condition cond
, BaseIndex address
, Imm32 mask
= Imm32(-1))
944 ASSERT((cond
== Zero
) || (cond
== NonZero
));
945 // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/
946 load32(address
, addressTempRegister
);
947 return branchTest32(cond
, addressTempRegister
, mask
);
950 Jump
branchTest8(Condition cond
, RegisterID reg
, Imm32 mask
= Imm32(-1))
952 ASSERT((cond
== Zero
) || (cond
== NonZero
));
954 return Jump(makeBranch(cond
));
957 Jump
branchTest8(Condition cond
, Address address
, Imm32 mask
= Imm32(-1))
959 ASSERT((cond
== Zero
) || (cond
== NonZero
));
960 // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/
961 load8(address
, addressTempRegister
);
962 return branchTest8(cond
, addressTempRegister
, mask
);
967 return Jump(makeJump());
970 void jump(RegisterID target
)
972 m_assembler
.bx(target
);
975 // Address is a memory location containing the address to jump to
976 void jump(Address address
)
978 load32(address
, dataTempRegister
);
979 m_assembler
.bx(dataTempRegister
);
983 // Arithmetic control flow operations:
985 // This set of conditional branch operations branch based
986 // on the result of an arithmetic operation. The operation
987 // is performed as normal, storing the result.
989 // * jz operations branch if the result is zero.
990 // * jo operations branch if the (signed) arithmetic
991 // operation caused an overflow to occur.
993 Jump
branchAdd32(Condition cond
, RegisterID src
, RegisterID dest
)
995 ASSERT((cond
== Overflow
) || (cond
== Signed
) || (cond
== Zero
) || (cond
== NonZero
));
996 m_assembler
.add_S(dest
, dest
, src
);
997 return Jump(makeBranch(cond
));
1000 Jump
branchAdd32(Condition cond
, Imm32 imm
, RegisterID dest
)
1002 ASSERT((cond
== Overflow
) || (cond
== Signed
) || (cond
== Zero
) || (cond
== NonZero
));
1003 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(imm
.m_value
);
1004 if (armImm
.isValid())
1005 m_assembler
.add_S(dest
, dest
, armImm
);
1007 move(imm
, dataTempRegister
);
1008 m_assembler
.add_S(dest
, dest
, dataTempRegister
);
1010 return Jump(makeBranch(cond
));
1013 Jump
branchMul32(Condition cond
, RegisterID src
, RegisterID dest
)
1015 ASSERT_UNUSED(cond
, cond
== Overflow
);
1016 m_assembler
.smull(dest
, dataTempRegister
, dest
, src
);
1017 m_assembler
.asr(addressTempRegister
, dest
, 31);
1018 return branch32(NotEqual
, addressTempRegister
, dataTempRegister
);
1021 Jump
branchMul32(Condition cond
, Imm32 imm
, RegisterID src
, RegisterID dest
)
1023 ASSERT_UNUSED(cond
, cond
== Overflow
);
1024 move(imm
, dataTempRegister
);
1025 m_assembler
.smull(dest
, dataTempRegister
, src
, dataTempRegister
);
1026 m_assembler
.asr(addressTempRegister
, dest
, 31);
1027 return branch32(NotEqual
, addressTempRegister
, dataTempRegister
);
1030 Jump
branchOr32(Condition cond
, RegisterID src
, RegisterID dest
)
1032 ASSERT((cond
== Signed
) || (cond
== Zero
) || (cond
== NonZero
));
1033 m_assembler
.orr_S(dest
, dest
, src
);
1034 return Jump(makeBranch(cond
));
1037 Jump
branchSub32(Condition cond
, RegisterID src
, RegisterID dest
)
1039 ASSERT((cond
== Overflow
) || (cond
== Signed
) || (cond
== Zero
) || (cond
== NonZero
));
1040 m_assembler
.sub_S(dest
, dest
, src
);
1041 return Jump(makeBranch(cond
));
1044 Jump
branchSub32(Condition cond
, Imm32 imm
, RegisterID dest
)
1046 ASSERT((cond
== Overflow
) || (cond
== Signed
) || (cond
== Zero
) || (cond
== NonZero
));
1047 ARMThumbImmediate armImm
= ARMThumbImmediate::makeEncodedImm(imm
.m_value
);
1048 if (armImm
.isValid())
1049 m_assembler
.sub_S(dest
, dest
, armImm
);
1051 move(imm
, dataTempRegister
);
1052 m_assembler
.sub_S(dest
, dest
, dataTempRegister
);
1054 return Jump(makeBranch(cond
));
1058 // Miscellaneous operations:
1067 moveFixedWidthEncoding(Imm32(0), dataTempRegister
);
1068 return Call(m_assembler
.blx(dataTempRegister
), Call::LinkableNear
);
1073 moveFixedWidthEncoding(Imm32(0), dataTempRegister
);
1074 return Call(m_assembler
.blx(dataTempRegister
), Call::Linkable
);
1077 Call
call(RegisterID target
)
1079 return Call(m_assembler
.blx(target
), Call::None
);
1082 Call
call(Address address
)
1084 load32(address
, dataTempRegister
);
1085 return Call(m_assembler
.blx(dataTempRegister
), Call::None
);
1090 m_assembler
.bx(linkRegister
);
1093 void set32(Condition cond
, RegisterID left
, RegisterID right
, RegisterID dest
)
1095 m_assembler
.cmp(left
, right
);
1096 m_assembler
.it(armV7Condition(cond
), false);
1097 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(1));
1098 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(0));
1101 void set32(Condition cond
, Address left
, RegisterID right
, RegisterID dest
)
1103 load32(left
, dataTempRegister
);
1104 set32(cond
, dataTempRegister
, right
, dest
);
1107 void set32(Condition cond
, RegisterID left
, Imm32 right
, RegisterID dest
)
1109 compare32(left
, right
);
1110 m_assembler
.it(armV7Condition(cond
), false);
1111 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(1));
1112 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(0));
1115 void set8(Condition cond
, RegisterID left
, RegisterID right
, RegisterID dest
)
1117 set32(cond
, left
, right
, dest
);
1120 void set8(Condition cond
, Address left
, RegisterID right
, RegisterID dest
)
1122 set32(cond
, left
, right
, dest
);
1125 void set8(Condition cond
, RegisterID left
, Imm32 right
, RegisterID dest
)
1127 set32(cond
, left
, right
, dest
);
1131 // The mask should be optional... paerhaps the argument order should be
1132 // dest-src, operations always have a dest? ... possibly not true, considering
1133 // asm ops like test, or pseudo ops like pop().
1134 void setTest32(Condition cond
, Address address
, Imm32 mask
, RegisterID dest
)
1136 load32(address
, dataTempRegister
);
1137 test32(dataTempRegister
, mask
);
1138 m_assembler
.it(armV7Condition(cond
), false);
1139 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(1));
1140 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(0));
1143 void setTest8(Condition cond
, Address address
, Imm32 mask
, RegisterID dest
)
1145 load8(address
, dataTempRegister
);
1146 test32(dataTempRegister
, mask
);
1147 m_assembler
.it(armV7Condition(cond
), false);
1148 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(1));
1149 m_assembler
.mov(dest
, ARMThumbImmediate::makeUInt16(0));
1152 DataLabel32
moveWithPatch(Imm32 imm
, RegisterID dst
)
1154 moveFixedWidthEncoding(imm
, dst
);
1155 return DataLabel32(this);
1158 DataLabelPtr
moveWithPatch(ImmPtr imm
, RegisterID dst
)
1160 moveFixedWidthEncoding(Imm32(imm
), dst
);
1161 return DataLabelPtr(this);
1164 Jump
branchPtrWithPatch(Condition cond
, RegisterID left
, DataLabelPtr
& dataLabel
, ImmPtr initialRightValue
= ImmPtr(0))
1166 dataLabel
= moveWithPatch(initialRightValue
, dataTempRegister
);
1167 return branch32(cond
, left
, dataTempRegister
);
1170 Jump
branchPtrWithPatch(Condition cond
, Address left
, DataLabelPtr
& dataLabel
, ImmPtr initialRightValue
= ImmPtr(0))
1172 load32(left
, addressTempRegister
);
1173 dataLabel
= moveWithPatch(initialRightValue
, dataTempRegister
);
1174 return branch32(cond
, addressTempRegister
, dataTempRegister
);
1177 DataLabelPtr
storePtrWithPatch(ImmPtr initialValue
, ImplicitAddress address
)
1179 DataLabelPtr label
= moveWithPatch(initialValue
, dataTempRegister
);
1180 store32(dataTempRegister
, address
);
1183 DataLabelPtr
storePtrWithPatch(ImplicitAddress address
) { return storePtrWithPatch(ImmPtr(0), address
); }
1186 Call
tailRecursiveCall()
1188 // Like a normal call, but don't link.
1189 moveFixedWidthEncoding(Imm32(0), dataTempRegister
);
1190 return Call(m_assembler
.bx(dataTempRegister
), Call::Linkable
);
1193 Call
makeTailRecursiveCall(Jump oldJump
)
1196 return tailRecursiveCall();
1201 ARMv7Assembler::JmpSrc
makeJump()
1203 moveFixedWidthEncoding(Imm32(0), dataTempRegister
);
1204 return m_assembler
.bx(dataTempRegister
);
1207 ARMv7Assembler::JmpSrc
makeBranch(ARMv7Assembler::Condition cond
)
1209 m_assembler
.it(cond
, true, true);
1210 moveFixedWidthEncoding(Imm32(0), dataTempRegister
);
1211 return m_assembler
.bx(dataTempRegister
);
1213 ARMv7Assembler::JmpSrc
makeBranch(Condition cond
) { return makeBranch(armV7Condition(cond
)); }
1214 ARMv7Assembler::JmpSrc
makeBranch(DoubleCondition cond
) { return makeBranch(armV7Condition(cond
)); }
1216 ArmAddress
setupArmAddress(BaseIndex address
)
1218 if (address
.offset
) {
1219 ARMThumbImmediate imm
= ARMThumbImmediate::makeUInt12OrEncodedImm(address
.offset
);
1221 m_assembler
.add(addressTempRegister
, address
.base
, imm
);
1223 move(Imm32(address
.offset
), addressTempRegister
);
1224 m_assembler
.add(addressTempRegister
, addressTempRegister
, address
.base
);
1227 return ArmAddress(addressTempRegister
, address
.index
, address
.scale
);
1229 return ArmAddress(address
.base
, address
.index
, address
.scale
);
1232 ArmAddress
setupArmAddress(Address address
)
1234 if ((address
.offset
>= -0xff) && (address
.offset
<= 0xfff))
1235 return ArmAddress(address
.base
, address
.offset
);
1237 move(Imm32(address
.offset
), addressTempRegister
);
1238 return ArmAddress(address
.base
, addressTempRegister
);
1241 ArmAddress
setupArmAddress(ImplicitAddress address
)
1243 if ((address
.offset
>= -0xff) && (address
.offset
<= 0xfff))
1244 return ArmAddress(address
.base
, address
.offset
);
1246 move(Imm32(address
.offset
), addressTempRegister
);
1247 return ArmAddress(address
.base
, addressTempRegister
);
1250 RegisterID
makeBaseIndexBase(BaseIndex address
)
1252 if (!address
.offset
)
1253 return address
.base
;
1255 ARMThumbImmediate imm
= ARMThumbImmediate::makeUInt12OrEncodedImm(address
.offset
);
1257 m_assembler
.add(addressTempRegister
, address
.base
, imm
);
1259 move(Imm32(address
.offset
), addressTempRegister
);
1260 m_assembler
.add(addressTempRegister
, addressTempRegister
, address
.base
);
1263 return addressTempRegister
;
1266 void moveFixedWidthEncoding(Imm32 imm
, RegisterID dst
)
1268 uint32_t value
= imm
.m_value
;
1269 m_assembler
.movT3(dst
, ARMThumbImmediate::makeUInt16(value
& 0xffff));
1270 m_assembler
.movt(dst
, ARMThumbImmediate::makeUInt16(value
>> 16));
1273 ARMv7Assembler::Condition
armV7Condition(Condition cond
)
1275 return static_cast<ARMv7Assembler::Condition
>(cond
);
1278 ARMv7Assembler::Condition
armV7Condition(DoubleCondition cond
)
1280 return static_cast<ARMv7Assembler::Condition
>(cond
);
1284 friend class LinkBuffer
;
1285 friend class RepatchBuffer
;
1287 static void linkCall(void* code
, Call call
, FunctionPtr function
)
1289 ARMv7Assembler::linkCall(code
, call
.m_jmp
, function
.value());
1292 static void repatchCall(CodeLocationCall call
, CodeLocationLabel destination
)
1294 ARMv7Assembler::relinkCall(call
.dataLocation(), destination
.executableAddress());
1297 static void repatchCall(CodeLocationCall call
, FunctionPtr destination
)
1299 ARMv7Assembler::relinkCall(call
.dataLocation(), destination
.executableAddress());
1305 #endif // ENABLE(ASSEMBLER)
1307 #endif // MacroAssemblerARMv7_h