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