Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / assembler / assembler / MacroAssemblerARM.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) 2008 Apple Inc.
6  * Copyright (C) 2009, 2010 University of Szeged
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  * 
30  * ***** END LICENSE BLOCK ***** */
31
32 #ifndef MacroAssemblerARM_h
33 #define MacroAssemblerARM_h
34
35 #include "assembler/wtf/Platform.h"
36
37 #if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL
38
39 #include "ARMAssembler.h"
40 #include "AbstractMacroAssembler.h"
41
42 namespace JSC {
43
44 class MacroAssemblerARM : public AbstractMacroAssembler<ARMAssembler> {
45     static const int DoubleConditionMask = 0x0f;
46     static const int DoubleConditionBitSpecial = 0x10;
47     COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes);
48 public:
49     enum Condition {
50         Equal = ARMAssembler::EQ,
51         NotEqual = ARMAssembler::NE,
52         Above = ARMAssembler::HI,
53         AboveOrEqual = ARMAssembler::CS,
54         Below = ARMAssembler::CC,
55         BelowOrEqual = ARMAssembler::LS,
56         GreaterThan = ARMAssembler::GT,
57         GreaterThanOrEqual = ARMAssembler::GE,
58         LessThan = ARMAssembler::LT,
59         LessThanOrEqual = ARMAssembler::LE,
60         Overflow = ARMAssembler::VS,
61         Signed = ARMAssembler::MI,
62         Zero = ARMAssembler::EQ,
63         NonZero = ARMAssembler::NE
64     };
65
66     enum DoubleCondition {
67         // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
68         DoubleEqual = ARMAssembler::EQ,
69         DoubleNotEqual = ARMAssembler::NE | DoubleConditionBitSpecial,
70         DoubleGreaterThan = ARMAssembler::GT,
71         DoubleGreaterThanOrEqual = ARMAssembler::GE,
72         DoubleLessThan = ARMAssembler::CC,
73         DoubleLessThanOrEqual = ARMAssembler::LS,
74         // If either operand is NaN, these conditions always evaluate to true.
75         DoubleEqualOrUnordered = ARMAssembler::EQ | DoubleConditionBitSpecial,
76         DoubleNotEqualOrUnordered = ARMAssembler::NE,
77         DoubleGreaterThanOrUnordered = ARMAssembler::HI,
78         DoubleGreaterThanOrEqualOrUnordered = ARMAssembler::CS,
79         DoubleLessThanOrUnordered = ARMAssembler::LT,
80         DoubleLessThanOrEqualOrUnordered = ARMAssembler::LE
81     };
82
83     static const RegisterID stackPointerRegister = ARMRegisters::sp;
84     static const RegisterID linkRegister = ARMRegisters::lr;
85
86     static const Scale ScalePtr = TimesFour;
87     static const unsigned int TotalRegisters = 16;
88
89     void add32(RegisterID src, RegisterID dest)
90     {
91         m_assembler.adds_r(dest, dest, src);
92     }
93
94     void add32(Imm32 imm, Address address)
95     {
96         load32(address, ARMRegisters::S1);
97         add32(imm, ARMRegisters::S1);
98         store32(ARMRegisters::S1, address);
99     }
100
101     void add32(Imm32 imm, RegisterID dest)
102     {
103         m_assembler.adds_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
104     }
105
106     void add32(Address src, RegisterID dest)
107     {
108         load32(src, ARMRegisters::S1);
109         add32(ARMRegisters::S1, dest);
110     }
111
112     void and32(Address src, RegisterID dest)
113     {
114         load32(src, ARMRegisters::S1);
115         and32(ARMRegisters::S1, dest);
116     }
117
118     void and32(RegisterID src, RegisterID dest)
119     {
120         m_assembler.ands_r(dest, dest, src);
121     }
122
123     void and32(Imm32 imm, RegisterID dest)
124     {
125         ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true);
126         if (w & ARMAssembler::OP2_INV_IMM)
127             m_assembler.bics_r(dest, dest, w & ~ARMAssembler::OP2_INV_IMM);
128         else
129             m_assembler.ands_r(dest, dest, w);
130     }
131
132     void lshift32(RegisterID shift_amount, RegisterID dest)
133     {
134         ARMWord w = ARMAssembler::getOp2(0x1f);
135         ASSERT(w != ARMAssembler::INVALID_IMM);
136         m_assembler.and_r(ARMRegisters::S0, shift_amount, w);
137
138         m_assembler.movs_r(dest, m_assembler.lsl_r(dest, ARMRegisters::S0));
139     }
140
141     void lshift32(Imm32 imm, RegisterID dest)
142     {
143         m_assembler.movs_r(dest, m_assembler.lsl(dest, imm.m_value & 0x1f));
144     }
145
146     void mul32(RegisterID src, RegisterID dest)
147     {
148         if (src == dest) {
149             move(src, ARMRegisters::S0);
150             src = ARMRegisters::S0;
151         }
152         m_assembler.muls_r(dest, dest, src);
153     }
154
155     void mul32(Imm32 imm, RegisterID src, RegisterID dest)
156     {
157         move(imm, ARMRegisters::S0);
158         m_assembler.muls_r(dest, src, ARMRegisters::S0);
159     }
160
161     void neg32(RegisterID srcDest)
162     {
163         m_assembler.rsbs_r(srcDest, srcDest, ARMAssembler::getOp2(0));
164     }
165
166     void not32(RegisterID dest)
167     {
168         m_assembler.mvns_r(dest, dest);
169     }
170
171     void or32(RegisterID src, RegisterID dest)
172     {
173         m_assembler.orrs_r(dest, dest, src);
174     }
175
176     void or32(Imm32 imm, RegisterID dest)
177     {
178         m_assembler.orrs_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
179     }
180
181     void rshift32(RegisterID shift_amount, RegisterID dest)
182     {
183         ARMWord w = ARMAssembler::getOp2(0x1f);
184         ASSERT(w != ARMAssembler::INVALID_IMM);
185         m_assembler.and_r(ARMRegisters::S0, shift_amount, w);
186
187         m_assembler.movs_r(dest, m_assembler.asr_r(dest, ARMRegisters::S0));
188     }
189
190     void rshift32(Imm32 imm, RegisterID dest)
191     {
192         m_assembler.movs_r(dest, m_assembler.asr(dest, imm.m_value & 0x1f));
193     }
194     
195     void urshift32(RegisterID shift_amount, RegisterID dest)
196     {
197         ARMWord w = ARMAssembler::getOp2(0x1f);
198         ASSERT(w != ARMAssembler::INVALID_IMM);
199         m_assembler.and_r(ARMRegisters::S0, shift_amount, w);
200         
201         m_assembler.movs_r(dest, m_assembler.lsr_r(dest, ARMRegisters::S0));
202     }
203     
204     void urshift32(Imm32 imm, RegisterID dest)
205     {
206         m_assembler.movs_r(dest, m_assembler.lsr(dest, imm.m_value & 0x1f));
207     }
208
209     void sub32(RegisterID src, RegisterID dest)
210     {
211         m_assembler.subs_r(dest, dest, src);
212     }
213
214     void sub32(Imm32 imm, RegisterID dest)
215     {
216         m_assembler.subs_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
217     }
218
219     void sub32(Imm32 imm, Address address)
220     {
221         load32(address, ARMRegisters::S1);
222         sub32(imm, ARMRegisters::S1);
223         store32(ARMRegisters::S1, address);
224     }
225
226     void sub32(Address src, RegisterID dest)
227     {
228         load32(src, ARMRegisters::S1);
229         sub32(ARMRegisters::S1, dest);
230     }
231
232     void or32(Address address, RegisterID dest)
233     {
234         load32(address, ARMRegisters::S1);
235         or32(ARMRegisters::S1, dest);
236     }
237
238     void xor32(RegisterID src, RegisterID dest)
239     {
240         m_assembler.eors_r(dest, dest, src);
241     }
242
243     void xor32(Imm32 imm, RegisterID dest)
244     {
245         m_assembler.eors_r(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
246     }
247
248     void xor32(Address src, RegisterID dest)
249     {
250         load32(src, ARMRegisters::S1);
251         m_assembler.eors_r(dest, dest, ARMRegisters::S1);
252     }
253
254     void load8(ImplicitAddress address, RegisterID dest)
255     {
256         m_assembler.dataTransfer8(true, dest, address.base, address.offset);
257     }
258
259     void load32(ImplicitAddress address, RegisterID dest)
260     {
261         m_assembler.dataTransfer32(true, dest, address.base, address.offset);
262     }
263
264     void load32(BaseIndex address, RegisterID dest)
265     {
266         m_assembler.baseIndexTransfer32(true, dest, address.base, address.index, static_cast<int>(address.scale), address.offset);
267     }
268
269 #if WTF_CPU_ARMV5_OR_LOWER
270     void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest);
271 #else
272     void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest)
273     {
274         load32(address, dest);
275     }
276 #endif
277
278     DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest)
279     {
280         ASSERT(address.base != ARMRegisters::S0);
281         DataLabel32 dataLabel(this);
282         m_assembler.ldr_un_imm(ARMRegisters::S0, 0);
283         m_assembler.dtr_ur(true, dest, address.base, ARMRegisters::S0);
284         return dataLabel;
285     }
286
287     DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID hi, RegisterID lo)
288     {
289         ASSERT(address.base != ARMRegisters::S0);
290         ASSERT(lo != ARMRegisters::S0);
291         DataLabel32 dataLabel(this);
292         m_assembler.ldr_un_imm(ARMRegisters::S0, 0);
293         m_assembler.add_r(ARMRegisters::S0, ARMRegisters::S0, address.base);
294         m_assembler.dtr_u(true, lo, ARMRegisters::S0, 0);
295         m_assembler.dtr_u(true, hi, ARMRegisters::S0, 4);
296         return dataLabel;
297     }
298
299     Label loadPtrWithPatchToLEA(Address address, RegisterID dest)
300     {
301         Label label(this);
302         load32(address, dest);
303         return label;
304     }
305
306     void load16(BaseIndex address, RegisterID dest)
307     {
308         m_assembler.add_r(ARMRegisters::S1, address.base, m_assembler.lsl(address.index, address.scale));
309         load16(Address(ARMRegisters::S1, address.offset), dest);
310     }
311     
312     void load16(ImplicitAddress address, RegisterID dest)
313     {
314         if (address.offset >= 0)
315             m_assembler.ldrh_u(dest, address.base, m_assembler.getOffsetForHalfwordDataTransfer(address.offset, ARMRegisters::S0));
316         else
317             m_assembler.ldrh_d(dest, address.base, m_assembler.getOffsetForHalfwordDataTransfer(-address.offset, ARMRegisters::S0));
318     }
319
320     DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address)
321     {
322         ASSERT(address.base != ARMRegisters::S0);
323         DataLabel32 dataLabel(this);
324         m_assembler.ldr_un_imm(ARMRegisters::S0, 0);
325         m_assembler.dtr_ur(false, src, address.base, ARMRegisters::S0);
326         return dataLabel;
327     }
328
329     DataLabel32 store64WithAddressOffsetPatch(RegisterID hi, RegisterID lo, Address address)
330     {
331         ASSERT(hi != ARMRegisters::S0);
332         ASSERT(lo != ARMRegisters::S0);
333         ASSERT(address.base != ARMRegisters::S0);
334         DataLabel32 dataLabel(this);
335         m_assembler.ldr_un_imm(ARMRegisters::S0, address.offset);
336         m_assembler.add_r(ARMRegisters::S0, ARMRegisters::S0, address.base);
337         m_assembler.dtr_u(false, lo, ARMRegisters::S0, 0);
338         m_assembler.dtr_u(false, hi, ARMRegisters::S0, 4);
339         return dataLabel;
340     }
341
342     DataLabel32 store64WithAddressOffsetPatch(Imm32 hi, RegisterID lo, Address address)
343     {
344         ASSERT(lo != ARMRegisters::S0);
345         ASSERT(lo != ARMRegisters::S1);
346         ASSERT(lo != address.base);
347         ASSERT(address.base != ARMRegisters::S0);
348         ASSERT(address.base != ARMRegisters::S1);
349         DataLabel32 dataLabel(this);
350         m_assembler.ldr_un_imm(ARMRegisters::S0, address.offset);
351         m_assembler.moveImm(hi.m_value, ARMRegisters::S1);
352         m_assembler.add_r(ARMRegisters::S0, ARMRegisters::S0, address.base);
353         m_assembler.dtr_u(false, lo, ARMRegisters::S0, 0);
354         m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 4);
355         return dataLabel;
356     }
357
358     DataLabel32 store64WithAddressOffsetPatch(Imm32 hi, Imm32 lo, Address address)
359     {
360         ASSERT(address.base != ARMRegisters::S0);
361         ASSERT(address.base != ARMRegisters::S1);
362         DataLabel32 dataLabel(this);
363         m_assembler.ldr_un_imm(ARMRegisters::S0, address.offset);
364         m_assembler.add_r(ARMRegisters::S0, ARMRegisters::S0, address.base);
365         m_assembler.moveImm(lo.m_value, ARMRegisters::S1);
366         m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 0);
367         /* TODO: improve this by getting another scratch register. */
368         m_assembler.moveImm(hi.m_value, ARMRegisters::S1);
369         m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 4);
370         return dataLabel;
371     }
372
373     void store32(RegisterID src, ImplicitAddress address)
374     {
375         m_assembler.dataTransfer32(false, src, address.base, address.offset);
376     }
377
378     void store32(RegisterID src, BaseIndex address)
379     {
380         m_assembler.baseIndexTransfer32(false, src, address.base, address.index, static_cast<int>(address.scale), address.offset);
381     }
382
383     void store32(Imm32 imm, BaseIndex address)
384     {
385         if (imm.m_isPointer)
386             m_assembler.ldr_un_imm(ARMRegisters::S1, imm.m_value);
387         else
388             move(imm, ARMRegisters::S1);
389         store32(ARMRegisters::S1, address);
390     }
391
392     void store32(Imm32 imm, ImplicitAddress address)
393     {
394         if (imm.m_isPointer)
395             m_assembler.ldr_un_imm(ARMRegisters::S1, imm.m_value);
396         else
397             move(imm, ARMRegisters::S1);
398         store32(ARMRegisters::S1, address);
399     }
400
401     void store32(RegisterID src, void* address)
402     {
403         m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast<ARMWord>(address));
404         m_assembler.dtr_u(false, src, ARMRegisters::S0, 0);
405     }
406
407     void store32(Imm32 imm, void* address)
408     {
409         m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast<ARMWord>(address));
410         if (imm.m_isPointer)
411             m_assembler.ldr_un_imm(ARMRegisters::S1, imm.m_value);
412         else
413             m_assembler.moveImm(imm.m_value, ARMRegisters::S1);
414         m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 0);
415     }
416
417     void pop(RegisterID dest)
418     {
419         m_assembler.pop_r(dest);
420     }
421
422     void push(RegisterID src)
423     {
424         m_assembler.push_r(src);
425     }
426
427     void push(Address address)
428     {
429         load32(address, ARMRegisters::S1);
430         push(ARMRegisters::S1);
431     }
432
433     void push(Imm32 imm)
434     {
435         move(imm, ARMRegisters::S0);
436         push(ARMRegisters::S0);
437     }
438
439     void move(Imm32 imm, RegisterID dest)
440     {
441         if (imm.m_isPointer)
442             m_assembler.ldr_un_imm(dest, imm.m_value);
443         else
444             m_assembler.moveImm(imm.m_value, dest);
445     }
446
447     void move(RegisterID src, RegisterID dest)
448     {
449         m_assembler.mov_r(dest, src);
450     }
451
452     void move(ImmPtr imm, RegisterID dest)
453     {
454         move(Imm32(imm), dest);
455     }
456
457     void swap(RegisterID reg1, RegisterID reg2)
458     {
459         m_assembler.mov_r(ARMRegisters::S0, reg1);
460         m_assembler.mov_r(reg1, reg2);
461         m_assembler.mov_r(reg2, ARMRegisters::S0);
462     }
463
464     void signExtend32ToPtr(RegisterID src, RegisterID dest)
465     {
466         if (src != dest)
467             move(src, dest);
468     }
469
470     void zeroExtend32ToPtr(RegisterID src, RegisterID dest)
471     {
472         if (src != dest)
473             move(src, dest);
474     }
475
476     Jump branch8(Condition cond, Address left, Imm32 right)
477     {
478         load8(left, ARMRegisters::S1);
479         return branch32(cond, ARMRegisters::S1, right);
480     }
481
482     Jump branch32(Condition cond, RegisterID left, RegisterID right, int useConstantPool = 0)
483     {
484         m_assembler.cmp_r(left, right);
485         return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool));
486     }
487
488     Jump branch32(Condition cond, RegisterID left, Imm32 right, int useConstantPool = 0)
489     {
490         ASSERT(left != ARMRegisters::S0);
491         if (right.m_isPointer) {
492             m_assembler.ldr_un_imm(ARMRegisters::S0, right.m_value);
493             m_assembler.cmp_r(left, ARMRegisters::S0);
494         } else
495             m_assembler.cmp_r(left, m_assembler.getImm(right.m_value, ARMRegisters::S0));
496         return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool));
497     }
498
499     // Like branch32, but emit a consistently-structured sequence such that the
500     // number of instructions emitted is constant, regardless of the argument
501     // values. For ARM, this is identical to branch32WithPatch, except that it
502     // does not generate a DataLabel32.
503     Jump branch32FixedLength(Condition cond, RegisterID left, Imm32 right)
504     {
505         m_assembler.ldr_un_imm(ARMRegisters::S1, right.m_value);
506         return branch32(cond, left, ARMRegisters::S1, true);
507     }
508
509     // As branch32_force32, but allow the value ('right') to be patched.
510     Jump branch32WithPatch(Condition cond, RegisterID left, Imm32 right, DataLabel32 &dataLabel)
511     {
512         ASSERT(left != ARMRegisters::S1);
513         dataLabel = moveWithPatch(right, ARMRegisters::S1);
514         return branch32(cond, left, ARMRegisters::S1, true);
515     }
516
517     Jump branch32WithPatch(Condition cond, Address left, Imm32 right, DataLabel32 &dataLabel)
518     {
519         ASSERT(left.base != ARMRegisters::S1);
520         load32(left, ARMRegisters::S1);
521         dataLabel = moveWithPatch(right, ARMRegisters::S0);
522         return branch32(cond, ARMRegisters::S1, ARMRegisters::S0, true);
523     }
524
525     Jump branch32(Condition cond, RegisterID left, Address right)
526     {
527         load32(right, ARMRegisters::S1);
528         return branch32(cond, left, ARMRegisters::S1);
529     }
530
531     Jump branch32(Condition cond, Address left, RegisterID right)
532     {
533         load32(left, ARMRegisters::S1);
534         return branch32(cond, ARMRegisters::S1, right);
535     }
536
537     Jump branch32(Condition cond, Address left, Imm32 right)
538     {
539         load32(left, ARMRegisters::S1);
540         return branch32(cond, ARMRegisters::S1, right);
541     }
542
543     Jump branch32(Condition cond, BaseIndex left, Imm32 right)
544     {
545         load32(left, ARMRegisters::S1);
546         return branch32(cond, ARMRegisters::S1, right);
547     }
548
549     Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, Imm32 right)
550     {
551         load32WithUnalignedHalfWords(left, ARMRegisters::S1);
552         return branch32(cond, ARMRegisters::S1, right);
553     }
554
555     Jump branch16(Condition cond, BaseIndex left, RegisterID right)
556     {
557         (void)(cond);
558         (void)(left);
559         (void)(right);
560         ASSERT_NOT_REACHED();
561         return jump();
562     }
563
564     Jump branch16(Condition cond, BaseIndex left, Imm32 right)
565     {
566         load16(left, ARMRegisters::S0);
567         move(right, ARMRegisters::S1);
568         m_assembler.cmp_r(ARMRegisters::S0, ARMRegisters::S1);
569         return m_assembler.jmp(ARMCondition(cond));
570     }
571
572     Jump branchTest8(Condition cond, Address address, Imm32 mask = Imm32(-1))
573     {
574         load8(address, ARMRegisters::S1);
575         return branchTest32(cond, ARMRegisters::S1, mask);
576     }
577
578     Jump branchTest32(Condition cond, RegisterID reg, RegisterID mask)
579     {
580         ASSERT((cond == Zero) || (cond == NonZero));
581         m_assembler.tst_r(reg, mask);
582         return Jump(m_assembler.jmp(ARMCondition(cond)));
583     }
584
585     Jump branchTest32(Condition cond, RegisterID reg, Imm32 mask = Imm32(-1))
586     {
587         ASSERT((cond == Zero) || (cond == NonZero));
588         ARMWord w = m_assembler.getImm(mask.m_value, ARMRegisters::S0, true);
589         if (w & ARMAssembler::OP2_INV_IMM)
590             m_assembler.bics_r(ARMRegisters::S0, reg, w & ~ARMAssembler::OP2_INV_IMM);
591         else
592             m_assembler.tst_r(reg, w);
593         return Jump(m_assembler.jmp(ARMCondition(cond)));
594     }
595
596     Jump branchTest32(Condition cond, Address address, Imm32 mask = Imm32(-1))
597     {
598         load32(address, ARMRegisters::S1);
599         return branchTest32(cond, ARMRegisters::S1, mask);
600     }
601
602     Jump branchTest32(Condition cond, BaseIndex address, Imm32 mask = Imm32(-1))
603     {
604         load32(address, ARMRegisters::S1);
605         return branchTest32(cond, ARMRegisters::S1, mask);
606     }
607
608     Jump jump()
609     {
610         return Jump(m_assembler.jmp());
611     }
612
613     void jump(RegisterID target)
614     {
615         m_assembler.bx(target);
616     }
617
618     void jump(Address address)
619     {
620         load32(address, ARMRegisters::pc);
621     }
622
623     Jump branchAdd32(Condition cond, RegisterID src, RegisterID dest)
624     {
625         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
626         add32(src, dest);
627         return Jump(m_assembler.jmp(ARMCondition(cond)));
628     }
629
630     Jump branchAdd32(Condition cond, Imm32 imm, RegisterID dest)
631     {
632         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
633         add32(imm, dest);
634         return Jump(m_assembler.jmp(ARMCondition(cond)));
635     }
636
637     Jump branchAdd32(Condition cond, Address src, RegisterID dest)
638     {
639         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
640         add32(src, dest);
641         return Jump(m_assembler.jmp(ARMCondition(cond)));
642     }
643
644     void mull32(RegisterID src1, RegisterID src2, RegisterID dest)
645     {
646         if (src1 == dest) {
647             move(src1, ARMRegisters::S0);
648             src1 = ARMRegisters::S0;
649         }
650         m_assembler.mull_r(ARMRegisters::S1, dest, src2, src1);
651         m_assembler.cmp_r(ARMRegisters::S1, m_assembler.asr(dest, 31));
652     }
653
654     Jump branchMul32(Condition cond, RegisterID src, RegisterID dest)
655     {
656         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
657         if (cond == Overflow) {
658             mull32(src, dest, dest);
659             cond = NonZero;
660         }
661         else
662             mul32(src, dest);
663         return Jump(m_assembler.jmp(ARMCondition(cond)));
664     }
665
666     Jump branchMul32(Condition cond, Imm32 imm, RegisterID src, RegisterID dest)
667     {
668         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
669         if (cond == Overflow) {
670             move(imm, ARMRegisters::S0);
671             mull32(ARMRegisters::S0, src, dest);
672             cond = NonZero;
673         }
674         else
675             mul32(imm, src, dest);
676         return Jump(m_assembler.jmp(ARMCondition(cond)));
677     }
678
679     Jump branchSub32(Condition cond, RegisterID src, RegisterID dest)
680     {
681         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
682         sub32(src, dest);
683         return Jump(m_assembler.jmp(ARMCondition(cond)));
684     }
685
686     Jump branchSub32(Condition cond, Imm32 imm, RegisterID dest)
687     {
688         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
689         sub32(imm, dest);
690         return Jump(m_assembler.jmp(ARMCondition(cond)));
691     }
692
693     Jump branchSub32(Condition cond, Address src, RegisterID dest)
694     {
695         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
696         sub32(src, dest);
697         return Jump(m_assembler.jmp(ARMCondition(cond)));
698     }
699
700     Jump branchSub32(Condition cond, Imm32 imm, Address dest)
701     {
702         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
703         sub32(imm, dest);
704         return Jump(m_assembler.jmp(ARMCondition(cond)));
705     }
706
707     Jump branchNeg32(Condition cond, RegisterID srcDest)
708     {
709         ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
710         neg32(srcDest);
711         return Jump(m_assembler.jmp(ARMCondition(cond)));
712     }
713
714     Jump branchOr32(Condition cond, RegisterID src, RegisterID dest)
715     {
716         ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero));
717         or32(src, dest);
718         return Jump(m_assembler.jmp(ARMCondition(cond)));
719     }
720
721     // Encode a NOP using "MOV rX, rX", where 'X' is defined by 'tag', and is
722     // in the range r0-r14.
723     void nop(int tag)
724     {
725         ASSERT((tag >= 0) && (tag <= 14));
726         m_assembler.mov_r(tag, tag);
727     }
728
729     void breakpoint()
730     {
731         m_assembler.bkpt(0);
732     }
733
734     Call nearCall()
735     {
736 #if WTF_ARM_ARCH_VERSION >= 5
737         Call    call(m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true), Call::LinkableNear);
738         m_assembler.blx(ARMRegisters::S1);
739         return call;
740 #else
741         prepareCall();
742         return Call(m_assembler.jmp(ARMAssembler::AL, true), Call::LinkableNear);
743 #endif
744     }
745
746     Call call(RegisterID target)
747     {
748         m_assembler.blx(target);
749         JmpSrc jmpSrc;
750         return Call(jmpSrc, Call::None);
751     }
752
753     void call(Address address)
754     {
755         call32(address.base, address.offset);
756     }
757
758     void ret()
759     {
760         m_assembler.bx(linkRegister);
761     }
762
763     void set32(Condition cond, Address left, RegisterID right, RegisterID dest)
764     {
765         load32(left, ARMRegisters::S1);
766         set32(cond, ARMRegisters::S1, right, dest);
767     }
768
769     void set32(Condition cond, RegisterID left, Address right, RegisterID dest)
770     {
771         load32(right, ARMRegisters::S1);
772         set32(cond, left, ARMRegisters::S1, dest);
773     }
774
775     void set32(Condition cond, RegisterID left, RegisterID right, RegisterID dest)
776     {
777         m_assembler.cmp_r(left, right);
778         m_assembler.mov_r(dest, ARMAssembler::getOp2(0));
779         m_assembler.mov_r(dest, ARMAssembler::getOp2(1), ARMCondition(cond));
780     }
781
782     void set32(Condition cond, RegisterID left, Imm32 right, RegisterID dest)
783     {
784         m_assembler.cmp_r(left, m_assembler.getImm(right.m_value, ARMRegisters::S0));
785         m_assembler.mov_r(dest, ARMAssembler::getOp2(0));
786         m_assembler.mov_r(dest, ARMAssembler::getOp2(1), ARMCondition(cond));
787     }
788
789     void set32(Condition cond, Address left, Imm32 right, RegisterID dest)
790     {
791         load32(left, ARMRegisters::S1);
792         set32(cond, ARMRegisters::S1, right, dest);
793     }
794
795     void set8(Condition cond, RegisterID left, RegisterID right, RegisterID dest)
796     {
797         // ARM doesn't have byte registers
798         set32(cond, left, right, dest);
799     }
800
801     void set8(Condition cond, Address left, RegisterID right, RegisterID dest)
802     {
803         // ARM doesn't have byte registers
804         load32(left, ARMRegisters::S1);
805         set32(cond, ARMRegisters::S1, right, dest);
806     }
807
808     void set8(Condition cond, RegisterID left, Imm32 right, RegisterID dest)
809     {
810         // ARM doesn't have byte registers
811         set32(cond, left, right, dest);
812     }
813
814     void setTest32(Condition cond, Address address, Imm32 mask, RegisterID dest)
815     {
816         load32(address, ARMRegisters::S1);
817         if (mask.m_value == -1)
818             m_assembler.cmp_r(0, ARMRegisters::S1);
819         else
820             m_assembler.tst_r(ARMRegisters::S1, m_assembler.getImm(mask.m_value, ARMRegisters::S0));
821         m_assembler.mov_r(dest, ARMAssembler::getOp2(0));
822         m_assembler.mov_r(dest, ARMAssembler::getOp2(1), ARMCondition(cond));
823     }
824
825     void setTest8(Condition cond, Address address, Imm32 mask, RegisterID dest)
826     {
827         // ARM doesn't have byte registers
828         setTest32(cond, address, mask, dest);
829     }
830
831     void add32(Imm32 imm, RegisterID src, RegisterID dest)
832     {
833         m_assembler.add_r(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
834     }
835
836     void lea(Address address, RegisterID dest)
837     {
838         m_assembler.add_r(dest, address.base, m_assembler.getImm(address.offset, ARMRegisters::S0));
839     }
840
841     void lea(BaseIndex address, RegisterID dest)
842     {
843         /* This could be better? */
844         move(address.index, ARMRegisters::S1);
845         if (address.scale != 0)
846             lshift32(Imm32(address.scale), ARMRegisters::S1);
847         if (address.offset)
848             add32(Imm32(address.offset), ARMRegisters::S1);
849         add32(address.base, ARMRegisters::S1);
850         move(ARMRegisters::S1, dest);
851     }
852
853     void add32(Imm32 imm, AbsoluteAddress address)
854     {
855         m_assembler.ldr_un_imm(ARMRegisters::S1, reinterpret_cast<ARMWord>(address.m_ptr));
856         m_assembler.dtr_u(true, ARMRegisters::S1, ARMRegisters::S1, 0);
857         add32(imm, ARMRegisters::S1);
858         m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast<ARMWord>(address.m_ptr));
859         m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 0);
860     }
861
862     void sub32(Imm32 imm, AbsoluteAddress address)
863     {
864         m_assembler.ldr_un_imm(ARMRegisters::S1, reinterpret_cast<ARMWord>(address.m_ptr));
865         m_assembler.dtr_u(true, ARMRegisters::S1, ARMRegisters::S1, 0);
866         sub32(imm, ARMRegisters::S1);
867         m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast<ARMWord>(address.m_ptr));
868         m_assembler.dtr_u(false, ARMRegisters::S1, ARMRegisters::S0, 0);
869     }
870
871     void load32(void* address, RegisterID dest)
872     {
873         m_assembler.ldr_un_imm(ARMRegisters::S0, reinterpret_cast<ARMWord>(address));
874         m_assembler.dtr_u(true, dest, ARMRegisters::S0, 0);
875     }
876
877     Jump branch32(Condition cond, AbsoluteAddress left, RegisterID right)
878     {
879         load32(left.m_ptr, ARMRegisters::S1);
880         return branch32(cond, ARMRegisters::S1, right);
881     }
882
883     Jump branch32(Condition cond, AbsoluteAddress left, Imm32 right)
884     {
885         load32(left.m_ptr, ARMRegisters::S1);
886         return branch32(cond, ARMRegisters::S1, right);
887     }
888
889     Call call()
890     {
891 #if WTF_ARM_ARCH_VERSION >= 5
892         Call    call(m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true), Call::Linkable);
893         m_assembler.blx(ARMRegisters::S1);
894         return call;
895 #else
896         prepareCall();
897         return Call(m_assembler.jmp(ARMAssembler::AL, true), Call::Linkable);
898 #endif
899     }
900
901     Call tailRecursiveCall()
902     {
903         return Call::fromTailJump(jump());
904     }
905
906     Call makeTailRecursiveCall(Jump oldJump)
907     {
908         return Call::fromTailJump(oldJump);
909     }
910
911     DataLabelPtr moveWithPatch(ImmPtr initialValue, RegisterID dest)
912     {
913         DataLabelPtr dataLabel(this);
914         m_assembler.ldr_un_imm(dest, reinterpret_cast<ARMWord>(initialValue.m_value));
915         return dataLabel;
916     }
917
918     DataLabel32 moveWithPatch(Imm32 initialValue, RegisterID dest)
919     {
920         DataLabel32 dataLabel(this);
921         m_assembler.ldr_un_imm(dest, initialValue.m_value);
922         return dataLabel;
923     }
924
925     Jump branchPtrWithPatch(Condition cond, RegisterID left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0))
926     {
927         dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1);
928         Jump jump = branch32(cond, left, ARMRegisters::S1, true);
929         return jump;
930     }
931
932     Jump branchPtrWithPatch(Condition cond, Address left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0))
933     {
934         load32(left, ARMRegisters::S1);
935         dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S0);
936         Jump jump = branch32(cond, ARMRegisters::S0, ARMRegisters::S1, true);
937         return jump;
938     }
939
940     DataLabelPtr storePtrWithPatch(ImmPtr initialValue, ImplicitAddress address)
941     {
942         DataLabelPtr dataLabel = moveWithPatch(initialValue, ARMRegisters::S1);
943         store32(ARMRegisters::S1, address);
944         return dataLabel;
945     }
946
947     DataLabelPtr storePtrWithPatch(ImplicitAddress address)
948     {
949         return storePtrWithPatch(ImmPtr(0), address);
950     }
951
952     // Floating point operators
953     bool supportsFloatingPoint() const
954     {
955         return s_isVFPPresent;
956     }
957
958     bool supportsFloatingPointTruncate() const
959     {
960         return true;
961     }
962
963     bool supportsFloatingPointSqrt() const
964     {
965         return s_isVFPPresent;
966     }
967
968     void moveDouble(FPRegisterID src, FPRegisterID dest)
969     {
970         m_assembler.fcpyd_r(dest, src);
971     }
972
973     void loadDouble(ImplicitAddress address, FPRegisterID dest)
974     {
975         m_assembler.doubleTransfer(true, dest, address.base, address.offset);
976     }
977
978     DataLabelPtr loadDouble(const void* address, FPRegisterID dest)
979     {
980         DataLabelPtr label = moveWithPatch(ImmPtr(address), ARMRegisters::S0);
981         m_assembler.fdtr_u(true, dest, ARMRegisters::S0, 0);
982         return label;
983     }
984
985     void storeDouble(FPRegisterID src, ImplicitAddress address)
986     {
987         m_assembler.doubleTransfer(false, src, address.base, address.offset);
988     }
989
990     void addDouble(FPRegisterID src, FPRegisterID dest)
991     {
992         m_assembler.faddd_r(dest, dest, src);
993     }
994
995     void addDouble(Address src, FPRegisterID dest)
996     {
997         loadDouble(src, ARMRegisters::SD0);
998         addDouble(ARMRegisters::SD0, dest);
999     }
1000
1001     void divDouble(FPRegisterID src, FPRegisterID dest)
1002     {
1003         m_assembler.fdivd_r(dest, dest, src);
1004     }
1005
1006     void divDouble(Address src, FPRegisterID dest)
1007     {
1008         ASSERT_NOT_REACHED(); // Untested
1009         loadDouble(src, ARMRegisters::SD0);
1010         divDouble(ARMRegisters::SD0, dest);
1011     }
1012
1013     void subDouble(FPRegisterID src, FPRegisterID dest)
1014     {
1015         m_assembler.fsubd_r(dest, dest, src);
1016     }
1017
1018     void subDouble(Address src, FPRegisterID dest)
1019     {
1020         loadDouble(src, ARMRegisters::SD0);
1021         subDouble(ARMRegisters::SD0, dest);
1022     }
1023
1024     void mulDouble(FPRegisterID src, FPRegisterID dest)
1025     {
1026         m_assembler.fmuld_r(dest, dest, src);
1027     }
1028
1029     void mulDouble(Address src, FPRegisterID dest)
1030     {
1031         loadDouble(src, ARMRegisters::SD0);
1032         mulDouble(ARMRegisters::SD0, dest);
1033     }
1034
1035     void negDouble(FPRegisterID src, FPRegisterID dest)
1036     {
1037         m_assembler.fnegd_r(dest, src);
1038     }
1039
1040     void sqrtDouble(FPRegisterID src, FPRegisterID dest)
1041     {
1042         m_assembler.fsqrtd_r(dest, src);
1043     }
1044
1045     void convertInt32ToDouble(RegisterID src, FPRegisterID dest)
1046     {
1047         m_assembler.fmsr_r(dest, src);
1048         m_assembler.fsitod_r(dest, dest);
1049     }
1050
1051     void convertInt32ToDouble(Address src, FPRegisterID dest)
1052     {
1053         // flds does not worth the effort here
1054         load32(src, ARMRegisters::S1);
1055         convertInt32ToDouble(ARMRegisters::S1, dest);
1056     }
1057
1058     void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest)
1059     {
1060         ASSERT_NOT_REACHED(); // Untested
1061         // flds does not worth the effort here
1062         m_assembler.ldr_un_imm(ARMRegisters::S1, (ARMWord)src.m_ptr);
1063         m_assembler.dtr_u(true, ARMRegisters::S1, ARMRegisters::S1, 0);
1064         convertInt32ToDouble(ARMRegisters::S1, dest);
1065     }
1066
1067     Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right)
1068     {
1069         m_assembler.fcmpd_r(left, right);
1070         m_assembler.fmstat();
1071         if (cond & DoubleConditionBitSpecial)
1072             m_assembler.cmp_r(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS);
1073         return Jump(m_assembler.jmp(static_cast<ARMAssembler::Condition>(cond & ~DoubleConditionMask)));
1074     }
1075
1076     // Truncates 'src' to an integer, and places the resulting 'dest'.
1077     // If the result is not representable as a 32 bit value, branch.
1078     // May also branch for some values that are representable in 32 bits
1079     Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest)
1080     {
1081         m_assembler.ftosizd_r(ARMRegisters::SD0, src);
1082         // If FTOSIZD (VCVT.S32.F64) can't fit the result into a 32-bit
1083         // integer, it saturates at INT_MAX or INT_MIN. Testing this is
1084         // probably quicker than testing FPSCR for exception.
1085         m_assembler.fmrs_r(dest, ARMRegisters::SD0);
1086         m_assembler.cmn_r(dest, ARMAssembler::getOp2(-0x7fffffff));
1087         m_assembler.cmp_r(dest, ARMAssembler::getOp2(0x80000000), ARMCondition(NonZero));
1088         return Jump(m_assembler.jmp(ARMCondition(Zero)));
1089     }
1090
1091     // Convert 'src' to an integer, and places the resulting 'dest'.
1092     // If the result is not representable as a 32 bit value, branch.
1093     // May also branch for some values that are representable in 32 bits
1094     // (specifically, in this case, 0).
1095     void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp)
1096     {
1097         m_assembler.ftosid_r(ARMRegisters::SD0, src);
1098         m_assembler.fmrs_r(dest, ARMRegisters::SD0);
1099
1100         // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump.
1101         m_assembler.fsitod_r(ARMRegisters::SD0, ARMRegisters::SD0);
1102         failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0));
1103
1104         // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0
1105         failureCases.append(branchTest32(Zero, dest));
1106     }
1107
1108     void zeroDouble(FPRegisterID srcDest)
1109     {
1110         m_assembler.mov_r(ARMRegisters::S0, ARMAssembler::getOp2(0));
1111         convertInt32ToDouble(ARMRegisters::S0, srcDest);
1112     }
1113
1114     void ensureSpace(int space)
1115     {
1116         m_assembler.ensureSpace(space);
1117     }
1118
1119     void forceFlushConstantPool()
1120     {
1121         m_assembler.forceFlushConstantPool();
1122     }
1123
1124 #ifdef DEBUG
1125     void allowPoolFlush(bool allowFlush)
1126     {
1127         m_assembler.allowPoolFlush(allowFlush);
1128     }
1129 #endif
1130
1131 protected:
1132     ARMAssembler::Condition ARMCondition(Condition cond)
1133     {
1134         return static_cast<ARMAssembler::Condition>(cond);
1135     }
1136
1137     void ensureSpace(int insnSpace, int constSpace)
1138     {
1139         m_assembler.ensureSpace(insnSpace, constSpace);
1140     }
1141
1142     int sizeOfConstantPool()
1143     {
1144         return m_assembler.sizeOfConstantPool();
1145     }
1146
1147 #if WTF_ARM_ARCH_VERSION < 5
1148     void prepareCall()
1149     {
1150 #if WTF_ARM_ARCH_VERSION < 5
1151         ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord));
1152
1153         m_assembler.mov_r(linkRegister, ARMRegisters::pc);
1154 #endif
1155     }
1156 #endif
1157
1158 #if WTF_ARM_ARCH_VERSION < 5
1159     void call32(RegisterID base, int32_t offset)
1160     {
1161 #if WTF_ARM_ARCH_VERSION >= 5
1162         int targetReg = ARMRegisters::S1;
1163 #else
1164         int targetReg = ARMRegisters::pc;
1165 #endif
1166         int tmpReg = ARMRegisters::S1;
1167
1168         if (base == ARMRegisters::sp)
1169             offset += 4;
1170
1171         if (offset >= 0) {
1172             if (offset <= 0xfff) {
1173                 prepareCall();
1174                 m_assembler.dtr_u(true, targetReg, base, offset);
1175             } else if (offset <= 0xfffff) {
1176                 m_assembler.add_r(tmpReg, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
1177                 prepareCall();
1178                 m_assembler.dtr_u(true, targetReg, tmpReg, offset & 0xfff);
1179             } else {
1180                 ARMWord reg = m_assembler.getImm(offset, tmpReg);
1181                 prepareCall();
1182                 m_assembler.dtr_ur(true, targetReg, base, reg);
1183             }
1184         } else  {
1185             offset = -offset;
1186             if (offset <= 0xfff) {
1187                 prepareCall();
1188                 m_assembler.dtr_d(true, targetReg, base, offset);
1189             } else if (offset <= 0xfffff) {
1190                 m_assembler.sub_r(tmpReg, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
1191                 prepareCall();
1192                 m_assembler.dtr_d(true, targetReg, tmpReg, offset & 0xfff);
1193             } else {
1194                 ARMWord reg = m_assembler.getImm(offset, tmpReg);
1195                 prepareCall();
1196                 m_assembler.dtr_dr(true, targetReg, base, reg);
1197             }
1198         }
1199 #if WTF_ARM_ARCH_VERSION >= 5
1200         m_assembler.blx(targetReg);
1201 #endif
1202     }
1203 #else
1204     void call32(RegisterID base, int32_t offset)
1205     {
1206         // TODO: Why is SP special?
1207         if (base == ARMRegisters::sp)
1208             offset += 4;
1209
1210         // Branch to the address stored in base+offset, using one of the
1211         // following sequences:
1212         // ----
1213         //  LDR     ip, [base, ±offset]
1214         //  BLX     ip
1215         // ----
1216         //  ADD/SUB ip, base, #(offset & 0xff000)
1217         //  LDR     ip, [ip, #(offset & 0xfff)]
1218         //  BLX     ip
1219         // ----
1220         //  LDR     ip, =offset
1221         //  LDR     ip, [base, ±ip]
1222         //  BLX     ip
1223
1224         if (offset >= 0) {
1225             if (offset <= 0xfff) {
1226                 m_assembler.dtr_u(true, ARMRegisters::S0, base, offset);
1227             } else if (offset <= 0xfffff) {
1228                 m_assembler.add_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
1229                 m_assembler.dtr_u(true, ARMRegisters::S0, ARMRegisters::S0, offset & 0xfff);
1230             } else {
1231                 m_assembler.moveImm(offset, ARMRegisters::S0);
1232                 m_assembler.dtr_ur(true, ARMRegisters::S0, base, ARMRegisters::S0);
1233             }
1234         } else  {
1235             offset = -offset;
1236             if (offset <= 0xfff) {
1237                 m_assembler.dtr_d(true, ARMRegisters::S0, base, offset);
1238             } else if (offset <= 0xfffff) {
1239                 m_assembler.sub_r(ARMRegisters::S0, base, ARMAssembler::OP2_IMM | (offset >> 12) | (10 << 8));
1240                 m_assembler.dtr_d(true, ARMRegisters::S0, ARMRegisters::S0, offset & 0xfff);
1241             } else {
1242                 m_assembler.moveImm(offset, ARMRegisters::S0);
1243                 m_assembler.dtr_dr(true, ARMRegisters::S0, base, ARMRegisters::S0);
1244             }
1245         }
1246         m_assembler.blx(ARMRegisters::S0);
1247     }
1248 #endif
1249
1250 private:
1251     friend class LinkBuffer;
1252     friend class RepatchBuffer;
1253
1254     static void linkCall(void* code, Call call, FunctionPtr function)
1255     {
1256         ARMAssembler::linkCall(code, call.m_jmp, function.value());
1257     }
1258
1259     static void repatchCall(CodeLocationCall call, CodeLocationLabel destination)
1260     {
1261         ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress());
1262     }
1263
1264     static void repatchCall(CodeLocationCall call, FunctionPtr destination)
1265     {
1266         ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress());
1267     }
1268
1269     static const bool s_isVFPPresent;
1270 };
1271
1272 }
1273
1274 #endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
1275
1276 #endif // MacroAssemblerARM_h