• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #ifndef MacroAssemblerARMv7_h
27 #define MacroAssemblerARMv7_h
28 
29 #include <wtf/Platform.h>
30 
31 #if ENABLE(ASSEMBLER)
32 
33 #include "ARMv7Assembler.h"
34 #include "AbstractMacroAssembler.h"
35 
36 namespace JSC {
37 
38 class MacroAssemblerARMv7 : public AbstractMacroAssembler<ARMv7Assembler> {
39     // FIXME: switch dataTempRegister & addressTempRegister, or possibly use r7?
40     //        - dTR is likely used more than aTR, and we'll get better instruction
41     //        encoding if it's in the low 8 registers.
42     static const ARMRegisters::RegisterID dataTempRegister = ARMRegisters::ip;
43     static const RegisterID addressTempRegister = ARMRegisters::r3;
44     static const FPRegisterID fpTempRegister = ARMRegisters::d7;
45 
46     struct ArmAddress {
47         enum AddressType {
48             HasOffset,
49             HasIndex,
50         } type;
51         RegisterID base;
52         union {
53             int32_t offset;
54             struct {
55                 RegisterID index;
56                 Scale scale;
57             };
58         } u;
59 
60         explicit ArmAddress(RegisterID base, int32_t offset = 0)
typeArmAddress61             : type(HasOffset)
62             , base(base)
63         {
64             u.offset = offset;
65         }
66 
67         explicit ArmAddress(RegisterID base, RegisterID index, Scale scale = TimesOne)
typeArmAddress68             : type(HasIndex)
69             , base(base)
70         {
71             u.index = index;
72             u.scale = scale;
73         }
74     };
75 
76 public:
77 
78     static const Scale ScalePtr = TimesFour;
79 
80     enum Condition {
81         Equal = ARMv7Assembler::ConditionEQ,
82         NotEqual = ARMv7Assembler::ConditionNE,
83         Above = ARMv7Assembler::ConditionHI,
84         AboveOrEqual = ARMv7Assembler::ConditionHS,
85         Below = ARMv7Assembler::ConditionLO,
86         BelowOrEqual = ARMv7Assembler::ConditionLS,
87         GreaterThan = ARMv7Assembler::ConditionGT,
88         GreaterThanOrEqual = ARMv7Assembler::ConditionGE,
89         LessThan = ARMv7Assembler::ConditionLT,
90         LessThanOrEqual = ARMv7Assembler::ConditionLE,
91         Overflow = ARMv7Assembler::ConditionVS,
92         Signed = ARMv7Assembler::ConditionMI,
93         Zero = ARMv7Assembler::ConditionEQ,
94         NonZero = ARMv7Assembler::ConditionNE
95     };
96     enum DoubleCondition {
97         // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
98         DoubleEqual = ARMv7Assembler::ConditionEQ,
99         DoubleNotEqual = ARMv7Assembler::ConditionVC, // Not the right flag! check for this & handle differently.
100         DoubleGreaterThan = ARMv7Assembler::ConditionGT,
101         DoubleGreaterThanOrEqual = ARMv7Assembler::ConditionGE,
102         DoubleLessThan = ARMv7Assembler::ConditionLO,
103         DoubleLessThanOrEqual = ARMv7Assembler::ConditionLS,
104         // If either operand is NaN, these conditions always evaluate to true.
105         DoubleEqualOrUnordered = ARMv7Assembler::ConditionVS, // Not the right flag! check for this & handle differently.
106         DoubleNotEqualOrUnordered = ARMv7Assembler::ConditionNE,
107         DoubleGreaterThanOrUnordered = ARMv7Assembler::ConditionHI,
108         DoubleGreaterThanOrEqualOrUnordered = ARMv7Assembler::ConditionHS,
109         DoubleLessThanOrUnordered = ARMv7Assembler::ConditionLT,
110         DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE,
111     };
112 
113     static const RegisterID stackPointerRegister = ARMRegisters::sp;
114     static const RegisterID linkRegister = ARMRegisters::lr;
115 
116     // Integer arithmetic operations:
117     //
118     // Operations are typically two operand - operation(source, srcDst)
119     // For many operations the source may be an Imm32, the srcDst operand
120     // may often be a memory location (explictly described using an Address
121     // object).
122 
add32(RegisterID src,RegisterID dest)123     void add32(RegisterID src, RegisterID dest)
124     {
125         m_assembler.add(dest, dest, src);
126     }
127 
add32(Imm32 imm,RegisterID dest)128     void add32(Imm32 imm, RegisterID dest)
129     {
130         add32(imm, dest, dest);
131     }
132 
add32(Imm32 imm,RegisterID src,RegisterID dest)133     void add32(Imm32 imm, RegisterID src, RegisterID dest)
134     {
135         ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value);
136         if (armImm.isValid())
137             m_assembler.add(dest, src, armImm);
138         else {
139             move(imm, dataTempRegister);
140             m_assembler.add(dest, src, dataTempRegister);
141         }
142     }
143 
add32(Imm32 imm,Address address)144     void add32(Imm32 imm, Address address)
145     {
146         load32(address, dataTempRegister);
147 
148         ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value);
149         if (armImm.isValid())
150             m_assembler.add(dataTempRegister, dataTempRegister, armImm);
151         else {
152             // Hrrrm, since dataTempRegister holds the data loaded,
153             // use addressTempRegister to hold the immediate.
154             move(imm, addressTempRegister);
155             m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister);
156         }
157 
158         store32(dataTempRegister, address);
159     }
160 
add32(Address src,RegisterID dest)161     void add32(Address src, RegisterID dest)
162     {
163         load32(src, dataTempRegister);
164         add32(dataTempRegister, dest);
165     }
166 
add32(Imm32 imm,AbsoluteAddress address)167     void add32(Imm32 imm, AbsoluteAddress address)
168     {
169         load32(address.m_ptr, dataTempRegister);
170 
171         ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value);
172         if (armImm.isValid())
173             m_assembler.add(dataTempRegister, dataTempRegister, armImm);
174         else {
175             // Hrrrm, since dataTempRegister holds the data loaded,
176             // use addressTempRegister to hold the immediate.
177             move(imm, addressTempRegister);
178             m_assembler.add(dataTempRegister, dataTempRegister, addressTempRegister);
179         }
180 
181         store32(dataTempRegister, address.m_ptr);
182     }
183 
and32(RegisterID src,RegisterID dest)184     void and32(RegisterID src, RegisterID dest)
185     {
186         m_assembler.ARM_and(dest, dest, src);
187     }
188 
and32(Imm32 imm,RegisterID dest)189     void and32(Imm32 imm, RegisterID dest)
190     {
191         ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value);
192         if (armImm.isValid())
193             m_assembler.ARM_and(dest, dest, armImm);
194         else {
195             move(imm, dataTempRegister);
196             m_assembler.ARM_and(dest, dest, dataTempRegister);
197         }
198     }
199 
lshift32(RegisterID shift_amount,RegisterID dest)200     void lshift32(RegisterID shift_amount, RegisterID dest)
201     {
202         // Clamp the shift to the range 0..31
203         ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f);
204         ASSERT(armImm.isValid());
205         m_assembler.ARM_and(dataTempRegister, shift_amount, armImm);
206 
207         m_assembler.lsl(dest, dest, dataTempRegister);
208     }
209 
lshift32(Imm32 imm,RegisterID dest)210     void lshift32(Imm32 imm, RegisterID dest)
211     {
212         m_assembler.lsl(dest, dest, imm.m_value & 0x1f);
213     }
214 
mul32(RegisterID src,RegisterID dest)215     void mul32(RegisterID src, RegisterID dest)
216     {
217         m_assembler.smull(dest, dataTempRegister, dest, src);
218     }
219 
mul32(Imm32 imm,RegisterID src,RegisterID dest)220     void mul32(Imm32 imm, RegisterID src, RegisterID dest)
221     {
222         move(imm, dataTempRegister);
223         m_assembler.smull(dest, dataTempRegister, src, dataTempRegister);
224     }
225 
not32(RegisterID srcDest)226     void not32(RegisterID srcDest)
227     {
228         m_assembler.mvn(srcDest, srcDest);
229     }
230 
or32(RegisterID src,RegisterID dest)231     void or32(RegisterID src, RegisterID dest)
232     {
233         m_assembler.orr(dest, dest, src);
234     }
235 
or32(Imm32 imm,RegisterID dest)236     void or32(Imm32 imm, RegisterID dest)
237     {
238         ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value);
239         if (armImm.isValid())
240             m_assembler.orr(dest, dest, armImm);
241         else {
242             move(imm, dataTempRegister);
243             m_assembler.orr(dest, dest, dataTempRegister);
244         }
245     }
246 
rshift32(RegisterID shift_amount,RegisterID dest)247     void rshift32(RegisterID shift_amount, RegisterID dest)
248     {
249         // Clamp the shift to the range 0..31
250         ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f);
251         ASSERT(armImm.isValid());
252         m_assembler.ARM_and(dataTempRegister, shift_amount, armImm);
253 
254         m_assembler.asr(dest, dest, dataTempRegister);
255     }
256 
rshift32(Imm32 imm,RegisterID dest)257     void rshift32(Imm32 imm, RegisterID dest)
258     {
259         m_assembler.asr(dest, dest, imm.m_value & 0x1f);
260     }
261 
sub32(RegisterID src,RegisterID dest)262     void sub32(RegisterID src, RegisterID dest)
263     {
264         m_assembler.sub(dest, dest, src);
265     }
266 
sub32(Imm32 imm,RegisterID dest)267     void sub32(Imm32 imm, RegisterID dest)
268     {
269         ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value);
270         if (armImm.isValid())
271             m_assembler.sub(dest, dest, armImm);
272         else {
273             move(imm, dataTempRegister);
274             m_assembler.sub(dest, dest, dataTempRegister);
275         }
276     }
277 
sub32(Imm32 imm,Address address)278     void sub32(Imm32 imm, Address address)
279     {
280         load32(address, dataTempRegister);
281 
282         ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value);
283         if (armImm.isValid())
284             m_assembler.sub(dataTempRegister, dataTempRegister, armImm);
285         else {
286             // Hrrrm, since dataTempRegister holds the data loaded,
287             // use addressTempRegister to hold the immediate.
288             move(imm, addressTempRegister);
289             m_assembler.sub(dataTempRegister, dataTempRegister, addressTempRegister);
290         }
291 
292         store32(dataTempRegister, address);
293     }
294 
sub32(Address src,RegisterID dest)295     void sub32(Address src, RegisterID dest)
296     {
297         load32(src, dataTempRegister);
298         sub32(dataTempRegister, dest);
299     }
300 
sub32(Imm32 imm,AbsoluteAddress address)301     void sub32(Imm32 imm, AbsoluteAddress address)
302     {
303         load32(address.m_ptr, dataTempRegister);
304 
305         ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12OrEncodedImm(imm.m_value);
306         if (armImm.isValid())
307             m_assembler.sub(dataTempRegister, dataTempRegister, armImm);
308         else {
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);
313         }
314 
315         store32(dataTempRegister, address.m_ptr);
316     }
317 
xor32(RegisterID src,RegisterID dest)318     void xor32(RegisterID src, RegisterID dest)
319     {
320         m_assembler.eor(dest, dest, src);
321     }
322 
xor32(Imm32 imm,RegisterID dest)323     void xor32(Imm32 imm, RegisterID dest)
324     {
325         ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value);
326         if (armImm.isValid())
327             m_assembler.eor(dest, dest, armImm);
328         else {
329             move(imm, dataTempRegister);
330             m_assembler.eor(dest, dest, dataTempRegister);
331         }
332     }
333 
334 
335     // Memory access operations:
336     //
337     // Loads are of the form load(address, destination) and stores of the form
338     // store(source, address).  The source for a store may be an Imm32.  Address
339     // operand objects to loads and store will be implicitly constructed if a
340     // register is passed.
341 
342 private:
load32(ArmAddress address,RegisterID dest)343     void load32(ArmAddress address, RegisterID dest)
344     {
345         if (address.type == ArmAddress::HasIndex)
346             m_assembler.ldr(dest, address.base, address.u.index, address.u.scale);
347         else if (address.u.offset >= 0) {
348             ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset);
349             ASSERT(armImm.isValid());
350             m_assembler.ldr(dest, address.base, armImm);
351         } else {
352             ASSERT(address.u.offset >= -255);
353             m_assembler.ldr(dest, address.base, address.u.offset, true, false);
354         }
355     }
356 
load16(ArmAddress address,RegisterID dest)357     void load16(ArmAddress address, RegisterID dest)
358     {
359         if (address.type == ArmAddress::HasIndex)
360             m_assembler.ldrh(dest, address.base, address.u.index, address.u.scale);
361         else if (address.u.offset >= 0) {
362             ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset);
363             ASSERT(armImm.isValid());
364             m_assembler.ldrh(dest, address.base, armImm);
365         } else {
366             ASSERT(address.u.offset >= -255);
367             m_assembler.ldrh(dest, address.base, address.u.offset, true, false);
368         }
369     }
370 
store32(RegisterID src,ArmAddress address)371     void store32(RegisterID src, ArmAddress address)
372     {
373         if (address.type == ArmAddress::HasIndex)
374             m_assembler.str(src, address.base, address.u.index, address.u.scale);
375         else if (address.u.offset >= 0) {
376             ARMThumbImmediate armImm = ARMThumbImmediate::makeUInt12(address.u.offset);
377             ASSERT(armImm.isValid());
378             m_assembler.str(src, address.base, armImm);
379         } else {
380             ASSERT(address.u.offset >= -255);
381             m_assembler.str(src, address.base, address.u.offset, true, false);
382         }
383     }
384 
385 public:
load32(ImplicitAddress address,RegisterID dest)386     void load32(ImplicitAddress address, RegisterID dest)
387     {
388         load32(setupArmAddress(address), dest);
389     }
390 
load32(BaseIndex address,RegisterID dest)391     void load32(BaseIndex address, RegisterID dest)
392     {
393         load32(setupArmAddress(address), dest);
394     }
395 
load32WithUnalignedHalfWords(BaseIndex address,RegisterID dest)396     void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest)
397     {
398         load32(setupArmAddress(address), dest);
399     }
400 
load32(void * address,RegisterID dest)401     void load32(void* address, RegisterID dest)
402     {
403         move(ImmPtr(address), addressTempRegister);
404         m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0));
405     }
406 
load32WithAddressOffsetPatch(Address address,RegisterID dest)407     DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest)
408     {
409         DataLabel32 label = moveWithPatch(Imm32(address.offset), dataTempRegister);
410         load32(ArmAddress(address.base, dataTempRegister), dest);
411         return label;
412     }
413 
loadPtrWithPatchToLEA(Address address,RegisterID dest)414     Label loadPtrWithPatchToLEA(Address address, RegisterID dest)
415     {
416         Label label(this);
417         moveFixedWidthEncoding(Imm32(address.offset), dataTempRegister);
418         load32(ArmAddress(address.base, dataTempRegister), dest);
419         return label;
420     }
421 
load16(BaseIndex address,RegisterID dest)422     void load16(BaseIndex address, RegisterID dest)
423     {
424         m_assembler.ldrh(dest, makeBaseIndexBase(address), address.index, address.scale);
425     }
426 
store32WithAddressOffsetPatch(RegisterID src,Address address)427     DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address)
428     {
429         DataLabel32 label = moveWithPatch(Imm32(address.offset), dataTempRegister);
430         store32(src, ArmAddress(address.base, dataTempRegister));
431         return label;
432     }
433 
store32(RegisterID src,ImplicitAddress address)434     void store32(RegisterID src, ImplicitAddress address)
435     {
436         store32(src, setupArmAddress(address));
437     }
438 
store32(RegisterID src,BaseIndex address)439     void store32(RegisterID src, BaseIndex address)
440     {
441         store32(src, setupArmAddress(address));
442     }
443 
store32(Imm32 imm,ImplicitAddress address)444     void store32(Imm32 imm, ImplicitAddress address)
445     {
446         move(imm, dataTempRegister);
447         store32(dataTempRegister, setupArmAddress(address));
448     }
449 
store32(RegisterID src,void * address)450     void store32(RegisterID src, void* address)
451     {
452         move(ImmPtr(address), addressTempRegister);
453         m_assembler.str(src, addressTempRegister, ARMThumbImmediate::makeUInt16(0));
454     }
455 
store32(Imm32 imm,void * address)456     void store32(Imm32 imm, void* address)
457     {
458         move(imm, dataTempRegister);
459         store32(dataTempRegister, address);
460     }
461 
462 
463     // Floating-point operations:
464 
supportsFloatingPoint()465     bool supportsFloatingPoint() const { return true; }
466     // On x86(_64) the MacroAssembler provides an interface to truncate a double to an integer.
467     // If a value is not representable as an integer, and possibly for some values that are,
468     // (on x86 INT_MIN, since this is indistinguishable from results for out-of-range/NaN input)
469     // a branch will  be taken.  It is not clear whether this interface will be well suited to
470     // other platforms.  On ARMv7 the hardware truncation operation produces multiple possible
471     // failure values (saturates to INT_MIN & INT_MAX, NaN reulsts in a value of 0).  This is a
472     // temporary solution while we work out what this interface should be.  Either we need to
473     // decide to make this interface work on all platforms, rework the interface to make it more
474     // generic, or decide that the MacroAssembler cannot practically be used to abstracted these
475     // operations, and make clients go directly to the m_assembler to plant truncation instructions.
476     // In short, FIXME:.
supportsFloatingPointTruncate()477     bool supportsFloatingPointTruncate() const { return false; }
478 
loadDouble(ImplicitAddress address,FPRegisterID dest)479     void loadDouble(ImplicitAddress address, FPRegisterID dest)
480     {
481         RegisterID base = address.base;
482         int32_t offset = address.offset;
483 
484         // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2.
485         if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) {
486             add32(Imm32(offset), base, addressTempRegister);
487             base = addressTempRegister;
488             offset = 0;
489         }
490 
491         m_assembler.vldr(dest, base, offset);
492     }
493 
storeDouble(FPRegisterID src,ImplicitAddress address)494     void storeDouble(FPRegisterID src, ImplicitAddress address)
495     {
496         RegisterID base = address.base;
497         int32_t offset = address.offset;
498 
499         // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2.
500         if ((offset & 3) || (offset > (255 * 4)) || (offset < -(255 * 4))) {
501             add32(Imm32(offset), base, addressTempRegister);
502             base = addressTempRegister;
503             offset = 0;
504         }
505 
506         m_assembler.vstr(src, base, offset);
507     }
508 
addDouble(FPRegisterID src,FPRegisterID dest)509     void addDouble(FPRegisterID src, FPRegisterID dest)
510     {
511         m_assembler.vadd_F64(dest, dest, src);
512     }
513 
addDouble(Address src,FPRegisterID dest)514     void addDouble(Address src, FPRegisterID dest)
515     {
516         loadDouble(src, fpTempRegister);
517         addDouble(fpTempRegister, dest);
518     }
519 
subDouble(FPRegisterID src,FPRegisterID dest)520     void subDouble(FPRegisterID src, FPRegisterID dest)
521     {
522         m_assembler.vsub_F64(dest, dest, src);
523     }
524 
subDouble(Address src,FPRegisterID dest)525     void subDouble(Address src, FPRegisterID dest)
526     {
527         loadDouble(src, fpTempRegister);
528         subDouble(fpTempRegister, dest);
529     }
530 
mulDouble(FPRegisterID src,FPRegisterID dest)531     void mulDouble(FPRegisterID src, FPRegisterID dest)
532     {
533         m_assembler.vmul_F64(dest, dest, src);
534     }
535 
mulDouble(Address src,FPRegisterID dest)536     void mulDouble(Address src, FPRegisterID dest)
537     {
538         loadDouble(src, fpTempRegister);
539         mulDouble(fpTempRegister, dest);
540     }
541 
convertInt32ToDouble(RegisterID src,FPRegisterID dest)542     void convertInt32ToDouble(RegisterID src, FPRegisterID dest)
543     {
544         m_assembler.vmov(fpTempRegister, src);
545         m_assembler.vcvt_F64_S32(dest, fpTempRegister);
546     }
547 
branchDouble(DoubleCondition cond,FPRegisterID left,FPRegisterID right)548     Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right)
549     {
550         m_assembler.vcmp_F64(left, right);
551         m_assembler.vmrs_APSR_nzcv_FPSCR();
552 
553         if (cond == DoubleNotEqual) {
554             // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump.
555             Jump unordered = makeBranch(ARMv7Assembler::ConditionVS);
556             Jump result = makeBranch(ARMv7Assembler::ConditionNE);
557             unordered.link(this);
558             return result;
559         }
560         if (cond == DoubleEqualOrUnordered) {
561             Jump unordered = makeBranch(ARMv7Assembler::ConditionVS);
562             Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE);
563             unordered.link(this);
564             // We get here if either unordered, or equal.
565             Jump result = makeJump();
566             notEqual.link(this);
567             return result;
568         }
569         return makeBranch(cond);
570     }
571 
branchTruncateDoubleToInt32(FPRegisterID,RegisterID)572     Jump branchTruncateDoubleToInt32(FPRegisterID, RegisterID)
573     {
574         ASSERT_NOT_REACHED();
575         return jump();
576     }
577 
578 
579     // Stack manipulation operations:
580     //
581     // The ABI is assumed to provide a stack abstraction to memory,
582     // containing machine word sized units of data.  Push and pop
583     // operations add and remove a single register sized unit of data
584     // to or from the stack.  Peek and poke operations read or write
585     // values on the stack, without moving the current stack position.
586 
pop(RegisterID dest)587     void pop(RegisterID dest)
588     {
589         // store postindexed with writeback
590         m_assembler.ldr(dest, ARMRegisters::sp, sizeof(void*), false, true);
591     }
592 
push(RegisterID src)593     void push(RegisterID src)
594     {
595         // store preindexed with writeback
596         m_assembler.str(src, ARMRegisters::sp, -sizeof(void*), true, true);
597     }
598 
push(Address address)599     void push(Address address)
600     {
601         load32(address, dataTempRegister);
602         push(dataTempRegister);
603     }
604 
push(Imm32 imm)605     void push(Imm32 imm)
606     {
607         move(imm, dataTempRegister);
608         push(dataTempRegister);
609     }
610 
611     // Register move operations:
612     //
613     // Move values in registers.
614 
move(Imm32 imm,RegisterID dest)615     void move(Imm32 imm, RegisterID dest)
616     {
617         uint32_t value = imm.m_value;
618 
619         if (imm.m_isPointer)
620             moveFixedWidthEncoding(imm, dest);
621         else {
622             ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(value);
623 
624             if (armImm.isValid())
625                 m_assembler.mov(dest, armImm);
626             else if ((armImm = ARMThumbImmediate::makeEncodedImm(~value)).isValid())
627                 m_assembler.mvn(dest, armImm);
628             else {
629                 m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(value));
630                 if (value & 0xffff0000)
631                     m_assembler.movt(dest, ARMThumbImmediate::makeUInt16(value >> 16));
632             }
633         }
634     }
635 
move(RegisterID src,RegisterID dest)636     void move(RegisterID src, RegisterID dest)
637     {
638         m_assembler.mov(dest, src);
639     }
640 
move(ImmPtr imm,RegisterID dest)641     void move(ImmPtr imm, RegisterID dest)
642     {
643         move(Imm32(imm), dest);
644     }
645 
swap(RegisterID reg1,RegisterID reg2)646     void swap(RegisterID reg1, RegisterID reg2)
647     {
648         move(reg1, dataTempRegister);
649         move(reg2, reg1);
650         move(dataTempRegister, reg2);
651     }
652 
signExtend32ToPtr(RegisterID src,RegisterID dest)653     void signExtend32ToPtr(RegisterID src, RegisterID dest)
654     {
655         if (src != dest)
656             move(src, dest);
657     }
658 
zeroExtend32ToPtr(RegisterID src,RegisterID dest)659     void zeroExtend32ToPtr(RegisterID src, RegisterID dest)
660     {
661         if (src != dest)
662             move(src, dest);
663     }
664 
665 
666     // Forwards / external control flow operations:
667     //
668     // This set of jump and conditional branch operations return a Jump
669     // object which may linked at a later point, allow forwards jump,
670     // or jumps that will require external linkage (after the code has been
671     // relocated).
672     //
673     // For branches, signed <, >, <= and >= are denoted as l, g, le, and ge
674     // respecitvely, for unsigned comparisons the names b, a, be, and ae are
675     // used (representing the names 'below' and 'above').
676     //
677     // Operands to the comparision are provided in the expected order, e.g.
678     // jle32(reg1, Imm32(5)) will branch if the value held in reg1, when
679     // treated as a signed 32bit value, is less than or equal to 5.
680     //
681     // jz and jnz test whether the first operand is equal to zero, and take
682     // an optional second operand of a mask under which to perform the test.
683 private:
684 
685     // Should we be using TEQ for equal/not-equal?
compare32(RegisterID left,Imm32 right)686     void compare32(RegisterID left, Imm32 right)
687     {
688         int32_t imm = right.m_value;
689         if (!imm)
690             m_assembler.tst(left, left);
691         else {
692             ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm);
693             if (armImm.isValid())
694                 m_assembler.cmp(left, armImm);
695             if ((armImm = ARMThumbImmediate::makeEncodedImm(-imm)).isValid())
696                 m_assembler.cmn(left, armImm);
697             else {
698                 move(Imm32(imm), dataTempRegister);
699                 m_assembler.cmp(left, dataTempRegister);
700             }
701         }
702     }
703 
test32(RegisterID reg,Imm32 mask)704     void test32(RegisterID reg, Imm32 mask)
705     {
706         int32_t imm = mask.m_value;
707 
708         if (imm == -1)
709             m_assembler.tst(reg, reg);
710         else {
711             ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm);
712             if (armImm.isValid())
713                 m_assembler.tst(reg, armImm);
714             else {
715                 move(mask, dataTempRegister);
716                 m_assembler.tst(reg, dataTempRegister);
717             }
718         }
719     }
720 
721 public:
branch32(Condition cond,RegisterID left,RegisterID right)722     Jump branch32(Condition cond, RegisterID left, RegisterID right)
723     {
724         m_assembler.cmp(left, right);
725         return Jump(makeBranch(cond));
726     }
727 
branch32(Condition cond,RegisterID left,Imm32 right)728     Jump branch32(Condition cond, RegisterID left, Imm32 right)
729     {
730         compare32(left, right);
731         return Jump(makeBranch(cond));
732     }
733 
branch32(Condition cond,RegisterID left,Address right)734     Jump branch32(Condition cond, RegisterID left, Address right)
735     {
736         load32(right, dataTempRegister);
737         return branch32(cond, left, dataTempRegister);
738     }
739 
branch32(Condition cond,Address left,RegisterID right)740     Jump branch32(Condition cond, Address left, RegisterID right)
741     {
742         load32(left, dataTempRegister);
743         return branch32(cond, dataTempRegister, right);
744     }
745 
branch32(Condition cond,Address left,Imm32 right)746     Jump branch32(Condition cond, Address left, Imm32 right)
747     {
748         // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
749         load32(left, addressTempRegister);
750         return branch32(cond, addressTempRegister, right);
751     }
752 
branch32(Condition cond,BaseIndex left,Imm32 right)753     Jump branch32(Condition cond, BaseIndex left, Imm32 right)
754     {
755         // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
756         load32(left, addressTempRegister);
757         return branch32(cond, addressTempRegister, right);
758     }
759 
branch32WithUnalignedHalfWords(Condition cond,BaseIndex left,Imm32 right)760     Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, Imm32 right)
761     {
762         // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
763         load32WithUnalignedHalfWords(left, addressTempRegister);
764         return branch32(cond, addressTempRegister, right);
765     }
766 
branch32(Condition cond,AbsoluteAddress left,RegisterID right)767     Jump branch32(Condition cond, AbsoluteAddress left, RegisterID right)
768     {
769         load32(left.m_ptr, dataTempRegister);
770         return branch32(cond, dataTempRegister, right);
771     }
772 
branch32(Condition cond,AbsoluteAddress left,Imm32 right)773     Jump branch32(Condition cond, AbsoluteAddress left, Imm32 right)
774     {
775         // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
776         load32(left.m_ptr, addressTempRegister);
777         return branch32(cond, addressTempRegister, right);
778     }
779 
branch16(Condition cond,BaseIndex left,RegisterID right)780     Jump branch16(Condition cond, BaseIndex left, RegisterID right)
781     {
782         load16(left, dataTempRegister);
783         m_assembler.lsl(addressTempRegister, right, 16);
784         m_assembler.lsl(dataTempRegister, dataTempRegister, 16);
785         return branch32(cond, dataTempRegister, addressTempRegister);
786     }
787 
branch16(Condition cond,BaseIndex left,Imm32 right)788     Jump branch16(Condition cond, BaseIndex left, Imm32 right)
789     {
790         // use addressTempRegister incase the branch32 we call uses dataTempRegister. :-/
791         load16(left, addressTempRegister);
792         m_assembler.lsl(addressTempRegister, addressTempRegister, 16);
793         return branch32(cond, addressTempRegister, Imm32(right.m_value << 16));
794     }
795 
branchTest32(Condition cond,RegisterID reg,RegisterID mask)796     Jump branchTest32(Condition cond, RegisterID reg, RegisterID mask)
797     {
798         ASSERT((cond == Zero) || (cond == NonZero));
799         m_assembler.tst(reg, mask);
800         return Jump(makeBranch(cond));
801     }
802 
803     Jump branchTest32(Condition cond, RegisterID reg, Imm32 mask = Imm32(-1))
804     {
805         ASSERT((cond == Zero) || (cond == NonZero));
806         test32(reg, mask);
807         return Jump(makeBranch(cond));
808     }
809 
810     Jump branchTest32(Condition cond, Address address, Imm32 mask = Imm32(-1))
811     {
812         ASSERT((cond == Zero) || (cond == NonZero));
813         // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/
814         load32(address, addressTempRegister);
815         return branchTest32(cond, addressTempRegister, mask);
816     }
817 
818     Jump branchTest32(Condition cond, BaseIndex address, Imm32 mask = Imm32(-1))
819     {
820         ASSERT((cond == Zero) || (cond == NonZero));
821         // use addressTempRegister incase the branchTest32 we call uses dataTempRegister. :-/
822         load32(address, addressTempRegister);
823         return branchTest32(cond, addressTempRegister, mask);
824     }
825 
jump()826     Jump jump()
827     {
828         return Jump(makeJump());
829     }
830 
jump(RegisterID target)831     void jump(RegisterID target)
832     {
833         m_assembler.bx(target);
834     }
835 
836     // Address is a memory location containing the address to jump to
jump(Address address)837     void jump(Address address)
838     {
839         load32(address, dataTempRegister);
840         m_assembler.bx(dataTempRegister);
841     }
842 
843 
844     // Arithmetic control flow operations:
845     //
846     // This set of conditional branch operations branch based
847     // on the result of an arithmetic operation.  The operation
848     // is performed as normal, storing the result.
849     //
850     // * jz operations branch if the result is zero.
851     // * jo operations branch if the (signed) arithmetic
852     //   operation caused an overflow to occur.
853 
branchAdd32(Condition cond,RegisterID src,RegisterID dest)854     Jump branchAdd32(Condition cond, RegisterID src, RegisterID dest)
855     {
856         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
857         m_assembler.add_S(dest, dest, src);
858         return Jump(makeBranch(cond));
859     }
860 
branchAdd32(Condition cond,Imm32 imm,RegisterID dest)861     Jump branchAdd32(Condition cond, Imm32 imm, RegisterID dest)
862     {
863         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
864         ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value);
865         if (armImm.isValid())
866             m_assembler.add_S(dest, dest, armImm);
867         else {
868             move(imm, dataTempRegister);
869             m_assembler.add_S(dest, dest, dataTempRegister);
870         }
871         return Jump(makeBranch(cond));
872     }
873 
branchMul32(Condition cond,RegisterID src,RegisterID dest)874     Jump branchMul32(Condition cond, RegisterID src, RegisterID dest)
875     {
876         ASSERT(cond == Overflow);
877         m_assembler.smull(dest, dataTempRegister, dest, src);
878         m_assembler.asr(addressTempRegister, dest, 31);
879         return branch32(NotEqual, addressTempRegister, dataTempRegister);
880     }
881 
branchMul32(Condition cond,Imm32 imm,RegisterID src,RegisterID dest)882     Jump branchMul32(Condition cond, Imm32 imm, RegisterID src, RegisterID dest)
883     {
884         ASSERT(cond == Overflow);
885         move(imm, dataTempRegister);
886         m_assembler.smull(dest, dataTempRegister, src, dataTempRegister);
887         m_assembler.asr(addressTempRegister, dest, 31);
888         return branch32(NotEqual, addressTempRegister, dataTempRegister);
889     }
890 
branchSub32(Condition cond,RegisterID src,RegisterID dest)891     Jump branchSub32(Condition cond, RegisterID src, RegisterID dest)
892     {
893         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
894         m_assembler.sub_S(dest, dest, src);
895         return Jump(makeBranch(cond));
896     }
897 
branchSub32(Condition cond,Imm32 imm,RegisterID dest)898     Jump branchSub32(Condition cond, Imm32 imm, RegisterID dest)
899     {
900         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
901         ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(imm.m_value);
902         if (armImm.isValid())
903             m_assembler.sub_S(dest, dest, armImm);
904         else {
905             move(imm, dataTempRegister);
906             m_assembler.sub_S(dest, dest, dataTempRegister);
907         }
908         return Jump(makeBranch(cond));
909     }
910 
911 
912     // Miscellaneous operations:
913 
breakpoint()914     void breakpoint()
915     {
916         m_assembler.bkpt();
917     }
918 
nearCall()919     Call nearCall()
920     {
921         moveFixedWidthEncoding(Imm32(0), dataTempRegister);
922         return Call(m_assembler.blx(dataTempRegister), Call::LinkableNear);
923     }
924 
call()925     Call call()
926     {
927         moveFixedWidthEncoding(Imm32(0), dataTempRegister);
928         return Call(m_assembler.blx(dataTempRegister), Call::Linkable);
929     }
930 
call(RegisterID target)931     Call call(RegisterID target)
932     {
933         return Call(m_assembler.blx(target), Call::None);
934     }
935 
call(Address address)936     Call call(Address address)
937     {
938         load32(address, dataTempRegister);
939         return Call(m_assembler.blx(dataTempRegister), Call::None);
940     }
941 
ret()942     void ret()
943     {
944         m_assembler.bx(linkRegister);
945     }
946 
set32(Condition cond,RegisterID left,RegisterID right,RegisterID dest)947     void set32(Condition cond, RegisterID left, RegisterID right, RegisterID dest)
948     {
949         m_assembler.cmp(left, right);
950         m_assembler.it(armV7Condition(cond), false);
951         m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1));
952         m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0));
953     }
954 
set32(Condition cond,RegisterID left,Imm32 right,RegisterID dest)955     void set32(Condition cond, RegisterID left, Imm32 right, RegisterID dest)
956     {
957         compare32(left, right);
958         m_assembler.it(armV7Condition(cond), false);
959         m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1));
960         m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0));
961     }
962 
963     // FIXME:
964     // The mask should be optional... paerhaps the argument order should be
965     // dest-src, operations always have a dest? ... possibly not true, considering
966     // asm ops like test, or pseudo ops like pop().
setTest32(Condition cond,Address address,Imm32 mask,RegisterID dest)967     void setTest32(Condition cond, Address address, Imm32 mask, RegisterID dest)
968     {
969         load32(address, dataTempRegister);
970         test32(dataTempRegister, mask);
971         m_assembler.it(armV7Condition(cond), false);
972         m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(1));
973         m_assembler.mov(dest, ARMThumbImmediate::makeUInt16(0));
974     }
975 
976 
moveWithPatch(Imm32 imm,RegisterID dst)977     DataLabel32 moveWithPatch(Imm32 imm, RegisterID dst)
978     {
979         moveFixedWidthEncoding(imm, dst);
980         return DataLabel32(this);
981     }
982 
moveWithPatch(ImmPtr imm,RegisterID dst)983     DataLabelPtr moveWithPatch(ImmPtr imm, RegisterID dst)
984     {
985         moveFixedWidthEncoding(Imm32(imm), dst);
986         return DataLabelPtr(this);
987     }
988 
989     Jump branchPtrWithPatch(Condition cond, RegisterID left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0))
990     {
991         dataLabel = moveWithPatch(initialRightValue, dataTempRegister);
992         return branch32(cond, left, dataTempRegister);
993     }
994 
995     Jump branchPtrWithPatch(Condition cond, Address left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0))
996     {
997         load32(left, addressTempRegister);
998         dataLabel = moveWithPatch(initialRightValue, dataTempRegister);
999         return branch32(cond, addressTempRegister, dataTempRegister);
1000     }
1001 
storePtrWithPatch(ImmPtr initialValue,ImplicitAddress address)1002     DataLabelPtr storePtrWithPatch(ImmPtr initialValue, ImplicitAddress address)
1003     {
1004         DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister);
1005         store32(dataTempRegister, address);
1006         return label;
1007     }
storePtrWithPatch(ImplicitAddress address)1008     DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(ImmPtr(0), address); }
1009 
1010 
tailRecursiveCall()1011     Call tailRecursiveCall()
1012     {
1013         // Like a normal call, but don't link.
1014         moveFixedWidthEncoding(Imm32(0), dataTempRegister);
1015         return Call(m_assembler.bx(dataTempRegister), Call::Linkable);
1016     }
1017 
makeTailRecursiveCall(Jump oldJump)1018     Call makeTailRecursiveCall(Jump oldJump)
1019     {
1020         oldJump.link(this);
1021         return tailRecursiveCall();
1022     }
1023 
1024 
1025 protected:
makeJump()1026     ARMv7Assembler::JmpSrc makeJump()
1027     {
1028         moveFixedWidthEncoding(Imm32(0), dataTempRegister);
1029         return m_assembler.bx(dataTempRegister);
1030     }
1031 
makeBranch(ARMv7Assembler::Condition cond)1032     ARMv7Assembler::JmpSrc makeBranch(ARMv7Assembler::Condition cond)
1033     {
1034         m_assembler.it(cond, true, true);
1035         moveFixedWidthEncoding(Imm32(0), dataTempRegister);
1036         return m_assembler.bx(dataTempRegister);
1037     }
makeBranch(Condition cond)1038     ARMv7Assembler::JmpSrc makeBranch(Condition cond) { return makeBranch(armV7Condition(cond)); }
makeBranch(DoubleCondition cond)1039     ARMv7Assembler::JmpSrc makeBranch(DoubleCondition cond) { return makeBranch(armV7Condition(cond)); }
1040 
setupArmAddress(BaseIndex address)1041     ArmAddress setupArmAddress(BaseIndex address)
1042     {
1043         if (address.offset) {
1044             ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset);
1045             if (imm.isValid())
1046                 m_assembler.add(addressTempRegister, address.base, imm);
1047             else {
1048                 move(Imm32(address.offset), addressTempRegister);
1049                 m_assembler.add(addressTempRegister, addressTempRegister, address.base);
1050             }
1051 
1052             return ArmAddress(addressTempRegister, address.index, address.scale);
1053         } else
1054             return ArmAddress(address.base, address.index, address.scale);
1055     }
1056 
setupArmAddress(Address address)1057     ArmAddress setupArmAddress(Address address)
1058     {
1059         if ((address.offset >= -0xff) && (address.offset <= 0xfff))
1060             return ArmAddress(address.base, address.offset);
1061 
1062         move(Imm32(address.offset), addressTempRegister);
1063         return ArmAddress(address.base, addressTempRegister);
1064     }
1065 
setupArmAddress(ImplicitAddress address)1066     ArmAddress setupArmAddress(ImplicitAddress address)
1067     {
1068         if ((address.offset >= -0xff) && (address.offset <= 0xfff))
1069             return ArmAddress(address.base, address.offset);
1070 
1071         move(Imm32(address.offset), addressTempRegister);
1072         return ArmAddress(address.base, addressTempRegister);
1073     }
1074 
makeBaseIndexBase(BaseIndex address)1075     RegisterID makeBaseIndexBase(BaseIndex address)
1076     {
1077         if (!address.offset)
1078             return address.base;
1079 
1080         ARMThumbImmediate imm = ARMThumbImmediate::makeUInt12OrEncodedImm(address.offset);
1081         if (imm.isValid())
1082             m_assembler.add(addressTempRegister, address.base, imm);
1083         else {
1084             move(Imm32(address.offset), addressTempRegister);
1085             m_assembler.add(addressTempRegister, addressTempRegister, address.base);
1086         }
1087 
1088         return addressTempRegister;
1089     }
1090 
moveFixedWidthEncoding(Imm32 imm,RegisterID dst)1091     void moveFixedWidthEncoding(Imm32 imm, RegisterID dst)
1092     {
1093         uint32_t value = imm.m_value;
1094         m_assembler.movT3(dst, ARMThumbImmediate::makeUInt16(value & 0xffff));
1095         m_assembler.movt(dst, ARMThumbImmediate::makeUInt16(value >> 16));
1096     }
1097 
armV7Condition(Condition cond)1098     ARMv7Assembler::Condition armV7Condition(Condition cond)
1099     {
1100         return static_cast<ARMv7Assembler::Condition>(cond);
1101     }
1102 
armV7Condition(DoubleCondition cond)1103     ARMv7Assembler::Condition armV7Condition(DoubleCondition cond)
1104     {
1105         return static_cast<ARMv7Assembler::Condition>(cond);
1106     }
1107 
1108 private:
1109     friend class LinkBuffer;
1110     friend class RepatchBuffer;
1111 
linkCall(void * code,Call call,FunctionPtr function)1112     static void linkCall(void* code, Call call, FunctionPtr function)
1113     {
1114         ARMv7Assembler::linkCall(code, call.m_jmp, function.value());
1115     }
1116 
repatchCall(CodeLocationCall call,CodeLocationLabel destination)1117     static void repatchCall(CodeLocationCall call, CodeLocationLabel destination)
1118     {
1119         ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress());
1120     }
1121 
repatchCall(CodeLocationCall call,FunctionPtr destination)1122     static void repatchCall(CodeLocationCall call, FunctionPtr destination)
1123     {
1124         ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress());
1125     }
1126 };
1127 
1128 } // namespace JSC
1129 
1130 #endif // ENABLE(ASSEMBLER)
1131 
1132 #endif // MacroAssemblerARMv7_h
1133