]>
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 ARMAssembler_h | |
27 | #define ARMAssembler_h | |
28 | ||
29 | #include <wtf/Platform.h> | |
30 | ||
f9bf01c6 | 31 | #if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) |
ba379fdc A |
32 | |
33 | #include "AssemblerBuffer.h" | |
34 | #include <wtf/Assertions.h> | |
35 | #include <wtf/Vector.h> | |
36 | #include <stdint.h> | |
37 | ||
38 | namespace JSC { | |
39 | ||
f9bf01c6 | 40 | namespace ARMRegisters { |
ba379fdc A |
41 | typedef enum { |
42 | r0, | |
43 | r1, | |
44 | r2, | |
45 | r3, | |
46 | r4, | |
47 | r5, | |
48 | r6, | |
49 | r7, wr = r7, // thumb work register | |
50 | r8, | |
51 | r9, sb = r9, // static base | |
52 | r10, sl = r10, // stack limit | |
53 | r11, fp = r11, // frame pointer | |
54 | r12, ip = r12, | |
55 | r13, sp = r13, | |
56 | r14, lr = r14, | |
57 | r15, pc = r15, | |
58 | } RegisterID; | |
59 | ||
60 | // s0 == d0 == q0 | |
61 | // s4 == d2 == q1 | |
62 | // etc | |
63 | typedef enum { | |
64 | s0 = 0, | |
65 | s1 = 1, | |
66 | s2 = 2, | |
67 | s3 = 3, | |
68 | s4 = 4, | |
69 | s5 = 5, | |
70 | s6 = 6, | |
71 | s7 = 7, | |
72 | s8 = 8, | |
73 | s9 = 9, | |
74 | s10 = 10, | |
75 | s11 = 11, | |
76 | s12 = 12, | |
77 | s13 = 13, | |
78 | s14 = 14, | |
79 | s15 = 15, | |
80 | s16 = 16, | |
81 | s17 = 17, | |
82 | s18 = 18, | |
83 | s19 = 19, | |
84 | s20 = 20, | |
85 | s21 = 21, | |
86 | s22 = 22, | |
87 | s23 = 23, | |
88 | s24 = 24, | |
89 | s25 = 25, | |
90 | s26 = 26, | |
91 | s27 = 27, | |
92 | s28 = 28, | |
93 | s29 = 29, | |
94 | s30 = 30, | |
95 | s31 = 31, | |
96 | d0 = 0 << 1, | |
97 | d1 = 1 << 1, | |
98 | d2 = 2 << 1, | |
99 | d3 = 3 << 1, | |
100 | d4 = 4 << 1, | |
101 | d5 = 5 << 1, | |
102 | d6 = 6 << 1, | |
103 | d7 = 7 << 1, | |
104 | d8 = 8 << 1, | |
105 | d9 = 9 << 1, | |
106 | d10 = 10 << 1, | |
107 | d11 = 11 << 1, | |
108 | d12 = 12 << 1, | |
109 | d13 = 13 << 1, | |
110 | d14 = 14 << 1, | |
111 | d15 = 15 << 1, | |
112 | d16 = 16 << 1, | |
113 | d17 = 17 << 1, | |
114 | d18 = 18 << 1, | |
115 | d19 = 19 << 1, | |
116 | d20 = 20 << 1, | |
117 | d21 = 21 << 1, | |
118 | d22 = 22 << 1, | |
119 | d23 = 23 << 1, | |
120 | d24 = 24 << 1, | |
121 | d25 = 25 << 1, | |
122 | d26 = 26 << 1, | |
123 | d27 = 27 << 1, | |
124 | d28 = 28 << 1, | |
125 | d29 = 29 << 1, | |
126 | d30 = 30 << 1, | |
127 | d31 = 31 << 1, | |
128 | q0 = 0 << 2, | |
129 | q1 = 1 << 2, | |
130 | q2 = 2 << 2, | |
131 | q3 = 3 << 2, | |
132 | q4 = 4 << 2, | |
133 | q5 = 5 << 2, | |
134 | q6 = 6 << 2, | |
135 | q7 = 7 << 2, | |
136 | q8 = 8 << 2, | |
137 | q9 = 9 << 2, | |
138 | q10 = 10 << 2, | |
139 | q11 = 11 << 2, | |
140 | q12 = 12 << 2, | |
141 | q13 = 13 << 2, | |
142 | q14 = 14 << 2, | |
143 | q15 = 15 << 2, | |
144 | q16 = 16 << 2, | |
145 | q17 = 17 << 2, | |
146 | q18 = 18 << 2, | |
147 | q19 = 19 << 2, | |
148 | q20 = 20 << 2, | |
149 | q21 = 21 << 2, | |
150 | q22 = 22 << 2, | |
151 | q23 = 23 << 2, | |
152 | q24 = 24 << 2, | |
153 | q25 = 25 << 2, | |
154 | q26 = 26 << 2, | |
155 | q27 = 27 << 2, | |
156 | q28 = 28 << 2, | |
157 | q29 = 29 << 2, | |
158 | q30 = 30 << 2, | |
159 | q31 = 31 << 2, | |
160 | } FPRegisterID; | |
161 | } | |
162 | ||
163 | class ARMv7Assembler; | |
164 | class ARMThumbImmediate { | |
165 | friend class ARMv7Assembler; | |
166 | ||
167 | typedef uint8_t ThumbImmediateType; | |
168 | static const ThumbImmediateType TypeInvalid = 0; | |
169 | static const ThumbImmediateType TypeEncoded = 1; | |
170 | static const ThumbImmediateType TypeUInt16 = 2; | |
171 | ||
172 | typedef union { | |
173 | int16_t asInt; | |
174 | struct { | |
175 | unsigned imm8 : 8; | |
176 | unsigned imm3 : 3; | |
177 | unsigned i : 1; | |
178 | unsigned imm4 : 4; | |
179 | }; | |
180 | // If this is an encoded immediate, then it may describe a shift, or a pattern. | |
181 | struct { | |
182 | unsigned shiftValue7 : 7; | |
183 | unsigned shiftAmount : 5; | |
184 | }; | |
185 | struct { | |
186 | unsigned immediate : 8; | |
187 | unsigned pattern : 4; | |
188 | }; | |
189 | } ThumbImmediateValue; | |
190 | ||
191 | // byte0 contains least significant bit; not using an array to make client code endian agnostic. | |
192 | typedef union { | |
193 | int32_t asInt; | |
194 | struct { | |
195 | uint8_t byte0; | |
196 | uint8_t byte1; | |
197 | uint8_t byte2; | |
198 | uint8_t byte3; | |
199 | }; | |
200 | } PatternBytes; | |
201 | ||
f9bf01c6 | 202 | ALWAYS_INLINE static void countLeadingZerosPartial(uint32_t& value, int32_t& zeros, const int N) |
ba379fdc | 203 | { |
f9bf01c6 A |
204 | if (value & ~((1 << N) - 1)) /* check for any of the top N bits (of 2N bits) are set */ |
205 | value >>= N; /* if any were set, lose the bottom N */ | |
206 | else /* if none of the top N bits are set, */ | |
207 | zeros += N; /* then we have identified N leading zeros */ | |
ba379fdc A |
208 | } |
209 | ||
210 | static int32_t countLeadingZeros(uint32_t value) | |
211 | { | |
212 | if (!value) | |
213 | return 32; | |
214 | ||
215 | int32_t zeros = 0; | |
216 | countLeadingZerosPartial(value, zeros, 16); | |
217 | countLeadingZerosPartial(value, zeros, 8); | |
218 | countLeadingZerosPartial(value, zeros, 4); | |
219 | countLeadingZerosPartial(value, zeros, 2); | |
220 | countLeadingZerosPartial(value, zeros, 1); | |
221 | return zeros; | |
222 | } | |
223 | ||
224 | ARMThumbImmediate() | |
225 | : m_type(TypeInvalid) | |
226 | { | |
227 | m_value.asInt = 0; | |
228 | } | |
229 | ||
230 | ARMThumbImmediate(ThumbImmediateType type, ThumbImmediateValue value) | |
231 | : m_type(type) | |
232 | , m_value(value) | |
233 | { | |
234 | } | |
235 | ||
236 | ARMThumbImmediate(ThumbImmediateType type, uint16_t value) | |
237 | : m_type(TypeUInt16) | |
238 | { | |
f9bf01c6 A |
239 | // Make sure this constructor is only reached with type TypeUInt16; |
240 | // this extra parameter makes the code a little clearer by making it | |
241 | // explicit at call sites which type is being constructed | |
242 | ASSERT_UNUSED(type, type == TypeUInt16); | |
243 | ||
ba379fdc A |
244 | m_value.asInt = value; |
245 | } | |
246 | ||
247 | public: | |
248 | static ARMThumbImmediate makeEncodedImm(uint32_t value) | |
249 | { | |
250 | ThumbImmediateValue encoding; | |
251 | encoding.asInt = 0; | |
252 | ||
253 | // okay, these are easy. | |
254 | if (value < 256) { | |
255 | encoding.immediate = value; | |
256 | encoding.pattern = 0; | |
257 | return ARMThumbImmediate(TypeEncoded, encoding); | |
258 | } | |
259 | ||
260 | int32_t leadingZeros = countLeadingZeros(value); | |
261 | // if there were 24 or more leading zeros, then we'd have hit the (value < 256) case. | |
262 | ASSERT(leadingZeros < 24); | |
263 | ||
264 | // Given a number with bit fields Z:B:C, where count(Z)+count(B)+count(C) == 32, | |
265 | // Z are the bits known zero, B is the 8-bit immediate, C are the bits to check for | |
266 | // zero. count(B) == 8, so the count of bits to be checked is 24 - count(Z). | |
267 | int32_t rightShiftAmount = 24 - leadingZeros; | |
268 | if (value == ((value >> rightShiftAmount) << rightShiftAmount)) { | |
269 | // Shift the value down to the low byte position. The assign to | |
270 | // shiftValue7 drops the implicit top bit. | |
271 | encoding.shiftValue7 = value >> rightShiftAmount; | |
272 | // The endoded shift amount is the magnitude of a right rotate. | |
273 | encoding.shiftAmount = 8 + leadingZeros; | |
274 | return ARMThumbImmediate(TypeEncoded, encoding); | |
275 | } | |
276 | ||
277 | PatternBytes bytes; | |
278 | bytes.asInt = value; | |
279 | ||
280 | if ((bytes.byte0 == bytes.byte1) && (bytes.byte0 == bytes.byte2) && (bytes.byte0 == bytes.byte3)) { | |
281 | encoding.immediate = bytes.byte0; | |
282 | encoding.pattern = 3; | |
283 | return ARMThumbImmediate(TypeEncoded, encoding); | |
284 | } | |
285 | ||
286 | if ((bytes.byte0 == bytes.byte2) && !(bytes.byte1 | bytes.byte3)) { | |
287 | encoding.immediate = bytes.byte0; | |
288 | encoding.pattern = 1; | |
289 | return ARMThumbImmediate(TypeEncoded, encoding); | |
290 | } | |
291 | ||
292 | if ((bytes.byte1 == bytes.byte3) && !(bytes.byte0 | bytes.byte2)) { | |
293 | encoding.immediate = bytes.byte0; | |
294 | encoding.pattern = 2; | |
295 | return ARMThumbImmediate(TypeEncoded, encoding); | |
296 | } | |
297 | ||
298 | return ARMThumbImmediate(); | |
299 | } | |
300 | ||
301 | static ARMThumbImmediate makeUInt12(int32_t value) | |
302 | { | |
303 | return (!(value & 0xfffff000)) | |
304 | ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) | |
305 | : ARMThumbImmediate(); | |
306 | } | |
307 | ||
308 | static ARMThumbImmediate makeUInt12OrEncodedImm(int32_t value) | |
309 | { | |
310 | // If this is not a 12-bit unsigned it, try making an encoded immediate. | |
311 | return (!(value & 0xfffff000)) | |
312 | ? ARMThumbImmediate(TypeUInt16, (uint16_t)value) | |
313 | : makeEncodedImm(value); | |
314 | } | |
315 | ||
316 | // The 'make' methods, above, return a !isValid() value if the argument | |
317 | // cannot be represented as the requested type. This methods is called | |
318 | // 'get' since the argument can always be represented. | |
319 | static ARMThumbImmediate makeUInt16(uint16_t value) | |
320 | { | |
321 | return ARMThumbImmediate(TypeUInt16, value); | |
322 | } | |
323 | ||
324 | bool isValid() | |
325 | { | |
326 | return m_type != TypeInvalid; | |
327 | } | |
328 | ||
329 | // These methods rely on the format of encoded byte values. | |
330 | bool isUInt3() { return !(m_value.asInt & 0xfff8); } | |
331 | bool isUInt4() { return !(m_value.asInt & 0xfff0); } | |
332 | bool isUInt5() { return !(m_value.asInt & 0xffe0); } | |
333 | bool isUInt6() { return !(m_value.asInt & 0xffc0); } | |
334 | bool isUInt7() { return !(m_value.asInt & 0xff80); } | |
335 | bool isUInt8() { return !(m_value.asInt & 0xff00); } | |
336 | bool isUInt9() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfe00); } | |
337 | bool isUInt10() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xfc00); } | |
338 | bool isUInt12() { return (m_type == TypeUInt16) && !(m_value.asInt & 0xf000); } | |
339 | bool isUInt16() { return m_type == TypeUInt16; } | |
340 | uint8_t getUInt3() { ASSERT(isUInt3()); return m_value.asInt; } | |
341 | uint8_t getUInt4() { ASSERT(isUInt4()); return m_value.asInt; } | |
342 | uint8_t getUInt5() { ASSERT(isUInt5()); return m_value.asInt; } | |
343 | uint8_t getUInt6() { ASSERT(isUInt6()); return m_value.asInt; } | |
344 | uint8_t getUInt7() { ASSERT(isUInt7()); return m_value.asInt; } | |
345 | uint8_t getUInt8() { ASSERT(isUInt8()); return m_value.asInt; } | |
346 | uint8_t getUInt9() { ASSERT(isUInt9()); return m_value.asInt; } | |
347 | uint8_t getUInt10() { ASSERT(isUInt10()); return m_value.asInt; } | |
348 | uint16_t getUInt12() { ASSERT(isUInt12()); return m_value.asInt; } | |
349 | uint16_t getUInt16() { ASSERT(isUInt16()); return m_value.asInt; } | |
350 | ||
351 | bool isEncodedImm() { return m_type == TypeEncoded; } | |
352 | ||
353 | private: | |
354 | ThumbImmediateType m_type; | |
355 | ThumbImmediateValue m_value; | |
356 | }; | |
357 | ||
358 | ||
359 | typedef enum { | |
360 | SRType_LSL, | |
361 | SRType_LSR, | |
362 | SRType_ASR, | |
363 | SRType_ROR, | |
364 | ||
365 | SRType_RRX = SRType_ROR | |
366 | } ARMShiftType; | |
367 | ||
368 | class ARMv7Assembler; | |
369 | class ShiftTypeAndAmount { | |
370 | friend class ARMv7Assembler; | |
371 | ||
372 | public: | |
373 | ShiftTypeAndAmount() | |
374 | { | |
375 | m_u.type = (ARMShiftType)0; | |
376 | m_u.amount = 0; | |
377 | } | |
378 | ||
379 | ShiftTypeAndAmount(ARMShiftType type, unsigned amount) | |
380 | { | |
381 | m_u.type = type; | |
382 | m_u.amount = amount & 31; | |
383 | } | |
384 | ||
385 | unsigned lo4() { return m_u.lo4; } | |
386 | unsigned hi4() { return m_u.hi4; } | |
387 | ||
388 | private: | |
389 | union { | |
390 | struct { | |
391 | unsigned lo4 : 4; | |
392 | unsigned hi4 : 4; | |
393 | }; | |
394 | struct { | |
395 | unsigned type : 2; | |
396 | unsigned amount : 5; | |
397 | }; | |
398 | } m_u; | |
399 | }; | |
400 | ||
401 | ||
402 | /* | |
403 | Some features of the Thumb instruction set are deprecated in ARMv7. Deprecated features affecting | |
404 | instructions supported by ARMv7-M are as follows: | |
405 | • use of the PC as <Rd> or <Rm> in a 16-bit ADD (SP plus register) instruction | |
406 | • use of the SP as <Rm> in a 16-bit ADD (SP plus register) instruction | |
407 | • use of the SP as <Rm> in a 16-bit CMP (register) instruction | |
408 | • use of MOV (register) instructions in which <Rd> is the SP or PC and <Rm> is also the SP or PC. | |
409 | • use of <Rn> as the lowest-numbered register in the register list of a 16-bit STM instruction with base | |
410 | register writeback | |
411 | */ | |
412 | ||
413 | class ARMv7Assembler { | |
414 | public: | |
f9bf01c6 A |
415 | ~ARMv7Assembler() |
416 | { | |
417 | ASSERT(m_jumpsToLink.isEmpty()); | |
418 | } | |
419 | ||
420 | typedef ARMRegisters::RegisterID RegisterID; | |
421 | typedef ARMRegisters::FPRegisterID FPRegisterID; | |
ba379fdc A |
422 | |
423 | // (HS, LO, HI, LS) -> (AE, B, A, BE) | |
424 | // (VS, VC) -> (O, NO) | |
425 | typedef enum { | |
426 | ConditionEQ, | |
427 | ConditionNE, | |
428 | ConditionHS, | |
429 | ConditionLO, | |
430 | ConditionMI, | |
431 | ConditionPL, | |
432 | ConditionVS, | |
433 | ConditionVC, | |
434 | ConditionHI, | |
435 | ConditionLS, | |
436 | ConditionGE, | |
437 | ConditionLT, | |
438 | ConditionGT, | |
439 | ConditionLE, | |
440 | ConditionAL, | |
441 | ||
442 | ConditionCS = ConditionHS, | |
443 | ConditionCC = ConditionLO, | |
444 | } Condition; | |
445 | ||
446 | class JmpSrc { | |
447 | friend class ARMv7Assembler; | |
448 | friend class ARMInstructionFormatter; | |
449 | public: | |
450 | JmpSrc() | |
451 | : m_offset(-1) | |
452 | { | |
453 | } | |
454 | ||
455 | private: | |
456 | JmpSrc(int offset) | |
457 | : m_offset(offset) | |
458 | { | |
459 | } | |
460 | ||
461 | int m_offset; | |
462 | }; | |
463 | ||
464 | class JmpDst { | |
465 | friend class ARMv7Assembler; | |
466 | friend class ARMInstructionFormatter; | |
467 | public: | |
468 | JmpDst() | |
469 | : m_offset(-1) | |
470 | , m_used(false) | |
471 | { | |
472 | } | |
473 | ||
474 | bool isUsed() const { return m_used; } | |
475 | void used() { m_used = true; } | |
476 | private: | |
477 | JmpDst(int offset) | |
478 | : m_offset(offset) | |
479 | , m_used(false) | |
480 | { | |
481 | ASSERT(m_offset == offset); | |
482 | } | |
483 | ||
484 | int m_offset : 31; | |
485 | int m_used : 1; | |
486 | }; | |
487 | ||
488 | private: | |
489 | ||
f9bf01c6 A |
490 | struct LinkRecord { |
491 | LinkRecord(intptr_t from, intptr_t to) | |
492 | : from(from) | |
493 | , to(to) | |
494 | { | |
495 | } | |
496 | ||
497 | intptr_t from; | |
498 | intptr_t to; | |
499 | }; | |
500 | ||
ba379fdc A |
501 | // ARMv7, Appx-A.6.3 |
502 | bool BadReg(RegisterID reg) | |
503 | { | |
f9bf01c6 | 504 | return (reg == ARMRegisters::sp) || (reg == ARMRegisters::pc); |
ba379fdc A |
505 | } |
506 | ||
507 | bool isSingleRegister(FPRegisterID reg) | |
508 | { | |
509 | // Check that the high bit isn't set (q16+), and that the low bit isn't (s1, s3, etc). | |
510 | return !(reg & ~31); | |
511 | } | |
512 | ||
513 | bool isDoubleRegister(FPRegisterID reg) | |
514 | { | |
515 | // Check that the high bit isn't set (q16+), and that the low bit isn't (s1, s3, etc). | |
516 | return !(reg & ~(31 << 1)); | |
517 | } | |
518 | ||
519 | bool isQuadRegister(FPRegisterID reg) | |
520 | { | |
521 | return !(reg & ~(31 << 2)); | |
522 | } | |
523 | ||
524 | uint32_t singleRegisterNum(FPRegisterID reg) | |
525 | { | |
526 | ASSERT(isSingleRegister(reg)); | |
527 | return reg; | |
528 | } | |
529 | ||
530 | uint32_t doubleRegisterNum(FPRegisterID reg) | |
531 | { | |
532 | ASSERT(isDoubleRegister(reg)); | |
533 | return reg >> 1; | |
534 | } | |
535 | ||
536 | uint32_t quadRegisterNum(FPRegisterID reg) | |
537 | { | |
538 | ASSERT(isQuadRegister(reg)); | |
539 | return reg >> 2; | |
540 | } | |
541 | ||
542 | uint32_t singleRegisterMask(FPRegisterID rd, int highBitsShift, int lowBitShift) | |
543 | { | |
544 | uint32_t rdNum = singleRegisterNum(rd); | |
545 | uint32_t rdMask = (rdNum >> 1) << highBitsShift; | |
546 | if (rdNum & 1) | |
547 | rdMask |= 1 << lowBitShift; | |
548 | return rdMask; | |
549 | } | |
550 | ||
551 | uint32_t doubleRegisterMask(FPRegisterID rd, int highBitShift, int lowBitsShift) | |
552 | { | |
553 | uint32_t rdNum = doubleRegisterNum(rd); | |
554 | uint32_t rdMask = (rdNum & 0xf) << lowBitsShift; | |
555 | if (rdNum & 16) | |
556 | rdMask |= 1 << highBitShift; | |
557 | return rdMask; | |
558 | } | |
559 | ||
560 | typedef enum { | |
561 | OP_ADD_reg_T1 = 0x1800, | |
562 | OP_ADD_S_reg_T1 = 0x1800, | |
563 | OP_SUB_reg_T1 = 0x1A00, | |
564 | OP_SUB_S_reg_T1 = 0x1A00, | |
565 | OP_ADD_imm_T1 = 0x1C00, | |
566 | OP_ADD_S_imm_T1 = 0x1C00, | |
567 | OP_SUB_imm_T1 = 0x1E00, | |
568 | OP_SUB_S_imm_T1 = 0x1E00, | |
569 | OP_MOV_imm_T1 = 0x2000, | |
570 | OP_CMP_imm_T1 = 0x2800, | |
571 | OP_ADD_imm_T2 = 0x3000, | |
572 | OP_ADD_S_imm_T2 = 0x3000, | |
573 | OP_SUB_imm_T2 = 0x3800, | |
574 | OP_SUB_S_imm_T2 = 0x3800, | |
575 | OP_AND_reg_T1 = 0x4000, | |
576 | OP_EOR_reg_T1 = 0x4040, | |
577 | OP_TST_reg_T1 = 0x4200, | |
578 | OP_CMP_reg_T1 = 0x4280, | |
579 | OP_ORR_reg_T1 = 0x4300, | |
580 | OP_MVN_reg_T1 = 0x43C0, | |
581 | OP_ADD_reg_T2 = 0x4400, | |
582 | OP_MOV_reg_T1 = 0x4600, | |
583 | OP_BLX = 0x4700, | |
584 | OP_BX = 0x4700, | |
585 | OP_LDRH_reg_T1 = 0x5A00, | |
586 | OP_STR_reg_T1 = 0x5000, | |
587 | OP_LDR_reg_T1 = 0x5800, | |
588 | OP_STR_imm_T1 = 0x6000, | |
589 | OP_LDR_imm_T1 = 0x6800, | |
590 | OP_LDRH_imm_T1 = 0x8800, | |
591 | OP_STR_imm_T2 = 0x9000, | |
592 | OP_LDR_imm_T2 = 0x9800, | |
593 | OP_ADD_SP_imm_T1 = 0xA800, | |
594 | OP_ADD_SP_imm_T2 = 0xB000, | |
595 | OP_SUB_SP_imm_T1 = 0xB080, | |
596 | OP_BKPT = 0xBE00, | |
597 | OP_IT = 0xBF00, | |
f9bf01c6 | 598 | OP_NOP_T1 = 0xBF00, |
ba379fdc A |
599 | } OpcodeID; |
600 | ||
601 | typedef enum { | |
602 | OP_AND_reg_T2 = 0xEA00, | |
603 | OP_TST_reg_T2 = 0xEA10, | |
604 | OP_ORR_reg_T2 = 0xEA40, | |
605 | OP_ASR_imm_T1 = 0xEA4F, | |
606 | OP_LSL_imm_T1 = 0xEA4F, | |
607 | OP_LSR_imm_T1 = 0xEA4F, | |
608 | OP_ROR_imm_T1 = 0xEA4F, | |
609 | OP_MVN_reg_T2 = 0xEA6F, | |
610 | OP_EOR_reg_T2 = 0xEA80, | |
611 | OP_ADD_reg_T3 = 0xEB00, | |
612 | OP_ADD_S_reg_T3 = 0xEB10, | |
613 | OP_SUB_reg_T2 = 0xEBA0, | |
614 | OP_SUB_S_reg_T2 = 0xEBB0, | |
615 | OP_CMP_reg_T2 = 0xEBB0, | |
616 | OP_B_T4a = 0xF000, | |
617 | OP_AND_imm_T1 = 0xF000, | |
618 | OP_TST_imm = 0xF010, | |
619 | OP_ORR_imm_T1 = 0xF040, | |
620 | OP_MOV_imm_T2 = 0xF040, | |
621 | OP_MVN_imm = 0xF060, | |
622 | OP_EOR_imm_T1 = 0xF080, | |
623 | OP_ADD_imm_T3 = 0xF100, | |
624 | OP_ADD_S_imm_T3 = 0xF110, | |
625 | OP_CMN_imm = 0xF110, | |
626 | OP_SUB_imm_T3 = 0xF1A0, | |
627 | OP_SUB_S_imm_T3 = 0xF1B0, | |
628 | OP_CMP_imm_T2 = 0xF1B0, | |
629 | OP_ADD_imm_T4 = 0xF200, | |
630 | OP_MOV_imm_T3 = 0xF240, | |
631 | OP_SUB_imm_T4 = 0xF2A0, | |
632 | OP_MOVT = 0xF2C0, | |
f9bf01c6 | 633 | OP_NOP_T2a = 0xF3AF, |
ba379fdc A |
634 | OP_LDRH_reg_T2 = 0xF830, |
635 | OP_LDRH_imm_T3 = 0xF830, | |
636 | OP_STR_imm_T4 = 0xF840, | |
637 | OP_STR_reg_T2 = 0xF840, | |
638 | OP_LDR_imm_T4 = 0xF850, | |
639 | OP_LDR_reg_T2 = 0xF850, | |
640 | OP_LDRH_imm_T2 = 0xF8B0, | |
641 | OP_STR_imm_T3 = 0xF8C0, | |
642 | OP_LDR_imm_T3 = 0xF8D0, | |
643 | OP_LSL_reg_T2 = 0xFA00, | |
644 | OP_LSR_reg_T2 = 0xFA20, | |
645 | OP_ASR_reg_T2 = 0xFA40, | |
646 | OP_ROR_reg_T2 = 0xFA60, | |
647 | OP_SMULL_T1 = 0xFB80, | |
648 | } OpcodeID1; | |
649 | ||
650 | typedef enum { | |
651 | OP_B_T4b = 0x9000, | |
f9bf01c6 | 652 | OP_NOP_T2b = 0x8000, |
ba379fdc A |
653 | } OpcodeID2; |
654 | ||
655 | struct FourFours { | |
656 | FourFours(unsigned f3, unsigned f2, unsigned f1, unsigned f0) | |
657 | { | |
658 | m_u.f0 = f0; | |
659 | m_u.f1 = f1; | |
660 | m_u.f2 = f2; | |
661 | m_u.f3 = f3; | |
662 | } | |
663 | ||
664 | union { | |
665 | unsigned value; | |
666 | struct { | |
667 | unsigned f0 : 4; | |
668 | unsigned f1 : 4; | |
669 | unsigned f2 : 4; | |
670 | unsigned f3 : 4; | |
671 | }; | |
672 | } m_u; | |
673 | }; | |
674 | ||
675 | class ARMInstructionFormatter; | |
676 | ||
677 | // false means else! | |
678 | bool ifThenElseConditionBit(Condition condition, bool isIf) | |
679 | { | |
680 | return isIf ? (condition & 1) : !(condition & 1); | |
681 | } | |
682 | uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if, bool inst4if) | |
683 | { | |
684 | int mask = (ifThenElseConditionBit(condition, inst2if) << 3) | |
685 | | (ifThenElseConditionBit(condition, inst3if) << 2) | |
686 | | (ifThenElseConditionBit(condition, inst4if) << 1) | |
687 | | 1; | |
688 | ASSERT((condition != ConditionAL) || (mask & (mask - 1))); | |
689 | return (condition << 4) | mask; | |
690 | } | |
691 | uint8_t ifThenElse(Condition condition, bool inst2if, bool inst3if) | |
692 | { | |
693 | int mask = (ifThenElseConditionBit(condition, inst2if) << 3) | |
694 | | (ifThenElseConditionBit(condition, inst3if) << 2) | |
695 | | 2; | |
696 | ASSERT((condition != ConditionAL) || (mask & (mask - 1))); | |
697 | return (condition << 4) | mask; | |
698 | } | |
699 | uint8_t ifThenElse(Condition condition, bool inst2if) | |
700 | { | |
701 | int mask = (ifThenElseConditionBit(condition, inst2if) << 3) | |
702 | | 4; | |
703 | ASSERT((condition != ConditionAL) || (mask & (mask - 1))); | |
704 | return (condition << 4) | mask; | |
705 | } | |
706 | ||
707 | uint8_t ifThenElse(Condition condition) | |
708 | { | |
709 | int mask = 8; | |
710 | ASSERT((condition != ConditionAL) || (mask & (mask - 1))); | |
711 | return (condition << 4) | mask; | |
712 | } | |
713 | ||
714 | public: | |
715 | ||
716 | void add(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) | |
717 | { | |
718 | // Rd can only be SP if Rn is also SP. | |
f9bf01c6 A |
719 | ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); |
720 | ASSERT(rd != ARMRegisters::pc); | |
721 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
722 | ASSERT(imm.isValid()); |
723 | ||
f9bf01c6 | 724 | if (rn == ARMRegisters::sp) { |
ba379fdc A |
725 | if (!(rd & 8) && imm.isUInt10()) { |
726 | m_formatter.oneWordOp5Reg3Imm8(OP_ADD_SP_imm_T1, rd, imm.getUInt10() >> 2); | |
727 | return; | |
f9bf01c6 | 728 | } else if ((rd == ARMRegisters::sp) && imm.isUInt9()) { |
ba379fdc A |
729 | m_formatter.oneWordOp9Imm7(OP_ADD_SP_imm_T2, imm.getUInt9() >> 2); |
730 | return; | |
731 | } | |
732 | } else if (!((rd | rn) & 8)) { | |
733 | if (imm.isUInt3()) { | |
734 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); | |
735 | return; | |
736 | } else if ((rd == rn) && imm.isUInt8()) { | |
737 | m_formatter.oneWordOp5Reg3Imm8(OP_ADD_imm_T2, rd, imm.getUInt8()); | |
738 | return; | |
739 | } | |
740 | } | |
741 | ||
742 | if (imm.isEncodedImm()) | |
743 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T3, rn, rd, imm); | |
744 | else { | |
745 | ASSERT(imm.isUInt12()); | |
746 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_imm_T4, rn, rd, imm); | |
747 | } | |
748 | } | |
749 | ||
750 | void add(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
751 | { | |
f9bf01c6 A |
752 | ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); |
753 | ASSERT(rd != ARMRegisters::pc); | |
754 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
755 | ASSERT(!BadReg(rm)); |
756 | m_formatter.twoWordOp12Reg4FourFours(OP_ADD_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
757 | } | |
758 | ||
759 | // NOTE: In an IT block, add doesn't modify the flags register. | |
760 | void add(RegisterID rd, RegisterID rn, RegisterID rm) | |
761 | { | |
762 | if (rd == rn) | |
763 | m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rm, rd); | |
764 | else if (rd == rm) | |
765 | m_formatter.oneWordOp8RegReg143(OP_ADD_reg_T2, rn, rd); | |
766 | else if (!((rd | rn | rm) & 8)) | |
767 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_reg_T1, rm, rn, rd); | |
768 | else | |
769 | add(rd, rn, rm, ShiftTypeAndAmount()); | |
770 | } | |
771 | ||
772 | // Not allowed in an IT (if then) block. | |
773 | void add_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) | |
774 | { | |
775 | // Rd can only be SP if Rn is also SP. | |
f9bf01c6 A |
776 | ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); |
777 | ASSERT(rd != ARMRegisters::pc); | |
778 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
779 | ASSERT(imm.isEncodedImm()); |
780 | ||
781 | if (!((rd | rn) & 8)) { | |
782 | if (imm.isUInt3()) { | |
783 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_S_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); | |
784 | return; | |
785 | } else if ((rd == rn) && imm.isUInt8()) { | |
786 | m_formatter.oneWordOp5Reg3Imm8(OP_ADD_S_imm_T2, rd, imm.getUInt8()); | |
787 | return; | |
788 | } | |
789 | } | |
790 | ||
791 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ADD_S_imm_T3, rn, rd, imm); | |
792 | } | |
793 | ||
794 | // Not allowed in an IT (if then) block? | |
795 | void add_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
796 | { | |
f9bf01c6 A |
797 | ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); |
798 | ASSERT(rd != ARMRegisters::pc); | |
799 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
800 | ASSERT(!BadReg(rm)); |
801 | m_formatter.twoWordOp12Reg4FourFours(OP_ADD_S_reg_T3, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
802 | } | |
803 | ||
804 | // Not allowed in an IT (if then) block. | |
805 | void add_S(RegisterID rd, RegisterID rn, RegisterID rm) | |
806 | { | |
807 | if (!((rd | rn | rm) & 8)) | |
808 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_ADD_S_reg_T1, rm, rn, rd); | |
809 | else | |
810 | add_S(rd, rn, rm, ShiftTypeAndAmount()); | |
811 | } | |
812 | ||
813 | void ARM_and(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) | |
814 | { | |
815 | ASSERT(!BadReg(rd)); | |
816 | ASSERT(!BadReg(rn)); | |
817 | ASSERT(imm.isEncodedImm()); | |
818 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_AND_imm_T1, rn, rd, imm); | |
819 | } | |
820 | ||
821 | void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
822 | { | |
823 | ASSERT(!BadReg(rd)); | |
824 | ASSERT(!BadReg(rn)); | |
825 | ASSERT(!BadReg(rm)); | |
826 | m_formatter.twoWordOp12Reg4FourFours(OP_AND_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
827 | } | |
828 | ||
829 | void ARM_and(RegisterID rd, RegisterID rn, RegisterID rm) | |
830 | { | |
831 | if ((rd == rn) && !((rd | rm) & 8)) | |
832 | m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rm, rd); | |
833 | else if ((rd == rm) && !((rd | rn) & 8)) | |
834 | m_formatter.oneWordOp10Reg3Reg3(OP_AND_reg_T1, rn, rd); | |
835 | else | |
836 | ARM_and(rd, rn, rm, ShiftTypeAndAmount()); | |
837 | } | |
838 | ||
839 | void asr(RegisterID rd, RegisterID rm, int32_t shiftAmount) | |
840 | { | |
841 | ASSERT(!BadReg(rd)); | |
842 | ASSERT(!BadReg(rm)); | |
843 | ShiftTypeAndAmount shift(SRType_ASR, shiftAmount); | |
844 | m_formatter.twoWordOp16FourFours(OP_ASR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
845 | } | |
846 | ||
847 | void asr(RegisterID rd, RegisterID rn, RegisterID rm) | |
848 | { | |
849 | ASSERT(!BadReg(rd)); | |
850 | ASSERT(!BadReg(rn)); | |
851 | ASSERT(!BadReg(rm)); | |
852 | m_formatter.twoWordOp12Reg4FourFours(OP_ASR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); | |
853 | } | |
854 | ||
855 | // Only allowed in IT (if then) block if last instruction. | |
856 | JmpSrc b() | |
857 | { | |
858 | m_formatter.twoWordOp16Op16(OP_B_T4a, OP_B_T4b); | |
859 | return JmpSrc(m_formatter.size()); | |
860 | } | |
861 | ||
862 | // Only allowed in IT (if then) block if last instruction. | |
863 | JmpSrc blx(RegisterID rm) | |
864 | { | |
f9bf01c6 | 865 | ASSERT(rm != ARMRegisters::pc); |
ba379fdc A |
866 | m_formatter.oneWordOp8RegReg143(OP_BLX, rm, (RegisterID)8); |
867 | return JmpSrc(m_formatter.size()); | |
868 | } | |
869 | ||
870 | // Only allowed in IT (if then) block if last instruction. | |
871 | JmpSrc bx(RegisterID rm) | |
872 | { | |
873 | m_formatter.oneWordOp8RegReg143(OP_BX, rm, (RegisterID)0); | |
874 | return JmpSrc(m_formatter.size()); | |
875 | } | |
876 | ||
877 | void bkpt(uint8_t imm=0) | |
878 | { | |
879 | m_formatter.oneWordOp8Imm8(OP_BKPT, imm); | |
880 | } | |
881 | ||
882 | void cmn(RegisterID rn, ARMThumbImmediate imm) | |
883 | { | |
f9bf01c6 | 884 | ASSERT(rn != ARMRegisters::pc); |
ba379fdc A |
885 | ASSERT(imm.isEncodedImm()); |
886 | ||
887 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMN_imm, rn, (RegisterID)0xf, imm); | |
888 | } | |
889 | ||
890 | void cmp(RegisterID rn, ARMThumbImmediate imm) | |
891 | { | |
f9bf01c6 | 892 | ASSERT(rn != ARMRegisters::pc); |
ba379fdc A |
893 | ASSERT(imm.isEncodedImm()); |
894 | ||
895 | if (!(rn & 8) && imm.isUInt8()) | |
896 | m_formatter.oneWordOp5Reg3Imm8(OP_CMP_imm_T1, rn, imm.getUInt8()); | |
897 | else | |
898 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_CMP_imm_T2, rn, (RegisterID)0xf, imm); | |
899 | } | |
900 | ||
901 | void cmp(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
902 | { | |
f9bf01c6 | 903 | ASSERT(rn != ARMRegisters::pc); |
ba379fdc A |
904 | ASSERT(!BadReg(rm)); |
905 | m_formatter.twoWordOp12Reg4FourFours(OP_CMP_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); | |
906 | } | |
907 | ||
908 | void cmp(RegisterID rn, RegisterID rm) | |
909 | { | |
910 | if ((rn | rm) & 8) | |
911 | cmp(rn, rm, ShiftTypeAndAmount()); | |
912 | else | |
913 | m_formatter.oneWordOp10Reg3Reg3(OP_CMP_reg_T1, rm, rn); | |
914 | } | |
915 | ||
916 | // xor is not spelled with an 'e'. :-( | |
917 | void eor(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) | |
918 | { | |
919 | ASSERT(!BadReg(rd)); | |
920 | ASSERT(!BadReg(rn)); | |
921 | ASSERT(imm.isEncodedImm()); | |
922 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_EOR_imm_T1, rn, rd, imm); | |
923 | } | |
924 | ||
925 | // xor is not spelled with an 'e'. :-( | |
926 | void eor(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
927 | { | |
928 | ASSERT(!BadReg(rd)); | |
929 | ASSERT(!BadReg(rn)); | |
930 | ASSERT(!BadReg(rm)); | |
931 | m_formatter.twoWordOp12Reg4FourFours(OP_EOR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
932 | } | |
933 | ||
934 | // xor is not spelled with an 'e'. :-( | |
935 | void eor(RegisterID rd, RegisterID rn, RegisterID rm) | |
936 | { | |
937 | if ((rd == rn) && !((rd | rm) & 8)) | |
938 | m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rm, rd); | |
939 | else if ((rd == rm) && !((rd | rn) & 8)) | |
940 | m_formatter.oneWordOp10Reg3Reg3(OP_EOR_reg_T1, rn, rd); | |
941 | else | |
942 | eor(rd, rn, rm, ShiftTypeAndAmount()); | |
943 | } | |
944 | ||
945 | void it(Condition cond) | |
946 | { | |
947 | m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond)); | |
948 | } | |
949 | ||
950 | void it(Condition cond, bool inst2if) | |
951 | { | |
952 | m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if)); | |
953 | } | |
954 | ||
955 | void it(Condition cond, bool inst2if, bool inst3if) | |
956 | { | |
957 | m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if)); | |
958 | } | |
959 | ||
960 | void it(Condition cond, bool inst2if, bool inst3if, bool inst4if) | |
961 | { | |
962 | m_formatter.oneWordOp8Imm8(OP_IT, ifThenElse(cond, inst2if, inst3if, inst4if)); | |
963 | } | |
964 | ||
f9bf01c6 | 965 | // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. |
ba379fdc A |
966 | void ldr(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) |
967 | { | |
f9bf01c6 | 968 | ASSERT(rn != ARMRegisters::pc); // LDR (literal) |
ba379fdc A |
969 | ASSERT(imm.isUInt12()); |
970 | ||
971 | if (!((rt | rn) & 8) && imm.isUInt7()) | |
972 | m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDR_imm_T1, imm.getUInt7() >> 2, rn, rt); | |
f9bf01c6 | 973 | else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) |
ba379fdc A |
974 | m_formatter.oneWordOp5Reg3Imm8(OP_LDR_imm_T2, rt, imm.getUInt10() >> 2); |
975 | else | |
976 | m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T3, rn, rt, imm.getUInt12()); | |
977 | } | |
978 | ||
979 | // If index is set, this is a regular offset or a pre-indexed load; | |
980 | // if index is not set then is is a post-index load. | |
981 | // | |
982 | // If wback is set rn is updated - this is a pre or post index load, | |
983 | // if wback is not set this is a regular offset memory access. | |
984 | // | |
985 | // (-255 <= offset <= 255) | |
986 | // _reg = REG[rn] | |
987 | // _tmp = _reg + offset | |
988 | // MEM[index ? _tmp : _reg] = REG[rt] | |
989 | // if (wback) REG[rn] = _tmp | |
990 | void ldr(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) | |
991 | { | |
f9bf01c6 A |
992 | ASSERT(rt != ARMRegisters::pc); |
993 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
994 | ASSERT(index || wback); |
995 | ASSERT(!wback | (rt != rn)); | |
996 | ||
997 | bool add = true; | |
998 | if (offset < 0) { | |
999 | add = false; | |
1000 | offset = -offset; | |
1001 | } | |
1002 | ASSERT((offset & ~0xff) == 0); | |
1003 | ||
1004 | offset |= (wback << 8); | |
1005 | offset |= (add << 9); | |
1006 | offset |= (index << 10); | |
1007 | offset |= (1 << 11); | |
1008 | ||
1009 | m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDR_imm_T4, rn, rt, offset); | |
1010 | } | |
1011 | ||
f9bf01c6 | 1012 | // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. |
ba379fdc A |
1013 | void ldr(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift=0) |
1014 | { | |
f9bf01c6 | 1015 | ASSERT(rn != ARMRegisters::pc); // LDR (literal) |
ba379fdc A |
1016 | ASSERT(!BadReg(rm)); |
1017 | ASSERT(shift <= 3); | |
1018 | ||
1019 | if (!shift && !((rt | rn | rm) & 8)) | |
1020 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDR_reg_T1, rm, rn, rt); | |
1021 | else | |
1022 | m_formatter.twoWordOp12Reg4FourFours(OP_LDR_reg_T2, rn, FourFours(rt, 0, shift, rm)); | |
1023 | } | |
1024 | ||
f9bf01c6 | 1025 | // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. |
ba379fdc A |
1026 | void ldrh(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) |
1027 | { | |
f9bf01c6 | 1028 | ASSERT(rn != ARMRegisters::pc); // LDR (literal) |
ba379fdc A |
1029 | ASSERT(imm.isUInt12()); |
1030 | ||
1031 | if (!((rt | rn) & 8) && imm.isUInt6()) | |
1032 | m_formatter.oneWordOp5Imm5Reg3Reg3(OP_LDRH_imm_T1, imm.getUInt6() >> 2, rn, rt); | |
1033 | else | |
1034 | m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T2, rn, rt, imm.getUInt12()); | |
1035 | } | |
1036 | ||
1037 | // If index is set, this is a regular offset or a pre-indexed load; | |
1038 | // if index is not set then is is a post-index load. | |
1039 | // | |
1040 | // If wback is set rn is updated - this is a pre or post index load, | |
1041 | // if wback is not set this is a regular offset memory access. | |
1042 | // | |
1043 | // (-255 <= offset <= 255) | |
1044 | // _reg = REG[rn] | |
1045 | // _tmp = _reg + offset | |
1046 | // MEM[index ? _tmp : _reg] = REG[rt] | |
1047 | // if (wback) REG[rn] = _tmp | |
1048 | void ldrh(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) | |
1049 | { | |
f9bf01c6 A |
1050 | ASSERT(rt != ARMRegisters::pc); |
1051 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
1052 | ASSERT(index || wback); |
1053 | ASSERT(!wback | (rt != rn)); | |
1054 | ||
1055 | bool add = true; | |
1056 | if (offset < 0) { | |
1057 | add = false; | |
1058 | offset = -offset; | |
1059 | } | |
1060 | ASSERT((offset & ~0xff) == 0); | |
1061 | ||
1062 | offset |= (wback << 8); | |
1063 | offset |= (add << 9); | |
1064 | offset |= (index << 10); | |
1065 | offset |= (1 << 11); | |
1066 | ||
1067 | m_formatter.twoWordOp12Reg4Reg4Imm12(OP_LDRH_imm_T3, rn, rt, offset); | |
1068 | } | |
1069 | ||
1070 | void ldrh(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift=0) | |
1071 | { | |
1072 | ASSERT(!BadReg(rt)); // Memory hint | |
f9bf01c6 | 1073 | ASSERT(rn != ARMRegisters::pc); // LDRH (literal) |
ba379fdc A |
1074 | ASSERT(!BadReg(rm)); |
1075 | ASSERT(shift <= 3); | |
1076 | ||
1077 | if (!shift && !((rt | rn | rm) & 8)) | |
1078 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_LDRH_reg_T1, rm, rn, rt); | |
1079 | else | |
1080 | m_formatter.twoWordOp12Reg4FourFours(OP_LDRH_reg_T2, rn, FourFours(rt, 0, shift, rm)); | |
1081 | } | |
1082 | ||
1083 | void lsl(RegisterID rd, RegisterID rm, int32_t shiftAmount) | |
1084 | { | |
1085 | ASSERT(!BadReg(rd)); | |
1086 | ASSERT(!BadReg(rm)); | |
1087 | ShiftTypeAndAmount shift(SRType_LSL, shiftAmount); | |
1088 | m_formatter.twoWordOp16FourFours(OP_LSL_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
1089 | } | |
1090 | ||
1091 | void lsl(RegisterID rd, RegisterID rn, RegisterID rm) | |
1092 | { | |
1093 | ASSERT(!BadReg(rd)); | |
1094 | ASSERT(!BadReg(rn)); | |
1095 | ASSERT(!BadReg(rm)); | |
1096 | m_formatter.twoWordOp12Reg4FourFours(OP_LSL_reg_T2, rn, FourFours(0xf, rd, 0, rm)); | |
1097 | } | |
1098 | ||
1099 | void lsr(RegisterID rd, RegisterID rm, int32_t shiftAmount) | |
1100 | { | |
1101 | ASSERT(!BadReg(rd)); | |
1102 | ASSERT(!BadReg(rm)); | |
1103 | ShiftTypeAndAmount shift(SRType_LSR, shiftAmount); | |
1104 | m_formatter.twoWordOp16FourFours(OP_LSR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
1105 | } | |
1106 | ||
1107 | void lsr(RegisterID rd, RegisterID rn, RegisterID rm) | |
1108 | { | |
1109 | ASSERT(!BadReg(rd)); | |
1110 | ASSERT(!BadReg(rn)); | |
1111 | ASSERT(!BadReg(rm)); | |
1112 | m_formatter.twoWordOp12Reg4FourFours(OP_LSR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); | |
1113 | } | |
1114 | ||
1115 | void movT3(RegisterID rd, ARMThumbImmediate imm) | |
1116 | { | |
1117 | ASSERT(imm.isValid()); | |
1118 | ASSERT(!imm.isEncodedImm()); | |
1119 | ASSERT(!BadReg(rd)); | |
1120 | ||
1121 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T3, imm.m_value.imm4, rd, imm); | |
1122 | } | |
1123 | ||
1124 | void mov(RegisterID rd, ARMThumbImmediate imm) | |
1125 | { | |
1126 | ASSERT(imm.isValid()); | |
1127 | ASSERT(!BadReg(rd)); | |
1128 | ||
1129 | if ((rd < 8) && imm.isUInt8()) | |
1130 | m_formatter.oneWordOp5Reg3Imm8(OP_MOV_imm_T1, rd, imm.getUInt8()); | |
1131 | else if (imm.isEncodedImm()) | |
1132 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOV_imm_T2, 0xf, rd, imm); | |
1133 | else | |
1134 | movT3(rd, imm); | |
1135 | } | |
1136 | ||
1137 | void mov(RegisterID rd, RegisterID rm) | |
1138 | { | |
1139 | m_formatter.oneWordOp8RegReg143(OP_MOV_reg_T1, rm, rd); | |
1140 | } | |
1141 | ||
1142 | void movt(RegisterID rd, ARMThumbImmediate imm) | |
1143 | { | |
1144 | ASSERT(imm.isUInt16()); | |
1145 | ASSERT(!BadReg(rd)); | |
1146 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MOVT, imm.m_value.imm4, rd, imm); | |
1147 | } | |
1148 | ||
1149 | void mvn(RegisterID rd, ARMThumbImmediate imm) | |
1150 | { | |
1151 | ASSERT(imm.isEncodedImm()); | |
1152 | ASSERT(!BadReg(rd)); | |
1153 | ||
1154 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_MVN_imm, 0xf, rd, imm); | |
1155 | } | |
1156 | ||
1157 | void mvn(RegisterID rd, RegisterID rm, ShiftTypeAndAmount shift) | |
1158 | { | |
1159 | ASSERT(!BadReg(rd)); | |
1160 | ASSERT(!BadReg(rm)); | |
1161 | m_formatter.twoWordOp16FourFours(OP_MVN_reg_T2, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
1162 | } | |
1163 | ||
1164 | void mvn(RegisterID rd, RegisterID rm) | |
1165 | { | |
1166 | if (!((rd | rm) & 8)) | |
1167 | m_formatter.oneWordOp10Reg3Reg3(OP_MVN_reg_T1, rm, rd); | |
1168 | else | |
1169 | mvn(rd, rm, ShiftTypeAndAmount()); | |
1170 | } | |
1171 | ||
1172 | void orr(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) | |
1173 | { | |
1174 | ASSERT(!BadReg(rd)); | |
1175 | ASSERT(!BadReg(rn)); | |
1176 | ASSERT(imm.isEncodedImm()); | |
1177 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_ORR_imm_T1, rn, rd, imm); | |
1178 | } | |
1179 | ||
1180 | void orr(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
1181 | { | |
1182 | ASSERT(!BadReg(rd)); | |
1183 | ASSERT(!BadReg(rn)); | |
1184 | ASSERT(!BadReg(rm)); | |
1185 | m_formatter.twoWordOp12Reg4FourFours(OP_ORR_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
1186 | } | |
1187 | ||
1188 | void orr(RegisterID rd, RegisterID rn, RegisterID rm) | |
1189 | { | |
1190 | if ((rd == rn) && !((rd | rm) & 8)) | |
1191 | m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rm, rd); | |
1192 | else if ((rd == rm) && !((rd | rn) & 8)) | |
1193 | m_formatter.oneWordOp10Reg3Reg3(OP_ORR_reg_T1, rn, rd); | |
1194 | else | |
1195 | orr(rd, rn, rm, ShiftTypeAndAmount()); | |
1196 | } | |
1197 | ||
1198 | void ror(RegisterID rd, RegisterID rm, int32_t shiftAmount) | |
1199 | { | |
1200 | ASSERT(!BadReg(rd)); | |
1201 | ASSERT(!BadReg(rm)); | |
1202 | ShiftTypeAndAmount shift(SRType_ROR, shiftAmount); | |
1203 | m_formatter.twoWordOp16FourFours(OP_ROR_imm_T1, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
1204 | } | |
1205 | ||
1206 | void ror(RegisterID rd, RegisterID rn, RegisterID rm) | |
1207 | { | |
1208 | ASSERT(!BadReg(rd)); | |
1209 | ASSERT(!BadReg(rn)); | |
1210 | ASSERT(!BadReg(rm)); | |
1211 | m_formatter.twoWordOp12Reg4FourFours(OP_ROR_reg_T2, rn, FourFours(0xf, rd, 0, rm)); | |
1212 | } | |
1213 | ||
1214 | void smull(RegisterID rdLo, RegisterID rdHi, RegisterID rn, RegisterID rm) | |
1215 | { | |
1216 | ASSERT(!BadReg(rdLo)); | |
1217 | ASSERT(!BadReg(rdHi)); | |
1218 | ASSERT(!BadReg(rn)); | |
1219 | ASSERT(!BadReg(rm)); | |
1220 | ASSERT(rdLo != rdHi); | |
1221 | m_formatter.twoWordOp12Reg4FourFours(OP_SMULL_T1, rn, FourFours(rdLo, rdHi, 0, rm)); | |
1222 | } | |
1223 | ||
f9bf01c6 | 1224 | // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. |
ba379fdc A |
1225 | void str(RegisterID rt, RegisterID rn, ARMThumbImmediate imm) |
1226 | { | |
f9bf01c6 A |
1227 | ASSERT(rt != ARMRegisters::pc); |
1228 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
1229 | ASSERT(imm.isUInt12()); |
1230 | ||
1231 | if (!((rt | rn) & 8) && imm.isUInt7()) | |
1232 | m_formatter.oneWordOp5Imm5Reg3Reg3(OP_STR_imm_T1, imm.getUInt7() >> 2, rn, rt); | |
f9bf01c6 | 1233 | else if ((rn == ARMRegisters::sp) && !(rt & 8) && imm.isUInt10()) |
ba379fdc A |
1234 | m_formatter.oneWordOp5Reg3Imm8(OP_STR_imm_T2, rt, imm.getUInt10() >> 2); |
1235 | else | |
1236 | m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T3, rn, rt, imm.getUInt12()); | |
1237 | } | |
1238 | ||
1239 | // If index is set, this is a regular offset or a pre-indexed store; | |
1240 | // if index is not set then is is a post-index store. | |
1241 | // | |
1242 | // If wback is set rn is updated - this is a pre or post index store, | |
1243 | // if wback is not set this is a regular offset memory access. | |
1244 | // | |
1245 | // (-255 <= offset <= 255) | |
1246 | // _reg = REG[rn] | |
1247 | // _tmp = _reg + offset | |
1248 | // MEM[index ? _tmp : _reg] = REG[rt] | |
1249 | // if (wback) REG[rn] = _tmp | |
1250 | void str(RegisterID rt, RegisterID rn, int offset, bool index, bool wback) | |
1251 | { | |
f9bf01c6 A |
1252 | ASSERT(rt != ARMRegisters::pc); |
1253 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
1254 | ASSERT(index || wback); |
1255 | ASSERT(!wback | (rt != rn)); | |
1256 | ||
1257 | bool add = true; | |
1258 | if (offset < 0) { | |
1259 | add = false; | |
1260 | offset = -offset; | |
1261 | } | |
1262 | ASSERT((offset & ~0xff) == 0); | |
1263 | ||
1264 | offset |= (wback << 8); | |
1265 | offset |= (add << 9); | |
1266 | offset |= (index << 10); | |
1267 | offset |= (1 << 11); | |
1268 | ||
1269 | m_formatter.twoWordOp12Reg4Reg4Imm12(OP_STR_imm_T4, rn, rt, offset); | |
1270 | } | |
1271 | ||
f9bf01c6 | 1272 | // rt == ARMRegisters::pc only allowed if last instruction in IT (if then) block. |
ba379fdc A |
1273 | void str(RegisterID rt, RegisterID rn, RegisterID rm, unsigned shift=0) |
1274 | { | |
f9bf01c6 | 1275 | ASSERT(rn != ARMRegisters::pc); |
ba379fdc A |
1276 | ASSERT(!BadReg(rm)); |
1277 | ASSERT(shift <= 3); | |
1278 | ||
1279 | if (!shift && !((rt | rn | rm) & 8)) | |
1280 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_STR_reg_T1, rm, rn, rt); | |
1281 | else | |
1282 | m_formatter.twoWordOp12Reg4FourFours(OP_STR_reg_T2, rn, FourFours(rt, 0, shift, rm)); | |
1283 | } | |
1284 | ||
1285 | void sub(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) | |
1286 | { | |
1287 | // Rd can only be SP if Rn is also SP. | |
f9bf01c6 A |
1288 | ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); |
1289 | ASSERT(rd != ARMRegisters::pc); | |
1290 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
1291 | ASSERT(imm.isValid()); |
1292 | ||
f9bf01c6 | 1293 | if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { |
ba379fdc A |
1294 | m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, imm.getUInt9() >> 2); |
1295 | return; | |
1296 | } else if (!((rd | rn) & 8)) { | |
1297 | if (imm.isUInt3()) { | |
1298 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); | |
1299 | return; | |
1300 | } else if ((rd == rn) && imm.isUInt8()) { | |
1301 | m_formatter.oneWordOp5Reg3Imm8(OP_SUB_imm_T2, rd, imm.getUInt8()); | |
1302 | return; | |
1303 | } | |
1304 | } | |
1305 | ||
1306 | if (imm.isEncodedImm()) | |
1307 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T3, rn, rd, imm); | |
1308 | else { | |
1309 | ASSERT(imm.isUInt12()); | |
1310 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_imm_T4, rn, rd, imm); | |
1311 | } | |
1312 | } | |
1313 | ||
1314 | void sub(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
1315 | { | |
f9bf01c6 A |
1316 | ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); |
1317 | ASSERT(rd != ARMRegisters::pc); | |
1318 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
1319 | ASSERT(!BadReg(rm)); |
1320 | m_formatter.twoWordOp12Reg4FourFours(OP_SUB_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
1321 | } | |
1322 | ||
1323 | // NOTE: In an IT block, add doesn't modify the flags register. | |
1324 | void sub(RegisterID rd, RegisterID rn, RegisterID rm) | |
1325 | { | |
1326 | if (!((rd | rn | rm) & 8)) | |
1327 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_reg_T1, rm, rn, rd); | |
1328 | else | |
1329 | sub(rd, rn, rm, ShiftTypeAndAmount()); | |
1330 | } | |
1331 | ||
1332 | // Not allowed in an IT (if then) block. | |
1333 | void sub_S(RegisterID rd, RegisterID rn, ARMThumbImmediate imm) | |
1334 | { | |
1335 | // Rd can only be SP if Rn is also SP. | |
f9bf01c6 A |
1336 | ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); |
1337 | ASSERT(rd != ARMRegisters::pc); | |
1338 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
1339 | ASSERT(imm.isValid()); |
1340 | ||
f9bf01c6 | 1341 | if ((rn == ARMRegisters::sp) && (rd == ARMRegisters::sp) && imm.isUInt9()) { |
ba379fdc A |
1342 | m_formatter.oneWordOp9Imm7(OP_SUB_SP_imm_T1, imm.getUInt9() >> 2); |
1343 | return; | |
1344 | } else if (!((rd | rn) & 8)) { | |
1345 | if (imm.isUInt3()) { | |
1346 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_S_imm_T1, (RegisterID)imm.getUInt3(), rn, rd); | |
1347 | return; | |
1348 | } else if ((rd == rn) && imm.isUInt8()) { | |
1349 | m_formatter.oneWordOp5Reg3Imm8(OP_SUB_S_imm_T2, rd, imm.getUInt8()); | |
1350 | return; | |
1351 | } | |
1352 | } | |
1353 | ||
1354 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_SUB_S_imm_T3, rn, rd, imm); | |
1355 | } | |
1356 | ||
1357 | // Not allowed in an IT (if then) block? | |
1358 | void sub_S(RegisterID rd, RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
1359 | { | |
f9bf01c6 A |
1360 | ASSERT((rd != ARMRegisters::sp) || (rn == ARMRegisters::sp)); |
1361 | ASSERT(rd != ARMRegisters::pc); | |
1362 | ASSERT(rn != ARMRegisters::pc); | |
ba379fdc A |
1363 | ASSERT(!BadReg(rm)); |
1364 | m_formatter.twoWordOp12Reg4FourFours(OP_SUB_S_reg_T2, rn, FourFours(shift.hi4(), rd, shift.lo4(), rm)); | |
1365 | } | |
1366 | ||
1367 | // Not allowed in an IT (if then) block. | |
1368 | void sub_S(RegisterID rd, RegisterID rn, RegisterID rm) | |
1369 | { | |
1370 | if (!((rd | rn | rm) & 8)) | |
1371 | m_formatter.oneWordOp7Reg3Reg3Reg3(OP_SUB_S_reg_T1, rm, rn, rd); | |
1372 | else | |
1373 | sub_S(rd, rn, rm, ShiftTypeAndAmount()); | |
1374 | } | |
1375 | ||
1376 | void tst(RegisterID rn, ARMThumbImmediate imm) | |
1377 | { | |
1378 | ASSERT(!BadReg(rn)); | |
1379 | ASSERT(imm.isEncodedImm()); | |
1380 | ||
1381 | m_formatter.twoWordOp5i6Imm4Reg4EncodedImm(OP_TST_imm, rn, (RegisterID)0xf, imm); | |
1382 | } | |
1383 | ||
1384 | void tst(RegisterID rn, RegisterID rm, ShiftTypeAndAmount shift) | |
1385 | { | |
1386 | ASSERT(!BadReg(rn)); | |
1387 | ASSERT(!BadReg(rm)); | |
1388 | m_formatter.twoWordOp12Reg4FourFours(OP_TST_reg_T2, rn, FourFours(shift.hi4(), 0xf, shift.lo4(), rm)); | |
1389 | } | |
1390 | ||
1391 | void tst(RegisterID rn, RegisterID rm) | |
1392 | { | |
1393 | if ((rn | rm) & 8) | |
1394 | tst(rn, rm, ShiftTypeAndAmount()); | |
1395 | else | |
1396 | m_formatter.oneWordOp10Reg3Reg3(OP_TST_reg_T1, rm, rn); | |
1397 | } | |
1398 | ||
1399 | void vadd_F64(FPRegisterID rd, FPRegisterID rn, FPRegisterID rm) | |
1400 | { | |
1401 | m_formatter.vfpOp(0x0b00ee30 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); | |
1402 | } | |
1403 | ||
1404 | void vcmp_F64(FPRegisterID rd, FPRegisterID rm) | |
1405 | { | |
1406 | m_formatter.vfpOp(0x0bc0eeb4 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rm, 21, 16)); | |
1407 | } | |
1408 | ||
1409 | void vcvt_F64_S32(FPRegisterID fd, FPRegisterID sm) | |
1410 | { | |
1411 | m_formatter.vfpOp(0x0bc0eeb8 | doubleRegisterMask(fd, 6, 28) | singleRegisterMask(sm, 16, 21)); | |
1412 | } | |
1413 | ||
1414 | void vcvt_S32_F64(FPRegisterID sd, FPRegisterID fm) | |
1415 | { | |
1416 | m_formatter.vfpOp(0x0bc0eebd | singleRegisterMask(sd, 28, 6) | doubleRegisterMask(fm, 21, 16)); | |
1417 | } | |
1418 | ||
1419 | void vldr(FPRegisterID rd, RegisterID rn, int32_t imm) | |
1420 | { | |
1421 | vmem(rd, rn, imm, true); | |
1422 | } | |
1423 | ||
1424 | void vmov(RegisterID rd, FPRegisterID sn) | |
1425 | { | |
1426 | m_formatter.vfpOp(0x0a10ee10 | (rd << 28) | singleRegisterMask(sn, 0, 23)); | |
1427 | } | |
1428 | ||
1429 | void vmov(FPRegisterID sn, RegisterID rd) | |
1430 | { | |
1431 | m_formatter.vfpOp(0x0a10ee00 | (rd << 28) | singleRegisterMask(sn, 0, 23)); | |
1432 | } | |
1433 | ||
1434 | // move FPSCR flags to APSR. | |
1435 | void vmrs_APSR_nzcv_FPSCR() | |
1436 | { | |
1437 | m_formatter.vfpOp(0xfa10eef1); | |
1438 | } | |
1439 | ||
1440 | void vmul_F64(FPRegisterID rd, FPRegisterID rn, FPRegisterID rm) | |
1441 | { | |
1442 | m_formatter.vfpOp(0x0b00ee20 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); | |
1443 | } | |
1444 | ||
1445 | void vstr(FPRegisterID rd, RegisterID rn, int32_t imm) | |
1446 | { | |
1447 | vmem(rd, rn, imm, false); | |
1448 | } | |
1449 | ||
1450 | void vsub_F64(FPRegisterID rd, FPRegisterID rn, FPRegisterID rm) | |
1451 | { | |
1452 | m_formatter.vfpOp(0x0b40ee30 | doubleRegisterMask(rd, 6, 28) | doubleRegisterMask(rn, 23, 0) | doubleRegisterMask(rm, 21, 16)); | |
1453 | } | |
1454 | ||
1455 | ||
1456 | JmpDst label() | |
1457 | { | |
1458 | return JmpDst(m_formatter.size()); | |
1459 | } | |
1460 | ||
1461 | JmpDst align(int alignment) | |
1462 | { | |
1463 | while (!m_formatter.isAligned(alignment)) | |
1464 | bkpt(); | |
1465 | ||
1466 | return label(); | |
1467 | } | |
1468 | ||
1469 | static void* getRelocatedAddress(void* code, JmpSrc jump) | |
1470 | { | |
1471 | ASSERT(jump.m_offset != -1); | |
1472 | ||
1473 | return reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(code) + jump.m_offset); | |
1474 | } | |
1475 | ||
1476 | static void* getRelocatedAddress(void* code, JmpDst destination) | |
1477 | { | |
1478 | ASSERT(destination.m_offset != -1); | |
1479 | ||
1480 | return reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(code) + destination.m_offset); | |
1481 | } | |
1482 | ||
1483 | static int getDifferenceBetweenLabels(JmpDst src, JmpDst dst) | |
1484 | { | |
1485 | return dst.m_offset - src.m_offset; | |
1486 | } | |
1487 | ||
1488 | static int getDifferenceBetweenLabels(JmpDst src, JmpSrc dst) | |
1489 | { | |
1490 | return dst.m_offset - src.m_offset; | |
1491 | } | |
1492 | ||
1493 | static int getDifferenceBetweenLabels(JmpSrc src, JmpDst dst) | |
1494 | { | |
1495 | return dst.m_offset - src.m_offset; | |
1496 | } | |
1497 | ||
1498 | // Assembler admin methods: | |
1499 | ||
1500 | size_t size() const | |
1501 | { | |
1502 | return m_formatter.size(); | |
1503 | } | |
1504 | ||
1505 | void* executableCopy(ExecutablePool* allocator) | |
1506 | { | |
1507 | void* copy = m_formatter.executableCopy(allocator); | |
f9bf01c6 A |
1508 | |
1509 | unsigned jumpCount = m_jumpsToLink.size(); | |
1510 | for (unsigned i = 0; i < jumpCount; ++i) { | |
1511 | uint16_t* location = reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(copy) + m_jumpsToLink[i].from); | |
1512 | uint16_t* target = reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(copy) + m_jumpsToLink[i].to); | |
1513 | linkJumpAbsolute(location, target); | |
1514 | } | |
1515 | m_jumpsToLink.clear(); | |
1516 | ||
ba379fdc A |
1517 | ASSERT(copy); |
1518 | return copy; | |
1519 | } | |
1520 | ||
1521 | static unsigned getCallReturnOffset(JmpSrc call) | |
1522 | { | |
1523 | ASSERT(call.m_offset >= 0); | |
1524 | return call.m_offset; | |
1525 | } | |
1526 | ||
1527 | // Linking & patching: | |
1528 | // | |
1529 | // 'link' and 'patch' methods are for use on unprotected code - such as the code | |
1530 | // within the AssemblerBuffer, and code being patched by the patch buffer. Once | |
1531 | // code has been finalized it is (platform support permitting) within a non- | |
1532 | // writable region of memory; to modify the code in an execute-only execuable | |
1533 | // pool the 'repatch' and 'relink' methods should be used. | |
1534 | ||
1535 | void linkJump(JmpSrc from, JmpDst to) | |
1536 | { | |
1537 | ASSERT(to.m_offset != -1); | |
1538 | ASSERT(from.m_offset != -1); | |
f9bf01c6 | 1539 | m_jumpsToLink.append(LinkRecord(from.m_offset, to.m_offset)); |
ba379fdc A |
1540 | } |
1541 | ||
1542 | static void linkJump(void* code, JmpSrc from, void* to) | |
1543 | { | |
1544 | ASSERT(from.m_offset != -1); | |
1545 | ||
1546 | uint16_t* location = reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(code) + from.m_offset); | |
f9bf01c6 | 1547 | linkJumpAbsolute(location, to); |
ba379fdc A |
1548 | } |
1549 | ||
1550 | // bah, this mathod should really be static, since it is used by the LinkBuffer. | |
1551 | // return a bool saying whether the link was successful? | |
1552 | static void linkCall(void* code, JmpSrc from, void* to) | |
1553 | { | |
1554 | ASSERT(!(reinterpret_cast<intptr_t>(code) & 1)); | |
1555 | ASSERT(from.m_offset != -1); | |
1556 | ASSERT(reinterpret_cast<intptr_t>(to) & 1); | |
1557 | ||
1558 | setPointer(reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(code) + from.m_offset) - 1, to); | |
1559 | } | |
1560 | ||
1561 | static void linkPointer(void* code, JmpDst where, void* value) | |
1562 | { | |
1563 | setPointer(reinterpret_cast<char*>(code) + where.m_offset, value); | |
1564 | } | |
1565 | ||
1566 | static void relinkJump(void* from, void* to) | |
1567 | { | |
1568 | ASSERT(!(reinterpret_cast<intptr_t>(from) & 1)); | |
1569 | ASSERT(!(reinterpret_cast<intptr_t>(to) & 1)); | |
1570 | ||
f9bf01c6 | 1571 | linkJumpAbsolute(reinterpret_cast<uint16_t*>(from), to); |
ba379fdc | 1572 | |
f9bf01c6 | 1573 | ExecutableAllocator::cacheFlush(reinterpret_cast<uint16_t*>(from) - 5, 5 * sizeof(uint16_t)); |
ba379fdc A |
1574 | } |
1575 | ||
1576 | static void relinkCall(void* from, void* to) | |
1577 | { | |
1578 | ASSERT(!(reinterpret_cast<intptr_t>(from) & 1)); | |
1579 | ASSERT(reinterpret_cast<intptr_t>(to) & 1); | |
1580 | ||
1581 | setPointer(reinterpret_cast<uint16_t*>(from) - 1, to); | |
1582 | ||
1583 | ExecutableAllocator::cacheFlush(reinterpret_cast<uint16_t*>(from) - 5, 4 * sizeof(uint16_t)); | |
1584 | } | |
1585 | ||
1586 | static void repatchInt32(void* where, int32_t value) | |
1587 | { | |
1588 | ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); | |
1589 | ||
1590 | setInt32(where, value); | |
1591 | ||
1592 | ExecutableAllocator::cacheFlush(reinterpret_cast<uint16_t*>(where) - 4, 4 * sizeof(uint16_t)); | |
1593 | } | |
1594 | ||
1595 | static void repatchPointer(void* where, void* value) | |
1596 | { | |
1597 | ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); | |
1598 | ||
1599 | setPointer(where, value); | |
1600 | ||
1601 | ExecutableAllocator::cacheFlush(reinterpret_cast<uint16_t*>(where) - 4, 4 * sizeof(uint16_t)); | |
1602 | } | |
1603 | ||
1604 | static void repatchLoadPtrToLEA(void* where) | |
1605 | { | |
1606 | ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); | |
1607 | ||
1608 | uint16_t* loadOp = reinterpret_cast<uint16_t*>(where) + 4; | |
1609 | ASSERT((*loadOp & 0xfff0) == OP_LDR_reg_T2); | |
1610 | ||
1611 | *loadOp = OP_ADD_reg_T3 | (*loadOp & 0xf); | |
1612 | ExecutableAllocator::cacheFlush(loadOp, sizeof(uint16_t)); | |
1613 | } | |
1614 | ||
1615 | private: | |
1616 | ||
1617 | // Arm vfp addresses can be offset by a 9-bit ones-comp immediate, left shifted by 2. | |
1618 | // (i.e. +/-(0..255) 32-bit words) | |
1619 | void vmem(FPRegisterID rd, RegisterID rn, int32_t imm, bool isLoad) | |
1620 | { | |
1621 | bool up; | |
1622 | uint32_t offset; | |
1623 | if (imm < 0) { | |
1624 | offset = -imm; | |
1625 | up = false; | |
1626 | } else { | |
1627 | offset = imm; | |
1628 | up = true; | |
1629 | } | |
1630 | ||
1631 | // offset is effectively leftshifted by 2 already (the bottom two bits are zero, and not | |
1632 | // reperesented in the instruction. Left shift by 14, to mov it into position 0x00AA0000. | |
1633 | ASSERT((offset & ~(0xff << 2)) == 0); | |
1634 | offset <<= 14; | |
1635 | ||
1636 | m_formatter.vfpOp(0x0b00ed00 | offset | (up << 7) | (isLoad << 4) | doubleRegisterMask(rd, 6, 28) | rn); | |
1637 | } | |
1638 | ||
1639 | static void setInt32(void* code, uint32_t value) | |
1640 | { | |
1641 | uint16_t* location = reinterpret_cast<uint16_t*>(code); | |
f9bf01c6 | 1642 | ASSERT(isMOV_imm_T3(location - 4) && isMOVT(location - 2)); |
ba379fdc | 1643 | |
f9bf01c6 A |
1644 | ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(value)); |
1645 | ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(value >> 16)); | |
1646 | location[-4] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); | |
1647 | location[-3] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-3] >> 8) & 0xf, lo16); | |
1648 | location[-2] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); | |
1649 | location[-1] = twoWordOp5i6Imm4Reg4EncodedImmSecond((location[-1] >> 8) & 0xf, hi16); | |
ba379fdc A |
1650 | |
1651 | ExecutableAllocator::cacheFlush(location - 4, 4 * sizeof(uint16_t)); | |
1652 | } | |
1653 | ||
1654 | static void setPointer(void* code, void* value) | |
1655 | { | |
1656 | setInt32(code, reinterpret_cast<uint32_t>(value)); | |
1657 | } | |
1658 | ||
f9bf01c6 A |
1659 | static bool isB(void* address) |
1660 | { | |
1661 | uint16_t* instruction = static_cast<uint16_t*>(address); | |
1662 | return ((instruction[0] & 0xf800) == OP_B_T4a) && ((instruction[1] & 0xd000) == OP_B_T4b); | |
1663 | } | |
ba379fdc | 1664 | |
f9bf01c6 A |
1665 | static bool isBX(void* address) |
1666 | { | |
1667 | uint16_t* instruction = static_cast<uint16_t*>(address); | |
1668 | return (instruction[0] & 0xff87) == OP_BX; | |
1669 | } | |
ba379fdc | 1670 | |
f9bf01c6 A |
1671 | static bool isMOV_imm_T3(void* address) |
1672 | { | |
1673 | uint16_t* instruction = static_cast<uint16_t*>(address); | |
1674 | return ((instruction[0] & 0xFBF0) == OP_MOV_imm_T3) && ((instruction[1] & 0x8000) == 0); | |
1675 | } | |
ba379fdc | 1676 | |
f9bf01c6 A |
1677 | static bool isMOVT(void* address) |
1678 | { | |
1679 | uint16_t* instruction = static_cast<uint16_t*>(address); | |
1680 | return ((instruction[0] & 0xFBF0) == OP_MOVT) && ((instruction[1] & 0x8000) == 0); | |
ba379fdc A |
1681 | } |
1682 | ||
f9bf01c6 | 1683 | static bool isNOP_T1(void* address) |
ba379fdc | 1684 | { |
f9bf01c6 A |
1685 | uint16_t* instruction = static_cast<uint16_t*>(address); |
1686 | return instruction[0] == OP_NOP_T1; | |
ba379fdc | 1687 | } |
f9bf01c6 A |
1688 | |
1689 | static bool isNOP_T2(void* address) | |
ba379fdc | 1690 | { |
f9bf01c6 A |
1691 | uint16_t* instruction = static_cast<uint16_t*>(address); |
1692 | return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b); | |
1693 | } | |
1694 | ||
1695 | static void linkJumpAbsolute(uint16_t* instruction, void* target) | |
1696 | { | |
1697 | // FIMXE: this should be up in the MacroAssembler layer. :-( | |
1698 | const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; | |
1699 | ||
1700 | ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); | |
1701 | ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); | |
1702 | ||
1703 | ASSERT( (isMOV_imm_T3(instruction - 5) && isMOVT(instruction - 3) && isBX(instruction - 1)) | |
1704 | || (isNOP_T1(instruction - 5) && isNOP_T2(instruction - 4) && isB(instruction - 2)) ); | |
1705 | ||
1706 | intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction)); | |
1707 | if (((relative << 7) >> 7) == relative) { | |
1708 | // ARM encoding for the top two bits below the sign bit is 'peculiar'. | |
1709 | if (relative >= 0) | |
1710 | relative ^= 0xC00000; | |
1711 | ||
1712 | // All branch offsets should be an even distance. | |
1713 | ASSERT(!(relative & 1)); | |
1714 | // There may be a better way to fix this, but right now put the NOPs first, since in the | |
1715 | // case of an conditional branch this will be coming after an ITTT predicating *three* | |
1716 | // instructions! Looking backwards to modify the ITTT to an IT is not easy, due to | |
1717 | // variable wdith encoding - the previous instruction might *look* like an ITTT but | |
1718 | // actually be the second half of a 2-word op. | |
1719 | instruction[-5] = OP_NOP_T1; | |
1720 | instruction[-4] = OP_NOP_T2a; | |
1721 | instruction[-3] = OP_NOP_T2b; | |
1722 | instruction[-2] = OP_B_T4a | ((relative & 0x1000000) >> 14) | ((relative & 0x3ff000) >> 12); | |
1723 | instruction[-1] = OP_B_T4b | ((relative & 0x800000) >> 10) | ((relative & 0x400000) >> 11) | ((relative & 0xffe) >> 1); | |
1724 | } else { | |
1725 | ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) + 1)); | |
1726 | ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) >> 16)); | |
1727 | instruction[-5] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOV_imm_T3, lo16); | |
1728 | instruction[-4] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, lo16); | |
1729 | instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); | |
1730 | instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); | |
1731 | instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); | |
1732 | } | |
1733 | } | |
1734 | ||
1735 | static uint16_t twoWordOp5i6Imm4Reg4EncodedImmFirst(uint16_t op, ARMThumbImmediate imm) | |
1736 | { | |
1737 | return op | (imm.m_value.i << 10) | imm.m_value.imm4; | |
1738 | } | |
1739 | static uint16_t twoWordOp5i6Imm4Reg4EncodedImmSecond(uint16_t rd, ARMThumbImmediate imm) | |
1740 | { | |
1741 | return (imm.m_value.imm3 << 12) | (rd << 8) | imm.m_value.imm8; | |
ba379fdc A |
1742 | } |
1743 | ||
1744 | class ARMInstructionFormatter { | |
1745 | public: | |
1746 | void oneWordOp5Reg3Imm8(OpcodeID op, RegisterID rd, uint8_t imm) | |
1747 | { | |
1748 | m_buffer.putShort(op | (rd << 8) | imm); | |
1749 | } | |
1750 | ||
1751 | void oneWordOp5Imm5Reg3Reg3(OpcodeID op, uint8_t imm, RegisterID reg1, RegisterID reg2) | |
1752 | { | |
1753 | m_buffer.putShort(op | (imm << 6) | (reg1 << 3) | reg2); | |
1754 | } | |
1755 | ||
1756 | void oneWordOp7Reg3Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2, RegisterID reg3) | |
1757 | { | |
1758 | m_buffer.putShort(op | (reg1 << 6) | (reg2 << 3) | reg3); | |
1759 | } | |
1760 | ||
1761 | void oneWordOp8Imm8(OpcodeID op, uint8_t imm) | |
1762 | { | |
1763 | m_buffer.putShort(op | imm); | |
1764 | } | |
1765 | ||
1766 | void oneWordOp8RegReg143(OpcodeID op, RegisterID reg1, RegisterID reg2) | |
1767 | { | |
1768 | m_buffer.putShort(op | ((reg2 & 8) << 4) | (reg1 << 3) | (reg2 & 7)); | |
1769 | } | |
1770 | void oneWordOp9Imm7(OpcodeID op, uint8_t imm) | |
1771 | { | |
1772 | m_buffer.putShort(op | imm); | |
1773 | } | |
1774 | ||
1775 | void oneWordOp10Reg3Reg3(OpcodeID op, RegisterID reg1, RegisterID reg2) | |
1776 | { | |
1777 | m_buffer.putShort(op | (reg1 << 3) | reg2); | |
1778 | } | |
1779 | ||
1780 | void twoWordOp12Reg4FourFours(OpcodeID1 op, RegisterID reg, FourFours ff) | |
1781 | { | |
1782 | m_buffer.putShort(op | reg); | |
1783 | m_buffer.putShort(ff.m_u.value); | |
1784 | } | |
1785 | ||
1786 | void twoWordOp16FourFours(OpcodeID1 op, FourFours ff) | |
1787 | { | |
1788 | m_buffer.putShort(op); | |
1789 | m_buffer.putShort(ff.m_u.value); | |
1790 | } | |
1791 | ||
1792 | void twoWordOp16Op16(OpcodeID1 op1, OpcodeID2 op2) | |
1793 | { | |
1794 | m_buffer.putShort(op1); | |
1795 | m_buffer.putShort(op2); | |
1796 | } | |
1797 | ||
1798 | void twoWordOp5i6Imm4Reg4EncodedImm(OpcodeID1 op, int imm4, RegisterID rd, ARMThumbImmediate imm) | |
1799 | { | |
f9bf01c6 A |
1800 | ARMThumbImmediate newImm = imm; |
1801 | newImm.m_value.imm4 = imm4; | |
1802 | ||
1803 | m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmFirst(op, newImm)); | |
1804 | m_buffer.putShort(ARMv7Assembler::twoWordOp5i6Imm4Reg4EncodedImmSecond(rd, newImm)); | |
ba379fdc A |
1805 | } |
1806 | ||
1807 | void twoWordOp12Reg4Reg4Imm12(OpcodeID1 op, RegisterID reg1, RegisterID reg2, uint16_t imm) | |
1808 | { | |
1809 | m_buffer.putShort(op | reg1); | |
1810 | m_buffer.putShort((reg2 << 12) | imm); | |
1811 | } | |
1812 | ||
1813 | void vfpOp(int32_t op) | |
1814 | { | |
1815 | m_buffer.putInt(op); | |
1816 | } | |
1817 | ||
1818 | ||
1819 | // Administrative methods: | |
1820 | ||
1821 | size_t size() const { return m_buffer.size(); } | |
1822 | bool isAligned(int alignment) const { return m_buffer.isAligned(alignment); } | |
1823 | void* data() const { return m_buffer.data(); } | |
1824 | void* executableCopy(ExecutablePool* allocator) { return m_buffer.executableCopy(allocator); } | |
1825 | ||
1826 | private: | |
1827 | AssemblerBuffer m_buffer; | |
1828 | } m_formatter; | |
f9bf01c6 A |
1829 | |
1830 | Vector<LinkRecord> m_jumpsToLink; | |
ba379fdc A |
1831 | }; |
1832 | ||
1833 | } // namespace JSC | |
1834 | ||
f9bf01c6 | 1835 | #endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) |
ba379fdc A |
1836 | |
1837 | #endif // ARMAssembler_h |