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