#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #if defined(HAVE_ARM) || defined(HAVE_AARCH64) #if defined(__APPLE__) #include #endif #if defined (_WIN32) #define WIN32_LEAN_AND_MEAN #include #endif #endif /** * SECTION:orcarm * @title: ARM * @short_description: code generation for ARM */ const char * orc_arm_cond_name (OrcArmCond cond) { static const char *cond_names[] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "", "" }; if ((int)cond < 0 || (int)cond >= 16) { return "ERROR"; } return cond_names[cond&0xf]; } const char * orc_arm_reg_name (int reg) { #if 0 static const char *gp_regs[] = { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "ip", "sp", "lr", "pc" }; #else static const char *gp_regs[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "ip", "sp", "lr", "pc" }; /* "r12", "r13", "r14", "r15" }; */ #endif if (reg < ORC_GP_REG_BASE || reg >= ORC_GP_REG_BASE+16) { return "ERROR"; } return gp_regs[reg&0xf]; } const char * orc_arm64_reg_name (int reg, OrcArm64RegBits bits) { static const char *gp_regs64[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp" }; static const char *gp_regs32[] = { "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", "w24", "w25", "w26", "w27", "w28", "w29", "w30", "sp" }; if (reg < ORC_GP_REG_BASE || reg >= ORC_GP_REG_BASE+32) { return "ERROR"; } return bits == ORC_ARM64_REG_64 ? gp_regs64[reg&0x1f] : gp_regs32[reg&0x1f]; } void orc_arm_emit (OrcCompiler *compiler, orc_uint32 insn) { ORC_WRITE_UINT32_LE (compiler->codeptr, insn); compiler->codeptr+=4; } void orc_arm_emit_bx_lr (OrcCompiler *compiler) { if (compiler->is_64bit) { orc_arm64_emit_ret (compiler, ORC_ARM64_LR); } else { ORC_ASM_CODE(compiler," bx lr\n"); orc_arm_emit (compiler, 0xe12fff1e); } } static int count_reg_ones (int regs) { int count = 0; while (regs) { count += regs & 1; regs >>= 1; } return count; } void orc_arm_emit_push (OrcCompiler *compiler, int regs, orc_uint32 vregs) { int i; if (regs) { int x = 0; if (compiler->is_64bit) { int count, stores; int stack_increased = 0; /** a number of 1s in regs */ count = count_reg_ones (regs); /** AArch64 requires a 16-byte aligned stack pointer */ stores = (count-1)/2+1; x = -1; for(i=0;i<32;i++){ if (stores == 0) break; if (regs & (1<> 4) << 22) | ((first & 0x0f) << 12) | (nregs << 1)); } } void orc_arm_emit_pop (OrcCompiler *compiler, int regs, orc_uint32 vregs) { int i; if (vregs) { int first = -1, last = -1, nregs; ORC_ASM_CODE(compiler, " vpop {"); for(i=0; i<32; ++i) { if (vregs & (1U << i)) { if (first==-1) { ORC_ASM_CODE(compiler, "d%d", i); first = i; } last = i; } } ORC_ASM_CODE(compiler, "-d%d}\n", last+1); nregs = last + 1 - first + 1; orc_arm_emit (compiler, 0xecbd0b00 | (((first & 0x10) >> 4) << 22) | ((first & 0x0f) << 12) | (nregs << 1)); } if (regs) { int x = 0; if (compiler->is_64bit) { int count, loads, tmp_loads; /** a number of 1s in regs */ count = count_reg_ones (regs); /** AArch64 requires a 16-byte aligned stack pointer */ loads = (count-1)/2+1; tmp_loads = loads; x = -1; for(i=31;i>=0;i--){ if (regs & (1<labels[label] = compiler->codeptr; } void orc_arm_add_fixup (OrcCompiler *compiler, int label, int type) { ORC_ASSERT (compiler->n_fixups < ORC_N_FIXUPS); compiler->fixups[compiler->n_fixups].ptr = compiler->codeptr; compiler->fixups[compiler->n_fixups].label = label; compiler->fixups[compiler->n_fixups].type = type; compiler->n_fixups++; } void orc_arm_do_fixups (OrcCompiler *compiler) { int i; for(i=0;in_fixups;i++){ unsigned char *label = compiler->labels[compiler->fixups[i].label]; unsigned char *ptr = compiler->fixups[i].ptr; orc_uint32 code; int diff; if (compiler->fixups[i].type == 0) { code = ORC_READ_UINT32_LE (ptr); diff = code; if (compiler->is_64bit) { diff = ((label - ptr) >> 2); /** time 4 */ if (diff != (diff << 6)>>6) { ORC_COMPILER_ERROR(compiler, "fixup out of range"); } /** check whether it's conditional or unconditioanl */ if ((code >> 30) & 1) { diff <<= 5; /** for cond */ ORC_WRITE_UINT32_LE(ptr, (code&0xff00001f) | (diff&0x00ffffe0)); } else { ORC_WRITE_UINT32_LE(ptr, (code&0xfc000000) | (diff&0x03ffffff)); } } else { diff = (diff << 8) >> 8; diff += ((label - ptr) >> 2); if (diff != (diff << 8)>>8) { ORC_COMPILER_ERROR(compiler, "fixup out of range"); } ORC_WRITE_UINT32_LE(ptr, (code&0xff000000) | (diff&0x00ffffff)); } } else { code = ORC_READ_UINT32_LE (ptr); diff = code; /* We store the offset in the code as signed, but the CPU considers * it unsigned */ diff = (diff << 24) >> 24; diff += ((label - ptr) >> 2); if (diff != (diff & 0xff)) { ORC_COMPILER_ERROR(compiler, "fixup out of range (%d > 255)", diff); } ORC_WRITE_UINT32_LE(ptr, (code&0xffffff00) | (diff&0x000000ff)); } } } void orc_arm_emit_align (OrcCompiler *compiler, int align_shift) { int diff; diff = (compiler->code - compiler->codeptr)&((1<is_64bit) orc_arm_emit (compiler, 0xd503201f); else orc_arm_emit (compiler, 0xe1a00000); } void orc_arm_emit_branch (OrcCompiler *compiler, int cond, int label) { orc_uint32 code; if (compiler->is_64bit) { /** B.cond * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +---------------------------------------------------------------+ * |0 1 0 1 0 1 0|0| imm19 |0| cond | * +---------------------------------------------------------------+ * * B * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +---------------------------------------------------------------+ * |0 0 0 1 0 1| imm26 | * +---------------------------------------------------------------+ * * Note that we don't know exact imm19/imm26 yet, so need fixup codes. */ if (cond < ORC_ARM_COND_AL) { code = 0x54000000; code |= cond&0xf; ORC_ASM_CODE(compiler," b.%s .L%d\n", orc_arm_cond_name(cond), label); } else { code = 0x14000000; ORC_ASM_CODE(compiler," b .L%d\n", label); } } else { code = 0x0afffffe; code |= (cond&0xf) << 28; ORC_ASM_CODE(compiler," b%s .L%d\n", orc_arm_cond_name(cond), label); } orc_arm_add_fixup (compiler, label, 0); orc_arm_emit (compiler, code); } void orc_arm_emit_load_imm (OrcCompiler *compiler, int dest, int imm) { orc_uint32 code; int shift2; unsigned int x; if ((imm & 0xff) == imm) { shift2 = 0; x = imm; } else { shift2 = 0; x = imm & 0xffffffff; while ((x & 3) == 0) { x >>= 2; shift2++; } if (x > 0xff) { ORC_PROGRAM_ERROR(compiler, "bad immediate value"); } } code = 0xe3a00000; code |= (dest&0xf) << 12; code |= (((16-shift2)&0xf) << 8); code |= (x&0xff); ORC_ASM_CODE(compiler," mov %s, #0x%08x\n", orc_arm_reg_name (dest), imm); orc_arm_emit (compiler, code); } void orc_arm_emit_add_imm (OrcCompiler *compiler, int dest, int src1, int imm) { orc_uint32 code; int shift2; unsigned int x; if ((imm & 0xff) == imm) { shift2 = 0; x = imm; } else { shift2 = 0; x = imm & 0xffffffff; while ((x & 3) == 0) { x >>= 2; shift2++; } if (x > 0xff) { ORC_PROGRAM_ERROR(compiler, "bad immediate value"); } } code = 0xe2800000; code |= (src1&0xf) << 16; code |= (dest&0xf) << 12; code |= (((16-shift2)&0xf) << 8); code |= (x&0xff); ORC_ASM_CODE(compiler," add %s, %s, #0x%08x\n", orc_arm_reg_name (dest), orc_arm_reg_name(src1), imm); orc_arm_emit (compiler, code); } void orc_arm_emit_and_imm (OrcCompiler *compiler, int dest, int src1, int value) { orc_uint32 code; code = 0xe2000000; code |= (src1&0xf) << 16; code |= (dest&0xf) << 12; code |= (value) << 0; ORC_ASM_CODE(compiler," and %s, %s, #%d\n", orc_arm_reg_name (dest), orc_arm_reg_name (src1), value); orc_arm_emit (compiler, code); } void orc_arm_emit_cmp (OrcCompiler *compiler, int src1, int src2) { orc_uint32 code; code = 0xe1500000; code |= (src1&0xf) << 16; code |= (src2&0xf) << 0; ORC_ASM_CODE(compiler," cmp %s, %s\n", orc_arm_reg_name (src1), orc_arm_reg_name (src2)); orc_arm_emit (compiler, code); } void orc_arm_emit_asr_imm (OrcCompiler *compiler, int dest, int src1, int value) { orc_uint32 code; if (value == 0) { ORC_ERROR("bad immediate value"); } code = 0xe1a00040; code |= (src1&0xf) << 0; code |= (dest&0xf) << 12; code |= (value) << 7; ORC_ASM_CODE(compiler," asr %s, %s, #%d\n", orc_arm_reg_name (dest), orc_arm_reg_name (src1), value); orc_arm_emit (compiler, code); } void orc_arm_emit_lsl_imm (OrcCompiler *compiler, int dest, int src1, int value) { orc_uint32 code; if (value == 0) { ORC_ERROR("bad immediate value"); } code = 0xe1a00000; code |= (src1&0xf) << 0; code |= (dest&0xf) << 12; code |= (value) << 7; ORC_ASM_CODE(compiler," lsl %s, %s, #%d\n", orc_arm_reg_name (dest), orc_arm_reg_name (src1), value); orc_arm_emit (compiler, code); } void orc_arm_emit_load_reg (OrcCompiler *compiler, int dest, int src1, int offset) { orc_uint32 code; code = 0xe5900000; code |= (src1&0xf) << 16; code |= (dest&0xf) << 12; code |= offset&0xfff; ORC_ASM_CODE(compiler," ldr %s, [%s, #%d]\n", orc_arm_reg_name (dest), orc_arm_reg_name (src1), offset); orc_arm_emit (compiler, code); } void orc_arm_emit_store_reg (OrcCompiler *compiler, int src1, int dest, int offset) { orc_uint32 code; code = 0xe5800000; code |= (dest&0xf) << 16; code |= (src1&0xf) << 12; code |= offset&0xfff; ORC_ASM_CODE(compiler," str %s, [%s, #%d]\n", orc_arm_reg_name (src1), orc_arm_reg_name (dest), offset); orc_arm_emit (compiler, code); } void orc_arm_emit_mov (OrcCompiler *compiler, int dest, int src) { if (dest == src) return; orc_arm_emit_mov_r(compiler, ORC_ARM_COND_AL, 0, dest, src); } void orc_arm_emit_sub (OrcCompiler *compiler, int dest, int src1, int src2) { orc_arm_emit_sub_r (compiler, ORC_ARM_COND_AL, 0, dest, src1, src2); } void orc_arm_emit_sub_imm (OrcCompiler *compiler, int dest, int src1, int value, int record) { orc_arm_emit_sub_i (compiler, ORC_ARM_COND_AL, record, dest, src1, value); } void orc_arm_emit_add (OrcCompiler *compiler, int dest, int src1, int src2) { orc_arm_emit_add_r (compiler, ORC_ARM_COND_AL, 0, dest, src1, src2); } void orc_arm_emit_cmp_imm (OrcCompiler *compiler, int src1, int value) { orc_arm_emit_cmp_i (compiler, ORC_ARM_COND_AL, src1, value); } /* shifter operands */ /* 1 * 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+ * |rotimm | immed_8 | * +-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm_so_i(rot,imm) ((((rot)&15)<<8)|((imm)&255)) /* 1 * 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+ * | Si | St|0| Rm | * +-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm_so_rsi(Si,St,Rm) ((((Si)&31)<<7)|(((St)&3)<<5)|((Rm)&15)) #define arm_so_rrx(Rm) arm_so_rsi(0,ORC_ARM_ROR,Rm) #define arm_so_r(Rm) ((Rm)&15) /* 1 * 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+ * | Rs |0| St|1| Rm | * +-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm_so_rsr(Rs,St,Rm) (0x010|(((Rs)&15)<<8)|(((St)&3)<<5)|((Rm)&15)) /* data processing instructions */ /* 3 2 2 2 2 2 2 2 1 1 1 1 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | cond |0 0|I| opcode|S| Rn | Rd | shifter_operand | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm_code_dp(cond,I,opcode,S,Rn,Rd,So) ((((cond)&15)<<28) | (((I)&1)<<25) | \ (((opcode)&15)<<21) | (((S)&1)<<20) | \ (((Rn)&15)<<16) | (((Rd)&15)<<12) | \ ((So)&0xfff)) /* * type 0: {}{s} {}, , #imm (imm = (val>>(shift*2))|(val<<(32-(shift*2)))) * type 1: {}{s} {}, , * type 2: {}{s} {}, , , [LSL|LSR|ASR] #imm * type 3: {}{s} {}, , , [LSL|LSR|ASR] * type 4: {}{s} {,} , , RRX */ void orc_arm_emit_dp (OrcCompiler *p, int type, OrcArmCond cond, OrcArmDP opcode, int S, int Rd, int Rn, int Rm, int shift, orc_uint32 val) { orc_uint32 code; int I = 0; int shifter_op; char shifter[64]; orc_uint32 imm; static const char *shift_names[] = { "LSL", "LSR", "ASR", "ROR" }; /* opcodes with Rd */ static const int op_Rd[] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1 }; /* opcodes using Rn */ static const int op_Rn[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0 }; static const char *dp_insn_names[] = { "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn" }; switch (type) { case 0: /* #imm */ imm = (orc_uint32) val; /* if imm <= 0xff we're done. It's recommanded that we choose the * smallest shifter value. Impossible values will overflow the shifter. */ while (imm > 0xff && shift < 16) { imm = (imm << 2) | (imm >> 30); shift++; } if (shift > 15) { ORC_COMPILER_ERROR(p,"invalid ARM immediate %08x", val); return; } shifter_op = arm_so_i (shift, imm); sprintf (shifter, "#0x%08x", val); I = 1; break; case 1: /* */ shifter_op = arm_so_r (Rm); sprintf (shifter, "%s", orc_arm_reg_name (Rm)); break; case 2: /* , [LSL|LSR|ASR] #imm */ shifter_op = arm_so_rsi (val,shift,Rm); sprintf (shifter, "%s, %s #%d", orc_arm_reg_name (Rm), shift_names[shift], val); break; case 3: /* , [LSL|LSR|ASR] */ shifter_op = arm_so_rsr (val,shift,Rm); sprintf (shifter, "%s, %s %s", orc_arm_reg_name (Rm), shift_names[shift], orc_arm_reg_name (val)); break; case 4: /* , RRX */ shifter_op = arm_so_rrx (Rm); sprintf (shifter, "%s, RRX", orc_arm_reg_name (Rm)); break; default: ORC_COMPILER_ERROR(p,"unknown data processing type %d", type); return; } if (op_Rd[opcode]) { if (op_Rn[opcode]) { /* opcode using Rn */ code = arm_code_dp (cond, I, opcode, S, Rn, Rd, shifter_op); ORC_ASM_CODE(p," %s%s%s %s, %s, %s\n", dp_insn_names[opcode], orc_arm_cond_name(cond), (S ? "s" : ""), orc_arm_reg_name (Rd), orc_arm_reg_name (Rn), shifter); } else { /* opcode using Rd and val (mov, mvn) */ code = arm_code_dp (cond, I, opcode, S, Rn, Rd, shifter_op); ORC_ASM_CODE(p," %s%s%s %s, %s\n", dp_insn_names[opcode], orc_arm_cond_name(cond), (S ? "s" : ""), orc_arm_reg_name (Rd), shifter); } } else { /* opcode does not change Rd, change status register (cmp, tst, ..) */ code = arm_code_dp (cond, I, opcode, 1, Rn, 0, shifter_op); ORC_ASM_CODE(p," %s%s %s, %s\n", dp_insn_names[opcode], orc_arm_cond_name(cond), orc_arm_reg_name (Rn), shifter); } orc_arm_emit (p, code); } /* parallel instructions */ /* 3 2 2 2 2 2 2 2 1 1 1 1 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | cond | mode | Rn | Rd |0 0 0 0| op | Rm | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm_code_par(cond,mode,Rn,Rd,op,Rm) (((cond)<<28)|((mode)<<20)|(((Rn)&0xf)<<16)|(((Rd)&0xf)<<12)|((op)<<4)|((Rm)&0xf)|0xf00) void orc_arm_emit_par (OrcCompiler *p, int op, int mode, OrcArmCond cond, int Rd, int Rn, int Rm) { orc_uint32 code; static const int par_op[] = { 1, 3, 5, 7, 9, 15, 11, 5, 5 }; static const char *par_op_names[] = { "add16", "addsubx", "subaddx", "sub16", "add8", "sub8", "sel", "add", "sub" }; static const int par_mode[] = { 0x61, 0x62, 0x63, 0x65, 0x66, 0x67, 0x68, 0x10, 0x12, 0x14, 0x16 }; static const char *par_mode_names[] = { "s", "q", "sh", "u", "uq", "uh", "", "q", "q", "qd", "qd" }; code = arm_code_par (cond, par_mode[mode], Rn, Rd, par_op[op], Rm); if (op == 7) { int tmp; /* gas does something screwy here */ code &= ~0xf00; tmp = Rn; Rn = Rm; Rm = tmp; } ORC_ASM_CODE(p," %s%s%s %s, %s, %s\n", par_mode_names[mode], par_op_names[op], orc_arm_cond_name(cond), orc_arm_reg_name (Rd), orc_arm_reg_name (Rn), orc_arm_reg_name (Rm)); orc_arm_emit (p, code); } /* extend instructions */ /* 3 2 2 2 2 2 2 2 1 1 1 1 1 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | cond |0 1 1 0 0 0 0 0| Rn | Rd |rot|0 0|0 1 1 1| Rm | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm_code_xt(op,cond,Rn,Rd,r8,Rm) (op|((cond)<<28)|(((Rn)&0xf)<<16)|(((Rd)&0xf)<<12)|((((r8)&0xf)&0x18)<<7)|((Rm)&0xf)) void orc_arm_emit_xt (OrcCompiler *p, int op, OrcArmCond cond, int Rd, int Rn, int Rm, int r8) { orc_uint32 code; char shifter[64]; static const orc_uint32 xt_opcodes[] = { 0x06800070, 0x06a00070, 0x06b00070, 0x06c00070, 0x06e00070, 0x06f00070 }; static const char *xt_insn_names[] = { "sxtb16", "sxtb", "sxth", "uxtb16", "uxtb", "uxth", "sxtab16", "sxtab", "sxtah", "uxtab16", "uxtab", "uxtah", }; if (r8 & 0x18) sprintf (shifter, ", ROR #%d", r8 & 0x18); else shifter[0] = '\0'; code = arm_code_xt (xt_opcodes[op], cond, Rn, Rd, r8, Rm); if (Rn < 15) { /* with Rn */ ORC_ASM_CODE(p," %s%s %s, %s, %s%s\n", xt_insn_names[op], orc_arm_cond_name(cond), orc_arm_reg_name (Rd), orc_arm_reg_name (Rn), orc_arm_reg_name (Rm), shifter); } else { ORC_ASM_CODE(p," %s%s %s, %s%s\n", xt_insn_names[op], orc_arm_cond_name(cond), orc_arm_reg_name (Rd), orc_arm_reg_name (Rm), shifter); } orc_arm_emit (p, code); } #define arm_code_pkh(op,cond,Rn,Rd,sh,Rm) (op|((cond)<<28)|(((Rn)&0xf)<<16)|(((Rd)&0xf)<<12)|((sh)<<7)|((Rm)&0xf)) void orc_arm_emit_pkh (OrcCompiler *p, int op, OrcArmCond cond, int Rd, int Rn, int Rm, int sh) { orc_uint32 code; char shifter[64]; static const orc_uint32 pkh_opcodes[] = { 0x06800010, 0x06800050 }; static const char *pkh_insn_names[] = { "pkhbt", "pkhtb" }; if (sh > 0) { sprintf (shifter, ", %s #%d", (op == 0 ? "LSL" : "ASR"), sh); } else { shifter[0] = '\0'; } code = arm_code_pkh (pkh_opcodes[op], cond, Rn, Rd, sh, Rm); ORC_ASM_CODE(p," %s%s %s, %s, %s%s\n", pkh_insn_names[op], orc_arm_cond_name(cond), orc_arm_reg_name (Rd), orc_arm_reg_name (Rn), orc_arm_reg_name (Rm), shifter); orc_arm_emit (p, code); } /* extend instructions */ /* 3 2 2 2 2 1 1 1 1 1 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | cond |0 1 1 0|x x x| sat | Rd | sh |a|0 1| Rm | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm_code_sat(op,cond,sat,Rd,sh,a,Rm) (op|(((cond)&15)<<28)|(((sat)&31)<<16)|\ (((Rd)&15)<<12)|(((sh)&31)<<7)|(((a)&1)<<6)|\ ((Rm)&15)) void orc_arm_emit_sat (OrcCompiler *p, int op, OrcArmCond cond, int Rd, int sat, int Rm, int sh, int asr) { orc_uint32 code; char shifter[64]; static const orc_uint32 sat_opcodes[] = { 0x06a00010, 0x06e00010, 0, 0 }; static const char *sat_insn_names[] = { "ssat", "usat", "ssat16", "usat16" }; static const int par_mode[] = { 0, 0, 0x6a, 0x6e }; static const int par_op[] = { 0, 0, 3, 3 }; if (sh > 0) { sprintf (shifter, ", %s #%d", (asr&1 ? "ASR" : "LSL"), sh); } else { shifter[0] = '\0'; } if (op < 2) { code = arm_code_sat (sat_opcodes[op], cond, sat, Rd, sh, asr, Rm); } else { if (op == 3) { code = arm_code_par (cond, par_mode[op], sat, Rd, par_op[op], Rm); } else { code = arm_code_par (cond, par_mode[op], sat - 1, Rd, par_op[op], Rm); } } ORC_ASM_CODE(p," %s%s %s, #%d, %s%s\n", sat_insn_names[op], orc_arm_cond_name(cond), orc_arm_reg_name (Rd), sat, orc_arm_reg_name (Rm), shifter); orc_arm_emit (p, code); } #define arm_code_rv(op,cond,Rd,Rm) (op|(((cond)&15)<<28)|(((Rd)&15)<<12)|((Rm)&15)) void orc_arm_emit_rv (OrcCompiler *p, int op, OrcArmCond cond, int Rd, int Rm) { orc_uint32 code; static const orc_uint32 rv_opcodes[] = { 0x06bf0f30, 0x06bf0fb0 }; static const char *rv_insn_names[] = { "rev", "rev16" }; code = arm_code_rv (rv_opcodes[op], cond, Rd, Rm); ORC_ASM_CODE(p," %s%s %s, %s\n", rv_insn_names[op], orc_arm_cond_name(cond), orc_arm_reg_name (Rd), orc_arm_reg_name (Rm)); orc_arm_emit (p, code); } void orc_arm_flush_cache (OrcCode *code) { #if defined (HAVE_ARM) || defined (HAVE_AARCH64) #ifdef __APPLE__ sys_dcache_flush(code->code, code->code_size); sys_icache_invalidate(code->exec, code->code_size); #elif defined (_WIN32) HANDLE h_proc = GetCurrentProcess(); FlushInstructionCache(h_proc, code->code, code->code_size); if ((void *) code->exec != (void *) code->code) FlushInstructionCache(h_proc, code->exec, code->code_size); #else __clear_cache (code->code, code->code + code->code_size); if ((void *) code->exec != (void *) code->code) __clear_cache (code->exec, code->exec + code->code_size); #endif #endif } void orc_arm_emit_data (OrcCompiler *compiler, orc_uint32 data) { if (compiler->target_flags & ORC_TARGET_CLEAN_COMPILE) { orc_arm_emit_nop (compiler); } else { ORC_ASM_CODE(compiler," .word 0x%08x\n", data); orc_arm_emit (compiler, data); } } void orc_arm_loadw (OrcCompiler *compiler, int dest, int src1, int offset) { orc_uint32 code; code = 0xe1d000b0; code |= (src1&0xf) << 16; code |= (dest&0xf) << 12; code |= (offset&0xf0) << 4; code |= offset&0x0f; ORC_ASM_CODE(compiler," ldrh %s, [%s, #%d]\n", orc_arm_reg_name (dest), orc_arm_reg_name (src1), offset); orc_arm_emit (compiler, code); } /** AArch64 instructions */ #define ARM64_MAX_OP_LEN 64 /** data processing instructions: Arithmetic * * Immediate * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|op |1 0 0 0 1|sft| imm12 | Rn | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Shifted register * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|op |0 1 0 1 1|sft|0| Rm | imm6 | Rn | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Extended register * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|op |0 1 0 1 1|0 0|1| Rm | opt |imm3 | Rn | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm64_code_arith_imm(b,op,sft,imm,Rn,Rd) (0x11000000 | \ ((((b)==64)&0x1)<<31) | (((op)&0x3)<<29) | (((sft)&0x3)<<22) | \ (((imm)&0xfff)<<10) | (((Rn)&0x1f)<<5) | ((Rd)&0x1f)) #define arm64_code_arith_reg(b,op,sft,Rm,imm,Rn,Rd) (0x0b000000 | \ ((((b)==64)&0x1)<<31) | (((op)&0x3)<<29) | (((sft)&0x3)<<22) | \ (((Rm)&0x1f)<<16) | (((imm)&0x3f)<<10) | (((Rn)&0x1f)<<5) | ((Rd)&0x1f)) #define arm64_code_arith_ext(b,op,Rm,opt,imm,Rn,Rd) (0x0b200000 | \ ((((b)==64)&0x1)<<31) | (((op)&0x3)<<29) | (((Rm)&0x1f)<<16) | \ (((opt)&0x7)<<13) | (((imm)&0x7)<<10) | (((Rn)&0x1f)<<5) | ((Rd)&0x1f)) void orc_arm64_emit_am (OrcCompiler *p, OrcArm64RegBits bits, OrcArm64DP opcode, OrcArm64Type type, int opt, int Rd, int Rn, int Rm, orc_uint64 val) { orc_uint32 code; orc_uint32 imm; int shift, extend; static const char *insn_names[] = { "add", "adds", "sub", "subs", }; static const char *insn_alias[] = { "ERROR", "cmn", "ERROR", "cmp", }; static const char *shift_names[] = { "lsl", "lsr", "asr", "ror" }; static const char *extend_names[] = { "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx" }; int alias_rd; char opt_rm[ARM64_MAX_OP_LEN]; opcode -= ORC_ARM64_DP_ADD; if (opcode >= sizeof(insn_names)/sizeof(insn_names[0])) { ORC_COMPILER_ERROR(p, "unsupported opcode %d", opcode); return; } /** if a reg is not specified, it's regarded as alias; set it to SP (== 0b11111) */ alias_rd = 0; if (Rd == 0) { Rd = ORC_ARM64_SP; alias_rd = 1; } memset (opt_rm, '\x00', ARM64_MAX_OP_LEN); switch (type) { case ORC_ARM64_TYPE_IMM: /** immediate */ /**, #imm */ shift = 0; imm = (orc_uint32) val; /** Support up to 24-bit immediate value, explicitly shifted left (LSL) by 0 or 12 */ if (val > 0xfff) { if (val > 0xffffff) { ORC_COMPILER_ERROR(p, "imm is out-of-range %llx", val); return; } if (val & 0xfff) { ORC_WARNING("offset is trucated %llx", val); } imm >>= 12; shift = 1; } if (shift) snprintf (opt_rm, ARM64_MAX_OP_LEN, ", #%u, lsl #12", imm); else snprintf (opt_rm, ARM64_MAX_OP_LEN, ", #%u", imm); code = arm64_code_arith_imm (bits, opcode, shift, imm, Rn, Rd); break; case ORC_ARM64_TYPE_REG: /** shifted register */ /**, , shift #amount */ shift = opt; imm = (orc_uint32) val; if (shift >= sizeof(shift_names)/sizeof(shift_names[0])) { ORC_COMPILER_ERROR(p, "unsupported shift %d", shift); return; } if (val > 0) { if (val > 63) { ORC_COMPILER_ERROR(p, "shift is out-of-range %llx", val); return; } snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s, %s #%u", orc_arm64_reg_name (Rm, bits), shift_names[shift], imm); } else snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s", orc_arm64_reg_name (Rm, bits)); code = arm64_code_arith_reg (bits, opcode, shift, Rm, imm, Rn, Rd); break; case ORC_ARM64_TYPE_EXT: /** extended register */ /**, , extend #amount */ extend = opt; imm = (orc_uint32) val; if (extend >= sizeof(extend_names)/sizeof(extend_names[0])) { ORC_COMPILER_ERROR(p, "unsupported extend %d", extend); return; } if (val > 0) { if (val > 4) { ORC_COMPILER_ERROR(p, "shift is out-of-range %llx\n", val); return; } /** its width is determined by extend; '0bx11' ==> 64-bit reg */ snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s, %s #%u", orc_arm64_reg_name (Rm, extend & 0x3 ? ORC_ARM64_REG_64 : ORC_ARM64_REG_32), extend_names[extend], imm); } else snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s", orc_arm64_reg_name (Rm, bits)); code = arm64_code_arith_ext(bits, opcode, Rm, extend, imm, Rn, Rd); break; default: ORC_COMPILER_ERROR(p, "unknown data processing type %d", type); return; } /** it's preferred to use alias names if exists */ if (alias_rd) { ORC_ASM_CODE(p, " %s %s%s\n", insn_alias[opcode], orc_arm64_reg_name(Rn, bits), opt_rm); } else { ORC_ASM_CODE(p, " %s %s, %s%s\n", insn_names[opcode], orc_arm64_reg_name(Rd, bits), orc_arm64_reg_name(Rn, bits), opt_rm); } orc_arm_emit (p, code); } /** data processing instructions: Logical * * Immediate * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|op |1 0 0 1 0 0| imm13 | Rn | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Shifted register * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|op |0 1 0 1 0|sft|0| Rm | imm6 | Rn | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm64_code_logical_imm(b,op,imm,Rn,Rd) (0x12000000 | \ ((((b)==64)&0x1)<<31) | (((op)&0x3)<<29) | (((imm)&0x1fff)<<10) | \ (((Rn)&0x1f)<<5) | ((Rd)&0x1f)) #define arm64_code_logical_reg(b,op,sft,Rm,imm,Rn,Rd) (0x0a000000 | \ ((((b)==64)&0x1)<<31) | (((op)&0x3)<<29) | (((sft)&0x3)<<22) | \ (((Rm)&0x1f)<<16) | (((imm)&0x3f)<<10) | (((Rn)&0x1f)<<5) | ((Rd)&0x1f)) /** * check if it's a non-empty sequence of ones starting at the LSB with the remainder zero * e.g., mask_ones(0x0000FFFF) == true */ #define mask_ones(val) ((val) && (((val) + 1) & (val)) == 0) /** * check if it contains a non-empty sequence of ones with the remainder zero * e.g., mask_shifted_ones(0x0000FF00) == true */ #define mask_shifted_ones(val) ((val) && mask_ones(((val) - 1) | (val))) #if defined(__GNUC__) #define count_trailing_zeros(val) __builtin_ctzll(val) #define count_leading_zeros(val) __builtin_clzll(val) #else static int count_trailing_zeros (orc_uint64 val) { int i, count = 0; for (i = 0; i < 64; i++) { if ((val >> i) & 1) break; count++; } return count; } static int count_leading_zeros (orc_uint64 val) { int i, count = 0; for (i = 63; i >= 0; i--) { if ((val >> i) & 1) break; count++; } return count; } #endif #define count_trailing_ones(val) count_trailing_zeros(~val) #define count_leading_ones(val) count_leading_zeros(~val) /** * Encode a logical immediate value (code reference: LLVM) * 32-bit variant: immr:imms * 64-bit variant: N:immr:imms */ static int encode_logical_imm (unsigned int size, orc_uint64 val, orc_uint32 *encoded) { orc_uint64 mask; orc_uint32 I, CTO, CLO; orc_uint32 immr, imms, N; if (size > 64) return -1; /** * immediate values of all-zero and all-cones are not encoded. * Requires at least one non-zero bit, and one zero bit. */ if (val == 0ULL || val == ~0ULL || (size != 64 && (val >> size != 0 || val == (~0ULL >> (64 - size))))) return -2; /** decide the element size (i.e., 2, 4, 8, 16, 32 or 64 bits) */ do { size /= 2; mask = (1ULL << size) - 1; if ((val & mask) != ((val >> size) & mask)) { size *= 2; break; } } while (size > 2); /** decide the rotations to make the element be: 0^m 1^n */ mask = ((orc_uint64)~0ULL) >> (64 - size); val &= mask; if (mask_shifted_ones (val)) { I = count_trailing_zeros (val); CTO = count_trailing_ones (val >> I); } else { val |= ~mask; if (!mask_shifted_ones (~val)) return 0; CLO = count_leading_ones (val); I = 64 - CLO; CTO = CLO + count_trailing_ones (val) - (64 - size); } /** encode N:immr:imms */ immr = (size - I) & (size - 1); imms = ~(size-1) << 1; imms |= (CTO-1); N = ((imms >> 6) & 1) ^ 1; *encoded = (N << 12) | (immr << 6) | (imms & 0x3f); return 0; } void orc_arm64_emit_lg (OrcCompiler *p, OrcArm64RegBits bits, OrcArm64DP opcode, OrcArm64Type type, int opt, int Rd, int Rn, int Rm, orc_uint64 val) { orc_uint32 code; orc_uint32 imm = 0xffffffff; int shift, ret; static const char *insn_names[] = { "and", "orr", "eor", "ands" }; static const char *insn_alias[] = { "ERROR", "mov", "ERROR", "tst" }; static const char *shift_names[] = { "lsl", "lsr", "asr", "ror" }; int alias_rd, alias_rn; char opt_rm[ARM64_MAX_OP_LEN]; opcode -= ORC_ARM64_DP_AND; if (opcode >= sizeof(insn_names)/sizeof(insn_names[0])) { ORC_COMPILER_ERROR(p, "unsupported opcode %d", opcode); return; } /** if a reg is not specified, it's regarded as alias; set it to SP (== 0b11111) */ alias_rd = alias_rn = 0; if (Rd == 0) { Rd = ORC_ARM64_SP; alias_rd = 1; } if (Rn == 0) { Rn = ORC_ARM64_SP; alias_rn = 1; } memset (opt_rm, '\x00', ARM64_MAX_OP_LEN); switch (type) { case ORC_ARM64_TYPE_IMM: /** immediate */ /**, #imm */ if (val == 0) { ORC_COMPILER_ERROR(p, "zero imm is not supported"); return; } ret = encode_logical_imm (bits, val, &imm); if (ret) { ORC_COMPILER_ERROR(p, "wrong immediate value %llx", val); return; } snprintf (opt_rm, ARM64_MAX_OP_LEN, ", #0x%08x", (orc_uint32) val); code = arm64_code_logical_imm (bits, opcode, imm, Rn, Rd); break; case ORC_ARM64_TYPE_REG: /** shifted register */ /**, , shift #amount */ shift = opt; imm = (orc_uint32) val; if (shift >= sizeof(shift_names)/sizeof(shift_names[0])) { ORC_COMPILER_ERROR(p, "unsupported shift %d", shift); return; } if (val > 0) { if (val > 63) { ORC_COMPILER_ERROR(p, "shift is out-of-range %llx", val); return; } snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s, %s #%u", orc_arm64_reg_name (Rm, bits), shift_names[shift], imm); } else snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s", orc_arm64_reg_name (Rm, bits)); code = arm64_code_logical_reg (bits, opcode, shift, Rm, imm, Rn, Rd); break; default: ORC_COMPILER_ERROR(p, "unknown data processing type %d", type); return; } /** it's preferred to use alias names if exists */ if (alias_rd) { ORC_ASM_CODE(p, " %s %s%s\n", insn_alias[opcode], orc_arm64_reg_name(Rn, bits), opt_rm); } else if (alias_rn) { ORC_ASM_CODE(p, " %s %s%s\n", insn_alias[opcode], orc_arm64_reg_name(Rd, bits), opt_rm); } else { ORC_ASM_CODE(p, " %s %s, %s%s\n", insn_names[opcode], orc_arm64_reg_name(Rd, bits), orc_arm64_reg_name(Rn, bits), opt_rm); } orc_arm_emit (p, code); } /** data processing instructions: move wide * * General formats * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|op |1 0 0 1 0 1|hw | imm16 | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm64_code_mov_wide(b,op,hw,imm,Rd) (0x12800000 | ((((b)==64)&0x1)<<31) | \ (((op)&0x3)<<29) | (((hw)&0x3)<<21) | (((imm)&0xffff)<<5) | ((Rd)&0x1f)) void orc_arm64_emit_mov_wide (OrcCompiler *p, OrcArm64RegBits bits, int mov_op, int hw, int Rd, orc_uint64 val) { orc_uint16 imm; orc_uint32 code; static const char *mov_names[] = { "movn", "ERROR", "movz", "movk" }; char opt_lsl[ARM64_MAX_OP_LEN]; if (mov_op >= sizeof(mov_names)/sizeof(mov_names[0])) { ORC_COMPILER_ERROR(p, "unsupported mov opcode %d", mov_op); return; } if (val > 65535) { ORC_COMPILER_ERROR(p, "unsupported amount of shift %llu", val); return; } if (bits == ORC_ARM64_REG_64) { if (!(hw == 0 || hw == 16 || hw == 32 || hw == 48)) { ORC_COMPILER_ERROR(p, "unsupported hw shift %d", hw); return; } } else { if (!(hw == 0 || hw == 16)) { ORC_COMPILER_ERROR(p, "unsupported hw shift %d", hw); return; } } memset (opt_lsl, '\x00', ARM64_MAX_OP_LEN); if (hw > 0) { snprintf (opt_lsl, ARM64_MAX_OP_LEN, ", lsl #%d", hw); } hw /= 16; imm = val; code = arm64_code_mov_wide(bits, mov_op, hw, imm, Rd); ORC_ASM_CODE(p, " %s %s, #%u%s\n", /** movz has one alias condition */ (mov_op == 2) && !(imm == 0 && hw != 0) ? "mov" : mov_names[mov_op], orc_arm64_reg_name(Rd, bits), imm, opt_lsl); orc_arm_emit (p, code); } /** data processing instructions: Shift (reg) * * General formats * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|0 0|1 1 0 1 0 1 1 0| Rm |0 0 1 0|sft| Rn | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm64_code_shift(b,Rm,sft,Rn,Rd) (0x1ac02000 | ((((b)==64)&0x1)<<31) | \ (((Rm)&0x1f)<<16) | (((sft)&0x3)<<10) | (((Rn)&0x1f)<<5) | ((Rd)&0x1f)) void orc_arm64_emit_sft (OrcCompiler *p, OrcArm64RegBits bits, OrcArmShift shift, int Rd, int Rn, int Rm) { orc_uint32 code; static const char *shift_names[] = { "lsl", "lsr", "asr", "ror" }; if (shift >= sizeof(shift_names)/sizeof(shift_names[0])) { ORC_COMPILER_ERROR(p, "unsupported shift %d", shift); return; } code = arm64_code_shift(bits, Rm, shift, Rn, Rd); ORC_ASM_CODE(p, " %s %s, %s, %s\n", shift_names[shift], orc_arm64_reg_name(Rd, bits), orc_arm64_reg_name(Rn, bits), orc_arm64_reg_name(Rm, bits)); orc_arm_emit (p, code); } /** data processing instructions: Bitfield Move * * General formats * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|op |1 0 0 1 1 0|N| immr | imms | Rn | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Note that Bitfiled Move is usually accessed via one of its aliases */ #define arm64_code_bfm(b,opcode,immr,imms,Rn,Rd) (0x13000000 | ((((b)==64)&0x1)<<31) | \ (((opcode)&0x3)<<29) | ((((b)==64)&0x1)<<22) | (((immr)&0x3f)<<16) | (((imms)&0x3f)<<10) | \ (((Rn)&0x1f)<<5) | ((Rd)&0x1f)) /** Return 1 if ubfx or sbfx is preferred. Must exclude more specific aliases */ static int bfx_preferred (OrcArm64RegBits bits, int is_unsigned, orc_uint32 imms, orc_uint32 immr) { if (imms < immr) return 0; /** must not match LSR/ASR/LSL alias */ if (imms == 0x1f) return 0; /** must not match UXTx/SXTx alias */ if (immr == 0) { if (bits == ORC_ARM64_REG_32) { if (imms == 0x7 || imms == 0xf) return 0; } else if (is_unsigned) { if (imms == 0x7 || imms == 0xf || imms == 0x1f) return 0; } } return 1; } void orc_arm64_emit_bfm (OrcCompiler *p, OrcArm64RegBits bits, OrcArm64DP opcode, int Rd, int Rn, orc_uint32 immr, orc_uint32 imms) { orc_uint32 code; int alias = -1; static const char *insn_names[3] = { "sbfm", "bfm", "ubfm" }; static const char *insn_alias[3][6] = { {"asr","sbfiz","sbfx","sxtb","sxth","sxtw"}, {"bfc","bfi","bfxil", "ERROR","ERROR","ERROR"}, {"lsl","lsr","ubfiz","ubfx","uxtb","uxth"} }; char opt_immr[ARM64_MAX_OP_LEN]; char opt_imms[ARM64_MAX_OP_LEN]; memset (opt_immr, '\x00', ARM64_MAX_OP_LEN); memset (opt_imms, '\x00', ARM64_MAX_OP_LEN); /** find its alias */ switch (opcode) { case ORC_ARM64_DP_SBFM: if (imms == 0x1f) { if (bits == ORC_ARM64_REG_64) imms |= 0x20; snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); alias = 0; } else if (imms < immr) { snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); snprintf (opt_imms, ARM64_MAX_OP_LEN, ", #%u", imms); alias = 1; } else if (bfx_preferred(bits, 0, imms, immr)) { snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); snprintf (opt_imms, ARM64_MAX_OP_LEN, ", #%u", imms); alias = 2; } else if (immr == 0) { if (imms == 0x7) alias = 3; else if (imms == 0xf) alias = 4; else if (imms == 0x1f) alias = 5; } break; case ORC_ARM64_DP_BFM: if (imms < immr) { if (Rn == 0x1f) { snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); alias = 0; } else { snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); snprintf (opt_imms, ARM64_MAX_OP_LEN, ", #%u", imms); alias = 1; } } else { snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); snprintf (opt_imms, ARM64_MAX_OP_LEN, ", #%u", imms); alias = 2; } break; case ORC_ARM64_DP_UBFM: if (imms == 0x1f) { if (bits == ORC_ARM64_REG_64) imms |= 0x20; snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); alias = 1; } else if (imms + 1 == immr) { if (bits == ORC_ARM64_REG_64) imms |= 0x20; snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); alias = 0; } else if (imms < immr) { snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); snprintf (opt_imms, ARM64_MAX_OP_LEN, ", #%u", imms); alias = 2; } else if (bfx_preferred(bits, 1, imms, immr)) { snprintf (opt_immr, ARM64_MAX_OP_LEN, ", #%u", immr); snprintf (opt_imms, ARM64_MAX_OP_LEN, ", #%u", imms); alias = 3; } else if (immr == 0) { if (imms == 0x7) alias = 4; else if (imms == 0xf) alias = 5; } break; default: ORC_COMPILER_ERROR(p, "unknown opcode %d", opcode); return; } opcode -= ORC_ARM64_DP_SBFM; code = arm64_code_bfm(bits, opcode, immr, imms, Rn, Rd); if (alias != -1) { ORC_ASM_CODE(p, " %s %s, %s%s%s\n", insn_alias[opcode][alias], orc_arm64_reg_name(Rd, bits), orc_arm64_reg_name(Rn, bits), opt_immr, opt_imms); } else { ORC_ASM_CODE(p, " %s %s, %s, #%u, #%u\n", insn_names[opcode], orc_arm64_reg_name(Rd, bits), orc_arm64_reg_name(Rn, bits), immr, imms); } orc_arm_emit (p, code); } /** data processing instructions: Extract, alias of ROR (imm) * * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b|0 0|1 0 0 1 1 1|N|0| Rm | imms | Rn | Rd | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm64_code_extr(b,Rm,imms,Rn,Rd) (0x13800000 | ((((b)==64)&0x1)<<31) | \ ((((b)==64)&0x1)<<22) | (((Rm)&0x1f)<<16) | (((imms)&0x3f)<<10) | \ (((Rn)&0x1f)<<5) | ((Rd)&0x1f)) void orc_arm64_emit_extr (OrcCompiler *p, OrcArm64RegBits bits, int Rd, int Rn, int Rm, orc_uint32 imm) { orc_uint32 code; code = arm64_code_extr (bits, Rm, imm, Rn, Rd); if (Rn == Rm) { /** ROR (imm) */ if (bits == ORC_ARM64_REG_32 && (imm & 0x20)) { ORC_COMPILER_ERROR(p, "invalid immediate value 0x%08x", imm); return; } ORC_ASM_CODE(p, " ror %s, %s, #%u\n", orc_arm64_reg_name(Rd, bits), orc_arm64_reg_name(Rn, bits), imm); } else { ORC_ASM_CODE(p, " extr %s, %s, %s, #%u\n", orc_arm64_reg_name(Rd, bits), orc_arm64_reg_name(Rn, bits), orc_arm64_reg_name(Rm, bits), imm); } orc_arm_emit (p, code); } /** memory access instructions * * Literal (LDR only) * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |0 b|0 1 1|0|0 0| imm19 | Rt | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Register Signed offset (Post-/Pre-index) * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |1 b|1 1 1|0|0 0|op |0| imm9 |P 1| Rn | Rt | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Register Unsigned offset * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |1 b|1 1 1|0|0 1|op | imm12 | Rn | Rt | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Register Extended * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |1 b|1 1 1|0|0 0|op |1| Rm | opt |S|1 0| Rn | Rt | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * */ #define arm64_code_mem_literal(b,imm,Rt) (0x18000000 | ((((b)==64)&0x1)<<30) | \ (((imm)&0x7ffff)<<5) | ((Rt)&0x1f)) #define arm64_code_mem_signed(b,op,imm,P,Rn,Rt) (0xb8000400 | ((((b)==64)&0x1)<<30) | \ (((op)&0x3)<<22) | (((imm)&0x3f)<<12) | (((P)&0x1)<<11) | (((Rn)&0x1f)<<5) | ((Rt)&0x1f)) #define arm64_code_mem_unsigned(b,op,imm,Rn,Rt) (0xb9000000 | ((((b)==64)&0x1)<<30) | \ (((op)&0x3)<<22) | (((imm)&0xfff)<<10) | (((Rn)&0x1f)<<5) | ((Rt)&0x1f)) #define arm64_code_mem_extended(b,op,Rm,opt,S,Rn,Rt) (0xb8200800 | ((((b)==64)&0x1)<<30) | \ (((op)&0x3)<<22) | (((Rm)&0x1f)<<16) | (((opt)&0x7)<<13) | (((S)&0x1)<<12) | \ (((Rn)&0x1f)<<5) | ((Rt)&0x1f)) void orc_arm64_emit_mem (OrcCompiler *p, OrcArm64RegBits bits, OrcArm64Mem opcode, OrcArm64Type type, int opt, int Rt, int Rn, int Rm, orc_uint32 val) { orc_uint32 code; orc_uint32 amount; orc_int32 imm; int is_signed, extend, S; static const char *insn_names[3] = { "str", "ldr" }; static const char *extend_names[] = { "ERROR", "ERROR", "uxtw", "lsl", "ERROR", "ERROR", "sxtw", "sxtx" }; char opt_rn[ARM64_MAX_OP_LEN]; char opt_rm[ARM64_MAX_OP_LEN]; if (opcode >= sizeof(insn_names)/sizeof(insn_names[0])) { ORC_COMPILER_ERROR(p, "unsupported opcode %d", opcode); return; } memset (opt_rn, '\x00', ARM64_MAX_OP_LEN); memset (opt_rm, '\x00', ARM64_MAX_OP_LEN); switch (type) { case ORC_ARM64_TYPE_IMM: if (opcode != ORC_ARM64_MEM_LDR) { ORC_COMPILER_ERROR(p, "str with immediate is not permitted\n"); return; } if (bits == ORC_ARM64_REG_64) { imm = val/8; } else { imm = val/4; } if (imm > 4095) { ORC_COMPILER_ERROR(p, "out-of-range immediate 0x%lx\n", val); return; } if (imm == 0) { int label = opt; /** resolve the actual address diff in fixup code */ orc_arm_add_fixup (p, label, 2); snprintf (opt_rn, ARM64_MAX_OP_LEN, ", .L%d", label); } else { snprintf (opt_rn, ARM64_MAX_OP_LEN, ", 0x%08x", val); } code = arm64_code_mem_literal (bits, imm, Rt); break; case ORC_ARM64_TYPE_REG: imm = val; is_signed = opt; if (is_signed) { /** signed offset */ if (imm > 0xff || imm < -0xff) { ORC_COMPILER_ERROR(p, "simm is out-of-range %x\n", imm); return; } if (is_signed == 1) { /** pre index */ snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s", orc_arm64_reg_name(Rn, bits)); snprintf (opt_rm, ARM64_MAX_OP_LEN, ", #%d]!", imm); code = arm64_code_mem_signed (bits, opcode, imm, 1, Rn, Rt); } else { /** post index */ snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s]", orc_arm64_reg_name(Rn, bits)); snprintf (opt_rm, ARM64_MAX_OP_LEN, ", #%d", imm); code = arm64_code_mem_signed (bits, opcode, imm, 0, Rn, Rt); } } else { /** unsigned offset */ if (imm != 0) { snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s", orc_arm64_reg_name(Rn, bits)); snprintf (opt_rm, ARM64_MAX_OP_LEN, ", #%d]", imm); } else { snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s]", orc_arm64_reg_name(Rn, bits)); } if (bits == ORC_ARM64_REG_64) { if (imm < 0 || imm > 0xfff * 8) ORC_COMPILER_ERROR(p, "imm is Out-of-range %x\n", imm); imm /= 8; } else { if (imm < 0 || imm > 0xfff * 4) ORC_COMPILER_ERROR(p, "imm is Out-of-range %x\n", imm); imm /= 4; } code = arm64_code_mem_unsigned (bits, opcode, imm, Rn, Rt); } break; case ORC_ARM64_TYPE_EXT: extend = opt; if ((extend >= sizeof(extend_names)/sizeof(extend_names[0])) || !strncmp (extend_names[extend], "ERROR", 5)) { ORC_COMPILER_ERROR(p, "unsupported extend %d\n", extend); return; } snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s", orc_arm64_reg_name(Rn, bits)); amount = val; if (amount > 0) { if (bits == ORC_ARM64_REG_64 && amount != 3) { ORC_COMPILER_ERROR(p, "unsupported amount %d\n", amount); return; } if (bits == ORC_ARM64_REG_32 && amount != 2) { ORC_COMPILER_ERROR(p, "unsupported amount %d\n", amount); return; } snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s, %s #%u]", orc_arm64_reg_name(Rm, bits), extend_names[extend], amount); S = 1; } else { if (extend == 3) { /** LSL */ snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s]", orc_arm64_reg_name(Rm, bits)); } else { snprintf (opt_rm, ARM64_MAX_OP_LEN, ", %s, %s]", orc_arm64_reg_name(Rm, bits), extend_names[extend]); } S = 0; } code = arm64_code_mem_extended (bits, opcode, Rm, extend, S, Rn, Rt); break; default: ORC_COMPILER_ERROR(p, "unsupported type %d\n", type); return; } ORC_ASM_CODE(p, " %s %s%s%s\n", insn_names[opcode], orc_arm64_reg_name(Rt, bits), opt_rn, opt_rm); orc_arm_emit (p, code); } /** memory access instructions (pair) * * 3 2 1 * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |b 0|1 0 1|0| opt |L| imm7 | Rt2 | Rn | Rt | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define arm64_code_mem_pair(b,opt,L,imm,Rt2,Rn,Rt) (0x40000000 | ((((b)==64)&0x1)<<31) | \ (((opt)&0x7)<<23) | (((L)&0x1)<<22) | (((imm)&0x7f)<<15) | (((Rt2)&0x1f)<<10) | \ (((Rn)&0x1f)<<5) | ((Rt)&0x1f)) void orc_arm64_emit_mem_pair (OrcCompiler *p, OrcArm64RegBits bits, OrcArm64Mem opcode, int opt, int Rt, int Rt2, int Rn, orc_int32 imm) { orc_uint32 code; static const char *insn_names[3] = { "stp", "ldp" }; char opt_rn[ARM64_MAX_OP_LEN]; if (opcode >= sizeof(insn_names)/sizeof(insn_names[0])) { ORC_COMPILER_ERROR(p, "unsupported opcode %d", opcode); return; } memset (opt_rn, '\x00', ARM64_MAX_OP_LEN); switch (opt) { case 1: /** post-index */ snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s], #%d", orc_arm64_reg_name(Rn, bits), imm); break; case 2: /** signed offset */ if (imm) { snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s, #%d]", orc_arm64_reg_name(Rn, bits), imm); } else { snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s]", orc_arm64_reg_name(Rn, bits)); } break; case 3: /** pre-index */ snprintf (opt_rn, ARM64_MAX_OP_LEN, ", [%s, #%d]!", orc_arm64_reg_name(Rn, bits), imm); break; default: ORC_COMPILER_ERROR(p, "unsupported variant %d\n", opt); return; } if (bits == ORC_ARM64_REG_64) { imm /= 8; } else { imm /= 4; } if (imm < -64 || imm > 63) { ORC_COMPILER_ERROR(p, "imm is Out-of-range\n"); return; } code = arm64_code_mem_pair (bits, opt, opcode, imm, Rt2, Rn, Rt); ORC_ASM_CODE(p, " %s %s, %s%s\n", insn_names[opcode], orc_arm64_reg_name(Rt, bits), orc_arm64_reg_name(Rt2, bits), opt_rn); orc_arm_emit (p, code); } /** Return from subroutine */ void orc_arm64_emit_ret (OrcCompiler *p, int Rn) { orc_uint32 code; code = 0xd65f0000 | ((Rn & 0x1f) << 5); ORC_ASM_CODE (p, " ret %s\n", Rn == ORC_ARM64_LR ? "" : orc_arm64_reg_name (Rn, ORC_ARM64_REG_64)); orc_arm_emit (p, code); }