You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ravi/mir/mir-gen-aarch64.c

2366 lines
96 KiB

/* This file is a part of MIR project.
Copyright (C) 2020-2021 Vladimir Makarov <vmakarov.gcc@gmail.com>.
*/
static void fancy_abort (int code) {
if (!code) abort ();
}
#undef gen_assert
#define gen_assert(c) fancy_abort (c)
#include <limits.h>
#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 -= <small aggr save area size> */
}
/* 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)<one or two hex digits> - 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;
}