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