1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=79:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Copyright (C) 2009, 2010 University of Szeged
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * ***** END LICENSE BLOCK ***** */
31 #ifndef ARMAssembler_h
32 #define ARMAssembler_h
34 #include "assembler/wtf/Platform.h"
36 // Some debug code uses s(n)printf for instruction logging.
39 #if ENABLE_ASSEMBLER && WTF_CPU_ARM_TRADITIONAL
41 #include "AssemblerBufferWithConstantPool.h"
42 #include "assembler/wtf/Assertions.h"
44 #include "methodjit/Logging.h"
47 #ifdef JS_METHODJIT_SPEW
48 # define MAYBE_PAD (isOOLPath ? "> " : "")
49 # define FIXME_INSN_PRINTING \
51 js::JaegerSpew(js::JSpew_Insns, \
52 IPFX "FIXME insn printing %s:%d\n", \
54 __FILE__, __LINE__); \
58 # define FIXME_INSN_PRINTING ((void) 0)
61 // TODO: We don't print the condition code in our JaegerSpew lines. Doing this
62 // is awkward whilst maintaining a consistent field width.
66 typedef uint32_t ARMWord;
68 namespace ARMRegisters {
130 } // namespace ARMRegisters
135 #ifdef JS_METHODJIT_SPEW
137 // Assign a default value to keep Valgrind quiet.
138 ARMAssembler() : isOOLPath(false) { }
143 typedef ARMRegisters::RegisterID RegisterID;
144 typedef ARMRegisters::FPRegisterID FPRegisterID;
145 typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer;
146 typedef SegmentedVector<int, 64> Jumps;
148 unsigned char *buffer() const { return m_buffer.buffer(); }
149 bool oom() const { return m_buffer.oom(); }
151 // ARM conditional constants
153 EQ = 0x00000000, // Zero
154 NE = 0x10000000, // Non-zero
170 // ARM instruction constants
206 #if WTF_ARM_ARCH_VERSION >= 5 || defined(__ARM_ARCH_4T__)
213 FTOSIZD = 0x0ebd0bc0,
215 #if WTF_ARM_ARCH_VERSION >= 5
220 #if WTF_ARM_ARCH_VERSION >= 7
228 OP2_IMMh = (1 << 22),
229 OP2_INV_IMM = (1 << 26),
231 OP2_OFSREG = (1 << 25),
235 // This flag is inlcuded in LDR and STR
241 // Masks of ARM instructions
243 BRANCH_MASK = 0x00ffffff,
245 SDT_MASK = 0x0c000000,
246 SDT_OFFSET_MASK = 0xfff
250 BOFFSET_MIN = -0x00800000,
251 BOFFSET_MAX = 0x007fffff,
256 padForAlign8 = (int)0x00,
257 padForAlign16 = (int)0x0000,
258 padForAlign32 = (int)0xe12fff7f // 'bkpt 0xffff'
268 static const ARMWord INVALID_IMM = 0xf0000000;
269 static const ARMWord InvalidBranchTarget = 0xffffffff;
270 static const int DefaultPrefetching = 2;
273 friend class ARMAssembler;
290 friend class ARMAssembler;
298 bool isUsed() const { return m_used; }
299 void used() { m_used = true; }
300 bool isValid() const { return m_offset != -1; }
306 ASSERT(m_offset == offset);
313 // Instruction formating
315 void emitInst(ARMWord op, int rd, int rn, ARMWord op2)
317 ASSERT ( ((op2 & ~OP2_IMM) <= 0xfff) || (((op2 & ~OP2_IMMh) <= 0xfff)) );
318 m_buffer.putInt(op | RN(rn) | RD(rd) | op2);
321 void and_r(int rd, int rn, ARMWord op2, Condition cc = AL)
323 spewInsWithOp2("and", cc, rd, rn, op2);
324 emitInst(static_cast<ARMWord>(cc) | AND, rd, rn, op2);
327 void ands_r(int rd, int rn, ARMWord op2, Condition cc = AL)
329 spewInsWithOp2("ands", cc, rd, rn, op2);
330 emitInst(static_cast<ARMWord>(cc) | AND | SET_CC, rd, rn, op2);
333 void eor_r(int rd, int rn, ARMWord op2, Condition cc = AL)
335 spewInsWithOp2("eor", cc, rd, rn, op2);
336 emitInst(static_cast<ARMWord>(cc) | EOR, rd, rn, op2);
339 void eors_r(int rd, int rn, ARMWord op2, Condition cc = AL)
341 spewInsWithOp2("eors", cc, rd, rn, op2);
342 emitInst(static_cast<ARMWord>(cc) | EOR | SET_CC, rd, rn, op2);
345 void sub_r(int rd, int rn, ARMWord op2, Condition cc = AL)
347 spewInsWithOp2("sub", cc, rd, rn, op2);
348 emitInst(static_cast<ARMWord>(cc) | SUB, rd, rn, op2);
351 void subs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
353 spewInsWithOp2("subs", cc, rd, rn, op2);
354 emitInst(static_cast<ARMWord>(cc) | SUB | SET_CC, rd, rn, op2);
357 void rsb_r(int rd, int rn, ARMWord op2, Condition cc = AL)
359 spewInsWithOp2("rsb", cc, rd, rn, op2);
360 emitInst(static_cast<ARMWord>(cc) | RSB, rd, rn, op2);
363 void rsbs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
365 spewInsWithOp2("rsbs", cc, rd, rn, op2);
366 emitInst(static_cast<ARMWord>(cc) | RSB | SET_CC, rd, rn, op2);
369 void add_r(int rd, int rn, ARMWord op2, Condition cc = AL)
371 spewInsWithOp2("add", cc, rd, rn, op2);
372 emitInst(static_cast<ARMWord>(cc) | ADD, rd, rn, op2);
375 void adds_r(int rd, int rn, ARMWord op2, Condition cc = AL)
377 spewInsWithOp2("adds", cc, rd, rn, op2);
378 emitInst(static_cast<ARMWord>(cc) | ADD | SET_CC, rd, rn, op2);
381 void adc_r(int rd, int rn, ARMWord op2, Condition cc = AL)
383 spewInsWithOp2("adc", cc, rd, rn, op2);
384 emitInst(static_cast<ARMWord>(cc) | ADC, rd, rn, op2);
387 void adcs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
389 spewInsWithOp2("adcs", cc, rd, rn, op2);
390 emitInst(static_cast<ARMWord>(cc) | ADC | SET_CC, rd, rn, op2);
393 void sbc_r(int rd, int rn, ARMWord op2, Condition cc = AL)
395 spewInsWithOp2("sbc", cc, rd, rn, op2);
396 emitInst(static_cast<ARMWord>(cc) | SBC, rd, rn, op2);
399 void sbcs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
401 spewInsWithOp2("sbcs", cc, rd, rn, op2);
402 emitInst(static_cast<ARMWord>(cc) | SBC | SET_CC, rd, rn, op2);
405 void rsc_r(int rd, int rn, ARMWord op2, Condition cc = AL)
407 spewInsWithOp2("rsc", cc, rd, rn, op2);
408 emitInst(static_cast<ARMWord>(cc) | RSC, rd, rn, op2);
411 void rscs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
413 spewInsWithOp2("rscs", cc, rd, rn, op2);
414 emitInst(static_cast<ARMWord>(cc) | RSC | SET_CC, rd, rn, op2);
417 void tst_r(int rn, ARMWord op2, Condition cc = AL)
419 spewInsWithOp2("tst", cc, rn, op2);
420 emitInst(static_cast<ARMWord>(cc) | TST | SET_CC, 0, rn, op2);
423 void teq_r(int rn, ARMWord op2, Condition cc = AL)
425 spewInsWithOp2("teq", cc, rn, op2);
426 emitInst(static_cast<ARMWord>(cc) | TEQ | SET_CC, 0, rn, op2);
429 void cmp_r(int rn, ARMWord op2, Condition cc = AL)
431 spewInsWithOp2("cmp", cc, rn, op2);
432 emitInst(static_cast<ARMWord>(cc) | CMP | SET_CC, 0, rn, op2);
435 void cmn_r(int rn, ARMWord op2, Condition cc = AL)
437 spewInsWithOp2("cmn", cc, rn, op2);
438 emitInst(static_cast<ARMWord>(cc) | CMN | SET_CC, 0, rn, op2);
441 void orr_r(int rd, int rn, ARMWord op2, Condition cc = AL)
443 spewInsWithOp2("orr", cc, rd, rn, op2);
444 emitInst(static_cast<ARMWord>(cc) | ORR, rd, rn, op2);
447 void orrs_r(int rd, int rn, ARMWord op2, Condition cc = AL)
449 spewInsWithOp2("orrs", cc, rd, rn, op2);
450 emitInst(static_cast<ARMWord>(cc) | ORR | SET_CC, rd, rn, op2);
453 void mov_r(int rd, ARMWord op2, Condition cc = AL)
455 spewInsWithOp2("mov", cc, rd, op2);
456 emitInst(static_cast<ARMWord>(cc) | MOV, rd, ARMRegisters::r0, op2);
459 #if WTF_ARM_ARCH_VERSION >= 7
460 void movw_r(int rd, ARMWord op2, Condition cc = AL)
462 ASSERT((op2 | 0xf0fff) == 0xf0fff);
463 js::JaegerSpew(js::JSpew_Insns,
464 IPFX "%-15s %s, 0x%04x\n", MAYBE_PAD, "movw", nameGpReg(rd), (op2 & 0xfff) | ((op2 >> 4) & 0xf000));
465 m_buffer.putInt(static_cast<ARMWord>(cc) | MOVW | RD(rd) | op2);
468 void movt_r(int rd, ARMWord op2, Condition cc = AL)
470 ASSERT((op2 | 0xf0fff) == 0xf0fff);
471 js::JaegerSpew(js::JSpew_Insns,
472 IPFX "%-15s %s, 0x%04x\n", MAYBE_PAD, "movt", nameGpReg(rd), (op2 & 0xfff) | ((op2 >> 4) & 0xf000));
473 m_buffer.putInt(static_cast<ARMWord>(cc) | MOVT | RD(rd) | op2);
477 void movs_r(int rd, ARMWord op2, Condition cc = AL)
479 spewInsWithOp2("movs", cc, rd, op2);
480 emitInst(static_cast<ARMWord>(cc) | MOV | SET_CC, rd, ARMRegisters::r0, op2);
483 void bic_r(int rd, int rn, ARMWord op2, Condition cc = AL)
485 spewInsWithOp2("bic", cc, rd, rn, op2);
486 emitInst(static_cast<ARMWord>(cc) | BIC, rd, rn, op2);
489 void bics_r(int rd, int rn, ARMWord op2, Condition cc = AL)
491 spewInsWithOp2("bics", cc, rd, rn, op2);
492 emitInst(static_cast<ARMWord>(cc) | BIC | SET_CC, rd, rn, op2);
495 void mvn_r(int rd, ARMWord op2, Condition cc = AL)
497 spewInsWithOp2("mvn", cc, rd, op2);
498 emitInst(static_cast<ARMWord>(cc) | MVN, rd, ARMRegisters::r0, op2);
501 void mvns_r(int rd, ARMWord op2, Condition cc = AL)
503 spewInsWithOp2("mvns", cc, rd, op2);
504 emitInst(static_cast<ARMWord>(cc) | MVN | SET_CC, rd, ARMRegisters::r0, op2);
507 void mul_r(int rd, int rn, int rm, Condition cc = AL)
509 spewInsWithOp2("mul", cc, rd, rn, static_cast<ARMWord>(rm));
510 m_buffer.putInt(static_cast<ARMWord>(cc) | MUL | RN(rd) | RS(rn) | RM(rm));
513 void muls_r(int rd, int rn, int rm, Condition cc = AL)
515 spewInsWithOp2("muls", cc, rd, rn, static_cast<ARMWord>(rm));
516 m_buffer.putInt(static_cast<ARMWord>(cc) | MUL | SET_CC | RN(rd) | RS(rn) | RM(rm));
519 void mull_r(int rdhi, int rdlo, int rn, int rm, Condition cc = AL)
521 js::JaegerSpew(js::JSpew_Insns,
522 IPFX "%-15s %s, %s, %s, %s\n", MAYBE_PAD, "mull", nameGpReg(rdlo), nameGpReg(rdhi), nameGpReg(rn), nameGpReg(rm));
523 m_buffer.putInt(static_cast<ARMWord>(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm));
526 void fcpyd_r(int dd, int dm, Condition cc = AL)
528 js::JaegerSpew(js::JSpew_Insns,
529 IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vmov.f64", nameFpRegD(dd), nameFpRegD(dm));
530 // TODO: emitInst doesn't work for VFP instructions, though it
531 // seems to work for current usage.
532 emitInst(static_cast<ARMWord>(cc) | FCPYD, dd, dd, dm);
535 void faddd_r(int dd, int dn, int dm, Condition cc = AL)
537 js::JaegerSpew(js::JSpew_Insns,
538 IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vadd.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
539 // TODO: emitInst doesn't work for VFP instructions, though it
540 // seems to work for current usage.
541 emitInst(static_cast<ARMWord>(cc) | FADDD, dd, dn, dm);
544 void fnegd_r(int dd, int dm, Condition cc = AL)
546 js::JaegerSpew(js::JSpew_Insns,
547 IPFX "%-15s %s, %s, %s, %s\n", MAYBE_PAD, "fnegd", nameFpRegD(dd), nameFpRegD(dm));
548 m_buffer.putInt(static_cast<ARMWord>(cc) | FNEGD | DD(dd) | DM(dm));
551 void fdivd_r(int dd, int dn, int dm, Condition cc = AL)
553 js::JaegerSpew(js::JSpew_Insns,
554 IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vdiv.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
555 // TODO: emitInst doesn't work for VFP instructions, though it
556 // seems to work for current usage.
557 emitInst(static_cast<ARMWord>(cc) | FDIVD, dd, dn, dm);
560 void fsubd_r(int dd, int dn, int dm, Condition cc = AL)
562 js::JaegerSpew(js::JSpew_Insns,
563 IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vsub.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
564 // TODO: emitInst doesn't work for VFP instructions, though it
565 // seems to work for current usage.
566 emitInst(static_cast<ARMWord>(cc) | FSUBD, dd, dn, dm);
569 void fmuld_r(int dd, int dn, int dm, Condition cc = AL)
571 js::JaegerSpew(js::JSpew_Insns,
572 IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, "vmul.f64", nameFpRegD(dd), nameFpRegD(dn), nameFpRegD(dm));
573 // TODO: emitInst doesn't work for VFP instructions, though it
574 // seems to work for current usage.
575 emitInst(static_cast<ARMWord>(cc) | FMULD, dd, dn, dm);
578 void fcmpd_r(int dd, int dm, Condition cc = AL)
580 js::JaegerSpew(js::JSpew_Insns,
581 IPFX "%-15s %s, %s\n", MAYBE_PAD, "vcmp.f64", nameFpRegD(dd), nameFpRegD(dm));
582 // TODO: emitInst doesn't work for VFP instructions, though it
583 // seems to work for current usage.
584 emitInst(static_cast<ARMWord>(cc) | FCMPD, dd, 0, dm);
587 void fsqrtd_r(int dd, int dm, Condition cc = AL)
589 js::JaegerSpew(js::JSpew_Insns,
590 IPFX "%-15s %s, %s\n", MAYBE_PAD, "vsqrt.f64", nameFpRegD(dd), nameFpRegD(dm));
591 // TODO: emitInst doesn't work for VFP instructions, though it
592 // seems to work for current usage.
593 emitInst(static_cast<ARMWord>(cc) | FSQRTD, dd, 0, dm);
596 void ldr_imm(int rd, ARMWord imm, Condition cc = AL)
599 snprintf(mnemonic, 16, "ldr%s", nameCC(cc));
600 js::JaegerSpew(js::JSpew_Insns,
601 IPFX "%-15s %s, =0x%x @ (%d) (reusable pool entry)\n", MAYBE_PAD, mnemonic, nameGpReg(rd), imm, static_cast<int32_t>(imm));
602 m_buffer.putIntWithConstantInt(static_cast<ARMWord>(cc) | DTR | DT_LOAD | DT_UP | RN(ARMRegisters::pc) | RD(rd), imm, true);
605 void ldr_un_imm(int rd, ARMWord imm, Condition cc = AL)
608 snprintf(mnemonic, 16, "ldr%s", nameCC(cc));
609 js::JaegerSpew(js::JSpew_Insns,
610 IPFX "%-15s %s, =0x%x @ (%d)\n", MAYBE_PAD, mnemonic, nameGpReg(rd), imm, static_cast<int32_t>(imm));
611 m_buffer.putIntWithConstantInt(static_cast<ARMWord>(cc) | DTR | DT_LOAD | DT_UP | RN(ARMRegisters::pc) | RD(rd), imm);
614 // Data transfers like this:
615 // LDR rd, [rb, +offset]
616 // STR rd, [rb, +offset]
617 void dtr_u(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
619 char const * mnemonic = (isLoad) ? ("ldr") : ("str");
620 js::JaegerSpew(js::JSpew_Insns,
621 IPFX "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
622 emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | DT_UP, rd, rb, offset);
625 // Data transfers like this:
628 void dtr_ur(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
630 char const * mnemonic = (isLoad) ? ("ldr") : ("str");
631 js::JaegerSpew(js::JSpew_Insns,
632 IPFX "%-15s %s, [%s, +%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
633 emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | DT_UP | OP2_OFSREG, rd, rb, rm);
636 // Data transfers like this:
637 // LDR rd, [rb, -offset]
638 // STR rd, [rb, -offset]
639 void dtr_d(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
641 char const * mnemonic = (isLoad) ? ("ldr") : ("str");
642 js::JaegerSpew(js::JSpew_Insns,
643 IPFX "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
644 emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0), rd, rb, offset);
647 // Data transfers like this:
650 void dtr_dr(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
652 char const * mnemonic = (isLoad) ? ("ldr") : ("str");
653 js::JaegerSpew(js::JSpew_Insns,
654 IPFX "%-15s %s, [%s, -%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
655 emitInst(static_cast<ARMWord>(cc) | DTR | (isLoad ? DT_LOAD : 0) | OP2_OFSREG, rd, rb, rm);
658 // Data transfers like this:
659 // LDRB rd, [rb, +offset]
660 // STRB rd, [rb, +offset]
661 void dtrb_u(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
663 char const * mnemonic = (isLoad) ? ("ldrb") : ("strb");
664 js::JaegerSpew(js::JSpew_Insns,
665 IPFX "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
666 emitInst(static_cast<ARMWord>(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0) | DT_UP, rd, rb, offset);
669 // Data transfers like this:
670 // LDRB rd, [rb, +rm]
671 // STRB rd, [rb, +rm]
672 void dtrb_ur(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
674 char const * mnemonic = (isLoad) ? ("ldrb") : ("strb");
675 js::JaegerSpew(js::JSpew_Insns,
676 IPFX "%-15s %s, [%s, +%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
677 emitInst(static_cast<ARMWord>(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0) | DT_UP | OP2_OFSREG, rd, rb, rm);
680 // Data transfers like this:
681 // LDRB rd, [rb, -offset]
682 // STRB rd, [rb, -offset]
683 void dtrb_d(bool isLoad, int rd, int rb, ARMWord offset, Condition cc = AL)
685 char const * mnemonic = (isLoad) ? ("ldrb") : ("strb");
686 js::JaegerSpew(js::JSpew_Insns,
687 IPFX "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), offset);
688 emitInst(static_cast<ARMWord>(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0), rd, rb, offset);
691 // Data transfers like this:
692 // LDRB rd, [rb, -rm]
693 // STRB rd, [rb, -rm]
694 void dtrb_dr(bool isLoad, int rd, int rb, int rm, Condition cc = AL)
696 char const * mnemonic = (isLoad) ? ("ldrb") : ("strb");
697 js::JaegerSpew(js::JSpew_Insns,
698 IPFX "%-15s %s, [%s, -%s]\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
699 emitInst(static_cast<ARMWord>(cc) | DTR | DT_BYTE | (isLoad ? DT_LOAD : 0) | OP2_OFSREG, rd, rb, rm);
702 void ldrh_r(int rd, int rb, int rm, Condition cc = AL)
704 js::JaegerSpew(js::JSpew_Insns,
705 IPFX "%-15s %s, [%s, +%s]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
706 emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_UP | DT_PRE, rd, rb, rm);
709 void ldrh_d(int rd, int rb, ARMWord offset, Condition cc = AL)
711 js::JaegerSpew(js::JSpew_Insns,
712 IPFX "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), offset);
713 emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_PRE, rd, rb, offset);
716 void ldrh_u(int rd, int rb, ARMWord offset, Condition cc = AL)
718 js::JaegerSpew(js::JSpew_Insns,
719 IPFX "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, "ldrh", nameGpReg(rd), nameGpReg(rb), offset);
720 emitInst(static_cast<ARMWord>(cc) | LDRH | HDT_UH | DT_UP | DT_PRE, rd, rb, offset);
723 void strh_r(int rb, int rm, int rd, Condition cc = AL)
725 js::JaegerSpew(js::JSpew_Insns,
726 IPFX "%-15s %s, [%s, +%s]\n", MAYBE_PAD, "strh", nameGpReg(rd), nameGpReg(rb), nameGpReg(rm));
727 emitInst(static_cast<ARMWord>(cc) | STRH | HDT_UH | DT_UP | DT_PRE, rd, rb, rm);
730 void fdtr_u(bool isLoad, int dd, int rn, ARMWord offset, Condition cc = AL)
732 char const * ins = isLoad ? "vldr.f64" : "vstr.f64";
733 js::JaegerSpew(js::JSpew_Insns,
734 IPFX "%-15s %s, [%s, #+%u]\n", MAYBE_PAD, ins, nameFpRegD(dd), nameGpReg(rn), offset);
735 ASSERT(offset <= 0xff);
736 emitInst(static_cast<ARMWord>(cc) | FDTR | DT_UP | (isLoad ? DT_LOAD : 0), dd, rn, offset);
739 void fdtr_d(bool isLoad, int dd, int rn, ARMWord offset, Condition cc = AL)
741 char const * ins = isLoad ? "vldr.f64" : "vstr.f64";
742 js::JaegerSpew(js::JSpew_Insns,
743 IPFX "%-15s %s, [%s, #-%u]\n", MAYBE_PAD, ins, nameFpRegD(dd), nameGpReg(rn), offset);
744 ASSERT(offset <= 0xff);
745 emitInst(static_cast<ARMWord>(cc) | FDTR | (isLoad ? DT_LOAD : 0), dd, rn, offset);
748 void push_r(int reg, Condition cc = AL)
750 js::JaegerSpew(js::JSpew_Insns,
751 IPFX "%-15s {%s}\n", MAYBE_PAD, "push", nameGpReg(reg));
752 ASSERT(ARMWord(reg) <= 0xf);
753 m_buffer.putInt(cc | DTR | DT_WB | RN(ARMRegisters::sp) | RD(reg) | 0x4);
756 void pop_r(int reg, Condition cc = AL)
758 js::JaegerSpew(js::JSpew_Insns,
759 IPFX "%-15s {%s}\n", MAYBE_PAD, "pop", nameGpReg(reg));
760 ASSERT(ARMWord(reg) <= 0xf);
761 m_buffer.putInt(cc | (DTR ^ DT_PRE) | DT_LOAD | DT_UP | RN(ARMRegisters::sp) | RD(reg) | 0x4);
764 inline void poke_r(int reg, Condition cc = AL)
766 dtr_d(false, ARMRegisters::sp, 0, reg, cc);
769 inline void peek_r(int reg, Condition cc = AL)
771 dtr_u(true, reg, ARMRegisters::sp, 0, cc);
774 void fmsr_r(int dd, int rn, Condition cc = AL)
776 // TODO: emitInst doesn't work for VFP instructions, though it
777 // seems to work for current usage.
778 emitInst(static_cast<ARMWord>(cc) | FMSR, rn, dd, 0);
781 void fmrs_r(int rd, int dn, Condition cc = AL)
783 // TODO: emitInst doesn't work for VFP instructions, though it
784 // seems to work for current usage.
785 emitInst(static_cast<ARMWord>(cc) | FMRS, rd, dn, 0);
788 void fsitod_r(int dd, int dm, Condition cc = AL)
790 // TODO: emitInst doesn't work for VFP instructions, though it
791 // seems to work for current usage.
792 emitInst(static_cast<ARMWord>(cc) | FSITOD, dd, 0, dm);
795 void ftosid_r(int fd, int dm, Condition cc = AL)
797 // TODO: emitInst doesn't work for VFP instructions, though it
798 // seems to work for current usage.
799 emitInst(static_cast<ARMWord>(cc) | FTOSID, fd, 0, dm);
802 void ftosizd_r(int fd, int dm, Condition cc = AL)
804 // TODO: emitInst doesn't work for VFP instructions, though it
805 // seems to work for current usage.
806 emitInst(static_cast<ARMWord>(cc) | FTOSIZD, fd, 0, dm);
809 void fmstat(Condition cc = AL)
811 // TODO: emitInst doesn't work for VFP instructions, though it
812 // seems to work for current usage.
813 m_buffer.putInt(static_cast<ARMWord>(cc) | FMSTAT);
816 #if WTF_ARM_ARCH_VERSION >= 5
817 void clz_r(int rd, int rm, Condition cc = AL)
819 spewInsWithOp2("clz", cc, rd, static_cast<ARMWord>(rm));
820 m_buffer.putInt(static_cast<ARMWord>(cc) | CLZ | RD(rd) | RM(rm));
824 void bkpt(ARMWord value)
826 #if WTF_ARM_ARCH_VERSION >= 5
827 js::JaegerSpew(js::JSpew_Insns,
828 IPFX "%-15s #0x%04x\n", MAYBE_PAD, "bkpt", value);
829 m_buffer.putInt(BKPT | ((value & 0xfff0) << 4) | (value & 0xf));
831 // Cannot access to Zero memory address
832 dtr_dr(true, ARMRegisters::S0, ARMRegisters::S0, ARMRegisters::S0);
836 void bx(int rm, Condition cc = AL)
838 #if WTF_ARM_ARCH_VERSION >= 5 || defined(__ARM_ARCH_4T__)
841 IPFX "bx%-13s %s\n", MAYBE_PAD, nameCC(cc), nameGpReg(rm));
842 emitInst(static_cast<ARMWord>(cc) | BX, 0, 0, RM(rm));
844 mov_r(ARMRegisters::pc, RM(rm), cc);
848 JmpSrc blx(int rm, Condition cc = AL)
850 #if WTF_ARM_ARCH_AT_LEAST(5)
851 int s = m_buffer.uncheckedSize();
854 IPFX "blx%-12s %s\n", MAYBE_PAD, nameCC(cc), nameGpReg(rm));
855 emitInst(static_cast<ARMWord>(cc) | BLX, 0, 0, RM(rm));
858 ensureSpace(2 * sizeof(ARMWord), 0);
859 mov_r(ARMRegisters::lr, ARMRegisters::pc, cc);
860 int s = m_buffer.uncheckedSize();
866 static ARMWord lsl(int reg, ARMWord value)
868 ASSERT(reg <= ARMRegisters::pc);
869 ASSERT(value <= 0x1f);
870 return reg | (value << 7) | (LSL << 5);
873 static ARMWord lsr(int reg, ARMWord value)
875 ASSERT(reg <= ARMRegisters::pc);
876 ASSERT(value <= 0x1f);
877 return reg | (value << 7) | (LSR << 5);
880 static ARMWord asr(int reg, ARMWord value)
882 ASSERT(reg <= ARMRegisters::pc);
883 ASSERT(value <= 0x1f);
884 return reg | (value << 7) | (ASR << 5);
887 static ARMWord lsl_r(int reg, int shiftReg)
889 ASSERT(reg <= ARMRegisters::pc);
890 ASSERT(shiftReg <= ARMRegisters::pc);
891 return reg | (shiftReg << 8) | (LSL << 5) | 0x10;
894 static ARMWord lsr_r(int reg, int shiftReg)
896 ASSERT(reg <= ARMRegisters::pc);
897 ASSERT(shiftReg <= ARMRegisters::pc);
898 return reg | (shiftReg << 8) | (LSR << 5) | 0x10;
901 static ARMWord asr_r(int reg, int shiftReg)
903 ASSERT(reg <= ARMRegisters::pc);
904 ASSERT(shiftReg <= ARMRegisters::pc);
905 return reg | (shiftReg << 8) | (ASR << 5) | 0x10;
910 void forceFlushConstantPool()
912 m_buffer.flushWithoutBarrier(true);
917 return m_buffer.size();
920 void ensureSpace(int insnSpace, int constSpace)
922 m_buffer.ensureSpace(insnSpace, constSpace);
925 void ensureSpace(int space)
927 m_buffer.ensureSpace(space);
930 int sizeOfConstantPool()
932 return m_buffer.sizeOfConstantPool();
936 void allowPoolFlush(bool allowFlush)
938 m_buffer.allowPoolFlush(allowFlush);
944 JmpDst label(m_buffer.size());
945 js::JaegerSpew(js::JSpew_Insns, IPFX "#label ((%d))\n", MAYBE_PAD, label.m_offset);
949 JmpDst align(int alignment)
951 while (!m_buffer.isAligned(alignment))
952 mov_r(ARMRegisters::r0, ARMRegisters::r0);
957 JmpSrc loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0)
959 // The 'useConstantPool' flag really just indicates where we have
960 // to use the constant pool, for repatching. We might still use it,
961 // so ensure there's space for a pool constant irrespective of
962 // 'useConstantPool'.
963 ensureSpace(sizeof(ARMWord), sizeof(ARMWord));
964 int s = m_buffer.uncheckedSize();
965 ldr_un_imm(rd, InvalidBranchTarget, cc);
966 m_jumps.append(s | (useConstantPool & 0x1));
970 JmpSrc jmp(Condition cc = AL, int useConstantPool = 0)
972 return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool);
975 void* executableCopy(ExecutablePool* allocator);
976 void* executableCopy(void* buffer);
977 void fixUpOffsets(void* buffer);
981 static ARMWord* getLdrImmAddress(ARMWord* insn)
983 #if WTF_ARM_ARCH_AT_LEAST(5)
985 if ((*insn & 0x0f7f0000) != 0x051f0000) {
987 ASSERT((*insn & 0x012fff30) == 0x012fff30);
991 // Must be an ldr ..., [pc +/- imm]
992 ASSERT((*insn & 0x0f7f0000) == 0x051f0000);
994 ARMWord addr = reinterpret_cast<ARMWord>(insn) + DefaultPrefetching * sizeof(ARMWord);
996 return reinterpret_cast<ARMWord*>(addr + (*insn & SDT_OFFSET_MASK));
997 return reinterpret_cast<ARMWord*>(addr - (*insn & SDT_OFFSET_MASK));
1000 static ARMWord* getLdrImmAddressOnPool(ARMWord* insn, uint32_t* constPool)
1002 // Must be an ldr ..., [pc +/- imm]
1003 ASSERT((*insn & 0x0f7f0000) == 0x051f0000);
1006 return reinterpret_cast<ARMWord*>(constPool + ((*insn & SDT_OFFSET_MASK) >> 1));
1007 return getLdrImmAddress(insn);
1010 static void patchPointerInternal(intptr_t from, void* to)
1012 ARMWord* insn = reinterpret_cast<ARMWord*>(from);
1013 ARMWord* addr = getLdrImmAddress(insn);
1014 *addr = reinterpret_cast<ARMWord>(to);
1017 static ARMWord patchConstantPoolLoad(ARMWord load, ARMWord value)
1019 value = (value << 1) + 1;
1020 ASSERT(!(value & ~0xfff));
1021 return (load & ~0xfff) | value;
1024 static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
1028 static void linkPointer(void* code, JmpDst from, void* to)
1030 js::JaegerSpew(js::JSpew_Insns,
1031 ISPFX "##linkPointer ((%p + %#x)) points to ((%p))\n",
1032 code, from.m_offset, to);
1034 patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
1037 static void repatchInt32(void* from, int32_t to)
1039 js::JaegerSpew(js::JSpew_Insns,
1040 ISPFX "##repatchInt32 ((%p)) holds ((%p))\n",
1043 patchPointerInternal(reinterpret_cast<intptr_t>(from), reinterpret_cast<void*>(to));
1046 static void repatchPointer(void* from, void* to)
1048 js::JaegerSpew(js::JSpew_Insns,
1049 ISPFX "##repatchPointer ((%p)) points to ((%p))\n",
1052 patchPointerInternal(reinterpret_cast<intptr_t>(from), to);
1055 static void repatchLoadPtrToLEA(void* from)
1057 // On arm, this is a patch from LDR to ADD. It is restricted conversion,
1058 // from special case to special case, altough enough for its purpose
1059 ARMWord* insn = reinterpret_cast<ARMWord*>(from);
1060 ASSERT((*insn & 0x0ff00f00) == 0x05900000);
1062 *insn = (*insn & 0xf00ff0ff) | 0x02800000;
1063 ExecutableAllocator::cacheFlush(insn, sizeof(ARMWord));
1066 static void repatchLEAToLoadPtr(void* from)
1068 // Like repatchLoadPtrToLEA, this is specialized for our purpose.
1069 ARMWord* insn = reinterpret_cast<ARMWord*>(from);
1070 if ((*insn & 0x0ff00f00) == 0x05900000)
1071 return; // Valid ldr instruction
1072 ASSERT((*insn & 0x0ff00000) == 0x02800000); // Valid add instruction
1073 ASSERT((*insn & 0x00000f00) == 0x00000000); // Simple-to-handle immediates (no rotate)
1075 *insn = (*insn & 0xf00ff0ff) | 0x05900000;
1076 ExecutableAllocator::cacheFlush(insn, sizeof(ARMWord));
1081 void linkJump(JmpSrc from, JmpDst to)
1083 ARMWord code = reinterpret_cast<ARMWord>(m_buffer.data());
1084 ARMWord* insn = reinterpret_cast<ARMWord*>(code + from.m_offset);
1085 ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress());
1087 js::JaegerSpew(js::JSpew_Insns,
1088 IPFX "##linkJump ((%#x)) jumps to ((%#x))\n", MAYBE_PAD,
1089 from.m_offset, to.m_offset);
1091 *addr = to.m_offset;
1094 static void linkJump(void* code, JmpSrc from, void* to)
1096 js::JaegerSpew(js::JSpew_Insns,
1097 ISPFX "##linkJump ((%p + %#x)) jumps to ((%p))\n",
1098 code, from.m_offset, to);
1100 patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
1103 static void relinkJump(void* from, void* to)
1105 js::JaegerSpew(js::JSpew_Insns,
1106 ISPFX "##relinkJump ((%p)) jumps to ((%p))\n",
1109 patchPointerInternal(reinterpret_cast<intptr_t>(from), to);
1112 static bool canRelinkJump(void* from, void* to)
1117 static void linkCall(void* code, JmpSrc from, void* to)
1119 js::JaegerSpew(js::JSpew_Insns,
1120 ISPFX "##linkCall ((%p + %#x)) jumps to ((%p))\n",
1121 code, from.m_offset, to);
1123 patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
1126 static void relinkCall(void* from, void* to)
1128 js::JaegerSpew(js::JSpew_Insns,
1129 ISPFX "##relinkCall ((%p)) jumps to ((%p))\n",
1132 patchPointerInternal(reinterpret_cast<intptr_t>(from), to);
1135 // Address operations
1137 static void* getRelocatedAddress(void* code, JmpSrc jump)
1139 return reinterpret_cast<void*>(reinterpret_cast<ARMWord*>(code) + jump.m_offset / sizeof(ARMWord));
1142 static void* getRelocatedAddress(void* code, JmpDst label)
1144 return reinterpret_cast<void*>(reinterpret_cast<ARMWord*>(code) + label.m_offset / sizeof(ARMWord));
1147 // Address differences
1149 static int getDifferenceBetweenLabels(JmpDst from, JmpSrc to)
1151 return to.m_offset - from.m_offset;
1154 static int getDifferenceBetweenLabels(JmpDst from, JmpDst to)
1156 return to.m_offset - from.m_offset;
1159 static unsigned getCallReturnOffset(JmpSrc call)
1161 return call.m_offset + sizeof(ARMWord);
1164 // Handle immediates
1166 static ARMWord getOp2Byte(ARMWord imm)
1168 ASSERT(imm <= 0xff);
1169 return OP2_IMMh | (imm & 0x0f) | ((imm & 0xf0) << 4) ;
1172 static ARMWord getOp2(ARMWord imm);
1174 #if WTF_ARM_ARCH_VERSION >= 7
1175 static ARMWord getImm16Op2(ARMWord imm)
1178 return (imm & 0xf000) << 4 | (imm & 0xfff);
1182 ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false);
1183 void moveImm(ARMWord imm, int dest);
1184 ARMWord encodeComplexImm(ARMWord imm, int dest);
1186 ARMWord getOffsetForHalfwordDataTransfer(ARMWord imm, int tmpReg)
1188 // Encode immediate data in the instruction if it is possible
1190 return getOp2Byte(imm);
1191 // Otherwise, store the data in a temporary register
1192 return encodeComplexImm(imm, tmpReg);
1195 // Memory load/store helpers
1197 void dataTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset);
1198 void dataTransfer8(bool isLoad, RegisterID srcDst, RegisterID base, int32_t offset);
1199 void baseIndexTransfer32(bool isLoad, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset);
1200 void doubleTransfer(bool isLoad, FPRegisterID srcDst, RegisterID base, int32_t offset);
1202 // Constant pool hnadlers
1204 static ARMWord placeConstantPoolBarrier(int offset)
1206 offset = (offset - sizeof(ARMWord)) >> 2;
1207 ASSERT((offset <= BOFFSET_MAX && offset >= BOFFSET_MIN));
1208 return AL | B | (offset & BRANCH_MASK);
1212 static char const * nameGpReg(int reg)
1216 static char const * names[] = {
1217 "r0", "r1", "r2", "r3",
1218 "r4", "r5", "r6", "r7",
1219 "r8", "r9", "r10", "r11",
1220 "ip", "sp", "lr", "pc"
1225 static char const * nameFpRegD(int reg)
1229 static char const * names[] = {
1230 "d0", "d1", "d2", "d3",
1231 "d4", "d5", "d6", "d7",
1232 "d8", "d9", "d10", "d11",
1233 "d12", "d13", "d14", "d15",
1234 "d16", "d17", "d18", "d19",
1235 "d20", "d21", "d22", "d23",
1236 "d24", "d25", "d26", "d27",
1237 "d28", "d29", "d30", "d31"
1242 static char const * nameCC(Condition cc)
1246 ASSERT((cc & 0x0fffffff) == 0);
1248 uint32_t ccIndex = cc >> 28;
1249 static char const * names[] = {
1257 " " // AL is the default, so don't show it.
1259 return names[ccIndex];
1262 // Decodes operand 2 immediate values (for debug output and assertions).
1263 inline uint32_t decOp2Imm(uint32_t op2)
1265 ASSERT((op2 & ~0xfff) == 0);
1267 uint32_t imm8 = op2 & 0xff;
1268 uint32_t rot = ((op2 >> 7) & 0x1e);
1270 // 'rot' is a right-rotate count.
1272 uint32_t imm = (imm8 >> rot);
1274 imm |= (imm8 << (32-rot));
1280 // Format the operand 2 argument for debug spew. The operand can be
1281 // either an immediate or a register specifier.
1282 void fmtOp2(char * out, ARMWord op2)
1284 static char const * const shifts[4] = {"LSL", "LSR", "ASR", "ROR"};
1286 if ((op2 & OP2_IMM) || (op2 & OP2_IMMh)) {
1287 // Immediate values.
1289 uint32_t imm = decOp2Imm(op2 & ~(OP2_IMM | OP2_IMMh));
1290 sprintf(out, "#0x%x @ (%d)", imm, static_cast<int32_t>(imm));
1294 char const * rm = nameGpReg(op2 & 0xf);
1295 Shift type = static_cast<Shift>((op2 >> 5) & 0x3);
1297 // Bit 4 specifies barrel-shifter parameters in operand 2.
1299 // Register-shifted register.
1300 // Example: "r0, LSL r6"
1301 char const * rs = nameGpReg((op2 >> 8) & 0xf);
1302 sprintf(out, "%s, %s %s", rm, shifts[type], rs);
1304 // Immediate-shifted register.
1305 // Example: "r0, ASR #31"
1306 uint32_t imm = (op2 >> 7) & 0x1f;
1308 // Deal with special encodings.
1309 if ((type == LSL) && (imm == 0)) {
1310 // "LSL #0" doesn't shift at all (and is the default).
1311 sprintf(out, "%s", rm);
1315 if ((type == ROR) && (imm == 0)) {
1316 // "ROR #0" is a special case ("RRX").
1317 sprintf(out, "%s, RRX", rm);
1321 if (((type == LSR) || (type == ASR)) && (imm == 0)) {
1322 // Both LSR and ASR have a range of 1-32, with 32
1327 // Print the result.
1329 sprintf(out, "%s, %s #%u", rm, shifts[type], imm);
1334 void spewInsWithOp2(char const * ins, Condition cc, int rd, int rn, ARMWord op2)
1337 snprintf(mnemonic, 16, "%s%s", ins, nameCC(cc));
1340 fmtOp2(op2_fmt, op2);
1342 js::JaegerSpew(js::JSpew_Insns,
1343 IPFX "%-15s %s, %s, %s\n", MAYBE_PAD, mnemonic, nameGpReg(rd), nameGpReg(rn), op2_fmt);
1346 void spewInsWithOp2(char const * ins, Condition cc, int r, ARMWord op2)
1349 snprintf(mnemonic, 16, "%s%s", ins, nameCC(cc));
1352 fmtOp2(op2_fmt, op2);
1354 js::JaegerSpew(js::JSpew_Insns,
1355 IPFX "%-15s %s, %s\n", MAYBE_PAD, mnemonic, nameGpReg(r), op2_fmt);
1360 ASSERT(reg <= ARMRegisters::pc);
1366 ASSERT(reg <= ARMRegisters::pc);
1372 ASSERT(reg <= ARMRegisters::pc);
1378 ASSERT(reg <= ARMRegisters::pc);
1384 ASSERT(reg <= ARMRegisters::d31);
1385 // Endoded as bits [22,15:12].
1386 return ((reg << 12) | (reg << 18)) & 0x0040f000;
1391 ASSERT(reg <= ARMRegisters::d31);
1392 // Endoded as bits [7,19:16].
1393 return ((reg << 16) | (reg << 3)) & 0x000f0080;
1398 ASSERT(reg <= ARMRegisters::d31);
1399 // Encoded as bits [5,3:0].
1400 return ((reg << 1) & 0x20) | (reg & 0xf);
1403 static ARMWord getConditionalField(ARMWord i)
1405 return i & 0xf0000000;
1408 int genInt(int reg, ARMWord imm, bool positive);
1416 #endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
1418 #endif // ARMAssembler_h