/* This file is a part of MIR project. Copyright (C) 2020-2021 Vladimir Makarov . */ static void fancy_abort (int code) { if (!code) abort (); } #undef gen_assert #define gen_assert(c) fancy_abort (c) #include #define HREG_EL(h) h##_HARD_REG #define REP_SEP , enum { REP8 (HREG_EL, R0, R1, R2, R3, R4, R5, R6, R7), REP8 (HREG_EL, R8, R9, R10, R11, R12, R13, R14, R15), REP8 (HREG_EL, R16, R17, R18, R19, R20, R21, R22, R23), REP8 (HREG_EL, R24, R25, R26, R27, R28, R29, R30, SP), ZR_HARD_REG = SP_HARD_REG, REP8 (HREG_EL, V0, V1, V2, V3, V4, V5, V6, V7), REP8 (HREG_EL, V8, V9, V10, V11, V12, V13, V14, V15), REP8 (HREG_EL, V16, V17, V18, V19, V20, V21, V22, V23), REP8 (HREG_EL, V24, V25, V26, V27, V28, V29, V30, V31), }; #undef REP_SEP static const MIR_reg_t MAX_HARD_REG = V31_HARD_REG; static const MIR_reg_t FP_HARD_REG = R29_HARD_REG; static const MIR_reg_t LINK_HARD_REG = R30_HARD_REG; static int target_locs_num (MIR_reg_t loc, MIR_type_t type) { return loc > MAX_HARD_REG && type == MIR_T_LD ? 2 : 1; } static inline MIR_reg_t target_nth_loc (MIR_reg_t loc, MIR_type_t type, int n) { return loc + n; } /* Hard regs not used in machinized code, preferably call used ones. */ const MIR_reg_t TEMP_INT_HARD_REG1 = R9_HARD_REG, TEMP_INT_HARD_REG2 = R10_HARD_REG; const MIR_reg_t TEMP_FLOAT_HARD_REG1 = V16_HARD_REG, TEMP_FLOAT_HARD_REG2 = V17_HARD_REG; const MIR_reg_t TEMP_DOUBLE_HARD_REG1 = V16_HARD_REG, TEMP_DOUBLE_HARD_REG2 = V17_HARD_REG; const MIR_reg_t TEMP_LDOUBLE_HARD_REG1 = V16_HARD_REG; const MIR_reg_t TEMP_LDOUBLE_HARD_REG2 = V17_HARD_REG; static inline int target_hard_reg_type_ok_p (MIR_reg_t hard_reg, MIR_type_t type) { assert (hard_reg <= MAX_HARD_REG); return MIR_fp_type_p (type) ? hard_reg >= V0_HARD_REG : hard_reg < V0_HARD_REG; } static inline int target_fixed_hard_reg_p (MIR_reg_t hard_reg) { assert (hard_reg <= MAX_HARD_REG); return (hard_reg == FP_HARD_REG || hard_reg == SP_HARD_REG || hard_reg == TEMP_INT_HARD_REG1 || hard_reg == TEMP_INT_HARD_REG2 || hard_reg == TEMP_FLOAT_HARD_REG1 || hard_reg == TEMP_FLOAT_HARD_REG2 || hard_reg == TEMP_DOUBLE_HARD_REG1 || hard_reg == TEMP_DOUBLE_HARD_REG2 || hard_reg == TEMP_LDOUBLE_HARD_REG1 || hard_reg == TEMP_LDOUBLE_HARD_REG2); } static inline int target_call_used_hard_reg_p (MIR_reg_t hard_reg, MIR_type_t type) { assert (hard_reg <= MAX_HARD_REG); if (hard_reg <= SP_HARD_REG) return !(hard_reg >= R19_HARD_REG && hard_reg <= R28_HARD_REG); return type == MIR_T_LD || !(hard_reg >= V8_HARD_REG && hard_reg <= V15_HARD_REG); } /* Stack layout (sp refers to the last reserved stack slot address) from higher address to lower address memory: | ... | prev func stack (start aligned to 16 bytes) |---------------| | gr save area | 64 bytes optional area for vararg func integer reg save area |---------------| | vr save area | 128 bytes optional area for vararg func fp reg save area |---------------| | saved regs | callee saved regs used in the func (known only after RA), rounded 16 bytes |---------------| | slots assigned| can be absent for small functions (known only after RA), rounded 16 bytes | to pseudos | |---------------| | previous | 16-bytes setup in prolog, used only for varag func or args passed on stack | stack start | to move args and to setup va_start on machinize pass |---------------| | LR | sp before prologue and after saving LR = start sp |---------------| | old FP | frame pointer for previous func stack frame; new FP refers for here | | it has lowest address as 12-bit offsets are only positive |---------------| | small aggr | | save area | optional |---------------| | alloca areas | optional |---------------| | slots for | dynamically allocated/deallocated by caller | passing args | size of slots and saved regs is multiple of 16 bytes */ static const int int_reg_save_area_size = 8 * 8; static const int reg_save_area_size = 8 * 8 + 8 * 16; static const MIR_insn_code_t target_io_dup_op_insn_codes[] = {MIR_INSN_BOUND}; static MIR_insn_code_t get_ext_code (MIR_type_t type) { switch (type) { case MIR_T_I8: return MIR_EXT8; case MIR_T_U8: return MIR_UEXT8; case MIR_T_I16: return MIR_EXT16; case MIR_T_U16: return MIR_UEXT16; case MIR_T_I32: return MIR_EXT32; case MIR_T_U32: return MIR_UEXT32; default: return MIR_INVALID_INSN; } } static MIR_reg_t get_arg_reg (MIR_type_t arg_type, size_t *int_arg_num, size_t *fp_arg_num, MIR_insn_code_t *mov_code) { MIR_reg_t arg_reg; if (arg_type == MIR_T_F || arg_type == MIR_T_D || arg_type == MIR_T_LD) { switch (*fp_arg_num) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: arg_reg = V0_HARD_REG + *fp_arg_num; break; default: arg_reg = MIR_NON_HARD_REG; break; } (*fp_arg_num)++; *mov_code = arg_type == MIR_T_F ? MIR_FMOV : arg_type == MIR_T_D ? MIR_DMOV : MIR_LDMOV; } else { /* including BLK, RBLK: */ switch (*int_arg_num) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: arg_reg = R0_HARD_REG + *int_arg_num; break; default: arg_reg = MIR_NON_HARD_REG; break; } (*int_arg_num)++; *mov_code = MIR_MOV; } return arg_reg; } static void mir_blk_mov (uint64_t *to, uint64_t *from, uint64_t nwords) { for (; nwords > 0; nwords--) *to++ = *from++; } static void gen_mov (gen_ctx_t gen_ctx, MIR_insn_t anchor, MIR_insn_code_t code, MIR_op_t dst_op, MIR_op_t src_op) { gen_add_insn_before (gen_ctx, anchor, MIR_new_insn (gen_ctx->ctx, code, dst_op, src_op)); } static const char *BLK_MOV = "mir.blk_mov"; static const char *BLK_MOV_P = "mir.blk_mov.p"; static void gen_blk_mov (gen_ctx_t gen_ctx, MIR_insn_t anchor, size_t to_disp, MIR_reg_t to_base_hard_reg, size_t from_disp, MIR_reg_t from_base_reg, size_t qwords, int save_regs) { MIR_context_t ctx = gen_ctx->ctx; MIR_func_t func = curr_func_item->u.func; MIR_item_t proto_item, func_import_item; MIR_insn_t new_insn; MIR_op_t ops[5], freg_op, treg_op, treg_op2, treg_op3; treg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); if (qwords <= 16) { for (; qwords > 0; qwords--, to_disp += 8, from_disp += 8) { gen_mov (gen_ctx, anchor, MIR_MOV, treg_op, MIR_new_mem_op (ctx, MIR_T_I64, from_disp, from_base_reg, 0, 1)); gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, to_disp, to_base_hard_reg, MIR_NON_HARD_REG, 1), treg_op); } return; } treg_op2 = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); treg_op3 = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); /* Save arg regs: */ if (save_regs > 0) gen_mov (gen_ctx, anchor, MIR_MOV, treg_op, _MIR_new_hard_reg_op (ctx, R0_HARD_REG)); if (save_regs > 1) gen_mov (gen_ctx, anchor, MIR_MOV, treg_op2, _MIR_new_hard_reg_op (ctx, R1_HARD_REG)); if (save_regs > 2) gen_mov (gen_ctx, anchor, MIR_MOV, treg_op3, _MIR_new_hard_reg_op (ctx, R2_HARD_REG)); /* call blk move: */ proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, BLK_MOV_P, 0, NULL, 3, MIR_T_I64, "to", MIR_T_I64, "from", MIR_T_I64, "nwords"); func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, BLK_MOV, mir_blk_mov); freg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); new_insn = MIR_new_insn (ctx, MIR_MOV, freg_op, MIR_new_ref_op (ctx, func_import_item)); gen_add_insn_before (gen_ctx, anchor, new_insn); gen_add_insn_before (gen_ctx, anchor, MIR_new_insn (gen_ctx->ctx, MIR_ADD, _MIR_new_hard_reg_op (ctx, R0_HARD_REG), _MIR_new_hard_reg_op (ctx, to_base_hard_reg), MIR_new_int_op (ctx, to_disp))); gen_add_insn_before (gen_ctx, anchor, MIR_new_insn (gen_ctx->ctx, MIR_ADD, _MIR_new_hard_reg_op (ctx, R1_HARD_REG), MIR_new_reg_op (ctx, from_base_reg), MIR_new_int_op (ctx, from_disp))); gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, R2_HARD_REG), MIR_new_int_op (ctx, qwords)); ops[0] = MIR_new_ref_op (ctx, proto_item); ops[1] = freg_op; ops[2] = _MIR_new_hard_reg_op (ctx, R0_HARD_REG); ops[3] = _MIR_new_hard_reg_op (ctx, R1_HARD_REG); ops[4] = _MIR_new_hard_reg_op (ctx, R2_HARD_REG); new_insn = MIR_new_insn_arr (ctx, MIR_CALL, 5, ops); gen_add_insn_before (gen_ctx, anchor, new_insn); /* Restore arg regs: */ if (save_regs > 0) gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, R0_HARD_REG), treg_op); if (save_regs > 1) gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, R1_HARD_REG), treg_op2); if (save_regs > 2) gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, R2_HARD_REG), treg_op3); } static void machinize_call (gen_ctx_t gen_ctx, MIR_insn_t call_insn) { MIR_context_t ctx = gen_ctx->ctx; MIR_func_t func = curr_func_item->u.func; MIR_proto_t proto = call_insn->ops[0].u.ref->u.proto; size_t nargs, nops = MIR_insn_nops (ctx, call_insn), start = proto->nres + 2; size_t int_arg_num = 0, fp_arg_num = 0, mem_size = 0, blk_offset = 0, qwords; MIR_type_t type, mem_type; MIR_op_mode_t mode; MIR_var_t *arg_vars = NULL; MIR_reg_t arg_reg; MIR_op_t arg_op, temp_op, arg_reg_op, ret_reg_op, mem_op; MIR_insn_code_t new_insn_code, ext_code; MIR_insn_t new_insn, prev_insn, next_insn, ext_insn; MIR_insn_t prev_call_insn = DLIST_PREV (MIR_insn_t, call_insn); uint32_t n_iregs, n_vregs; if (call_insn->code == MIR_INLINE) call_insn->code = MIR_CALL; if (proto->args == NULL) { nargs = 0; } else { gen_assert (nops >= VARR_LENGTH (MIR_var_t, proto->args) && (proto->vararg_p || nops - start == VARR_LENGTH (MIR_var_t, proto->args))); nargs = VARR_LENGTH (MIR_var_t, proto->args); arg_vars = VARR_ADDR (MIR_var_t, proto->args); } if (call_insn->ops[1].mode != MIR_OP_REG && call_insn->ops[1].mode != MIR_OP_HARD_REG) { // ??? to optimize (can be immediate operand for func call) temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); new_insn = MIR_new_insn (ctx, MIR_MOV, temp_op, call_insn->ops[1]); call_insn->ops[1] = temp_op; gen_add_insn_before (gen_ctx, call_insn, new_insn); } for (size_t i = start; i < nops; i++) { /* calculate offset for blk params */ if (i - start < nargs) { type = arg_vars[i - start].type; } else if (call_insn->ops[i].mode == MIR_OP_MEM) { type = call_insn->ops[i].u.mem.type; gen_assert (MIR_all_blk_type_p (type)); } else { mode = call_insn->ops[i].value_mode; // ??? smaller ints gen_assert (mode == MIR_OP_INT || mode == MIR_OP_UINT || mode == MIR_OP_FLOAT || mode == MIR_OP_DOUBLE || mode == MIR_OP_LDOUBLE); if (mode == MIR_OP_FLOAT) (*MIR_get_error_func (ctx)) (MIR_call_op_error, "passing float variadic arg (should be passed as double)"); type = mode == MIR_OP_DOUBLE ? MIR_T_D : mode == MIR_OP_LDOUBLE ? MIR_T_LD : MIR_T_I64; } gen_assert (!MIR_all_blk_type_p (type) || call_insn->ops[i].mode == MIR_OP_MEM); if (type == MIR_T_RBLK && i == start) continue; /* hidden arg */ if (MIR_blk_type_p (type) && (qwords = (call_insn->ops[i].u.mem.disp + 7) / 8) <= 2) { if (int_arg_num + qwords > 8) blk_offset += qwords * 8; int_arg_num += qwords; } else if (get_arg_reg (type, &int_arg_num, &fp_arg_num, &new_insn_code) == MIR_NON_HARD_REG) { if (type == MIR_T_LD && blk_offset % 16 != 0) blk_offset = (blk_offset + 15) / 16 * 16; blk_offset += type == MIR_T_LD ? 16 : 8; } } blk_offset = (blk_offset + 15) / 16 * 16; int_arg_num = fp_arg_num = 0; for (size_t i = start; i < nops; i++) { arg_op = call_insn->ops[i]; gen_assert (arg_op.mode == MIR_OP_REG || arg_op.mode == MIR_OP_HARD_REG || (arg_op.mode == MIR_OP_MEM && MIR_all_blk_type_p (arg_op.u.mem.type))); if (i - start < nargs) { type = arg_vars[i - start].type; } else if (call_insn->ops[i].mode == MIR_OP_MEM) { type = call_insn->ops[i].u.mem.type; gen_assert (MIR_all_blk_type_p (type)); } else { mode = call_insn->ops[i].value_mode; // ??? smaller ints type = mode == MIR_OP_DOUBLE ? MIR_T_D : mode == MIR_OP_LDOUBLE ? MIR_T_LD : MIR_T_I64; } ext_insn = NULL; if ((ext_code = get_ext_code (type)) != MIR_INVALID_INSN) { /* extend arg if necessary */ temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); ext_insn = MIR_new_insn (ctx, ext_code, temp_op, arg_op); call_insn->ops[i] = arg_op = temp_op; } gen_assert ( !MIR_all_blk_type_p (type) || (arg_op.mode == MIR_OP_MEM && arg_op.u.mem.disp >= 0 && arg_op.u.mem.index == 0)); if (type == MIR_T_RBLK && i == start) { /* hidden arg */ arg_reg_op = _MIR_new_hard_reg_op (ctx, R8_HARD_REG); gen_mov (gen_ctx, call_insn, MIR_MOV, arg_reg_op, MIR_new_reg_op (ctx, arg_op.u.mem.base)); call_insn->ops[i] = arg_reg_op; continue; } else if (MIR_blk_type_p (type)) { qwords = (arg_op.u.mem.disp + 7) / 8; if (qwords <= 2) { arg_reg = R0_HARD_REG + int_arg_num; if (int_arg_num + qwords <= 8) { /* A trick to keep arg regs live: */ call_insn->ops[i] = _MIR_new_hard_reg_mem_op (ctx, MIR_T_UNDEF, 0, int_arg_num, qwords < 2 ? MIR_NON_HARD_REG : int_arg_num + 1, 1); if (qwords == 0) continue; new_insn = MIR_new_insn (ctx, MIR_MOV, _MIR_new_hard_reg_op (ctx, R0_HARD_REG + int_arg_num++), MIR_new_mem_op (ctx, MIR_T_I64, 0, arg_op.u.mem.base, 0, 1)); gen_add_insn_before (gen_ctx, call_insn, new_insn); if (qwords == 2) { new_insn = MIR_new_insn (ctx, MIR_MOV, _MIR_new_hard_reg_op (ctx, R0_HARD_REG + int_arg_num++), MIR_new_mem_op (ctx, MIR_T_I64, 8, arg_op.u.mem.base, 0, 1)); gen_add_insn_before (gen_ctx, call_insn, new_insn); } } else { /* pass on stack w/o address: */ gen_blk_mov (gen_ctx, call_insn, mem_size, SP_HARD_REG, 0, arg_op.u.mem.base, qwords, int_arg_num); call_insn->ops[i] = _MIR_new_hard_reg_mem_op (ctx, MIR_T_UNDEF, mem_size, SP_HARD_REG, MIR_NON_HARD_REG, 1); mem_size += qwords * 8; blk_offset += qwords * 8; int_arg_num += qwords; } continue; } gen_blk_mov (gen_ctx, call_insn, blk_offset, SP_HARD_REG, 0, arg_op.u.mem.base, qwords, int_arg_num); arg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); gen_add_insn_before (gen_ctx, call_insn, MIR_new_insn (gen_ctx->ctx, MIR_ADD, arg_op, _MIR_new_hard_reg_op (ctx, SP_HARD_REG), MIR_new_int_op (ctx, blk_offset))); blk_offset += qwords * 8; } if ((arg_reg = get_arg_reg (type, &int_arg_num, &fp_arg_num, &new_insn_code)) != MIR_NON_HARD_REG) { /* put arguments to argument hard regs */ if (ext_insn != NULL) gen_add_insn_before (gen_ctx, call_insn, ext_insn); arg_reg_op = _MIR_new_hard_reg_op (ctx, arg_reg); if (type != MIR_T_RBLK) { new_insn = MIR_new_insn (ctx, new_insn_code, arg_reg_op, arg_op); } else { assert (arg_op.mode == MIR_OP_MEM); new_insn = MIR_new_insn (ctx, MIR_MOV, arg_reg_op, MIR_new_reg_op (ctx, arg_op.u.mem.base)); arg_reg_op = _MIR_new_hard_reg_mem_op (ctx, MIR_T_RBLK, arg_op.u.mem.disp, arg_reg, MIR_NON_HARD_REG, 1); } gen_add_insn_before (gen_ctx, call_insn, new_insn); call_insn->ops[i] = arg_reg_op; } else { /* put arguments on the stack */ if (type == MIR_T_LD && mem_size % 16 != 0) mem_size = (mem_size + 15) / 16 * 16; mem_type = type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD ? type : MIR_T_I64; new_insn_code = (type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV); mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, mem_size, SP_HARD_REG, MIR_NON_HARD_REG, 1); if (type != MIR_T_RBLK) { new_insn = MIR_new_insn (ctx, new_insn_code, mem_op, arg_op); } else { assert (arg_op.mode == MIR_OP_MEM); new_insn = MIR_new_insn (ctx, new_insn_code, mem_op, MIR_new_reg_op (ctx, arg_op.u.mem.base)); } gen_assert (prev_call_insn != NULL); /* call_insn should not be 1st after simplification */ MIR_insert_insn_after (ctx, curr_func_item, prev_call_insn, new_insn); prev_insn = DLIST_PREV (MIR_insn_t, new_insn); next_insn = DLIST_NEXT (MIR_insn_t, new_insn); create_new_bb_insns (gen_ctx, prev_insn, next_insn, call_insn); call_insn->ops[i] = mem_op; mem_size += type == MIR_T_LD ? 16 : 8; if (ext_insn != NULL) gen_add_insn_after (gen_ctx, prev_call_insn, ext_insn); } } blk_offset = (blk_offset + 15) / 16 * 16; if (blk_offset != 0) mem_size = blk_offset; n_iregs = n_vregs = 0; for (size_t i = 0; i < proto->nres; i++) { int float_p; ret_reg_op = call_insn->ops[i + 2]; gen_assert (ret_reg_op.mode == MIR_OP_REG || ret_reg_op.mode == MIR_OP_HARD_REG); type = proto->res_types[i]; float_p = type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD; if (float_p && n_vregs < 8) { new_insn = MIR_new_insn (ctx, type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : MIR_LDMOV, ret_reg_op, _MIR_new_hard_reg_op (ctx, V0_HARD_REG + n_vregs)); n_vregs++; } else if (!float_p && n_iregs < 8) { new_insn = MIR_new_insn (ctx, MIR_MOV, ret_reg_op, _MIR_new_hard_reg_op (ctx, R0_HARD_REG + n_iregs)); n_iregs++; } else { (*MIR_get_error_func (ctx)) (MIR_ret_error, "aarch64 can not handle this combination of return values"); } MIR_insert_insn_after (ctx, curr_func_item, call_insn, new_insn); call_insn->ops[i + 2] = new_insn->ops[1]; if ((ext_code = get_ext_code (type)) != MIR_INVALID_INSN) { MIR_insert_insn_after (ctx, curr_func_item, new_insn, MIR_new_insn (ctx, ext_code, ret_reg_op, ret_reg_op)); new_insn = DLIST_NEXT (MIR_insn_t, new_insn); } create_new_bb_insns (gen_ctx, call_insn, DLIST_NEXT (MIR_insn_t, new_insn), call_insn); } if (mem_size != 0) { /* allocate/deallocate stack for args passed on stack */ mem_size = (mem_size + 15) / 16 * 16; /* make it of several 16 bytes */ new_insn = MIR_new_insn (ctx, MIR_SUB, _MIR_new_hard_reg_op (ctx, SP_HARD_REG), _MIR_new_hard_reg_op (ctx, SP_HARD_REG), MIR_new_int_op (ctx, mem_size)); MIR_insert_insn_after (ctx, curr_func_item, prev_call_insn, new_insn); next_insn = DLIST_NEXT (MIR_insn_t, new_insn); create_new_bb_insns (gen_ctx, prev_call_insn, next_insn, call_insn); new_insn = MIR_new_insn (ctx, MIR_ADD, _MIR_new_hard_reg_op (ctx, SP_HARD_REG), _MIR_new_hard_reg_op (ctx, SP_HARD_REG), MIR_new_int_op (ctx, mem_size)); MIR_insert_insn_after (ctx, curr_func_item, call_insn, new_insn); next_insn = DLIST_NEXT (MIR_insn_t, new_insn); create_new_bb_insns (gen_ctx, call_insn, next_insn, call_insn); } } static long double mir_i2ld (int64_t i) { return i; } static const char *I2LD = "mir.i2ld"; static const char *I2LD_P = "mir.i2ld.p"; static long double mir_ui2ld (uint64_t i) { return i; } static const char *UI2LD = "mir.ui2ld"; static const char *UI2LD_P = "mir.ui2ld.p"; static long double mir_f2ld (float f) { return f; } static const char *F2LD = "mir.f2ld"; static const char *F2LD_P = "mir.f2ld.p"; static long double mir_d2ld (double d) { return d; } static const char *D2LD = "mir.d2ld"; static const char *D2LD_P = "mir.d2ld.p"; static int64_t mir_ld2i (long double ld) { return ld; } static const char *LD2I = "mir.ld2i"; static const char *LD2I_P = "mir.ld2i.p"; static float mir_ld2f (long double ld) { return ld; } static const char *LD2F = "mir.ld2f"; static const char *LD2F_P = "mir.ld2f.p"; static double mir_ld2d (long double ld) { return ld; } static const char *LD2D = "mir.ld2d"; static const char *LD2D_P = "mir.ld2d.p"; static long double mir_ldadd (long double d1, long double d2) { return d1 + d2; } static const char *LDADD = "mir.ldadd"; static const char *LDADD_P = "mir.ldadd.p"; static long double mir_ldsub (long double d1, long double d2) { return d1 - d2; } static const char *LDSUB = "mir.ldsub"; static const char *LDSUB_P = "mir.ldsub.p"; static long double mir_ldmul (long double d1, long double d2) { return d1 * d2; } static const char *LDMUL = "mir.ldmul"; static const char *LDMUL_P = "mir.ldmul.p"; static long double mir_lddiv (long double d1, long double d2) { return d1 / d2; } static const char *LDDIV = "mir.lddiv"; static const char *LDDIV_P = "mir.lddiv.p"; static long double mir_ldneg (long double d) { return -d; } static const char *LDNEG = "mir.ldneg"; static const char *LDNEG_P = "mir.ldneg.p"; static const char *VA_ARG_P = "mir.va_arg.p"; static const char *VA_ARG = "mir.va_arg"; static const char *VA_BLOCK_ARG_P = "mir.va_block_arg.p"; static const char *VA_BLOCK_ARG = "mir.va_block_arg"; static int64_t mir_ldeq (long double d1, long double d2) { return d1 == d2; } static const char *LDEQ = "mir.ldeq"; static const char *LDEQ_P = "mir.ldeq.p"; static int64_t mir_ldne (long double d1, long double d2) { return d1 != d2; } static const char *LDNE = "mir.ldne"; static const char *LDNE_P = "mir.ldne.p"; static int64_t mir_ldlt (long double d1, long double d2) { return d1 < d2; } static const char *LDLT = "mir.ldlt"; static const char *LDLT_P = "mir.ldlt.p"; static int64_t mir_ldge (long double d1, long double d2) { return d1 >= d2; } static const char *LDGE = "mir.ldge"; static const char *LDGE_P = "mir.ldge.p"; static int64_t mir_ldgt (long double d1, long double d2) { return d1 > d2; } static const char *LDGT = "mir.ldgt"; static const char *LDGT_P = "mir.ldgt.p"; static int64_t mir_ldle (long double d1, long double d2) { return d1 <= d2; } static const char *LDLE = "mir.ldle"; static const char *LDLE_P = "mir.ldle.p"; static int get_builtin (gen_ctx_t gen_ctx, MIR_insn_code_t code, MIR_item_t *proto_item, MIR_item_t *func_import_item) { MIR_context_t ctx = gen_ctx->ctx; MIR_type_t res_type; *func_import_item = *proto_item = NULL; /* to remove uninitialized warning */ switch (code) { case MIR_I2LD: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, I2LD_P, 1, &res_type, 1, MIR_T_I64, "v"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, I2LD, mir_i2ld); return 1; case MIR_UI2LD: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, UI2LD_P, 1, &res_type, 1, MIR_T_I64, "v"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, UI2LD, mir_ui2ld); return 1; case MIR_F2LD: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, F2LD_P, 1, &res_type, 1, MIR_T_F, "v"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, F2LD, mir_f2ld); return 1; case MIR_D2LD: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, D2LD_P, 1, &res_type, 1, MIR_T_D, "v"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, D2LD, mir_d2ld); return 1; case MIR_LD2I: res_type = MIR_T_I64; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LD2I_P, 1, &res_type, 1, MIR_T_LD, "v"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LD2I, mir_ld2i); return 1; case MIR_LD2F: res_type = MIR_T_F; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LD2F_P, 1, &res_type, 1, MIR_T_LD, "v"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LD2F, mir_ld2f); return 1; case MIR_LD2D: res_type = MIR_T_D; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LD2D_P, 1, &res_type, 1, MIR_T_LD, "v"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LD2D, mir_ld2d); return 1; case MIR_LDADD: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDADD_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDADD, mir_ldadd); return 2; case MIR_LDSUB: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDSUB_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDSUB, mir_ldsub); return 2; case MIR_LDMUL: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDMUL_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDMUL, mir_ldmul); return 2; case MIR_LDDIV: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDDIV_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDDIV, mir_lddiv); return 2; case MIR_LDNEG: res_type = MIR_T_LD; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDNEG_P, 1, &res_type, 1, MIR_T_LD, "d"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDNEG, mir_ldneg); return 1; case MIR_LDEQ: res_type = MIR_T_I64; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDEQ_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDEQ, mir_ldeq); return 2; case MIR_LDNE: res_type = MIR_T_I64; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDNE_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDNE, mir_ldne); return 2; case MIR_LDLT: res_type = MIR_T_I64; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDLT_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDLT, mir_ldlt); return 2; case MIR_LDGE: res_type = MIR_T_I64; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDGE_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDGE, mir_ldge); return 2; case MIR_LDGT: res_type = MIR_T_I64; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDGT_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDGT, mir_ldgt); return 2; case MIR_LDLE: res_type = MIR_T_I64; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, LDLE_P, 1, &res_type, 2, MIR_T_LD, "d1", MIR_T_LD, "d2"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, LDLE, mir_ldle); return 2; case MIR_VA_ARG: res_type = MIR_T_I64; *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, VA_ARG_P, 1, &res_type, 2, MIR_T_I64, "va", MIR_T_I64, "type"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, VA_ARG, va_arg_builtin); return 2; case MIR_VA_BLOCK_ARG: *proto_item = _MIR_builtin_proto (ctx, curr_func_item->module, VA_BLOCK_ARG_P, 0, NULL, 4, MIR_T_I64, "res", MIR_T_I64, "va", MIR_T_I64, "size", MIR_T_I64, "ncase"); *func_import_item = _MIR_builtin_func (ctx, curr_func_item->module, VA_BLOCK_ARG, va_block_arg_builtin); return 4; default: return 0; } } DEF_VARR (int); DEF_VARR (uint8_t); DEF_VARR (uint64_t); struct insn_pattern_info { int start, num; }; typedef struct insn_pattern_info insn_pattern_info_t; DEF_VARR (insn_pattern_info_t); struct label_ref { int abs_addr_p, short_p; size_t label_val_disp; MIR_label_t label; }; typedef struct label_ref label_ref_t; DEF_VARR (label_ref_t); DEF_VARR (MIR_code_reloc_t); struct target_ctx { unsigned char alloca_p, block_arg_func_p, leaf_p; size_t small_aggregate_save_area; VARR (int) * pattern_indexes; VARR (insn_pattern_info_t) * insn_pattern_info; VARR (uint8_t) * result_code; VARR (label_ref_t) * label_refs; VARR (uint64_t) * abs_address_locs; VARR (MIR_code_reloc_t) * relocs; }; #define alloca_p gen_ctx->target_ctx->alloca_p #define block_arg_func_p gen_ctx->target_ctx->block_arg_func_p #define leaf_p gen_ctx->target_ctx->leaf_p #define small_aggregate_save_area gen_ctx->target_ctx->small_aggregate_save_area #define pattern_indexes gen_ctx->target_ctx->pattern_indexes #define insn_pattern_info gen_ctx->target_ctx->insn_pattern_info #define result_code gen_ctx->target_ctx->result_code #define label_refs gen_ctx->target_ctx->label_refs #define abs_address_locs gen_ctx->target_ctx->abs_address_locs #define relocs gen_ctx->target_ctx->relocs static MIR_disp_t target_get_stack_slot_offset (gen_ctx_t gen_ctx, MIR_type_t type, MIR_reg_t slot) { /* slot is 0, 1, ... */ size_t offset = curr_func_item->u.func->vararg_p || block_arg_func_p ? 32 : 16; return ((MIR_disp_t) slot * 8 + offset); } static void target_machinize (gen_ctx_t gen_ctx) { MIR_context_t ctx = gen_ctx->ctx; MIR_func_t func; MIR_type_t type, mem_type, res_type; MIR_insn_code_t code, new_insn_code; MIR_insn_t insn, next_insn, new_insn, anchor; MIR_var_t var; MIR_reg_t ret_reg, arg_reg; MIR_op_t ret_reg_op, arg_reg_op, mem_op, temp_op; size_t i, int_arg_num, fp_arg_num, mem_size, qwords; assert (curr_func_item->item_type == MIR_func_item); func = curr_func_item->u.func; block_arg_func_p = FALSE; anchor = DLIST_HEAD (MIR_insn_t, func->insns); small_aggregate_save_area = 0; for (i = int_arg_num = fp_arg_num = mem_size = 0; i < func->nargs; i++) { /* Argument extensions is already done in simplify */ /* Prologue: generate arg_var = hard_reg|stack mem|stack addr ... */ var = VARR_GET (MIR_var_t, func->vars, i); type = var.type; if (type == MIR_T_RBLK && i == 0) { /* hidden arg */ arg_reg_op = _MIR_new_hard_reg_op (ctx, R8_HARD_REG); gen_mov (gen_ctx, anchor, MIR_MOV, MIR_new_reg_op (ctx, i + 1), arg_reg_op); continue; } else if (MIR_blk_type_p (type) && (qwords = (var.size + 7) / 8) <= 2) { if (int_arg_num + qwords <= 8) { small_aggregate_save_area += qwords * 8; new_insn = MIR_new_insn (ctx, MIR_SUB, MIR_new_reg_op (ctx, i + 1), _MIR_new_hard_reg_op (ctx, FP_HARD_REG), MIR_new_int_op (ctx, small_aggregate_save_area)); gen_add_insn_before (gen_ctx, anchor, new_insn); if (qwords == 0) continue; gen_mov (gen_ctx, anchor, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 0, i + 1, 0, 1), _MIR_new_hard_reg_op (ctx, int_arg_num)); if (qwords == 2) gen_mov (gen_ctx, anchor, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 8, i + 1, 0, 1), _MIR_new_hard_reg_op (ctx, int_arg_num + 1)); } else { /* pass on stack w/o address: */ if (!block_arg_func_p) { block_arg_func_p = TRUE; gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, R8_HARD_REG), _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 16, FP_HARD_REG, MIR_NON_HARD_REG, 1)); } gen_add_insn_before (gen_ctx, anchor, MIR_new_insn (ctx, MIR_ADD, MIR_new_reg_op (ctx, i + 1), _MIR_new_hard_reg_op (ctx, R8_HARD_REG), MIR_new_int_op (ctx, mem_size))); mem_size += qwords * 8; } int_arg_num += qwords; continue; } arg_reg = get_arg_reg (type, &int_arg_num, &fp_arg_num, &new_insn_code); if (arg_reg != MIR_NON_HARD_REG) { arg_reg_op = _MIR_new_hard_reg_op (ctx, arg_reg); gen_mov (gen_ctx, anchor, new_insn_code, MIR_new_reg_op (ctx, i + 1), arg_reg_op); } else { /* arg is on the stack */ if (!block_arg_func_p) { block_arg_func_p = TRUE; gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, R8_HARD_REG), _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 16, FP_HARD_REG, MIR_NON_HARD_REG, 1)); } mem_type = type == MIR_T_F || type == MIR_T_D || type == MIR_T_LD ? type : MIR_T_I64; if (type == MIR_T_LD) mem_size = (mem_size + 15) / 16 * 16; new_insn_code = (type == MIR_T_F ? MIR_FMOV : type == MIR_T_D ? MIR_DMOV : type == MIR_T_LD ? MIR_LDMOV : MIR_MOV); mem_op = _MIR_new_hard_reg_mem_op (ctx, mem_type, mem_size, R8_HARD_REG, MIR_NON_HARD_REG, 1); gen_mov (gen_ctx, anchor, new_insn_code, MIR_new_reg_op (ctx, i + 1), mem_op); mem_size += type == MIR_T_LD ? 16 : 8; } } alloca_p = FALSE; leaf_p = TRUE; for (insn = DLIST_HEAD (MIR_insn_t, func->insns); insn != NULL; insn = next_insn) { MIR_item_t proto_item, func_import_item; int nargs; next_insn = DLIST_NEXT (MIR_insn_t, insn); code = insn->code; if (code == MIR_LDBEQ || code == MIR_LDBNE || code == MIR_LDBLT || code == MIR_LDBGE || code == MIR_LDBGT || code == MIR_LDBLE) { temp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); code = (code == MIR_LDBEQ ? MIR_LDEQ : code == MIR_LDBNE ? MIR_LDNE : code == MIR_LDBLT ? MIR_LDLT : code == MIR_LDBGE ? MIR_LDGE : code == MIR_LDBGT ? MIR_LDGT : MIR_LDLE); new_insn = MIR_new_insn (ctx, code, temp_op, insn->ops[1], insn->ops[2]); gen_add_insn_before (gen_ctx, insn, new_insn); next_insn = MIR_new_insn (ctx, MIR_BT, insn->ops[0], temp_op); gen_add_insn_after (gen_ctx, new_insn, next_insn); gen_delete_insn (gen_ctx, insn); insn = new_insn; } if ((nargs = get_builtin (gen_ctx, code, &proto_item, &func_import_item)) > 0) { if (code == MIR_VA_ARG || code == MIR_VA_BLOCK_ARG) { /* Use a builtin func call: mov func_reg, func ref; [mov reg3, type;] call proto, func_reg, res_reg, va_reg, reg3 */ MIR_op_t ops[6], func_reg_op, reg_op3; MIR_op_t res_reg_op = insn->ops[0], va_reg_op = insn->ops[1], op3 = insn->ops[2]; assert (res_reg_op.mode == MIR_OP_REG && va_reg_op.mode == MIR_OP_REG && op3.mode == (code == MIR_VA_ARG ? MIR_OP_MEM : MIR_OP_REG)); func_reg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); reg_op3 = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); next_insn = new_insn = MIR_new_insn (ctx, MIR_MOV, func_reg_op, MIR_new_ref_op (ctx, func_import_item)); gen_add_insn_before (gen_ctx, insn, new_insn); if (code == MIR_VA_ARG) { new_insn = MIR_new_insn (ctx, MIR_MOV, reg_op3, MIR_new_int_op (ctx, (int64_t) op3.u.mem.type)); op3 = reg_op3; gen_add_insn_before (gen_ctx, insn, new_insn); } ops[0] = MIR_new_ref_op (ctx, proto_item); ops[1] = func_reg_op; ops[2] = res_reg_op; ops[3] = va_reg_op; ops[4] = op3; if (code == MIR_VA_BLOCK_ARG) ops[5] = insn->ops[3]; new_insn = MIR_new_insn_arr (ctx, MIR_CALL, code == MIR_VA_ARG ? 5 : 6, ops); gen_add_insn_before (gen_ctx, insn, new_insn); gen_delete_insn (gen_ctx, insn); } else { /* Use builtin: mov freg, func ref; call proto, freg, res_reg, op_reg[, op_reg2] */ MIR_op_t freg_op, res_reg_op = insn->ops[0], op_reg_op = insn->ops[1], ops[5]; assert (res_reg_op.mode == MIR_OP_REG && op_reg_op.mode == MIR_OP_REG); freg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); next_insn = new_insn = MIR_new_insn (ctx, MIR_MOV, freg_op, MIR_new_ref_op (ctx, func_import_item)); gen_add_insn_before (gen_ctx, insn, new_insn); ops[0] = MIR_new_ref_op (ctx, proto_item); ops[1] = freg_op; ops[2] = res_reg_op; ops[3] = op_reg_op; if (nargs == 2) ops[4] = insn->ops[2]; new_insn = MIR_new_insn_arr (ctx, MIR_CALL, nargs + 3, ops); gen_add_insn_before (gen_ctx, insn, new_insn); gen_delete_insn (gen_ctx, insn); } } else if (code == MIR_VA_START) { MIR_op_t treg_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); MIR_op_t prev_sp_op = MIR_new_reg_op (ctx, gen_new_temp_reg (gen_ctx, MIR_T_I64, func)); MIR_op_t va_op = insn->ops[0]; MIR_reg_t va_reg; int gp_offset, fp_offset; assert (func->vararg_p && va_op.mode == MIR_OP_REG); gp_offset = (int_arg_num >= 8 ? 0 : 8 * int_arg_num - 64); fp_offset = (fp_arg_num >= 8 ? 0 : 16 * fp_arg_num - 128); va_reg = va_op.u.reg; /* Insns can be not simplified as soon as they match a machine insn. */ /* mem32[va_reg].__gr_offset = gp_offset; mem32[va_reg].__vr_offset = fp_offset */ gen_mov (gen_ctx, insn, MIR_MOV, treg_op, MIR_new_int_op (ctx, gp_offset)); gen_mov (gen_ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_U32, 24, va_reg, 0, 1), treg_op); gen_mov (gen_ctx, insn, MIR_MOV, treg_op, MIR_new_int_op (ctx, fp_offset)); gen_mov (gen_ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_U32, 28, va_reg, 0, 1), treg_op); /* __stack: prev_sp = mem64[fp + 16]; mem64[va_reg].__stack = prev_sp + mem_size */ gen_mov (gen_ctx, insn, MIR_MOV, prev_sp_op, _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 16, FP_HARD_REG, MIR_NON_HARD_REG, 1)); new_insn = MIR_new_insn (ctx, MIR_ADD, treg_op, prev_sp_op, MIR_new_int_op (ctx, mem_size)); gen_add_insn_before (gen_ctx, insn, new_insn); gen_mov (gen_ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 0, va_reg, 0, 1), treg_op); /* __gr_top: mem64[va_reg].__gr_top = prev_sp */ gen_mov (gen_ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 8, va_reg, 0, 1), prev_sp_op); /* __vr_top: treg = prev_sp - int_reg_save_area; mem64[va_reg].__vr_top = treg */ new_insn = MIR_new_insn (ctx, MIR_SUB, treg_op, prev_sp_op, MIR_new_int_op (ctx, int_reg_save_area_size)); gen_add_insn_before (gen_ctx, insn, new_insn); gen_mov (gen_ctx, insn, MIR_MOV, MIR_new_mem_op (ctx, MIR_T_I64, 16, va_reg, 0, 1), treg_op); gen_delete_insn (gen_ctx, insn); } else if (code == MIR_VA_END) { /* do nothing */ gen_delete_insn (gen_ctx, insn); } else if (MIR_call_code_p (code)) { machinize_call (gen_ctx, insn); leaf_p = FALSE; } else if (code == MIR_ALLOCA) { alloca_p = TRUE; } else if (code == MIR_RET) { /* In simplify we already transformed code for one return insn and added extension insn (if any). */ uint32_t n_iregs = 0, n_vregs = 0; assert (func->nres == MIR_insn_nops (ctx, insn)); for (size_t i = 0; i < func->nres; i++) { assert (insn->ops[i].mode == MIR_OP_REG); res_type = func->res_types[i]; if ((res_type == MIR_T_F || res_type == MIR_T_D || res_type == MIR_T_LD) && n_vregs < 8) { new_insn_code = res_type == MIR_T_F ? MIR_FMOV : res_type == MIR_T_D ? MIR_DMOV : MIR_LDMOV; ret_reg = V0_HARD_REG + n_vregs++; } else if (n_iregs < 8) { new_insn_code = MIR_MOV; ret_reg = R0_HARD_REG + n_iregs++; } else { (*MIR_get_error_func (ctx)) (MIR_ret_error, "aarch64 can not handle this combination of return values"); } ret_reg_op = _MIR_new_hard_reg_op (ctx, ret_reg); gen_mov (gen_ctx, insn, new_insn_code, ret_reg_op, insn->ops[i]); insn->ops[i] = ret_reg_op; } } } } static void isave (gen_ctx_t gen_ctx, MIR_insn_t anchor, int disp, MIR_reg_t base, MIR_reg_t hard_reg) { gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_mem_op (gen_ctx->ctx, MIR_T_I64, disp, base, MIR_NON_HARD_REG, 1), _MIR_new_hard_reg_op (gen_ctx->ctx, hard_reg)); } static void fsave (gen_ctx_t gen_ctx, MIR_insn_t anchor, int disp, MIR_reg_t base, MIR_reg_t hard_reg) { gen_mov (gen_ctx, anchor, MIR_LDMOV, _MIR_new_hard_reg_mem_op (gen_ctx->ctx, MIR_T_LD, disp, base, MIR_NON_HARD_REG, 1), _MIR_new_hard_reg_op (gen_ctx->ctx, hard_reg)); } static void target_make_prolog_epilog (gen_ctx_t gen_ctx, bitmap_t used_hard_regs, size_t stack_slots_num) { MIR_context_t ctx = gen_ctx->ctx; MIR_func_t func; MIR_insn_t anchor, new_insn; MIR_op_t sp_reg_op, fp_reg_op, treg_op, treg_op2; int64_t start; int save_prev_stack_p; size_t i, offset, frame_size, frame_size_after_saved_regs, saved_iregs_num, saved_fregs_num; assert (curr_func_item->item_type == MIR_func_item); func = curr_func_item->u.func; for (i = saved_iregs_num = saved_fregs_num = 0; i <= MAX_HARD_REG; i++) if (!target_call_used_hard_reg_p (i, MIR_T_UNDEF) && bitmap_bit_p (used_hard_regs, i)) { if (i < V0_HARD_REG) saved_iregs_num++; else saved_fregs_num++; } if (leaf_p && !alloca_p && saved_iregs_num == 0 && saved_fregs_num == 0 && !func->vararg_p && stack_slots_num == 0 && !block_arg_func_p && small_aggregate_save_area == 0) return; sp_reg_op = _MIR_new_hard_reg_op (ctx, SP_HARD_REG); fp_reg_op = _MIR_new_hard_reg_op (ctx, FP_HARD_REG); /* Prologue: */ anchor = DLIST_HEAD (MIR_insn_t, func->insns); frame_size = func->vararg_p ? reg_save_area_size : 0; for (i = 0; i <= MAX_HARD_REG; i++) if (!target_call_used_hard_reg_p (i, MIR_T_UNDEF) && bitmap_bit_p (used_hard_regs, i)) { if (i < V0_HARD_REG) { frame_size += 8; } else { if (frame_size % 16 != 0) frame_size = (frame_size + 15) / 16 * 16; frame_size += 16; } } if (frame_size % 16 != 0) frame_size = (frame_size + 15) / 16 * 16; frame_size_after_saved_regs = frame_size; frame_size += stack_slots_num * 8; if (frame_size % 16 != 0) frame_size = (frame_size + 15) / 16 * 16; save_prev_stack_p = func->vararg_p || block_arg_func_p; treg_op = _MIR_new_hard_reg_op (ctx, R9_HARD_REG); if (save_prev_stack_p) { /* prev stack pointer */ gen_mov (gen_ctx, anchor, MIR_MOV, treg_op, sp_reg_op); frame_size += 16; } frame_size += 16; /* lr/fp */ if (frame_size < (1 << 12)) { new_insn = MIR_new_insn (ctx, MIR_SUB, sp_reg_op, sp_reg_op, MIR_new_int_op (ctx, frame_size)); } else { treg_op2 = _MIR_new_hard_reg_op (ctx, R10_HARD_REG); new_insn = MIR_new_insn (ctx, MIR_MOV, treg_op2, MIR_new_int_op (ctx, frame_size)); gen_add_insn_before (gen_ctx, anchor, new_insn); /* t = frame_size */ new_insn = MIR_new_insn (ctx, MIR_SUB, sp_reg_op, sp_reg_op, treg_op2); } gen_add_insn_before (gen_ctx, anchor, new_insn); /* sp = sp - (frame_size|t) */ if (save_prev_stack_p) gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 16, SP_HARD_REG, MIR_NON_HARD_REG, 1), treg_op); /* mem[sp + 16] = treg */ gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 8, SP_HARD_REG, MIR_NON_HARD_REG, 1), _MIR_new_hard_reg_op (ctx, LINK_HARD_REG)); /* mem[sp + 8] = lr */ gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 0, SP_HARD_REG, MIR_NON_HARD_REG, 1), _MIR_new_hard_reg_op (ctx, FP_HARD_REG)); /* mem[sp] = fp */ gen_mov (gen_ctx, anchor, MIR_MOV, fp_reg_op, sp_reg_op); /* fp = sp */ if (func->vararg_p) { // ??? saving only regs corresponding to ... MIR_reg_t base = SP_HARD_REG; start = (int64_t) frame_size - reg_save_area_size; if ((start + 184) >= (1 << 12)) { new_insn = MIR_new_insn (ctx, MIR_MOV, treg_op, MIR_new_int_op (ctx, start)); gen_add_insn_before (gen_ctx, anchor, new_insn); /* t = frame_size - reg_save_area_size */ start = 0; base = R9_HARD_REG; } fsave (gen_ctx, anchor, start, base, V0_HARD_REG); fsave (gen_ctx, anchor, start + 16, base, V1_HARD_REG); fsave (gen_ctx, anchor, start + 32, base, V2_HARD_REG); fsave (gen_ctx, anchor, start + 48, base, V3_HARD_REG); fsave (gen_ctx, anchor, start + 64, base, V4_HARD_REG); fsave (gen_ctx, anchor, start + 80, base, V5_HARD_REG); fsave (gen_ctx, anchor, start + 96, base, V6_HARD_REG); fsave (gen_ctx, anchor, start + 112, base, V7_HARD_REG); isave (gen_ctx, anchor, start + 128, base, R0_HARD_REG); isave (gen_ctx, anchor, start + 136, base, R1_HARD_REG); isave (gen_ctx, anchor, start + 144, base, R2_HARD_REG); isave (gen_ctx, anchor, start + 152, base, R3_HARD_REG); isave (gen_ctx, anchor, start + 160, base, R4_HARD_REG); isave (gen_ctx, anchor, start + 168, base, R5_HARD_REG); isave (gen_ctx, anchor, start + 176, base, R6_HARD_REG); isave (gen_ctx, anchor, start + 184, base, R7_HARD_REG); } /* Saving callee saved hard registers: */ offset = frame_size - frame_size_after_saved_regs; for (i = 0; i <= MAX_HARD_REG; i++) if (!target_call_used_hard_reg_p (i, MIR_T_UNDEF) && bitmap_bit_p (used_hard_regs, i)) { if (i < V0_HARD_REG) { gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, offset, FP_HARD_REG, MIR_NON_HARD_REG, 1), _MIR_new_hard_reg_op (ctx, i)); offset += 8; } else { if (offset % 16 != 0) offset = (offset + 15) / 16 * 16; gen_mov (gen_ctx, anchor, MIR_LDMOV, _MIR_new_hard_reg_mem_op (ctx, MIR_T_LD, offset, FP_HARD_REG, MIR_NON_HARD_REG, 1), _MIR_new_hard_reg_op (ctx, i)); offset += 16; } } if (small_aggregate_save_area != 0) { // ??? duplication with vararg saved regs if (small_aggregate_save_area % 16 != 0) small_aggregate_save_area = (small_aggregate_save_area + 15) / 16 * 16; new_insn = MIR_new_insn (ctx, MIR_SUB, sp_reg_op, sp_reg_op, MIR_new_int_op (ctx, small_aggregate_save_area)); gen_add_insn_before (gen_ctx, anchor, new_insn); /* sp -= */ } /* Epilogue: */ anchor = DLIST_TAIL (MIR_insn_t, func->insns); assert (anchor->code == MIR_RET); /* Restoring hard registers: */ offset = frame_size - frame_size_after_saved_regs; for (i = 0; i <= MAX_HARD_REG; i++) if (!target_call_used_hard_reg_p (i, MIR_T_UNDEF) && bitmap_bit_p (used_hard_regs, i)) { if (i < V0_HARD_REG) { gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, i), _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, offset, FP_HARD_REG, MIR_NON_HARD_REG, 1)); offset += 8; } else { if (offset % 16 != 0) offset = (offset + 15) / 16 * 16; gen_mov (gen_ctx, anchor, MIR_LDMOV, _MIR_new_hard_reg_op (ctx, i), _MIR_new_hard_reg_mem_op (ctx, MIR_T_LD, offset, FP_HARD_REG, MIR_NON_HARD_REG, 1)); offset += 16; } } /* Restore lr, sp, fp */ gen_mov (gen_ctx, anchor, MIR_MOV, _MIR_new_hard_reg_op (ctx, LINK_HARD_REG), _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 8, FP_HARD_REG, MIR_NON_HARD_REG, 1)); if (frame_size < (1 << 12)) { new_insn = MIR_new_insn (ctx, MIR_ADD, sp_reg_op, fp_reg_op, MIR_new_int_op (ctx, frame_size)); } else { new_insn = MIR_new_insn (ctx, MIR_MOV, treg_op, MIR_new_int_op (ctx, frame_size)); gen_add_insn_before (gen_ctx, anchor, new_insn); /* t = frame_size */ new_insn = MIR_new_insn (ctx, MIR_ADD, sp_reg_op, fp_reg_op, treg_op); } gen_add_insn_before (gen_ctx, anchor, new_insn); /* sp = fp + (frame_size|t) */ gen_mov (gen_ctx, anchor, MIR_MOV, fp_reg_op, _MIR_new_hard_reg_mem_op (ctx, MIR_T_I64, 0, FP_HARD_REG, MIR_NON_HARD_REG, 1)); } struct pattern { MIR_insn_code_t code; /* Pattern elements: blank - ignore X - match everything $ - finish successfully matching r - register h[0-63] - hard register with given number memory with indexed reg offset: m[0-3] - int (signed or unsigned) type memory of size 8,16,32,64-bits ms[0-3] - signed int type memory of size 8,16,32,64-bits mu[0-3] - unsigned int type memory of size 8,16,32,64-bits option(field[13..15]) == 011 -- shifted reg (Rm=R31 means SP) option == 010 (UXTW), 110 (SXTW), 111 (SXTX) -- extended reg (RM=R31 means ZR) we use option == 111 only for non-index mem and 011 for indexed memory memory with immediate offset: M[0-3] - int (signed or unsigned) type memory of size 8,16,32,64-bits Ms[0-3] - signed int type memory of size 8,16,32,64-bits Mu[0-3] - unsigned int type memory of size 8,16,32,64-bits zero extended scaled 12-bit offset (field[10..21]) N[0-2] - 2nd immediate (or reference) operand can be created by movn and n movk insns Z[0-2] - 2nd immediate (or reference) operand can be created by movz and n movk insns Z3 - any 2nd 64-bit immediate (or reference) operand Zf - floating point 0.0 Zd - double floating point 0.0 memory with indexed reg offset: mf - memory of float md - memory of double mld - memory of long double memory with immediate offset: Mf - memory of float Md - memory of double Mld - memory of long double I -- immediate as 3th op for arithemtic insn (12-bit unsigned with possible 12-bit LSL) Iu -- immediate for arithemtic insn roundup to 16 SR -- any immediate for right 64-bit shift (0-63) Sr -- any immediate for right 32-bit shift (0-31) SL -- any immediate for left 64-bit shift (0-63) Sl -- any immediate for left 32-bit shift (0-31) L - reference or label as the 1st or 2nd op which can be present by signed 26-bit pc offset (in 4 insn bytes) l - label as the 1st op which can be present by signed 19-bit pc offset (in 4 insn bytes) Remember we have no float or (long) double immediate at this stage. They are represented by a reference to data item. */ const char *pattern; /* Replacement elements: blank - ignore ; - insn separation hex:hex - opcode and its mask (the mask should include opcode, the mask defines bits which can not be changed by other fields) rd[0-2] - put n-th operand register into rd field [0..4] rn[0-2] - put n-th operand register into rn field [5..9] rm[0-2] - put n-th operand register into rm field [16..20] ra[0-2] - put n-th operand register into ra field [10..14] h(d,n,m) - hardware register with given number in rd,rn,rm field m = 1st or 2nd operand is (8-,16-,32-,64-bit) mem with base, scaled index M = 1st or 2nd operand is (8-,16-,32-,64-bit) mem with base, scaled imm12 disp [10..21] S - immr[16..21] for right shift SR/Sr SL, Sl - immr[16..21] and imms[10..15] for left shift SL/Sl Z[0-3] -- n-th 16-bit immediate[5..20] from Z[0-3] and its shift [21..22] N[0-3] -- n-th 16-bit immediate[5..20] from N[0-3] and its shift [21..22] I -- arithmetic op 12-bit immediate [10..21] and its shift [22..23] Iu -- arithmetic op immediate [10..21] got from roundup value to 16 and its shift [22..23] L -- operand-label as 26-bit offset l -- operand-label as 19-bit offset T -- pc-relative address [5..23] */ const char *replacement; }; static const struct pattern patterns[] = { {MIR_MOV, "r h31", "91000000:fffffc00 rd0 hn1f"}, /* mov Rd,SP */ {MIR_MOV, "h31 r", "91000000:fffffc00 hd1f rn1"}, /* mov SP,Rn */ {MIR_MOV, "r r", "aa0003e0:ffe0ffe0 rd0 rm1"}, /* mov Rd,Rm */ // ??? patterns to extend 32-bit index register {MIR_MOV, "r m3", "f8600800:ffe00c00 rd0 m"}, /* ldr Rd,[Rn,Rm{,#3}] */ {MIR_MOV, "m3 r", "f8200800:ffe00c00 rd1 m"}, /* str Rd,[Rn,Rm{,#3}] */ {MIR_MOV, "r M3", "f9400000:ffc00000 rd0 M"}, /* ldr Rd,[Rn,{,#imm12}] */ {MIR_MOV, "M3 r", "f9000000:ffc00000 rd1 M"}, /* str Rd,[Rn,Rm{,#imm12}] */ {MIR_MOV, "r mu2", "b8600800:ffe00c00 rd0 m"}, /* ldr Wd,[Rn,Rm{,#2}] */ {MIR_MOV, "m2 r", "b8200800:ffe00c00 rd1 m"}, /* str Wd,[Rn,Rm{,#2}] */ {MIR_MOV, "r Mu2", "b9400000:ffc00000 rd0 M"}, /* ldr Wd,[Rn{,#imm12}] */ {MIR_MOV, "M2 r", "b9000000:ffc00000 rd1 M"}, /* str Wd,[Rn,Rm{,#imm12}] */ {MIR_MOV, "r ms2", "b8a00800:ffe00c00 rd0 m"}, /* ldrsw Rd,[Rn,Rm{,#2}] */ {MIR_MOV, "r Ms2", "b9800000:ffc00000 rd0 M"}, /* ldrsw Rd,[Rn{,#imm12}] */ {MIR_MOV, "r mu1", "78600800:ffe00c00 rd0 m"}, /* ldrh Wd,[Rn,Rm{,#1}] */ {MIR_MOV, "m1 r", "78200800:ffe00c00 rd1 m"}, /* strh Wd,[Rn,Rm{,#1}] */ {MIR_MOV, "r Mu1", "79400000:ffc00000 rd0 M"}, /* ldrh Wd,[Rn{,#imm12}] */ {MIR_MOV, "M1 r", "79000000:ffc00000 rd1 M"}, /* strh Wd,[Rn,Rm{,#imm12}] */ {MIR_MOV, "r ms1", "78a00800:ffe00c00 rd0 m"}, /* ldrsh Wd,[Rn,Rm{,#2}] */ {MIR_MOV, "r Ms1", "79800000:ffc00000 rd0 M"}, /* ldrsh Wd,[Rn{,#imm12}] */ {MIR_MOV, "r mu0", "38600800:ffe00c00 rd0 m"}, /* ldrb Wd,[Rn,Rm{,#1}] */ {MIR_MOV, "m0 r", "38200800:ffe00c00 rd1 m"}, /* strb Wd,[Rn,Rm{,#1}] */ {MIR_MOV, "r Mu0", "39400000:ffc00000 rd0 M"}, /* ldrb Wd,[Rn{,#imm12}] */ {MIR_MOV, "M0 r", "39000000:ffc00000 rd1 M"}, /* strb Wd,[Rn,Rm{,#imm12}] */ {MIR_MOV, "r ms0", "38a00800:ffa00c00 rd0 m"}, /* ldrsb Rd,[Rn,Rm{,#1}] */ {MIR_MOV, "r Ms0", "39800000:ffc00000 rd0 M"}, /* ldrsb Rd,[Rn{,#imm12}] */ {MIR_MOV, "r Z0", "d2800000:ff800000 rd0 Z0"}, /* movz Rd, imm */ {MIR_MOV, "r N0", "92800000:ff800000 rd0 N0"}, /* movn Rd, imm */ /* movn Rd, imm0, sh0; movk Rd, imm1, sh1: */ {MIR_MOV, "r Z1", "d2800000:ff800000 rd0 Z0; f2800000:ff800000 rd0 Z1"}, /* movn imm0, sh0; movk Rd, imm1, sh1: */ {MIR_MOV, "r N1", "92800000:ff800000 rd0 N0; f2800000:ff800000 rd0 N1"}, /* movz Rd, imm0, sh0; movk Rd, imm1, sh1; movk Rd, imm3, sh3: */ {MIR_MOV, "r Z2", "d2800000:ff800000 rd0 Z0; f2800000:ff800000 rd0 Z1; f2800000:ff800000 rd0 Z2"}, /* movn Rd, imm0, sh0; movk Rd, imm1, sh1; movk Rd, imm3, sh3: */ {MIR_MOV, "r N2", "92800000:ff800000 rd0 N0; f2800000:ff800000 rd0 N1; f2800000:ff800000 rd0 N2"}, /* movz Rd, imm0, sh0; movk Rd, imm1, sh1; movk Rd, imm2, sh2; movk Rd, imm3, sh3: */ {MIR_MOV, "r Z3", "d2800000:ff800000 rd0 Z0; f2800000:ff800000 rd0 Z1; f2800000:ff800000 rd0 Z2;" "f2800000:ff800000 rd0 Z3"}, {MIR_FMOV, "r r", "1e204000:fffffc00 vd0 vn1"}, /* fmov Sd,Sn */ {MIR_FMOV, "r mf", "bc600800:ff600c00 vd0 m"}, /* ldr Sd,[Rn,Rm{,#2}] */ {MIR_FMOV, "mf r", "bc200800:ff600c00 vd1 m"}, /* str Sd,[Rn,Rm{,#2}] */ {MIR_FMOV, "r Mf", "bd400000:ffc00000 vd0 M"}, /* ldr Sd,[Rn,{,#imm12}] */ {MIR_FMOV, "Mf r", "bd000000:ffc00000 vd1 M"}, /* str Sd,[Rn,Rm{,#imm12}] */ {MIR_DMOV, "r r", "1e604000:fffffc00 vd0 vn1"}, /* fmov Dd,Dn */ {MIR_DMOV, "r md", "fc600800:ff600c00 vd0 m"}, /* ldr Dd,[Rn,Rm{,#3}] */ {MIR_DMOV, "md r", "fc200800:ff600c00 vd1 m"}, /* str Dd,[Rn,Rm{,#3}] */ {MIR_DMOV, "r Md", "fd400000:ffc00000 vd0 M"}, /* ldr Dd,[Rn,{,#imm12}] */ {MIR_DMOV, "Md r", "fd000000:ffc00000 vd1 M"}, /* str Dd,[Rn,Rm{,#imm12}] */ {MIR_LDMOV, "r r", "4ea01c00:ffe0fc00 vd0 vm1 vn1"}, /* orr Qd.16b,Qm.16b,Qn.16b */ {MIR_LDMOV, "r mld", "3ce00800:ffe00c00 vd0 m"}, /* ldr Qd,[Rn,Rm{,#4}] */ {MIR_LDMOV, "mld r", "3ca00800:ffe00c00 vd1 m"}, /* str Qd,[Rn,Rm{,#4}] */ {MIR_LDMOV, "r Mld", "3dc00000:ffc00000 vd0 M"}, /* ldr Qd,[Rn,{,#imm12}] */ {MIR_LDMOV, "Mld r", "3d800000:ffc00000 vd1 M"}, /* str Qd,[Rn,Rm{,#imm12}] */ {MIR_EXT8, "r r", "93401c00:fffffc00 rd0 rn1"}, /* sxtb rd, wn */ {MIR_EXT16, "r r", "93403c00:fffffc00 rd0 rn1"}, /* sxth rd, wn */ {MIR_EXT32, "r r", "93407c00:fffffc00 rd0 rn1"}, /* sxtw rd, wn */ {MIR_UEXT8, "r r", "53001c00:fffffc00 rd0 rn1"}, /* uxtb wd, wn */ {MIR_UEXT16, "r r", "53003c00:fffffc00 rd0 rn1"}, /* uxth wd, wn */ {MIR_UEXT32, "r r", "2a0003e0:7fe0ffe0 rd0 rm1"}, /* mov wd, wm */ {MIR_ADD, "r r r", "8b206000:ffe0fc00 rd0 rn1 rm2"}, /* extended add Rd,Rn,Rm*/ {MIR_ADD, "r r I", "91000000:ff000000 rd0 rn1 I"}, /* add Rd,Rn,I,shift */ {MIR_ADDS, "r r r", "0b000000:ff200000 rd0 rn1 rm2"}, /* add Wd,Wn,Wm*/ {MIR_ADDS, "r r I", "11000000:ff000000 rd0 rn1 I"}, /* add Wd,Wn,I,shift */ {MIR_FADD, "r r r", "1e202800:ffe0fc00 vd0 vn1 vm2"}, /* fadd Sd,Sn,Sm*/ {MIR_DADD, "r r r", "1e602800:ffe0fc00 vd0 vn1 vm2"}, /* fadd Dd,Dn,Dm*/ // ldadd is implemented through builtin {MIR_SUB, "r r r", "cb206000:ffe0fc00 rd0 rn1 rm2"}, /* extended sub Rd,Rn,Rm*/ {MIR_SUB, "r r I", "d1000000:ff000000 rd0 rn1 I"}, /* sub Rd,Rn,I,shift */ {MIR_SUBS, "r r r", "4b000000:ff200000 rd0 rn1 rm2"}, /* sub Wd,Wn,Wm*/ {MIR_SUBS, "r r I", "51000000:ff000000 rd0 rn1 I"}, /* sub Wd,Wn,I,shift */ {MIR_FSUB, "r r r", "1e203800:ffe0fc00 vd0 vn1 vm2"}, /* fsub Sd,Sn,Sm*/ {MIR_DSUB, "r r r", "1e603800:ffe0fc00 vd0 vn1 vm2"}, /* fsub Dd,Dn,Dm*/ // ldsub is implemented through builtin {MIR_MUL, "r r r", "9b007c00:ffe0fc00 rd0 rn1 rm2"}, /* mul Rd,Rn,Rm*/ {MIR_MULS, "r r r", "1b007c00:ffe0fc00 rd0 rn1 rm2"}, /* mul Wd,Wn,Wm*/ {MIR_FMUL, "r r r", "1e200800:ffe0fc00 vd0 vn1 vm2"}, /* fmul Sd,Sn,Sm*/ {MIR_DMUL, "r r r", "1e600800:ffe0fc00 vd0 vn1 vm2"}, /* fmul Dd,Dn,Dm*/ // ldmul is implemented through builtin {MIR_DIV, "r r r", "9ac00c00:ffe0fc00 rd0 rn1 rm2"}, /* sdiv Rd,Rn,Rm*/ {MIR_DIVS, "r r r", "1ac00c00:ffe0fc00 rd0 rn1 rm2"}, /* sdiv Wd,Wn,Wm*/ {MIR_UDIV, "r r r", "9ac00800:ffe0fc00 rd0 rn1 rm2"}, /* udiv Rd,Rn,Rm*/ {MIR_UDIVS, "r r r", "1ac00800:ffe0fc00 rd0 rn1 rm2"}, /* udiv Wd,Wn,Wm*/ {MIR_FDIV, "r r r", "1e201800:ffe0fc00 vd0 vn1 vm2"}, /* fdiv Sd,Sn,Sm*/ {MIR_DDIV, "r r r", "1e601800:ffe0fc00 vd0 vn1 vm2"}, /* fmul Dd,Dn,Dm*/ // lddiv is implemented through builtin /* sdiv r8,Rn,Rm;msub Rd,r8,Rm,Rn: */ {MIR_MOD, "r r r", "9ac00c00:ffe0fc00 hd8 rn1 rm2;9b008000:ffe08000 rd0 hm8 rn2 ra1"}, /* sdiv r8,Wn,Wm;msub Wd,r8,Wm,Wn: */ {MIR_MODS, "r r r", "1ac00c00:ffe0fc00 hd8 rn1 rm2;1b008000:ffe08000 rd0 hm8 rn2 ra1"}, /* udiv r8,Rn,Rm;msub Rd,r8,Rm,Rn: */ {MIR_UMOD, "r r r", "9ac00800:ffe0fc00 hd8 rn1 rm2;9b008000:ffe08000 rd0 hm8 rn2 ra1"}, /* udiv r8,Wn,Wm;msub Wd,r8,Wm,Wn: */ {MIR_UMODS, "r r r", "1ac00800:ffe0fc00 hd8 rn1 rm2;1b008000:ffe08000 rd0 hm8 rn2 ra1"}, #define CMPR "eb00001f:ff20001f rn1 rm2" #define CMPI "f100001f:ff00001f rn1 I" #define SCMPR "6b00001f:ff20001f rn1 rm2" #define SCMPI "7100001f:ff00001f rn1 I" #define FCMP "1e202010:ffe0fc1f vn1 vm2" #define DCMP "1e602010:ffe0fc1f vn1 vm2" #define REQ "9a9f17e0:ffffffe0 rd0" #define REQS "1a9f17e0:ffffffe0 rd0" // ??? add extended reg cmp insns: // all ld insn are changed to builtins /* cmp Rn,Rm; cset Rd,eq */ {MIR_EQ, "r r r", CMPR "; " REQ}, /* cmp Rn,I,shift ; cset Wd,eq */ {MIR_EQ, "r r I", CMPI "; " REQ}, /* cmp Wn,Wm;cset Rd,eq */ {MIR_EQS, "r r r", SCMPR "; " REQS}, /* cmp Wn,I,shift; cset Wd,eq */ {MIR_EQS, "r r I", SCMPI "; " REQS}, /* fcmpe Sn,Sm; cset Rd,mi */ {MIR_FEQ, "r r r", FCMP "; " REQ}, /* fcmpe Dn,Dm; cset Rd,mi */ {MIR_DEQ, "r r r", DCMP "; " REQ}, /* fcmpe Sn,0.0; cset Rd,mi */ {MIR_FEQ, "r r Zf", "1e202018:fffffc1f vn1 vm2; " REQ}, /* fcmpe Dn,0.0; cset Rd,mi */ {MIR_DEQ, "r r Zd", "1e602018:fffffc1f vn1 vm2; " REQ}, #define RNE "9a9f07e0:ffffffe0 rd0" #define RNES "1a9f07e0:ffffffe0 rd0" /* cmp Rn,Rm; cset Rd,ne */ {MIR_NE, "r r r", CMPR "; " RNE}, /* cmp Rn,I,shift ; cset Wd,ne */ {MIR_NE, "r r I", CMPI "; " RNE}, /* cmp Wn,Wm;cset Rd,ne */ {MIR_NES, "r r r", SCMPR "; " RNES}, /* cmp Wn,I,shift; cset Wd,ne */ {MIR_NES, "r r I", SCMPI "; " RNES}, /* fcmpe Sn,Sm; cset Rd,ne */ {MIR_FNE, "r r r", FCMP "; " RNE}, /* fcmpe Dn,Dm; cset Rd,ne */ {MIR_DNE, "r r r", DCMP "; " RNE}, /* fcmpe Sn,0.0; cset Rd,ne */ {MIR_FNE, "r r Zf", "1e202018:fffffc1f vn1 vm2; " RNE}, /* fcmpe Dn,0.0; cset Rd,ne */ {MIR_DNE, "r r Zd", "1e602018:fffffc1f vn1 vm2; " RNE}, #define RLT "9a9fa7e0:ffffffe0 rd0" #define RLTS "1a9fa7e0:ffffffe0 rd0" #define RULT "9a9f27e0:ffffffe0 rd0" #define RULTS "1a9f27e0:ffffffe0 rd0" #define FLT "9a9f57e0:ffffffe0 rd0" /* cmp Rn,Rm; cset Rd,lt */ {MIR_LT, "r r r", CMPR "; " RLT}, /* cmp Rn,I,shift ; cset Wd,lt */ {MIR_LT, "r r I", CMPI "; " RLT}, /* cmp Wn,Wm;cset Rd,lt */ {MIR_LTS, "r r r", SCMPR "; " RLTS}, /* cmp Wn,I,shift; cset Wd,lt */ {MIR_LTS, "r r I", SCMPI "; " RLTS}, /* cmp Rn,Rm; cset Rd,ult */ {MIR_ULT, "r r r", CMPR "; " RULT}, /* cmp Rn,I,shift ; cset Wd,cc */ {MIR_ULT, "r r I", CMPI "; " RULT}, /* cmp Wn,Wm;cset Rd,cc */ {MIR_ULTS, "r r r", SCMPR "; " RULTS}, /* cmp Wn,I,shift; cset Wd,cc */ {MIR_ULTS, "r r I", SCMPI "; " RULTS}, /* fcmpe Sn,Sm; cset Rd,mi */ {MIR_FLT, "r r r", FCMP "; " FLT}, /* fcmpe Dn,Dm; cset Rd,mi */ {MIR_DLT, "r r r", DCMP "; " FLT}, /* fcmpe Sn,0.0; cset Rd,mi */ {MIR_FLT, "r r Zf", "1e202018:fffffc1f vn1 vm2; " FLT}, /* fcmpe Dn,0.0; cset Rd,mi */ {MIR_DLT, "r r Zd", "1e602018:fffffc1f vn1 vm2; " FLT}, #define RGE "9a9fb7e0:ffffffe0 rd0" #define RGES "1a9fb7e0:ffffffe0 rd0" #define RUGE "9a9f37e0:ffffffe0 rd0" #define RUGES "1a9f37e0:ffffffe0 rd0" /* cmp Rn,Rm; cset Rd,ge */ {MIR_GE, "r r r", CMPR "; " RGE}, /* cmp Rn,I,shift ; cset Wd,ge */ {MIR_GE, "r r I", CMPI "; " RGE}, /* cmp Wn,Wm;cset Rd,ge */ {MIR_GES, "r r r", SCMPR "; " RGES}, /* cmp Wn,I,shift; cset Wd,ge */ {MIR_GES, "r r I", SCMPI "; " RGES}, /* cmp Rn,Rm; cset Rd,cs */ {MIR_UGE, "r r r", CMPR "; " RUGE}, /* cmp Rn,I,shift ; cset Wd,cs */ {MIR_UGE, "r r I", CMPI "; " RUGE}, /* cmp Wn,Wm;cset Rd,cs */ {MIR_UGES, "r r r", SCMPR "; " RUGES}, /* cmp Wn,I,shift; cset Wd,cs */ {MIR_UGES, "r r I", SCMPI "; " RUGES}, /* fcmpe Sn,Sm; cset Rd,ge */ {MIR_FGE, "r r r", FCMP "; " RGE}, /* fcmpe Dn,Dm; cset Rd,ge */ {MIR_DGE, "r r r", DCMP "; " RGE}, /* fcmpe Sn,0.0; cset Rd,ge */ {MIR_FGE, "r r Zf", "1e202018:fffffc1f vn1 vm2; " RGE}, /* fcmpe Dn,0.0; cset Rd,ge */ {MIR_DGE, "r r Zd", "1e602018:fffffc1f vn1 vm2; " RGE}, #define RGT "9a9fd7e0:ffffffe0 rd0" #define RGTS "1a9fd7e0:ffffffe0 rd0" #define RUGT "9a9f97e0:ffffffe0 rd0" #define RUGTS "1a9f97e0:ffffffe0 rd0" /* cmp Rn,Rm; cset Rd,gt */ {MIR_GT, "r r r", CMPR "; " RGT}, /* cmp Rn,I,shift ; cset Wd,gt */ {MIR_GT, "r r I", CMPI "; " RGT}, /* cmp Wn,Wm;cset Rd,gt */ {MIR_GTS, "r r r", SCMPR "; " RGTS}, /* cmp Wn,I,shift; cset Wd,gt */ {MIR_GTS, "r r I", SCMPI "; " RGTS}, /* cmp Rn,Rm; cset Rd,hi */ {MIR_UGT, "r r r", CMPR "; " RUGT}, /* cmp Rn,I,shift ; cset Wd,hi */ {MIR_UGT, "r r I", CMPI "; " RUGT}, /* cmp Wn,Wm;cset Rd,hi */ {MIR_UGTS, "r r r", SCMPR "; " RUGTS}, /* cmp Wn,I,shift; cset Wd,hi */ {MIR_UGTS, "r r I", SCMPI "; " RUGTS}, /* fcmpe Sn,Sm; cset Rd,gt */ {MIR_FGT, "r r r", FCMP "; " RGT}, /* fcmpe Dn,Dm; cset Rd,gt */ {MIR_DGT, "r r r", DCMP "; " RGT}, /* fcmpe Sn,0.0; cset Rd,gt */ {MIR_FGT, "r r Zf", "1e202018:fffffc1f vn1 vm2; " RGT}, /* fcmpe Dn,0.0; cset Rd,gt */ {MIR_DGT, "r r Zd", "1e602018:fffffc1f vn1 vm2; " RGT}, #define RLE "9a9fc7e0:ffffffe0 rd0" #define RLES "1a9fc7e0:ffffffe0 rd0" #define RULE "9a9f87e0:ffffffe0 rd0" #define RULES "1a9f87e0:ffffffe0 rd0" #define FLE "9a9f87e0:ffffffe0 rd0" /* cmp Rn,Rm; cset Rd,le */ {MIR_LE, "r r r", CMPR "; " RLE}, /* cmp Rn,I,shift ; cset Wd,le */ {MIR_LE, "r r I", CMPI "; " RLE}, /* cmp Wn,Wm;cset Rd,le */ {MIR_LES, "r r r", SCMPR "; " RLES}, /* cmp Wn,I,shift; cset Wd,le */ {MIR_LES, "r r I", SCMPI "; " RLES}, /* cmp Rn,Rm; cset Rd,ls */ {MIR_ULE, "r r r", CMPR "; " RULE}, /* cmp Rn,I,shift ; cset Wd,ls */ {MIR_ULE, "r r I", CMPI "; " RULE}, /* cmp Wn,Wm;cset Rd,ls */ {MIR_ULES, "r r r", SCMPR "; " RULES}, /* cmp Wn,I,shift; cset Wd,ls */ {MIR_ULES, "r r I", SCMPI "; " RULES}, /* fcmpe Sn,Sm; cset Rd,ls */ {MIR_FLE, "r r r", FCMP "; " FLE}, /* fcmpe Dn,Dm; cset Rd,ls */ {MIR_DLE, "r r r", DCMP "; " FLE}, /* fcmpe Sn,0.0; cset Rd,ls */ {MIR_FLE, "r r Zf", "1e202018:fffffc1f vn1 vm2; " FLE}, /* fcmpe Dn,0.0; cset Rd,ls */ {MIR_DLE, "r r Zd", "1e602018:fffffc1f vn1 vm2; " FLE}, {MIR_JMP, "L", "14000000:fc000000 L"}, /* 26-bit offset jmp */ {MIR_BT, "l r", "b5000000:ff000000 rd1 l"}, /* cbnz rd,l */ {MIR_BTS, "l r", "35000000:ff000000 rd1 l"}, /* cbnz wd,l */ {MIR_BF, "l r", "b4000000:ff000000 rd1 l"}, /* cbz rd,l */ {MIR_BFS, "l r", "34000000:ff000000 rd1 l"}, /* cbz wd,l */ #define BEQ "54000000:ff00001f l" // ??? add extended reg cmp insns: // all ld insn are changed to builtins and bt/bts /* cmp Rn,Rm; beq l */ {MIR_BEQ, "l r r", CMPR "; " BEQ}, /* cmp Rn,I,shift ; beq l */ {MIR_BEQ, "l r I", CMPI "; " BEQ}, /* cmp Wn,Wm;beq l */ {MIR_BEQS, "l r r", SCMPR "; " BEQ}, /* cmp Wn,I,shift; beq l */ {MIR_BEQS, "l r I", SCMPI "; " BEQ}, /* fcmpe Sn,Sm; beq l */ {MIR_FBEQ, "l r r", FCMP "; " BEQ}, /* fcmpe Dn,Dm; beq l */ {MIR_DBEQ, "l r r", DCMP "; " BEQ}, /* fcmpe Sn,0.0; beq l */ {MIR_FBEQ, "l r Zf", "1e202018:fffffc1f vn1 vm2; " BEQ}, /* fcmpe Dn,0.0; beq l */ {MIR_DBEQ, "l r Zd", "1e602018:fffffc1f vn1 vm2; " BEQ}, #define BNE "54000001:ff00001f l" /* cmp Rn,Rm; bne l */ {MIR_BNE, "l r r", CMPR "; " BNE}, /* cmp Rn,I,shift ; bne l */ {MIR_BNE, "l r I", CMPI "; " BNE}, /* cmp Wn,Wm;bne l */ {MIR_BNES, "l r r", SCMPR "; " BNE}, /* cmp Wn,I,shift; bne l */ {MIR_BNES, "l r I", SCMPI "; " BNE}, /* fcmpe Sn,Sm; bne l */ {MIR_FBNE, "l r r", FCMP "; " BNE}, /* fcmpe Dn,Dm; bne l */ {MIR_DBNE, "l r r", DCMP "; " BNE}, /* fcmpe Sn,0.0; bne l */ {MIR_FBNE, "l r Zf", "1e202018:fffffc1f vn1 vm2; " BNE}, /* fcmpe Dn,0.0; bne l */ {MIR_DBNE, "l r Zd", "1e602018:fffffc1f vn1 vm2; " BNE}, #define BLT "5400000b:ff00001f l" #define UBLT "54000003:ff00001f l" /* cmp Rn,Rm; blt l */ {MIR_BLT, "l r r", CMPR "; " BLT}, /* cmp Rn,I,shift ; blt l */ {MIR_BLT, "l r I", CMPI "; " BLT}, /* cmp Wn,Wm;blt l */ {MIR_BLTS, "l r r", SCMPR "; " BLT}, /* cmp Wn,I,shift; blt l */ {MIR_BLTS, "l r I", SCMPI "; " BLT}, /* cmp Rn,Rm; bcc l */ {MIR_UBLT, "l r r", CMPR "; " UBLT}, /* cmp Rn,I,shift ; bcc l */ {MIR_UBLT, "l r I", CMPI "; " UBLT}, /* cmp Wn,Wm;bcc l */ {MIR_UBLTS, "l r r", SCMPR "; " UBLT}, /* cmp Wn,I,shift; bcc l */ {MIR_UBLTS, "l r I", SCMPI "; " UBLT}, /* fcmpe Sn,Sm; blt l */ {MIR_FBLT, "l r r", FCMP "; " BLT}, /* fcmpe Dn,Dm; blt l */ {MIR_DBLT, "l r r", DCMP "; " BLT}, /* fcmpe Sn,0.0; blt l */ {MIR_FBLT, "l r Zf", "1e202018:fffffc1f vn1 vm2; " BLT}, /* fcmpe Dn,0.0; blt l */ {MIR_DBLT, "l r Zd", "1e602018:fffffc1f vn1 vm2; " BLT}, #define BGE "5400000a:ff00001f l" #define UBGE "54000002:ff00001f l" #define FBGE "54000005:ff00001f l" /* cmp Rn,Rm; bge l */ {MIR_BGE, "l r r", CMPR "; " BGE}, /* cmp Rn,I,shift ; bge l */ {MIR_BGE, "l r I", CMPI "; " BGE}, /* cmp Wn,Wm;bge l */ {MIR_BGES, "l r r", SCMPR "; " BGE}, /* cmp Wn,I,shift; bge l */ {MIR_BGES, "l r I", SCMPI "; " BGE}, /* cmp Rn,Rm; bcs l */ {MIR_UBGE, "l r r", CMPR "; " UBGE}, /* cmp Rn,I,shift ; bcs l */ {MIR_UBGE, "l r I", CMPI "; " UBGE}, /* cmp Wn,Wm;bcs l */ {MIR_UBGES, "l r r", SCMPR "; " UBGE}, /* cmp Wn,I,shift; bcs l */ {MIR_UBGES, "l r I", SCMPI "; " UBGE}, /* fcmpe Sn,Sm; bpl l */ {MIR_FBGE, "l r r", FCMP "; " FBGE}, /* fcmpe Dn,Dm; bpl l */ {MIR_DBGE, "l r r", DCMP "; " FBGE}, /* fcmpe Sn,0.0; bpl l */ {MIR_FBGE, "l r Zf", "1e202018:fffffc1f vn1 vm2; " FBGE}, /* fcmpe Dn,0.0; bpl l */ {MIR_DBGE, "l r Zd", "1e602018:fffffc1f vn1 vm2; " FBGE}, #define BGT "5400000c:ff00001f l" #define UBGT "54000008:ff00001f l" /* cmp Rn,Rm; bgt l */ {MIR_BGT, "l r r", CMPR "; " BGT}, /* cmp Rn,I,shift ; bgt l */ {MIR_BGT, "l r I", CMPI "; " BGT}, /* cmp Wn,Wm;bgt l */ {MIR_BGTS, "l r r", SCMPR "; " BGT}, /* cmp Wn,I,shift; bgt l */ {MIR_BGTS, "l r I", SCMPI "; " BGT}, /* cmp Rn,Rm; bhi l */ {MIR_UBGT, "l r r", CMPR "; " UBGT}, /* cmp Rn,I,shift ; bhi l */ {MIR_UBGT, "l r I", CMPI "; " UBGT}, /* cmp Wn,Wm;bhi l */ {MIR_UBGTS, "l r r", SCMPR "; " UBGT}, /* cmp Wn,I,shift; bhi l */ {MIR_UBGTS, "l r I", SCMPI "; " UBGT}, /* fcmpe Sn,Sm; bhi l */ {MIR_FBGT, "l r r", FCMP "; " UBGT}, /* fcmpe Dn,Dm; bhi l */ {MIR_DBGT, "l r r", DCMP "; " UBGT}, /* fcmpe Sn,0.0; bhi l */ {MIR_FBGT, "l r Zf", "1e202018:fffffc1f vn1 vm2; " UBGT}, /* fcmpe Dn,0.0; bhi l */ {MIR_DBGT, "l r Zd", "1e602018:fffffc1f vn1 vm2; " UBGT}, #define BLE "5400000d:ff00001f l" #define UBLE "54000009:ff00001f l" /* cmp Rn,Rm; ble l */ {MIR_BLE, "l r r", CMPR "; " BLE}, /* cmp Rn,I,shift ; ble l */ {MIR_BLE, "l r I", CMPI "; " BLE}, /* cmp Wn,Wm;ble l */ {MIR_BLES, "l r r", SCMPR "; " BLE}, /* cmp Wn,I,shift; ble l */ {MIR_BLES, "l r I", SCMPI "; " BLE}, /* cmp Rn,Rm; bls l */ {MIR_UBLE, "l r r", CMPR "; " UBLE}, /* cmp Rn,I,shift ; bls l */ {MIR_UBLE, "l r I", CMPI "; " UBLE}, /* cmp Wn,Wm;bls l */ {MIR_UBLES, "l r r", SCMPR "; " UBLE}, /* cmp Wn,I,shift; bls l */ {MIR_UBLES, "l r I", SCMPI "; " UBLE}, /* fcmpe Sn,Sm; ble l */ {MIR_FBLE, "l r r", FCMP "; " BLE}, /* fcmpe Dn,Dm; ble l */ {MIR_DBLE, "l r r", DCMP "; " BLE}, /* fcmpe Sn,0.0; ble l */ {MIR_FBLE, "l r Zf", "1e202018:fffffc1f vn1 vm2; " BLE}, /* fcmpe Dn,0.0; ble l */ {MIR_DBLE, "l r Zd", "1e602018:fffffc1f vn1 vm2; " BLE}, // ??? with shift {MIR_NEG, "r r", "cb0003e0:ff2003e0 rd0 rm1"}, /* neg Rd,Rm */ {MIR_NEGS, "r r", "4b0003e0:ff2003e0 rd0 rm1"}, /* neg Wd,Wm */ {MIR_FNEG, "r r", "1e214000:fffffc00 vd0 vn1"}, /* fneg Sd,Sn */ {MIR_DNEG, "r r", "1e614000:fffffc00 vd0 vn1"}, /* fneg Dd,Dn */ // ldneg is a builtin {MIR_LSH, "r r r", "9ac02000:ffe0fc00 rd0 rn1 rm2"}, /* lsl Rd,Rn,Rm */ {MIR_LSHS, "r r r", "1ac02000:ffe0fc00 rd0 rn1 rm2"}, /* lsl Wd,Wn,Wm */ {MIR_LSH, "r r SL", "d3400000:ffc00000 rd0 rn1 SL"}, /* ubfm Rd,Rn,immr,imms */ {MIR_LSHS, "r r Sl", "53000000:ffc00000 rd0 rn1 Sl"}, /* ubfm Wd,Wn,immr,imms */ {MIR_RSH, "r r r", "9ac02800:ffe0fc00 rd0 rn1 rm2"}, /* asr Rd,Rn,Rm */ {MIR_RSHS, "r r r", "1ac02800:ffe0fc00 rd0 rn1 rm2"}, /* asr Wd,Wn,Wm */ {MIR_RSH, "r r SR", "9340fc00:ffc0fc00 rd0 rn1 S"}, /* asr Rd,Rn,S */ {MIR_RSHS, "r r Sr", "13007c00:ffc0fc00 rd0 rn1 S"}, /* asr Wd,Wn,S */ {MIR_URSH, "r r r", "9ac02400:ffe0fc00 rd0 rn1 rm2"}, /* lsr Rd,Rn,Rm */ {MIR_URSHS, "r r r", "1ac02400:ffe0fc00 rd0 rn1 rm2"}, /* lsr Wd,Wn,Wm */ {MIR_URSH, "r r SR", "d340fc00:ffc0fc00 rd0 rn1 S"}, /* lsr Rd,Rn,S */ {MIR_URSHS, "r r Sr", "53007c00:ffc0fc00 rd0 rn1 S"}, /* lsr Wd,Wn,S */ // ??? adding shift, negate, immediate {MIR_AND, "r r r", "8a000000:ffe0fc00 rd0 rn1 rm2"}, /* and Rd,Rn,Rm */ {MIR_ANDS, "r r r", "0a000000:ffe0fc00 rd0 rn1 rm2"}, /* and Wd,Wn,Wm */ {MIR_OR, "r r r", "aa000000:ffe0fc00 rd0 rn1 rm2"}, /* orr Rd,Rn,Rm */ {MIR_ORS, "r r r", "2a000000:ffe0fc00 rd0 rn1 rm2"}, /* orr Wd,Wn,Wm */ {MIR_XOR, "r r r", "ca000000:ffe0fc00 rd0 rn1 rm2"}, /* eor Rd,Rn,Rm */ {MIR_XORS, "r r r", "4a000000:ffe0fc00 rd0 rn1 rm2"}, /* eor Wd,Wn,Wm */ // ??? can we add scale {MIR_I2F, "r r", "9e220000:ffff0000 vd0 rn1"}, /* scvtf Sd,Rn */ {MIR_I2D, "r r", "9e620000:ffff0000 vd0 rn1"}, /* scvtf Dd,Rn */ {MIR_UI2F, "r r", "9e230000:ffff0000 vd0 rn1"}, /* ucvtf Sd,Rn */ {MIR_UI2D, "r r", "9e630000:ffff0000 vd0 rn1"}, /* ucvtf Dd,Rn */ {MIR_F2I, "r r", "9e380000:ffff0000 rd0 vn1"}, /* fcvtzs Rd,Sn */ {MIR_D2I, "r r", "9e780000:ffff0000 rd0 vn1"}, /* fcvtzs Rd,Dn */ {MIR_F2D, "r r", "1e22c000:fffffc00 vd0 vn1"}, /* fcvt Dd,Sn */ {MIR_D2F, "r r", "1e624000:fffffc00 vd0 vn1"}, /* fcvt Sd,Dn */ // i2ld, ui2ld, ld2i, f2ld, d2ld, ld2f, ld2d are builtins {MIR_CALL, "X r $", "d63f0000:fffffc1f rn1"}, /* blr *Rn */ {MIR_CALL, "X L $", "94000000:fc000000 rn1"}, /* bl address */ {MIR_INLINE, "X r $", "d63f0000:fffffc1f rn1"}, /* blr *Rn */ {MIR_INLINE, "X L $", "94000000:fc000000 rn1"}, /* bl address */ {MIR_RET, "$", "d65f0000:fffffc1f hn1e"}, /* ret R30 */ /* add r0, r1, 15; and r0, r0, -16; sub sp, sp, r0; mov r0, sp: */ {MIR_ALLOCA, "r r", "91003c00:fffffc00 rd0 rn1; 927cec00:fffffc00 rd0 rn0;" /* add r0,r1,15;and r0,r0,-16 */ "cb206000:ffe0fc00 hn1f hd1f rm0; 91000000:fffffc00 rd0 hn1f"}, /* sub sp,sp,r0; mov r0,sp */ /* sub sp, sp, roundup (imm, 16); mov r0, sp: */ {MIR_ALLOCA, "r Iu", "d1000000:ff000000 hd1f hn1f Iu; 91000000:fffffc00 rd0 hn1f"}, {MIR_BSTART, "r", "91000000:fffffc00 rd0 hn1f"}, /* Rd = sp */ {MIR_BEND, "r", "91000000:fffffc00 hd1f rn0"}, /* sp = Rn */ /* adr r10,PC-relative TableAddress; ldr r10,(r10,r,8);br r10; TableContent We use r10 as r9 can be used if switch operand is memory. */ {MIR_SWITCH, "r $", "10000000:ff000000 hda T; f8607800:ffe0fc00 hda hna rm0; d61f0000:fffffc00 hna;"}, }; static void target_get_early_clobbered_hard_regs (MIR_insn_t insn, MIR_reg_t *hr1, MIR_reg_t *hr2) { *hr1 = *hr2 = MIR_NON_HARD_REG; if (insn->code == MIR_MOD || insn->code == MIR_MODS || insn->code == MIR_UMOD || insn->code == MIR_UMODS) *hr1 = R8_HARD_REG; } static int pattern_index_cmp (const void *a1, const void *a2) { int i1 = *(const int *) a1, i2 = *(const int *) a2; int c1 = (int) patterns[i1].code, c2 = (int) patterns[i2].code; return c1 != c2 ? c1 - c2 : (long) i1 - (long) i2; } static void patterns_init (gen_ctx_t gen_ctx) { int i, ind, n = sizeof (patterns) / sizeof (struct pattern); MIR_insn_code_t prev_code, code; insn_pattern_info_t *info_addr; insn_pattern_info_t pinfo = {0, 0}; VARR_CREATE (int, pattern_indexes, 0); for (i = 0; i < n; i++) VARR_PUSH (int, pattern_indexes, i); qsort (VARR_ADDR (int, pattern_indexes), n, sizeof (int), pattern_index_cmp); VARR_CREATE (insn_pattern_info_t, insn_pattern_info, 0); for (i = 0; i < MIR_INSN_BOUND; i++) VARR_PUSH (insn_pattern_info_t, insn_pattern_info, pinfo); info_addr = VARR_ADDR (insn_pattern_info_t, insn_pattern_info); for (prev_code = MIR_INSN_BOUND, i = 0; i < n; i++) { ind = VARR_GET (int, pattern_indexes, i); if ((code = patterns[ind].code) != prev_code) { if (i != 0) info_addr[prev_code].num = i - info_addr[prev_code].start; info_addr[code].start = i; prev_code = code; } } assert (prev_code != MIR_INSN_BOUND); info_addr[prev_code].num = n - info_addr[prev_code].start; } struct imm { int v, shift; }; /* Return number of insn mov{n|z} movk* to express constant V. Return immediates with their shifts for mov{n|z}, movk in IMMS. */ static int movnzk_const (uint64_t v, int n_p, struct imm *imms) { int i16, shift, n = 0; if (n_p) v = ~v; if (v == 0) { imms[0].v = 0; imms[0].shift = 0; return 1; } for (shift = 0; v != 0; v >>= 16, shift += 16) { for (; (i16 = v & 0xffff) == 0; shift += 16) v >>= 16; gen_assert (n < 4); imms[n].v = n_p && n != 0 ? (~i16 & 0xffff) : i16; imms[n++].shift = shift; } return n; } /* Return shift flag (0 or 1) for arithm insn 12-bit immediate. If V cannot be represented, return -1. */ static int arithm_const (uint64_t v, int *imm) { if (v < (1 << 12)) { *imm = v; return 0; } if ((v & 0xfff) == 0 && (v >> 12) < (1 << 12)) { *imm = v >> 12; return 1; } return -1; } /* Return shift flag (0 or 1) for arithm insn 12-bit immediate rounded up to 16. If the rounded V cannot be represented, return -1. */ static int arithm_roundup_const (uint64_t v, int *imm) { return arithm_const ((v + 15) / 16 * 16, imm); } /* Return immr for right 64-bit or 32-bit (if SHORT_P) shift by V. If the shift can not be be represented, return FALSE. */ static int rshift_const (int64_t v, int short_p) { return v < 0 || v > 63 || (short_p && v > 31) ? -1 : v; } /* Return immr and imms for left 64-bit or 32-bit (if SHORT_P) shift by V. If the shift can not be be represented, return FALSE. */ static int lshift_const_p (int64_t v, int short_p, int *immr, int *imms) { if (short_p) { if (v < 0 || v > 31) return FALSE; *immr = (-v) & 0x1f; *imms = 31 - v; } else { if (v < 0 || v > 63) return FALSE; *immr = (-v) & 0x3f; *imms = 63 - v; } return TRUE; } static int pattern_match_p (gen_ctx_t gen_ctx, const struct pattern *pat, MIR_insn_t insn) { MIR_context_t ctx = gen_ctx->ctx; int nop; size_t nops = MIR_insn_nops (ctx, insn); const char *p; char ch, start_ch; MIR_op_t op; MIR_reg_t hr; for (nop = 0, p = pat->pattern; *p != 0; p++, nop++) { while (*p == ' ' || *p == '\t') p++; if (*p == '$') return TRUE; if (MIR_call_code_p (insn->code) && nop >= nops) return FALSE; gen_assert (nop < nops); op = insn->ops[nop]; switch (start_ch = *p) { case 'X': break; case 'r': if (op.mode != MIR_OP_HARD_REG) return FALSE; break; case 'h': if (op.mode != MIR_OP_HARD_REG) return FALSE; ch = *++p; gen_assert ('0' <= ch && ch <= '9'); hr = ch - '0'; ch = *++p; if ('0' <= ch && ch <= '9') hr = hr * 10 + ch - '0'; else --p; gen_assert (hr <= MAX_HARD_REG); if (op.u.hard_reg != hr) return FALSE; break; case 'm': case 'M': { MIR_type_t type, type2, type3 = MIR_T_BOUND; int scale, u_p, s_p; if (op.mode != MIR_OP_HARD_REG_MEM) return FALSE; u_p = s_p = TRUE; ch = *++p; switch (ch) { case 'f': type = MIR_T_F; type2 = MIR_T_BOUND; scale = 4; break; case 'd': type = MIR_T_D; type2 = MIR_T_BOUND; scale = 8; break; case 'l': ch = *++p; gen_assert (ch == 'd'); type = MIR_T_LD; type2 = MIR_T_BOUND; scale = 16; break; case 'u': case 's': u_p = ch == 'u'; s_p = ch == 's'; ch = *++p; /* Fall through: */ default: gen_assert ('0' <= ch && ch <= '3'); scale = 1 << (ch - '0'); if (ch == '0') { type = u_p ? MIR_T_U8 : MIR_T_I8; type2 = u_p && s_p ? MIR_T_I8 : MIR_T_BOUND; } else if (ch == '1') { type = u_p ? MIR_T_U16 : MIR_T_I16; type2 = u_p && s_p ? MIR_T_I16 : MIR_T_BOUND; } else if (ch == '2') { type = u_p ? MIR_T_U32 : MIR_T_I32; type2 = u_p && s_p ? MIR_T_I32 : MIR_T_BOUND; #if MIR_PTR32 if (u_p) type3 = MIR_T_P; #endif } else { type = u_p ? MIR_T_U64 : MIR_T_I64; type2 = u_p && s_p ? MIR_T_I64 : MIR_T_BOUND; #if MIR_PTR64 type3 = MIR_T_P; #endif } } if (op.u.hard_reg_mem.type != type && op.u.hard_reg_mem.type != type2 && op.u.hard_reg_mem.type != type3) return FALSE; if (start_ch == 'm' && (op.u.hard_reg_mem.disp != 0 || (op.u.hard_reg_mem.index != MIR_NON_HARD_REG && op.u.hard_reg_mem.scale != 1 && op.u.hard_reg_mem.scale != scale))) return FALSE; if (start_ch == 'M' && (op.u.hard_reg_mem.index != MIR_NON_HARD_REG || op.u.hard_reg_mem.disp < 0 || op.u.hard_reg_mem.disp % scale != 0 || op.u.hard_reg_mem.disp / scale >= (1 << 12))) return FALSE; break; } case 'Z': case 'N': { int n; uint64_t v; struct imm imms[4]; ch = *++p; if (ch == 'f' && op.mode == MIR_OP_FLOAT) { if (op.u.f != 0.0f) return FALSE; } else if (ch == 'd' && op.mode == MIR_OP_DOUBLE) { if (op.u.d != 0.0) return FALSE; } else { if (op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT && op.mode != MIR_OP_REF) return FALSE; gen_assert (('0' <= ch && ch <= '2') || (start_ch == 'Z' && ch == '3')); n = ch - '0'; if (op.mode != MIR_OP_REF) { v = op.u.u; } else if (op.u.ref->item_type == MIR_data_item && op.u.ref->u.data->name != NULL && _MIR_reserved_ref_name_p (ctx, op.u.ref->u.data->name)) { v = (uint64_t) op.u.ref->u.data->u.els; } else { v = (uint64_t) op.u.ref->addr; } if (movnzk_const (v, start_ch == 'N', imms) > n + 1) return FALSE; gen_assert (nop == 1); /* only 2nd move operand */ } break; } case 'I': { int imm; if (op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) return FALSE; ch = *++p; if (ch == 'u') { if (arithm_roundup_const (op.u.u, &imm) < 0) return FALSE; } else { p--; if (arithm_const (op.u.u, &imm) < 0) return FALSE; } break; } case 'S': { int immr, imms; if (op.mode != MIR_OP_INT && op.mode != MIR_OP_UINT) return FALSE; gen_assert (op.mode != MIR_OP_INT || op.u.i >= 0); ch = *++p; if (ch == 'r' || ch == 'R') { if ((op.mode == MIR_OP_UINT && op.u.i < 0) || rshift_const (op.u.i, ch == 'r') < 0) return FALSE; } else { gen_assert (ch == 'l' || ch == 'L'); if ((op.mode == MIR_OP_UINT && op.u.i < 0) || !lshift_const_p (op.u.i, ch == 'l', &immr, &imms)) return FALSE; } break; } case 'l': if (op.mode != MIR_OP_LABEL) return FALSE; break; case 'L': if (op.mode != MIR_OP_LABEL && op.mode != MIR_OP_REF) return FALSE; break; default: gen_assert (FALSE); } } gen_assert (nop == nops); return TRUE; } static const char *find_insn_pattern_replacement (gen_ctx_t gen_ctx, MIR_insn_t insn) { int i; const struct pattern *pat; insn_pattern_info_t info = VARR_GET (insn_pattern_info_t, insn_pattern_info, insn->code); for (i = 0; i < info.num; i++) { pat = &patterns[VARR_GET (int, pattern_indexes, info.start + i)]; if (pattern_match_p (gen_ctx, pat, insn)) return pat->replacement; } return NULL; } static void patterns_finish (gen_ctx_t gen_ctx) { VARR_DESTROY (int, pattern_indexes); VARR_DESTROY (insn_pattern_info_t, insn_pattern_info); } static int hex_value (int ch) { return ('0' <= ch && ch <= '9' ? ch - '0' : 'A' <= ch && ch <= 'F' ? ch - 'A' + 10 : 'a' <= ch && ch <= 'f' ? ch - 'a' + 10 : -1); } static uint64_t read_hex (const char **ptr) { int v; const char *p; uint64_t res = 0; for (p = *ptr; (v = hex_value (*p)) >= 0; p++) { gen_assert ((res >> 60) == 0); res = res * 16 + v; } gen_assert (p != *ptr); *ptr = p - 1; return res; } static void put_byte (struct gen_ctx *gen_ctx, int byte) { VARR_PUSH (uint8_t, result_code, byte); } static void put_uint64 (struct gen_ctx *gen_ctx, uint64_t v, int nb) { for (; nb > 0; nb--) { put_byte (gen_ctx, v & 0xff); v >>= 8; } } static void set_int64 (uint8_t *addr, int64_t v, int nb) { /* Little endian */ for (; nb > 0; nb--) { *addr++ = v & 0xff; v >>= 8; } } static int64_t get_int64 (uint8_t *addr, int nb) { /* Little endian */ int64_t v = 0; int i, sh = (8 - nb) * 8; for (i = nb - 1; i >= 0; i--) v = (v << 8) | addr[i]; if (sh > 0) v = (v << sh) >> sh; /* make it signed */ return v; } static uint32_t check_and_set_mask (uint32_t opcode_mask, uint32_t mask) { gen_assert ((opcode_mask & mask) == 0); return opcode_mask | mask; } static void out_insn (gen_ctx_t gen_ctx, MIR_insn_t insn, const char *replacement) { MIR_context_t ctx = gen_ctx->ctx; size_t offset; const char *p, *insn_str; label_ref_t lr; int switch_table_adr_insn_start = -1; if (insn->code == MIR_ALLOCA && (insn->ops[1].mode == MIR_OP_INT || insn->ops[1].mode == MIR_OP_UINT)) insn->ops[1].u.u = (insn->ops[1].u.u + 15) & -16; for (insn_str = replacement;; insn_str = p + 1) { char ch, ch2, start_ch, d; uint32_t opcode = 0, opcode_mask = 0xffffffff; int rd = -1, rn = -1, rm = -1, ra = -1, disp = -1, scale = -1; int immr = -1, imms = -1, imm16 = -1, imm16_shift = -1, imm12 = -1, imm12_shift = -1; MIR_op_t op; int label_ref_num = -1, switch_table_addr_p = FALSE; for (p = insn_str; (ch = *p) != '\0' && ch != ';'; p++) { if ((d = hex_value (ch = *p)) >= 0) { /* opcode and mask */ gen_assert (opcode == 0 && opcode_mask == 0xffffffff); do { opcode = opcode * 16 + d; p++; } while ((d = hex_value (*p)) >= 0); if ((ch = *p) == ':') { p++; opcode_mask = 0; while ((d = hex_value (ch = *p)) >= 0) { opcode_mask = opcode_mask * 16 + d; p++; } } gen_assert ((opcode & ~opcode_mask) == 0); } if ((ch = *p) == 0 || ch == ';') break; switch ((start_ch = ch = *p)) { case ' ': case '\t': break; case 'r': case 'v': case 'h': { int reg; ch2 = *++p; gen_assert (ch2 == 'd' || ch2 == 'n' || ch2 == 'm' || (ch2 == 'a' && (insn->code == MIR_MOD || insn->code == MIR_MODS || insn->code == MIR_UMOD || insn->code == MIR_UMODS))); ch = *++p; if (start_ch == 'h') { reg = read_hex (&p); } else { gen_assert ('0' <= ch && ch <= '2' && ch - '0' < insn->nops); op = insn->ops[ch - '0']; gen_assert (op.mode == MIR_OP_HARD_REG); reg = op.u.hard_reg; if (start_ch != 'v') { gen_assert (reg < V0_HARD_REG); } else { gen_assert (reg >= V0_HARD_REG); reg -= V0_HARD_REG; } } gen_assert (reg <= 31); if (ch2 == 'd') rd = reg; else if (ch2 == 'n') rn = reg; else if (ch2 == 'm') rm = reg; else ra = reg; break; } case 'm': op = insn->ops[0].mode == MIR_OP_HARD_REG_MEM ? insn->ops[0] : insn->ops[1]; rn = op.u.hard_reg_mem.base; rm = op.u.hard_reg_mem.index == MIR_NON_HARD_REG ? ZR_HARD_REG : op.u.hard_reg_mem.index; scale = op.u.hard_reg_mem.scale; break; case 'M': { int scale = 1; op = insn->ops[0].mode == MIR_OP_HARD_REG_MEM ? insn->ops[0] : insn->ops[1]; switch (op.u.hard_reg_mem.type) { case MIR_T_I8: case MIR_T_U8: scale = 1; break; case MIR_T_I16: case MIR_T_U16: scale = 2; break; #if MIR_PTR32 case MIR_T_P: #endif case MIR_T_I32: case MIR_T_U32: case MIR_T_F: scale = 4; break; #if MIR_PTR64 case MIR_T_P: #endif case MIR_T_I64: case MIR_T_U64: case MIR_T_D: scale = 8; break; case MIR_T_LD: scale = 16; break; default: assert (FALSE); } gen_assert (op.u.hard_reg_mem.disp % scale == 0); rn = op.u.hard_reg_mem.base; disp = op.u.hard_reg_mem.disp / scale; gen_assert (disp < (1 << 12)); break; } case 'S': { /* S, SL, Sl */ int flag; op = insn->ops[2]; gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT); ch = *++p; if (ch == 'L' || ch == 'l') { flag = lshift_const_p (op.u.i, ch == 'l', &immr, &imms); gen_assert (flag); } else { p--; immr = rshift_const (op.u.i, FALSE); gen_assert (immr >= 0); } break; } case 'N': case 'Z': { int n, n2; uint64_t v; struct imm imms[4]; ch = *++p; gen_assert ('0' <= ch && ch <= '3'); op = insn->ops[1]; n = ch - '0'; if (op.mode != MIR_OP_REF) { v = op.u.u; } else if (op.u.ref->item_type == MIR_data_item && op.u.ref->u.data->name != NULL && _MIR_reserved_ref_name_p (ctx, op.u.ref->u.data->name)) { v = (uint64_t) op.u.ref->u.data->u.els; } else { v = (uint64_t) op.u.ref->addr; } n2 = movnzk_const (v, start_ch == 'N', imms); gen_assert (n < n2); imm16 = imms[n].v; imm16_shift = imms[n].shift >> 4; break; } case 'I': { ch = *++p; if (ch != 'u') { /* I */ op = insn->ops[2]; gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT); imm12_shift = arithm_const (op.u.u, &imm12); p--; } else { op = insn->ops[1]; gen_assert (op.mode == MIR_OP_INT || op.mode == MIR_OP_UINT); imm12_shift = arithm_roundup_const (op.u.u, &imm12); } break; } case 'T': { gen_assert (!switch_table_addr_p && switch_table_adr_insn_start < 0); switch_table_addr_p = TRUE; break; } case 'l': case 'L': { op = insn->ops[start_ch == 'l' || (insn->code != MIR_CALL && insn->code != MIR_INLINE) ? 0 : 1]; gen_assert (op.mode == MIR_OP_LABEL || (start_ch == 'L' && op.mode == MIR_OP_REF)); lr.abs_addr_p = FALSE; lr.short_p = start_ch == 'l'; lr.label_val_disp = 0; lr.label = op.u.label; label_ref_num = VARR_LENGTH (label_ref_t, label_refs); VARR_PUSH (label_ref_t, label_refs, lr); break; } default: gen_assert (FALSE); } } if (rd >= 0) { gen_assert (rd <= 31); opcode |= rd; opcode_mask = check_and_set_mask (opcode_mask, 0x1f); } if (rn >= 0) { gen_assert (rn <= 31); opcode |= rn << 5; opcode_mask = check_and_set_mask (opcode_mask, 0x1f << 5); } if (rm >= 0) { gen_assert (rm <= 31); opcode |= rm << 16; opcode_mask = check_and_set_mask (opcode_mask, 0x1f << 16); } if (ra >= 0) { gen_assert (rm <= 31); opcode |= ra << 10; opcode_mask = check_and_set_mask (opcode_mask, 0x1f << 10); } if (scale >= 0) { opcode |= (scale == 1 ? 0x6 : 0x7) << 12; opcode_mask = check_and_set_mask (opcode_mask, 0xf << 12); } if (disp >= 0) { gen_assert (disp < (1 << 12)); opcode |= disp << 10; opcode_mask = check_and_set_mask (opcode_mask, 0xfff << 10); } if (immr >= 0) { gen_assert (immr < (1 << 6)); opcode |= immr << 16; opcode_mask = check_and_set_mask (opcode_mask, 0x3f << 16); } if (imms >= 0) { gen_assert (imms < (1 << 6)); opcode |= imms << 10; opcode_mask = check_and_set_mask (opcode_mask, 0x3f << 10); } if (imm16 >= 0) { gen_assert (imm16 < (1 << 16)); opcode |= imm16 << 5; opcode_mask = check_and_set_mask (opcode_mask, 0xffff << 5); } if (imm16_shift >= 0) { gen_assert (imm16_shift < (1 << 2)); opcode |= imm16_shift << 21; opcode_mask = check_and_set_mask (opcode_mask, 0x3 << 21); } if (imm12 >= 0) { gen_assert (imm12 < (1 << 12)); opcode |= imm12 << 10; opcode_mask = check_and_set_mask (opcode_mask, 0xfff << 10); } if (imm12_shift >= 0) { gen_assert (imm12_shift < (1 << 2)); opcode |= imm12_shift << 22; opcode_mask = check_and_set_mask (opcode_mask, 0x3 << 22); } if (label_ref_num >= 0) VARR_ADDR (label_ref_t, label_refs) [label_ref_num].label_val_disp = VARR_LENGTH (uint8_t, result_code); if (switch_table_addr_p) switch_table_adr_insn_start = VARR_LENGTH (uint8_t, result_code); put_uint64 (gen_ctx, opcode, 4); /* output the machine insn */ if (*p == 0) break; } if (switch_table_adr_insn_start < 0) return; if (VARR_LENGTH (uint8_t, result_code) % 8 == 4) put_uint64 (gen_ctx, 0, 4); offset = (VARR_LENGTH (uint8_t, result_code) - switch_table_adr_insn_start) / 4; /* pc offset */ *(uint32_t *) (VARR_ADDR (uint8_t, result_code) + switch_table_adr_insn_start) |= (offset << 5); gen_assert (insn->code == MIR_SWITCH); for (size_t i = 1; i < insn->nops; i++) { gen_assert (insn->ops[i].mode == MIR_OP_LABEL); lr.abs_addr_p = TRUE; lr.short_p = FALSE; lr.label_val_disp = VARR_LENGTH (uint8_t, result_code); lr.label = insn->ops[i].u.label; VARR_PUSH (label_ref_t, label_refs, lr); put_uint64 (gen_ctx, 0, 8); } } static int target_insn_ok_p (gen_ctx_t gen_ctx, MIR_insn_t insn) { return find_insn_pattern_replacement (gen_ctx, insn) != NULL; } static uint8_t *target_translate (gen_ctx_t gen_ctx, size_t *len) { MIR_context_t ctx = gen_ctx->ctx; size_t i; MIR_insn_t insn; const char *replacement; gen_assert (curr_func_item->item_type == MIR_func_item); VARR_TRUNC (uint8_t, result_code, 0); VARR_TRUNC (label_ref_t, label_refs, 0); VARR_TRUNC (uint64_t, abs_address_locs, 0); for (insn = DLIST_HEAD (MIR_insn_t, curr_func_item->u.func->insns); insn != NULL; insn = DLIST_NEXT (MIR_insn_t, insn)) { if (insn->code == MIR_LABEL) { set_label_disp (gen_ctx, insn, VARR_LENGTH (uint8_t, result_code)); } else { replacement = find_insn_pattern_replacement (gen_ctx, insn); if (replacement == NULL) { fprintf (stderr, "fatal failure in matching insn:"); MIR_output_insn (ctx, stderr, insn, curr_func_item->u.func, TRUE); exit (1); } else { gen_assert (replacement != NULL); out_insn (gen_ctx, insn, replacement); } } } /* Setting up labels */ for (i = 0; i < VARR_LENGTH (label_ref_t, label_refs); i++) { label_ref_t lr = VARR_GET (label_ref_t, label_refs, i); if (!lr.abs_addr_p) { int64_t offset = (int64_t) get_label_disp (gen_ctx, lr.label) - (int64_t) lr.label_val_disp; /* pc offset */ gen_assert ((offset & 0x3) == 0); if (lr.short_p) *(uint32_t *) (VARR_ADDR (uint8_t, result_code) + lr.label_val_disp) |= ((offset / 4) & 0x7ffff) << 5; /* 19-bit */ else *(uint32_t *) (VARR_ADDR (uint8_t, result_code) + lr.label_val_disp) |= (offset / 4) & 0x3ffffff; /* 26-bit */ } else { set_int64 (&VARR_ADDR (uint8_t, result_code)[lr.label_val_disp], (int64_t) get_label_disp (gen_ctx, lr.label), 8); VARR_PUSH (uint64_t, abs_address_locs, lr.label_val_disp); } } while (VARR_LENGTH (uint8_t, result_code) % 16 != 0) /* Align the pool */ VARR_PUSH (uint8_t, result_code, 0); *len = VARR_LENGTH (uint8_t, result_code); return VARR_ADDR (uint8_t, result_code); } static void target_rebase (gen_ctx_t gen_ctx, uint8_t *base) { MIR_code_reloc_t reloc; VARR_TRUNC (MIR_code_reloc_t, relocs, 0); for (size_t i = 0; i < VARR_LENGTH (uint64_t, abs_address_locs); i++) { reloc.offset = VARR_GET (uint64_t, abs_address_locs, i); reloc.value = base + get_int64 (base + reloc.offset, 8); VARR_PUSH (MIR_code_reloc_t, relocs, reloc); } _MIR_update_code_arr (gen_ctx->ctx, base, VARR_LENGTH (MIR_code_reloc_t, relocs), VARR_ADDR (MIR_code_reloc_t, relocs)); } static void target_init (gen_ctx_t gen_ctx) { gen_ctx->target_ctx = gen_malloc (gen_ctx, sizeof (struct target_ctx)); VARR_CREATE (uint8_t, result_code, 0); VARR_CREATE (label_ref_t, label_refs, 0); VARR_CREATE (uint64_t, abs_address_locs, 0); VARR_CREATE (MIR_code_reloc_t, relocs, 0); patterns_init (gen_ctx); } static void target_finish (gen_ctx_t gen_ctx) { patterns_finish (gen_ctx); VARR_DESTROY (uint8_t, result_code); VARR_DESTROY (label_ref_t, label_refs); VARR_DESTROY (uint64_t, abs_address_locs); VARR_DESTROY (MIR_code_reloc_t, relocs); free (gen_ctx->target_ctx); gen_ctx->target_ctx = NULL; }