mirror of
https://github.com/danog/ir.git
synced 2024-11-30 04:39:43 +01:00
5099 lines
137 KiB
Plaintext
5099 lines
137 KiB
Plaintext
#include "ir.h"
|
|
#include "ir_aarch64.h"
|
|
#include "ir_private.h"
|
|
|
|
#ifdef _WIN32
|
|
# define IR_SET_ALIGNED(alignment, decl) __declspec(align(alignment)) decl
|
|
#elif defined(HAVE_ATTRIBUTE_ALIGNED)
|
|
# define IR_SET_ALIGNED(alignment, decl) decl __attribute__ ((__aligned__ (alignment)))
|
|
#else
|
|
# define IR_SET_ALIGNED(alignment, decl) decl
|
|
#endif
|
|
|
|
#include "dynasm/dasm_proto.h"
|
|
#include "dynasm/dasm_arm64.h"
|
|
|
|
|.arch arm64
|
|
|
|
|.actionlist dasm_actions
|
|
|.globals ir_lb
|
|
|.section code, cold_code, rodata, jmp_table
|
|
|
|
//???#define IR_IS_SIGNED_32BIT(val) ((((intptr_t)(val)) <= 0x7fffffff) && (((intptr_t)(val)) >= (-2147483647 - 1)))
|
|
//???#define IR_IS_UNSIGNED_32BIT(val) ((((uintptr_t)(val)) <= 0xffffffff) && (((uintptr_t)(val)) >= 0))
|
|
//???#define IR_IS_32BIT(type, val) (IR_IS_TYPE_SIGNED(type) ? IR_IS_SIGNED_32BIT((val).i64) : IR_IS_UNSIGNED_32BIT((val).u64))
|
|
|
|
#define IR_SPILL_POS_TO_OFFSET(offset) \
|
|
((ctx->flags & IR_USE_FRAME_POINTER) ? \
|
|
(offset) - (data->ra_data.stack_frame_size - data->stack_frame_alignment) : \
|
|
(offset) + data->call_stack_size)
|
|
|
|
/* Determine whether "val" falls into two allowed ranges:
|
|
* Range 1: [0, 0xfff]
|
|
* Range 2: LSL #12 to Range 1
|
|
* Used to guard the immediate encoding for add/adds/sub/subs/cmp/cmn instructions. */
|
|
static bool aarch64_may_encode_imm12(const int64_t val)
|
|
{
|
|
return (val >= 0 && (val <= 0xfff || !(val & 0xffffffffff000fff)));
|
|
}
|
|
|
|
/* Determine whether an immediate value can be encoded as the immediate operand of logical instructions. */
|
|
static bool aarch64_may_encode_logical_imm(uint64_t value, uint32_t type_size)
|
|
{
|
|
/* fast path: power of two */
|
|
if (value > 0 && !(value & (value - 1))) {
|
|
return 1;
|
|
}
|
|
|
|
if (type_size == 8) {
|
|
if (dasm_imm13((uint32_t)value, (uint32_t)(value >> 32)) != -1) {
|
|
return 1;
|
|
}
|
|
} else {
|
|
if (dasm_imm13((uint32_t)value, (uint32_t)value) != -1) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool aarch64_may_encode_addr_offset(int64_t offset, uint32_t type_size)
|
|
{
|
|
//???
|
|
return 0;
|
|
}
|
|
|
|
|.macro ASM_REG_OP, op, type, reg
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 1:
|
|
| op Rb(reg)
|
|
|| break;
|
|
|| case 2:
|
|
| op Rw(reg)
|
|
|| break;
|
|
|| case 4:
|
|
| op Rd(reg)
|
|
|| break;
|
|
|| case 8:
|
|
| op Rq(reg)
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_MEM_OP, op, type, mem
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 1:
|
|
| op byte mem
|
|
|| break;
|
|
|| case 2:
|
|
| op word mem
|
|
|| break;
|
|
|| case 4:
|
|
| op dword mem
|
|
|| break;
|
|
|| case 8:
|
|
| op qword mem
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_MREF_OP, op, type, ref
|
|
|| do {
|
|
|| int32_t offset = ir_ref_spill_slot(ctx, ref);
|
|
|| ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
| ASM_MEM_OP op, type, [Ra(fp)+offset]
|
|
|| } while (0);
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_REG_OP, op, type, dst, src
|
|
|| if (ir_type_size[type] == 8) {
|
|
| op Rx(dst), Rx(src)
|
|
|| } else {
|
|
| op Rw(dst), Rw(src)
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_REG_REG_OP, op, type, dst, src1, src2
|
|
|| if (ir_type_size[type] == 8) {
|
|
| op Rx(dst), Rx(src1), Rx(src2)
|
|
|| } else {
|
|
| op Rw(dst), Rw(src1), Rw(src2)
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_REG_REG_REG_OP, op, type, dst, src1, src2, src3
|
|
|| if (ir_type_size[type] == 8) {
|
|
| op Rx(dst), Rx(src1), Rx(src2), Rx(src3)
|
|
|| } else {
|
|
| op Rw(dst), Rw(src1), Rw(src2), Rw(src3);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_REG_IMM_OP, op, type, dst, src1, val
|
|
|| if (ir_type_size[type] == 8) {
|
|
| op Rx(dst), Rx(src1), #val
|
|
|| } else {
|
|
| op Rw(dst), Rw(src1), #val
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_REG_OP2, op, type, dst, src
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 1:
|
|
|| case 2:
|
|
| op Rw(dst), Rw(src)
|
|
|| break;
|
|
|| case 4:
|
|
| op Rd(dst), Rd(src)
|
|
|| break;
|
|
|| case 8:
|
|
| op Rq(dst), Rq(src)
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_IMM_OP, op, type, reg, val
|
|
|| if (ir_type_size[type] == 8) {
|
|
| op Rx(reg), #val
|
|
|| } else {
|
|
| op Rw(reg), #val
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_MEM_REG_OP, op, type, dst, src
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 1:
|
|
| op byte dst, Rb(src)
|
|
|| break;
|
|
|| case 2:
|
|
| op word dst, Rw(src)
|
|
|| break;
|
|
|| case 4:
|
|
| op dword dst, Rd(src)
|
|
|| break;
|
|
|| case 8:
|
|
| op qword dst, Rq(src)
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_MEM_IMM_OP, op, type, dst, src
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 1:
|
|
| op byte dst, src
|
|
|| break;
|
|
|| case 2:
|
|
| op word dst, src
|
|
|| break;
|
|
|| case 4:
|
|
| op dword dst, src
|
|
|| break;
|
|
|| case 8:
|
|
| op qword dst, src
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_MEM_OP, op, type, dst, src
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 1:
|
|
| op Rb(dst), byte src
|
|
|| break;
|
|
|| case 2:
|
|
| op Rw(dst), word src
|
|
|| break;
|
|
|| case 4:
|
|
| op Rd(dst), dword src
|
|
|| break;
|
|
|| case 8:
|
|
| op Rq(dst), qword src
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_MREF_OP, _op, type, dst, src
|
|
|| if (IR_IS_CONST_REF(src)) {
|
|
|| ir_insn *_insn = &ctx->ir_base[src];
|
|
|| IR_ASSERT(IR_IS_SIGNED_32BIT(_insn->val.i64));
|
|
| ASM_REG_IMM_OP _op, type, dst, _insn->val.i32
|
|
|| } else {
|
|
|| int32_t offset = ir_ref_spill_slot(ctx, src);
|
|
|| ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
| ASM_REG_MEM_OP _op, type, dst, [Ra(fp)+offset]
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_MREF_REG_OP, op, type, dst, src
|
|
|| do {
|
|
|| int32_t offset = ir_ref_spill_slot(ctx, dst);
|
|
|| ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
| ASM_MEM_REG_OP op, type, [Ra(fp)+offset], src
|
|
|| } while (0);
|
|
|.endmacro
|
|
|
|
|.macro ASM_MREF_IMM_OP, op, type, dst, src
|
|
|| do {
|
|
|| int32_t offset = ir_ref_spill_slot(ctx, dst);
|
|
|| ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
| ASM_MEM_IMM_OP op, type, [Ra(fp)+offset], src
|
|
|| } while (0);
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_REG_IMUL, type, dst, src
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 2:
|
|
| imul Rw(dst), Rw(src)
|
|
|| break;
|
|
|| case 4:
|
|
| imul Rd(dst), Rd(src)
|
|
|| break;
|
|
|| case 8:
|
|
| imul Rq(dst), Rq(src)
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_IMM_IMUL, type, dst, src
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 2:
|
|
| imul Rw(dst), src
|
|
|| break;
|
|
|| case 4:
|
|
| imul Rd(dst), src
|
|
|| break;
|
|
|| case 8:
|
|
| imul Rq(dst), src
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_MEM_IMUL, type, dst, src
|
|
|| switch (ir_type_size[type]) {
|
|
|| case 2:
|
|
| imul Rw(dst), word src
|
|
|| break;
|
|
|| case 4:
|
|
| imul Rd(dst), dword src
|
|
|| break;
|
|
|| case 8:
|
|
| imul Rq(dst), qword src
|
|
|| break;
|
|
|| default:
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_REG_MREF_IMUL, type, dst, src
|
|
|| if (IR_IS_CONST_REF(src)) {
|
|
|| ir_insn *_insn = &ctx->ir_base[src];
|
|
|| IR_ASSERT(IR_IS_SIGNED_32BIT(_insn->val.i64));
|
|
| ASM_REG_IMM_IMUL type, dst, _insn->val.i32
|
|
|| } else {
|
|
|| int32_t offset = ir_ref_spill_slot(ctx, src);
|
|
|| ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
| ASM_REG_MEM_IMUL type, dst, [Ra(fp)+offset]
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_FP_REG_REG_REG_OP, op, type, dst, src1, src2
|
|
|| if (type == IR_DOUBLE) {
|
|
| op Rd(dst-IR_REG_FP_FIRST), Rd(src1-IR_REG_FP_FIRST), Rd(src2-IR_REG_FP_FIRST)
|
|
|| } else {
|
|
|| IR_ASSERT(type == IR_FLOAT);
|
|
| op Rs(dst-IR_REG_FP_FIRST), Rs(src1-IR_REG_FP_FIRST), Rs(src2-IR_REG_FP_FIRST)
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_FP_REG_MEM_OP, fop, dop, type, dst, src
|
|
|| if (type == IR_FLOAT) {
|
|
| fop xmm(dst-IR_REG_FP_FIRST), dword src
|
|
|| } else if (type == IR_DOUBLE) {
|
|
| dop xmm(dst-IR_REG_FP_FIRST), qword src
|
|
|| } else {
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_FP_REG_MREF_OP, fop, dop, type, dst, src
|
|
|| if (IR_IS_CONST_REF(src)) {
|
|
|| ir_insn *_insn = &ctx->ir_base[src];
|
|
|| int label = ctx->cfg_blocks_count - src;
|
|
|| _insn->emit_const = 1;
|
|
| ASM_SSE2_REG_MEM_OP fop, dop, type, dst, [=>label]
|
|
|| } else {
|
|
|| int32_t offset = ir_ref_spill_slot(ctx, src);
|
|
|| ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
| ASM_SSE2_REG_MEM_OP fop, dop, type, dst, [Ra(fp)+offset]
|
|
|| }
|
|
|.endmacro
|
|
|
|
|.macro ASM_FP_MEM_REG_OP, fop, dop, type, dst, src
|
|
|| if (type == IR_FLOAT) {
|
|
| fop dword dst, xmm(src-IR_REG_FP_FIRST)
|
|
|| } else if (type == IR_DOUBLE) {
|
|
| dop qword dst, xmm(src-IR_REG_FP_FIRST)
|
|
|| } else {
|
|
|| IR_ASSERT(0);
|
|
|| }
|
|
|.endmacro
|
|
|
|
typedef struct _ir_backend_data {
|
|
ir_reg_alloc_data ra_data;
|
|
int32_t stack_frame_alignment;
|
|
int32_t call_stack_size;
|
|
ir_regset used_preserved_regs;
|
|
uint32_t dessa_from_block;
|
|
dasm_State *dasm_state;
|
|
int rodata_label, jmp_table_label;
|
|
} ir_backend_data;
|
|
|
|
#define IR_GP_REG_NAME(code, name64, name32) \
|
|
#name64,
|
|
#define IR_GP_REG_NAME32(code, name64, name32) \
|
|
#name32,
|
|
#define IR_FP_REG_NAME(code, name64, name32, name16, name8) \
|
|
#name64,
|
|
#define IR_FP_REG_NAME32(code, name64, name32, name16, name8) \
|
|
#name32,
|
|
|
|
static const char *_ir_reg_name[IR_REG_NUM] = {
|
|
IR_GP_REGS(IR_GP_REG_NAME)
|
|
IR_FP_REGS(IR_FP_REG_NAME)
|
|
};
|
|
|
|
static const char *_ir_reg_name32[IR_REG_NUM] = {
|
|
IR_GP_REGS(IR_GP_REG_NAME32)
|
|
IR_FP_REGS(IR_FP_REG_NAME32)
|
|
};
|
|
|
|
/* Calling Convention */
|
|
static const int8_t _ir_int_reg_params[IR_REG_INT_ARGS] = {
|
|
IR_REG_INT_ARG1,
|
|
IR_REG_INT_ARG2,
|
|
IR_REG_INT_ARG3,
|
|
IR_REG_INT_ARG4,
|
|
IR_REG_INT_ARG5,
|
|
IR_REG_INT_ARG6,
|
|
IR_REG_INT_ARG7,
|
|
IR_REG_INT_ARG8,
|
|
};
|
|
|
|
static const int8_t _ir_fp_reg_params[IR_REG_FP_ARGS] = {
|
|
IR_REG_FP_ARG1,
|
|
IR_REG_FP_ARG2,
|
|
IR_REG_FP_ARG3,
|
|
IR_REG_FP_ARG4,
|
|
IR_REG_FP_ARG5,
|
|
IR_REG_FP_ARG6,
|
|
IR_REG_FP_ARG7,
|
|
IR_REG_FP_ARG8,
|
|
};
|
|
|
|
const char *ir_reg_name(int8_t reg, ir_type type)
|
|
{
|
|
IR_ASSERT(reg >= 0 && reg < IR_REG_NUM);
|
|
if (ir_type_size[type] == 8) {
|
|
return _ir_reg_name[reg];
|
|
} else {
|
|
return _ir_reg_name32[reg];
|
|
}
|
|
IR_ASSERT(0);
|
|
return NULL;
|
|
}
|
|
|
|
typedef enum _ir_rule {
|
|
IR_SKIP = IR_LAST_OP,
|
|
IR_SKIP_REG,
|
|
IR_CMP_INT, // res=reg({%rax,%rbx,%rcx,%rdx}), op1=reg(GP), op2=reg(GP)|mem|imm
|
|
// res=reg({%rax,%rbx,%rcx,%rdx}), op1=mem, op2=reg(GP)|imm
|
|
IR_CMP_FP, // res=reg({%rax,%rbx,%rcx,%rdx}), op1=reg(FP), op2=reg(FP)|mem
|
|
|
|
IR_MUL_PWR2,
|
|
IR_DIV_PWR2,
|
|
IR_MOD_PWR2,
|
|
|
|
IR_OP_INT,
|
|
IR_OP_FP,
|
|
IR_BINOP_INT, // res=reg(GP), op1.reg(GP, hint=res), op2=any
|
|
IR_BINOP_FP, // res=reg(FP), op1.reg(FP, hint=res), op2=reg(FP)|mem
|
|
IR_SHIFT, // res=reg(GP), op1.reg(GP, hint=res), op2=%rcx
|
|
IR_SHIFT_CONST, // res=reg(GP), op1.reg(GP, hint=res), imm
|
|
IR_COPY_INT, // res=reg(GP), op1=reg(GP)|mem|imm
|
|
// res=mem, op1=reg(GP)|imm
|
|
IR_COPY_FP, // res=reg(FP), op1=reg(FP)|mem
|
|
// res=mem, op2=reg(GP)
|
|
IR_CMP_AND_BRANCH_INT, // op1=reg(GP), op2=reg(GP)|mem|imm
|
|
// op1=mem, op2=reg(GP)|imm
|
|
IR_CMP_AND_BRANCH_FP, // op1=reg(FP), op2=reg(FP)|mem
|
|
IR_OVERFLOW_AND_BRANCH,
|
|
IR_MIN_MAX_INT,
|
|
|
|
//??? IR_SKIP_BINOP_INT,
|
|
//??? IR_SKIP_SHIFT,
|
|
|
|
IR_VLOAD_INT,
|
|
IR_VLOAD_FP,
|
|
IR_VSTORE_INT,
|
|
IR_VSTORE_FP,
|
|
IR_LOAD_INT,
|
|
IR_LOAD_FP,
|
|
IR_STORE_INT,
|
|
IR_STORE_FP,
|
|
IR_IF_INT, // op1=reg(GP)|mem
|
|
IR_RETURN_VOID, //
|
|
IR_RETURN_INT, // op1=reg(GP, hint=%rax)|mem|imm
|
|
IR_RETURN_FP, // op1=reg(FP, hint=%xmm0)|mem
|
|
} ir_rule;
|
|
|
|
/* instruction selection */
|
|
|
|
bool ir_needs_vreg(ir_ctx *ctx, ir_ref ref)
|
|
{
|
|
IR_ASSERT(ctx->rules);
|
|
return ctx->rules[ref] != IR_SKIP;
|
|
}
|
|
|
|
ir_regset ir_get_scratch_regset(ir_ctx *ctx, ir_ref ref, ir_live_pos *start, ir_live_pos *end)
|
|
{
|
|
ir_ref rule;
|
|
|
|
rule = ctx->rules[ref];
|
|
if (rule == IR_CALL) {
|
|
*start = IR_USE_SUB_REF;
|
|
*end = IR_DEF_SUB_REF;
|
|
return IR_REGSET_SCRATCH;
|
|
}
|
|
return IR_REGSET_EMPTY;
|
|
}
|
|
|
|
static ir_reg ir_get_param_reg(ir_ctx *ctx, ir_ref ref)
|
|
{
|
|
ir_use_list *use_list = &ctx->use_lists[1];
|
|
int i;
|
|
ir_ref use, *p;
|
|
ir_insn *insn;
|
|
int int_param = 0;
|
|
int fp_param = 0;
|
|
int int_reg_params_count = IR_REG_INT_ARGS;
|
|
int fp_reg_params_count = IR_REG_FP_ARGS;
|
|
const int8_t *int_reg_params = _ir_int_reg_params;
|
|
const int8_t *fp_reg_params = _ir_fp_reg_params;
|
|
|
|
for (i = 0, p = &ctx->use_edges[use_list->refs]; i < use_list->count; i++, p++) {
|
|
use = *p;
|
|
insn = &ctx->ir_base[use];
|
|
if (insn->op == IR_PARAM) {
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
if (use == ref) {
|
|
if (int_param < int_reg_params_count) {
|
|
return int_reg_params[int_param];
|
|
} else {
|
|
return IR_REG_NONE;
|
|
}
|
|
}
|
|
int_param++;
|
|
} else if (IR_IS_TYPE_FP(insn->type)) {
|
|
if (use == ref) {
|
|
if (fp_param < fp_reg_params_count) {
|
|
return fp_reg_params[fp_param];
|
|
} else {
|
|
return IR_REG_NONE;
|
|
}
|
|
}
|
|
fp_param++;
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
return IR_REG_NONE;
|
|
}
|
|
|
|
static ir_reg ir_get_arg_reg(ir_ctx *ctx, ir_insn *insn, int op_num)
|
|
{
|
|
int j, n;
|
|
ir_type type;
|
|
int int_param = 0;
|
|
int fp_param = 0;
|
|
int int_reg_params_count = IR_REG_INT_ARGS;
|
|
int fp_reg_params_count = IR_REG_FP_ARGS;
|
|
const int8_t *int_reg_params = _ir_int_reg_params;
|
|
const int8_t *fp_reg_params = _ir_fp_reg_params;
|
|
|
|
n = ir_input_edges_count(ctx, insn);
|
|
for (j = 3; j <= n; j++) {
|
|
type = ctx->ir_base[insn->ops[j]].type;
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (j == op_num) {
|
|
if (int_param < int_reg_params_count) {
|
|
return int_reg_params[int_param];
|
|
} else {
|
|
return IR_REG_NONE;
|
|
}
|
|
}
|
|
int_param++;
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
if (j == op_num) {
|
|
if (fp_param < fp_reg_params_count) {
|
|
return fp_reg_params[fp_param];
|
|
} else {
|
|
return IR_REG_NONE;
|
|
}
|
|
}
|
|
fp_param++;
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
return IR_REG_NONE;
|
|
}
|
|
|
|
ir_reg ir_uses_fixed_reg(ir_ctx *ctx, ir_ref ref, int op_num)
|
|
{
|
|
ir_ref rule;
|
|
|
|
rule = ctx->rules[ref];
|
|
if (rule == IR_RETURN_INT) {
|
|
if (op_num == 2) {
|
|
return IR_REG_INT_RET1;
|
|
}
|
|
} else if (rule == IR_RETURN_FP) {
|
|
if (op_num == 2) {
|
|
return IR_REG_FP_RET1;
|
|
}
|
|
} else if (rule == IR_SKIP_REG) {
|
|
if (ctx->ir_base[ref].op == IR_PARAM && op_num == 0) {
|
|
return ir_get_param_reg(ctx, ref);
|
|
}
|
|
} else if (rule == IR_CALL || (rule == IR_TAILCALL && op_num > 0)) {
|
|
ir_insn *insn = &ctx->ir_base[ref];
|
|
if (op_num == 0) {
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
return IR_REG_INT_RET1;
|
|
} else {
|
|
return IR_REG_FP_RET1;
|
|
}
|
|
} else {
|
|
return ir_get_arg_reg(ctx, insn, op_num);
|
|
}
|
|
}
|
|
return IR_REG_NONE;
|
|
}
|
|
|
|
static bool ir_call_needs_tmp_int_reg(ir_ctx *ctx, ir_ref ref)
|
|
{
|
|
ir_insn *insn = &ctx->ir_base[ref];
|
|
ir_ref arg;
|
|
ir_insn *arg_insn;
|
|
int j, n;
|
|
ir_type type;
|
|
int int_param = 0;
|
|
int int_reg_params_count = IR_REG_INT_ARGS;
|
|
|
|
n = ir_input_edges_count(ctx, insn);
|
|
for (j = 3; j <= n; j++) {
|
|
arg = insn->ops[j];
|
|
arg_insn = &ctx->ir_base[arg];
|
|
type = arg_insn->type;
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (int_param < int_reg_params_count) {
|
|
if (int_param > 0) {
|
|
return 1; /* for swap */
|
|
}
|
|
} else if (IR_IS_CONST_REF(arg) && arg_insn->op == IR_STR) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
int_param++;
|
|
} else if (type == IR_DOUBLE) {
|
|
if (IR_IS_CONST_REF(arg) && arg_insn->val.i64 != 0) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint8_t ir_get_def_flags(ir_ctx *ctx, ir_ref ref)
|
|
{
|
|
ir_ref rule = ctx->rules[ref];
|
|
ir_insn *insn;
|
|
|
|
switch (rule) {
|
|
#if 0 //???
|
|
case IR_BINOP_INT:
|
|
case IR_BINOP_FP:
|
|
case IR_SHIFT:
|
|
case IR_SHIFT_CONST:
|
|
case IR_COPY_INT:
|
|
case IR_COPY_FP:
|
|
case IR_MUL_PWR2:
|
|
case IR_DIV_PWR2:
|
|
case IR_MOD_PWR2:
|
|
case IR_OP_INT:
|
|
case IR_OP_FP:
|
|
case IR_MIN_MAX_INT:
|
|
return IR_DEF_REUSES_OP1_REG | IR_USE_MUST_BE_IN_REG;
|
|
#endif
|
|
case IR_SKIP_REG: /* PARAM PHI PI */
|
|
insn = &ctx->ir_base[ref];
|
|
if (insn->op == IR_PARAM) {
|
|
if (ir_get_param_reg(ctx, ref) == IR_REG_NONE) {
|
|
return IR_USE_MUST_BE_IN_REG;
|
|
}
|
|
}
|
|
return IR_USE_SHOULD_BE_IN_REG;
|
|
}
|
|
|
|
return IR_USE_MUST_BE_IN_REG;
|
|
}
|
|
|
|
uint8_t ir_get_use_flags(ir_ctx *ctx, ir_ref ref, int op_num)
|
|
{
|
|
#if 0 //???
|
|
ir_ref rule = ctx->rules[ref];
|
|
ir_insn *insn;
|
|
|
|
IR_ASSERT(op_num > 0);
|
|
switch (rule) {
|
|
case IR_BINOP_INT:
|
|
case IR_SKIP_BINOP_INT:
|
|
case IR_BINOP_FP:
|
|
case IR_IF_INT:
|
|
return (op_num == 2) ? IR_USE_SHOULD_BE_IN_REG : IR_USE_MUST_BE_IN_REG;
|
|
case IR_CMP_INT:
|
|
if (op_num == 2) {
|
|
return IR_USE_SHOULD_BE_IN_REG;
|
|
} else if (op_num == 1) {
|
|
insn = &ctx->ir_base[ref];
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
return IR_USE_SHOULD_BE_IN_REG;
|
|
}
|
|
}
|
|
return IR_USE_MUST_BE_IN_REG;
|
|
case IR_MIN_MAX_INT:
|
|
return (op_num == 1) ? IR_USE_SHOULD_BE_IN_REG : IR_USE_MUST_BE_IN_REG;
|
|
case IR_CMP_FP:
|
|
insn = &ctx->ir_base[ref];
|
|
if (insn->op == IR_LT || insn->op == IR_LE) {
|
|
return (op_num == 1) ? IR_USE_SHOULD_BE_IN_REG : IR_USE_MUST_BE_IN_REG;
|
|
} else {
|
|
return (op_num == 2) ? IR_USE_SHOULD_BE_IN_REG : IR_USE_MUST_BE_IN_REG;
|
|
}
|
|
case IR_CALL:
|
|
case IR_TAILCALL:
|
|
if (op_num > 2) {
|
|
return IR_USE_SHOULD_BE_IN_REG;
|
|
}
|
|
return IR_USE_SHOULD_BE_IN_REG;
|
|
case IR_RETURN_INT:
|
|
case IR_RETURN_FP:
|
|
case IR_IJMP:
|
|
case IR_SKIP_REG: /* PARAM PHI PI */
|
|
return IR_USE_SHOULD_BE_IN_REG;
|
|
// case IR_VLOAD_INT:
|
|
// case IR_VLOAD_FP:
|
|
// case IR_VSTORE_INT:
|
|
// case IR_VSTORE_FP:
|
|
// return (op_num == 2) ? 0 : IR_USE_MUST_BE_IN_REG;
|
|
case IR_SKIP:
|
|
insn = &ctx->ir_base[ref];
|
|
switch (insn->op) {
|
|
case IR_EQ:
|
|
case IR_NE:
|
|
case IR_LT:
|
|
case IR_GE:
|
|
case IR_LE:
|
|
case IR_GT:
|
|
case IR_ULT:
|
|
case IR_UGE:
|
|
case IR_ULE:
|
|
case IR_UGT:
|
|
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type)) {
|
|
if (op_num == 2 || (op_num == 1 && IR_IS_CONST_REF(insn->op2))) {
|
|
return IR_USE_SHOULD_BE_IN_REG;
|
|
}
|
|
} else {
|
|
if (insn->op == IR_LT || insn->op == IR_LE) {
|
|
return (op_num == 1) ? IR_USE_SHOULD_BE_IN_REG : IR_USE_MUST_BE_IN_REG;
|
|
} else {
|
|
return (op_num == 2) ? IR_USE_SHOULD_BE_IN_REG : IR_USE_MUST_BE_IN_REG;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return IR_USE_MUST_BE_IN_REG;
|
|
}
|
|
|
|
int ir_get_temporary_regs(ir_ctx *ctx, ir_ref ref, ir_tmp_reg *tmp_regs)
|
|
{
|
|
ir_ref rule;
|
|
ir_insn *insn;
|
|
int n;
|
|
|
|
rule = ctx->rules[ref];
|
|
switch (rule) {
|
|
case IR_BINOP_INT:
|
|
insn = &ctx->ir_base[ref];
|
|
n = 0;
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
tmp_regs[n].num = 1;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) {
|
|
ir_insn *val_insn = &ctx->ir_base[insn->op2];
|
|
switch (insn->op) {
|
|
case IR_ADD:
|
|
case IR_ADD_OV:
|
|
case IR_SUB:
|
|
case IR_SUB_OV:
|
|
if (!aarch64_may_encode_imm12(val_insn->val.u64)) {
|
|
tmp_regs[n].num = 2;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
break;
|
|
case IR_AND:
|
|
case IR_OR:
|
|
case IR_XOR:
|
|
if (!aarch64_may_encode_logical_imm(val_insn->val.u64, ir_type_size[insn->type])) {
|
|
tmp_regs[n].num = 2;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
break;
|
|
case IR_MUL:
|
|
case IR_DIV:
|
|
case IR_MOD:
|
|
tmp_regs[n].num = 2;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
break;
|
|
}
|
|
}
|
|
if (insn->op == IR_MOD) {
|
|
tmp_regs[n].num = 3;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_SAVE_SUB_REF;
|
|
n++;
|
|
}
|
|
return n;
|
|
case IR_MUL_PWR2:
|
|
case IR_DIV_PWR2:
|
|
case IR_MOD_PWR2:
|
|
case IR_SHIFT:
|
|
case IR_SHIFT_CONST:
|
|
case IR_OP_INT:
|
|
case IR_OP_FP:
|
|
insn = &ctx->ir_base[ref];
|
|
n = 0;
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
tmp_regs[n].num = 1;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
return n;
|
|
case IR_BINOP_FP:
|
|
case IR_MIN_MAX_INT:
|
|
insn = &ctx->ir_base[ref];
|
|
n = 0;
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
tmp_regs[n].num = 1;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) {
|
|
tmp_regs[n].num = 2;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
return n;
|
|
#if 0 //???
|
|
case IR_SKIP_BINOP_INT:
|
|
insn = &ctx->ir_base[ref];
|
|
if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) {
|
|
insn = &ctx->ir_base[insn->op2];
|
|
if (ir_type_size[insn->type] == 8 && !IR_IS_32BIT(insn->type, insn->val)) {
|
|
tmp_regs[0].num = 2;
|
|
tmp_regs[0].type = insn->type;
|
|
tmp_regs[0].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[0].end = IR_DEF_SUB_REF;
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
case IR_CMP_INT:
|
|
insn = &ctx->ir_base[ref];
|
|
n = 0;
|
|
cmp_int:
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
tmp_regs[n].num = 1;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) {
|
|
insn = &ctx->ir_base[insn->op2];
|
|
if (!aarch64_may_encode_imm12(insn->val.u64)) {
|
|
tmp_regs[n].num = 2;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
}
|
|
return n;
|
|
case IR_CMP_FP:
|
|
insn = &ctx->ir_base[ref];
|
|
tmp_regs[0].num = 3;
|
|
tmp_regs[0].type = IR_BOOL;
|
|
tmp_regs[0].start = IR_DEF_SUB_REF;
|
|
tmp_regs[0].end = IR_SAVE_SUB_REF;
|
|
n = 1;
|
|
cmp_fp:
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
ir_insn *val_insn = &ctx->ir_base[insn->op1];
|
|
tmp_regs[n].num = 1;
|
|
tmp_regs[n].type = val_insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) {
|
|
ir_insn *val_insn = &ctx->ir_base[insn->op2];
|
|
tmp_regs[n].num = 2;
|
|
tmp_regs[n].type = val_insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
return n;
|
|
#if 0 //???
|
|
case IR_VSTORE_INT:
|
|
case IR_STORE_INT:
|
|
insn = &ctx->ir_base[ref];
|
|
if (IR_IS_CONST_REF(insn->op3)) {
|
|
insn = &ctx->ir_base[insn->op3];
|
|
if (ir_type_size[insn->type] == 8 && !IR_IS_32BIT(insn->type, insn->val)) {
|
|
tmp_regs[0].num = 3;
|
|
tmp_regs[0].type = insn->type;
|
|
tmp_regs[0].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[0].end = IR_DEF_SUB_REF;
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
case IR_VSTORE_FP:
|
|
case IR_STORE_FP:
|
|
insn = &ctx->ir_base[ref];
|
|
if (IR_IS_CONST_REF(insn->op3)) {
|
|
insn = &ctx->ir_base[insn->op3];
|
|
tmp_regs[0].num = 3;
|
|
tmp_regs[0].type = insn->type;
|
|
tmp_regs[0].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[0].end = IR_DEF_SUB_REF;
|
|
return 1;
|
|
}
|
|
break;
|
|
case IR_SWITCH:
|
|
insn = &ctx->ir_base[ref];
|
|
n = 0;
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
insn = &ctx->ir_base[insn->op2];
|
|
tmp_regs[n].num = 2;
|
|
tmp_regs[n].type = insn->type;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
if (sizeof(void*) == 8) {
|
|
tmp_regs[n].num = 3;
|
|
tmp_regs[n].type = IR_ADDR;
|
|
tmp_regs[n].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[n].end = IR_DEF_SUB_REF;
|
|
n++;
|
|
}
|
|
return n;
|
|
case IR_CALL:
|
|
case IR_TAILCALL:
|
|
if (ir_call_needs_tmp_int_reg(ctx, ref)) {
|
|
tmp_regs[0].num = 1;
|
|
tmp_regs[0].type = IR_ADDR;
|
|
tmp_regs[0].start = IR_LOAD_SUB_REF;
|
|
tmp_regs[0].end = IR_USE_SUB_REF;
|
|
return 1;
|
|
}
|
|
break;
|
|
#endif
|
|
case IR_SKIP:
|
|
insn = &ctx->ir_base[ref];
|
|
switch (insn->op) {
|
|
case IR_EQ:
|
|
case IR_NE:
|
|
case IR_LT:
|
|
case IR_GE:
|
|
case IR_LE:
|
|
case IR_GT:
|
|
case IR_ULT:
|
|
case IR_UGE:
|
|
case IR_ULE:
|
|
case IR_UGT:
|
|
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type)) {
|
|
/* part of IR_CMP_AND_BRANCH_INT */
|
|
n = 0;
|
|
goto cmp_int;
|
|
} else {
|
|
/* part of IR_CMP_AND_BRANCH_FP */
|
|
n = 0;
|
|
goto cmp_fp;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t ir_match_insn(ir_ctx *ctx, ir_ref ref, ir_block *bb)
|
|
{
|
|
ir_insn *op2_insn;
|
|
ir_insn *insn = &ctx->ir_base[ref];
|
|
|
|
switch (insn->op) {
|
|
case IR_EQ:
|
|
case IR_NE:
|
|
case IR_LT:
|
|
case IR_GE:
|
|
case IR_LE:
|
|
case IR_GT:
|
|
case IR_ULT:
|
|
case IR_UGE:
|
|
case IR_ULE:
|
|
case IR_UGT:
|
|
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type)) {
|
|
return IR_CMP_INT;
|
|
} else {
|
|
return IR_CMP_FP;
|
|
}
|
|
break;
|
|
case IR_ADD:
|
|
case IR_SUB:
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
if ((ctx->flags & IR_OPT_CODEGEN) && IR_IS_CONST_REF(insn->op2)) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
} else if (op2_insn->val.i64 == 0) {
|
|
return IR_COPY_INT;
|
|
}
|
|
}
|
|
binop_int:
|
|
return IR_BINOP_INT;
|
|
} else {
|
|
binop_fp:
|
|
return IR_BINOP_FP;
|
|
}
|
|
break;
|
|
case IR_MUL:
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
if ((ctx->flags & IR_OPT_CODEGEN) && IR_IS_CONST_REF(insn->op2)) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
} else if (op2_insn->val.u64 == 0) {
|
|
// 0
|
|
} else if (op2_insn->val.u64 == 1) {
|
|
return IR_COPY_INT;
|
|
} else if (IR_IS_POWER_OF_TWO(op2_insn->val.u64)) {
|
|
return IR_MUL_PWR2; // shl op1, IR_LOG2(op2_insn->val.u64)
|
|
}
|
|
}
|
|
return IR_BINOP_INT;
|
|
} else {
|
|
goto binop_fp;
|
|
}
|
|
break;
|
|
case IR_ADD_OV:
|
|
case IR_SUB_OV:
|
|
IR_ASSERT(IR_IS_TYPE_INT(insn->type));
|
|
goto binop_int;
|
|
case IR_MUL_OV:
|
|
IR_ASSERT(IR_IS_TYPE_INT(insn->type));
|
|
IR_ASSERT(0);//???
|
|
goto binop_int;
|
|
case IR_DIV:
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
if ((ctx->flags & IR_OPT_CODEGEN) && IR_IS_CONST_REF(insn->op2)) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
} else if (op2_insn->val.u64 == 1) {
|
|
return IR_COPY_INT;
|
|
} else if (IR_IS_POWER_OF_TWO(op2_insn->val.u64)) {
|
|
return IR_DIV_PWR2; // shr op1, IR_LOG2(op2_insn->val.u64)
|
|
}
|
|
}
|
|
return IR_BINOP_INT;
|
|
} else {
|
|
goto binop_fp;
|
|
}
|
|
break;
|
|
case IR_MOD:
|
|
if ((ctx->flags & IR_OPT_CODEGEN) && IR_IS_CONST_REF(insn->op2)) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
} else if (IR_IS_POWER_OF_TWO(op2_insn->val.u64)) {
|
|
return IR_MOD_PWR2; // and op1, op2_insn->val.u64-1
|
|
}
|
|
}
|
|
return IR_BINOP_INT;
|
|
case IR_BSWAP:
|
|
case IR_NOT:
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
return IR_OP_INT;
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
break;
|
|
case IR_NEG:
|
|
case IR_ABS:
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
return IR_OP_INT;
|
|
} else {
|
|
return IR_OP_FP;
|
|
}
|
|
case IR_OR:
|
|
if ((ctx->flags & IR_OPT_CODEGEN) && IR_IS_CONST_REF(insn->op2)) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
} else if (op2_insn->val.i64 == 0) {
|
|
return IR_COPY_INT;
|
|
} else if (op2_insn->val.i64 == -1) {
|
|
// -1
|
|
}
|
|
}
|
|
goto binop_int;
|
|
case IR_AND:
|
|
if ((ctx->flags & IR_OPT_CODEGEN) && IR_IS_CONST_REF(insn->op2)) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
} else if (op2_insn->val.i64 == 0) {
|
|
// 0
|
|
} else if (op2_insn->val.i64 == -1) {
|
|
return IR_COPY_INT;
|
|
}
|
|
}
|
|
goto binop_int;
|
|
case IR_XOR:
|
|
if ((ctx->flags & IR_OPT_CODEGEN) && IR_IS_CONST_REF(insn->op2)) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
}
|
|
}
|
|
goto binop_int;
|
|
case IR_SHL:
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
if (ctx->flags & IR_OPT_CODEGEN) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
} else if (op2_insn->val.u64 == 0) {
|
|
return IR_COPY_INT;
|
|
} else if (ir_type_size[insn->type] >= 4) {
|
|
if (op2_insn->val.u64 == 1) {
|
|
// lea [op1*2]
|
|
} else if (op2_insn->val.u64 == 2) {
|
|
// lea [op1*4]
|
|
} else if (op2_insn->val.u64 == 3) {
|
|
// lea [op1*8]
|
|
}
|
|
}
|
|
}
|
|
return IR_SHIFT_CONST;
|
|
}
|
|
return IR_SHIFT;
|
|
case IR_SHR:
|
|
case IR_SAR:
|
|
case IR_ROL:
|
|
case IR_ROR:
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
if (ctx->flags & IR_OPT_CODEGEN) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
// const
|
|
} else if (op2_insn->val.u64 == 0) {
|
|
return IR_COPY_INT;
|
|
}
|
|
}
|
|
return IR_SHIFT_CONST;
|
|
}
|
|
return IR_SHIFT;
|
|
case IR_MIN:
|
|
case IR_MAX:
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
return IR_MIN_MAX_INT;
|
|
} else {
|
|
goto binop_fp;
|
|
}
|
|
break;
|
|
// case IR_COND:
|
|
case IR_COPY:
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
return IR_COPY_INT;
|
|
} else {
|
|
return IR_COPY_FP;
|
|
}
|
|
break;
|
|
case IR_PHI:
|
|
case IR_PI:
|
|
case IR_PARAM:
|
|
return IR_SKIP_REG;
|
|
|
|
case IR_CALL:
|
|
ctx->flags |= IR_HAS_CALLS;
|
|
return IR_CALL;
|
|
|
|
case IR_VAR:
|
|
if (ctx->use_lists[ref].count > 0) {
|
|
return IR_VAR;
|
|
} else {
|
|
return IR_SKIP;
|
|
}
|
|
break;
|
|
case IR_ALLOCA:
|
|
ctx->flags |= IR_USE_FRAME_POINTER;
|
|
return IR_ALLOCA;
|
|
case IR_VLOAD:
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
return IR_VLOAD_INT;
|
|
} else {
|
|
return IR_VLOAD_FP;
|
|
}
|
|
break;
|
|
case IR_VSTORE:
|
|
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op3].type)) {
|
|
return IR_VSTORE_INT;
|
|
} else {
|
|
return IR_VSTORE_FP;
|
|
}
|
|
break;
|
|
case IR_LOAD:
|
|
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op3].type)) {
|
|
return IR_LOAD_INT;
|
|
} else {
|
|
return IR_LOAD_FP;
|
|
}
|
|
break;
|
|
case IR_STORE:
|
|
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op3].type)) {
|
|
return IR_STORE_INT;
|
|
} else {
|
|
return IR_STORE_FP;
|
|
}
|
|
break;
|
|
case IR_START:
|
|
case IR_BEGIN:
|
|
// case IR_END:
|
|
case IR_IF_TRUE:
|
|
case IR_IF_FALSE:
|
|
case IR_CASE_VAL:
|
|
case IR_CASE_DEFAULT:
|
|
case IR_MERGE:
|
|
case IR_LOOP_BEGIN:
|
|
// case IR_LOOP_END:
|
|
case IR_LOOP_EXIT:
|
|
case IR_UNREACHABLE:
|
|
return IR_SKIP;
|
|
case IR_RETURN:
|
|
if (!insn->op2) {
|
|
return IR_RETURN_VOID;
|
|
} else if (IR_IS_TYPE_INT(ctx->ir_base[insn->op2].type)) {
|
|
return IR_RETURN_INT;
|
|
} else {
|
|
return IR_RETURN_FP;
|
|
}
|
|
case IR_IF:
|
|
if (insn->op2 > bb->start && insn->op2 < ref && ctx->use_lists[insn->op2].count == 1) {
|
|
op2_insn = &ctx->ir_base[insn->op2];
|
|
if (op2_insn->op >= IR_EQ && op2_insn->op <= IR_UGT) {
|
|
ctx->rules[insn->op2] = IR_SKIP;
|
|
if (IR_IS_TYPE_INT(ctx->ir_base[op2_insn->op1].type)) {
|
|
return IR_CMP_AND_BRANCH_INT;
|
|
} else {
|
|
return IR_CMP_AND_BRANCH_FP;
|
|
}
|
|
} else if (op2_insn->op == IR_OVERFLOW) {
|
|
ctx->rules[insn->op2] = IR_SKIP;
|
|
return IR_OVERFLOW_AND_BRANCH;
|
|
}
|
|
}
|
|
if (IR_IS_TYPE_INT(ctx->ir_base[insn->op2].type)) {
|
|
return IR_IF_INT;
|
|
} else {
|
|
IR_ASSERT(0 && "NIY IR_IF_FP");
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return insn->op;
|
|
}
|
|
|
|
int ir_match(ir_ctx *ctx)
|
|
{
|
|
int b, n;
|
|
ir_ref i;
|
|
ir_block *bb;
|
|
ir_insn *insn;
|
|
|
|
if (!ctx->prev_insn_len) {
|
|
ctx->prev_insn_len = ir_mem_malloc(ctx->insns_count * sizeof(uint32_t));
|
|
n = 1;
|
|
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
|
|
for (i = bb->start, insn = ctx->ir_base + i; i <= bb->end;) {
|
|
ctx->prev_insn_len[i] = n;
|
|
n = ir_operands_count(ctx, insn);
|
|
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
|
|
i += n;
|
|
insn += n;
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx->rules = ir_mem_calloc(ctx->insns_count, sizeof(uint32_t));
|
|
for (b = ctx->cfg_blocks_count, bb = ctx->cfg_blocks + b; b > 0; b--, bb--) {
|
|
for (i = bb->end; i >= bb->start; i -= ctx->prev_insn_len[i]) {
|
|
insn = &ctx->ir_base[i];
|
|
if (!ctx->rules[i]) {
|
|
// if (ctx->rules[i] != IR_SKIP) {
|
|
ctx->rules[i] = ir_match_insn(ctx, i, bb);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* code genertion */
|
|
static int ir_skip_empty_blocks(ir_ctx *ctx, int b)
|
|
{
|
|
while (ctx->cfg_blocks[b].flags & IR_BB_MAY_SKIP) {
|
|
b++;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
static ir_reg ir_ref_spill_slot(ir_ctx *ctx, ir_ref ref)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
int32_t offset;
|
|
|
|
IR_ASSERT(ref >= 0);
|
|
offset = ctx->live_intervals[ctx->vregs[ref]]->stack_spill_pos;
|
|
IR_ASSERT(offset != -1);
|
|
return IR_SPILL_POS_TO_OFFSET(offset);
|
|
}
|
|
|
|
static void ir_emit_load_imm_int(ir_ctx *ctx, ir_type type, ir_reg reg, int64_t val)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
IR_ASSERT(IR_IS_TYPE_INT(type));
|
|
if (ir_type_size[type] == 8) {
|
|
if (val == 0) {
|
|
| mov Rx(reg), xzr
|
|
} else if (((uint64_t)(val)) <= 0xffff) {
|
|
| movz Rx(reg), #((uint64_t)(val))
|
|
} else if (~((uint64_t)(val)) <= 0xffff) {
|
|
| movn Rx(reg), #(~((uint64_t)(val)))
|
|
} else if ((uint64_t)(val) & 0xffff) {
|
|
| movz Rx(reg), #((uint64_t)(val) & 0xffff)
|
|
if (((uint64_t)(val) >> 16) & 0xffff) {
|
|
| movk Rx(reg), #(((uint64_t)(val) >> 16) & 0xffff), lsl #16
|
|
}
|
|
if (((uint64_t)(val) >> 32) & 0xffff) {
|
|
| movk Rx(reg), #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
|
|
}
|
|
if ((((uint64_t)(val) >> 48) & 0xffff)) {
|
|
| movk Rx(reg), #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
|
|
}
|
|
} else if (((uint64_t)(val) >> 16) & 0xffff) {
|
|
| movz Rx(reg), #(((uint64_t)(val) >> 16) & 0xffff), lsl #16
|
|
if (((uint64_t)(val) >> 32) & 0xffff) {
|
|
| movk Rx(reg), #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
|
|
}
|
|
if ((((uint64_t)(val) >> 48) & 0xffff)) {
|
|
| movk Rx(reg), #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
|
|
}
|
|
} else if (((uint64_t)(val) >> 32) & 0xffff) {
|
|
| movz Rx(reg), #(((uint64_t)(val) >> 32) & 0xffff), lsl #32
|
|
if ((((uint64_t)(val) >> 48) & 0xffff)) {
|
|
| movk Rx(reg), #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
|
|
}
|
|
} else {
|
|
| movz Rx(reg), #(((uint64_t)(val) >> 48) & 0xffff), lsl #48
|
|
}
|
|
} else {
|
|
if (val == 0) {
|
|
| mov Rw(reg), wzr
|
|
} else if (((uint64_t)(val)) <= 0xffff) {
|
|
| movz Rw(reg), #((uint64_t)(val))
|
|
} else if (~((uint64_t)(val)) <= 0xffff) {
|
|
| movn Rw(reg), #(~((uint64_t)(val)))
|
|
} else if ((uint64_t)(val) & 0xffff) {
|
|
| movz Rw(reg), #((uint64_t)(val) & 0xffff)
|
|
if (((uint64_t)(val) >> 16) & 0xffff) {
|
|
| movk Rw(reg), #(((uint64_t)(val) >> 16) & 0xffff), lsl #16
|
|
}
|
|
} else if (((uint64_t)(val) >> 16) & 0xffff) {
|
|
| movz Rw(reg), #(((uint64_t)(val) >> 16) & 0xffff), lsl #16
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_load_mem_int(ir_ctx *ctx, ir_type type, ir_reg reg, ir_reg base_reg, int32_t offset)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (aarch64_may_encode_addr_offset(offset, ir_type_size[type])) {
|
|
switch (ir_type_size[type]) {
|
|
case 8:
|
|
| ldr Rx(reg), [Rx(base_reg), #offset]
|
|
break;
|
|
case 4:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| ldrsw Rx(reg), [Rx(base_reg), #offset]
|
|
} else {
|
|
| ldr Rw(reg), [Rx(base_reg), #offset]
|
|
}
|
|
break;
|
|
case 2:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| ldrsh Rx(reg), [Rx(base_reg), #offset]
|
|
} else {
|
|
| ldrh Rw(reg), [Rx(base_reg), #offset]
|
|
}
|
|
break;
|
|
case 1:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| ldrsb Rx(reg), [Rx(base_reg), #offset]
|
|
} else {
|
|
| ldrb Rw(reg), [Rx(base_reg), #offset]
|
|
}
|
|
break;
|
|
default:
|
|
IR_ASSERT(0);
|
|
}
|
|
} else {
|
|
ir_emit_load_imm_int(ctx, IR_TYPE_ADDR, reg, offset);
|
|
switch (ir_type_size[type]) {
|
|
case 8:
|
|
| ldr Rx(reg), [Rx(base_reg), Rx(reg)]
|
|
break;
|
|
case 4:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| ldrsw Rx(reg), [Rx(base_reg), Rx(reg)]
|
|
} else {
|
|
| ldr Rw(reg), [Rx(base_reg), Rx(reg)]
|
|
}
|
|
break;
|
|
case 2:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| ldrsh Rx(reg), [Rx(base_reg), Rx(reg)]
|
|
} else {
|
|
| ldrh Rw(reg), [Rx(base_reg), Rx(reg)]
|
|
}
|
|
break;
|
|
case 1:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| ldrsb Rx(reg), [Rx(base_reg), Rx(reg)]
|
|
} else {
|
|
| ldrb Rw(reg), [Rx(base_reg), Rx(reg)]
|
|
}
|
|
break;
|
|
default:
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_load_imm_fp(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref src)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_insn *insn = &ctx->ir_base[src];
|
|
int label;
|
|
|
|
if (type == IR_FLOAT && insn->val.f == 0) {
|
|
| fmov Rs(reg-IR_REG_FP_FIRST), wzr
|
|
} else if (type == IR_DOUBLE && insn->val.d == 0) {
|
|
| fmov Rd(reg-IR_REG_FP_FIRST), xzr
|
|
} else {
|
|
label = ctx->cfg_blocks_count - src;
|
|
insn->emit_const = 1;
|
|
if (type == IR_DOUBLE) {
|
|
| ldr Rd(reg-IR_REG_FP_FIRST), =>label
|
|
} else {
|
|
IR_ASSERT(type == IR_FLOAT);
|
|
| ldr Rs(reg-IR_REG_FP_FIRST), =>label
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_load_mem_fp(ir_ctx *ctx, ir_type type, ir_reg reg, ir_reg base_reg, int32_t offset)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (aarch64_may_encode_addr_offset(offset, ir_type_size[type])) {
|
|
if (type == IR_DOUBLE) {
|
|
| ldr Rd(reg-IR_REG_FP_FIRST), [Rx(base_reg), #offset]
|
|
} else {
|
|
IR_ASSERT(type == IR_FLOAT);
|
|
| ldr Rs(reg-IR_REG_FP_FIRST), [Rx(base_reg), #offset]
|
|
}
|
|
} else {
|
|
ir_reg tmp_reg = IR_REG_X16; //??? TODO: TMP1
|
|
|
|
ir_emit_load_imm_int(ctx, IR_TYPE_ADDR, tmp_reg, offset);
|
|
if (type == IR_DOUBLE) {
|
|
| ldr Rd(reg-IR_REG_FP_FIRST), [Rx(base_reg), Rx(tmp_reg)]
|
|
} else {
|
|
IR_ASSERT(type == IR_FLOAT);
|
|
| ldr Rs(reg-IR_REG_FP_FIRST), [Rx(base_reg), Rx(tmp_reg)]
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_load(ir_ctx *ctx, ir_type type, ir_reg reg, ir_ref src)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
int32_t offset;
|
|
ir_reg fp;
|
|
|
|
if (IR_IS_CONST_REF(src)) {
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
ir_insn *insn = &ctx->ir_base[src];
|
|
|
|
IR_ASSERT(insn->op != IR_STR);
|
|
ir_emit_load_imm_int(ctx, type, reg, insn->val.i64);
|
|
} else {
|
|
ir_emit_load_imm_fp(ctx, type, reg, src);
|
|
}
|
|
} else {
|
|
offset = ctx->live_intervals[ctx->vregs[src]]->stack_spill_pos;
|
|
IR_ASSERT(offset != -1);
|
|
offset = IR_SPILL_POS_TO_OFFSET(offset);
|
|
fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
ir_emit_load_mem_int(ctx, type, reg, fp, offset);
|
|
} else {
|
|
ir_emit_load_mem_fp(ctx, type, reg, fp, offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_store_mem_int(ir_ctx *ctx, ir_type type, ir_reg base_reg, int32_t offset, ir_reg reg)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (aarch64_may_encode_addr_offset(offset, ir_type_size[type])) {
|
|
switch (ir_type_size[type]) {
|
|
case 8:
|
|
| str Rx(reg), [Rx(base_reg), #offset]
|
|
break;
|
|
case 4:
|
|
| str Rw(reg), [Rx(base_reg), #offset]
|
|
break;
|
|
case 2:
|
|
| strh Rw(reg), [Rx(base_reg), #offset]
|
|
break;
|
|
case 1:
|
|
| strb Rw(reg), [Rx(base_reg), #offset]
|
|
break;
|
|
default:
|
|
IR_ASSERT(0);
|
|
}
|
|
} else {
|
|
ir_emit_load_imm_int(ctx, IR_TYPE_ADDR, reg, offset);
|
|
switch (ir_type_size[type]) {
|
|
case 8:
|
|
| str Rx(reg), [Rx(base_reg), Rx(reg)]
|
|
break;
|
|
case 4:
|
|
| str Rw(reg), [Rx(base_reg), Rx(reg)]
|
|
break;
|
|
case 2:
|
|
| strh Rw(reg), [Rx(base_reg), Rx(reg)]
|
|
break;
|
|
case 1:
|
|
| strb Rw(reg), [Rx(base_reg), Rx(reg)]
|
|
break;
|
|
default:
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_store_mem_fp(ir_ctx *ctx, ir_type type, ir_reg base_reg, int32_t offset, ir_reg reg)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (aarch64_may_encode_addr_offset(offset, ir_type_size[type])) {
|
|
if (type == IR_DOUBLE) {
|
|
| str Rd(reg-IR_REG_FP_FIRST), [Rx(base_reg), #offset]
|
|
} else {
|
|
IR_ASSERT(type == IR_FLOAT);
|
|
| str Rs(reg-IR_REG_FP_FIRST), [Rx(base_reg), #offset]
|
|
}
|
|
} else {
|
|
ir_reg tmp_reg = IR_REG_X16; //??? TODO: TMP1
|
|
|
|
ir_emit_load_imm_int(ctx, IR_TYPE_ADDR, tmp_reg, offset);
|
|
if (type == IR_DOUBLE) {
|
|
| str Rd(reg-IR_REG_FP_FIRST), [Rx(base_reg), Rx(tmp_reg)]
|
|
} else {
|
|
IR_ASSERT(type == IR_FLOAT);
|
|
| str Rs(reg-IR_REG_FP_FIRST), [Rx(base_reg), Rx(tmp_reg)]
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_store(ir_ctx *ctx, ir_type type, ir_ref dst, ir_reg reg)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
int32_t offset;
|
|
ir_reg fp;
|
|
|
|
IR_ASSERT(dst >= 0);
|
|
offset = ctx->live_intervals[ctx->vregs[dst]]->stack_spill_pos;
|
|
IR_ASSERT(offset != -1);
|
|
offset = IR_SPILL_POS_TO_OFFSET(offset);
|
|
fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
ir_emit_store_mem_int(ctx, type, fp, offset, reg);
|
|
} else {
|
|
ir_emit_store_mem_fp(ctx, type, fp, offset, reg);
|
|
}
|
|
}
|
|
|
|
static bool ir_is_same_mem(ir_ctx *ctx, ir_ref r1, ir_ref r2)
|
|
{
|
|
ir_live_interval *ival1, *ival2;
|
|
int32_t o1, o2;
|
|
|
|
if (IR_IS_CONST_REF(r1) || IR_IS_CONST_REF(r2)) {
|
|
return 0;
|
|
}
|
|
|
|
IR_ASSERT(ctx->vregs[r1] && ctx->vregs[r2]);
|
|
ival1 = ctx->live_intervals[ctx->vregs[r1]];
|
|
ival2 = ctx->live_intervals[ctx->vregs[r2]];
|
|
IR_ASSERT(ival1 && ival2);
|
|
o1 = ival1->stack_spill_pos;
|
|
o2 = ival2->stack_spill_pos;
|
|
IR_ASSERT(o1 != -1 && o2 != -1);
|
|
return o1 == o2;
|
|
}
|
|
|
|
static void ir_emit_mov(ir_ctx *ctx, ir_type type, ir_reg dst, ir_reg src)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (ir_type_size[type] == 8) {
|
|
| mov Rx(dst), Rx(src)
|
|
} else {
|
|
| mov Rw(dst), Rw(src)
|
|
}
|
|
}
|
|
|
|
static void ir_emit_fp_mov(ir_ctx *ctx, ir_type type, ir_reg dst, ir_reg src)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (ir_type_size[type] == 8) {
|
|
| fmov Rd(dst-IR_REG_FP_FIRST), Rd(src-IR_REG_FP_FIRST)
|
|
} else {
|
|
| fmov Rs(dst-IR_REG_FP_FIRST), Rs(src-IR_REG_FP_FIRST)
|
|
}
|
|
}
|
|
|
|
static void ir_emit_prologue(ir_ctx *ctx)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (ctx->flags & IR_USE_FRAME_POINTER) {
|
|
//??? | push Ra(IR_REG_RBP)
|
|
//??? | mov Ra(IR_REG_RBP), Ra(IR_REG_RSP)
|
|
}
|
|
if (data->ra_data.stack_frame_size + data->call_stack_size) {
|
|
//??? | sub Ra(IR_REG_RSP), (data->ra_data.stack_frame_size + data->call_stack_size)
|
|
}
|
|
if (data->used_preserved_regs) {
|
|
int offset;
|
|
uint32_t i;
|
|
|
|
if (ctx->flags & IR_USE_FRAME_POINTER) {
|
|
offset = 0;
|
|
} else {
|
|
offset = data->ra_data.stack_frame_size + data->call_stack_size;
|
|
}
|
|
for (i = 0; i < IR_REG_NUM; i++) {
|
|
if (IR_REGSET_IN(data->used_preserved_regs, i)) {
|
|
if (i < IR_REG_FP_FIRST) {
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
offset -= sizeof(void*);
|
|
//??? | mov aword [Ra(fp)+offset], Ra(i)
|
|
} else {
|
|
IR_ASSERT(0 && "NIY FP register saing");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_epilogue(ir_ctx *ctx)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (data->used_preserved_regs) {
|
|
int offset;
|
|
uint32_t i;
|
|
|
|
if (ctx->flags & IR_USE_FRAME_POINTER) {
|
|
offset = 0;
|
|
} else {
|
|
offset = data->ra_data.stack_frame_size + data->call_stack_size;
|
|
}
|
|
for (i = 0; i < IR_REG_NUM; i++) {
|
|
if (IR_REGSET_IN(data->used_preserved_regs, i)) {
|
|
if (i < IR_REG_FP_FIRST) {
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
offset -= sizeof(void*);
|
|
//??? | mov Ra(i), aword [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(0 && "NIY FP register saing");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctx->flags & IR_USE_FRAME_POINTER) {
|
|
//??? | mov Ra(IR_REG_RSP), Ra(IR_REG_RBP)
|
|
//??? | pop Ra(IR_REG_RBP)
|
|
} else if (data->ra_data.stack_frame_size + data->call_stack_size) {
|
|
//??? | add Ra(IR_REG_RSP), (data->ra_data.stack_frame_size + data->call_stack_size)
|
|
}
|
|
}
|
|
|
|
static void ir_emit_binop_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = insn->type;
|
|
ir_ref op1 = insn->op1;
|
|
ir_ref op2 = insn->op2;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
ir_reg tmp_reg;
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE);
|
|
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if ((op2_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op2)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
if (op1 != op2) {
|
|
ir_emit_load(ctx, type, op2_reg, op2);
|
|
}
|
|
}
|
|
switch (insn->op) {
|
|
case IR_ADD:
|
|
| ASM_REG_REG_REG_OP add, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_ADD_OV:
|
|
| ASM_REG_REG_REG_OP adds, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_SUB:
|
|
| ASM_REG_REG_REG_OP sub, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_SUB_OV:
|
|
| ASM_REG_REG_REG_OP subs, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_MUL:
|
|
| ASM_REG_REG_REG_OP mul, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
//??? case IR_MUL_OV:
|
|
//??? | ASM_REG_REG_IMUL type, def_reg, op2_reg
|
|
//??? break;
|
|
case IR_DIV:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| ASM_REG_REG_REG_OP sdiv, type, def_reg, op1_reg, op2_reg
|
|
} else {
|
|
| ASM_REG_REG_REG_OP udiv, type, def_reg, op1_reg, op2_reg
|
|
}
|
|
break;
|
|
case IR_MOD:
|
|
tmp_reg = ctx->regs[def][3];
|
|
IR_ASSERT(tmp_reg != IR_REG_NONE);
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| ASM_REG_REG_REG_OP sdiv, type, tmp_reg, op1_reg, op2_reg
|
|
| ASM_REG_REG_REG_REG_OP msub, type, def_reg, tmp_reg, op2_reg, op1_reg
|
|
} else {
|
|
| ASM_REG_REG_REG_OP udiv, type, tmp_reg, op1_reg, op2_reg
|
|
| ASM_REG_REG_REG_REG_OP msub, type, def_reg, tmp_reg, op2_reg, op1_reg
|
|
}
|
|
break;
|
|
case IR_OR:
|
|
| ASM_REG_REG_REG_OP orr, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_AND:
|
|
| ASM_REG_REG_REG_OP and, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_XOR:
|
|
| ASM_REG_REG_REG_OP eor, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
default:
|
|
IR_ASSERT(0 && "NIY binary op");
|
|
break;
|
|
}
|
|
} else {
|
|
IR_ASSERT(IR_IS_CONST_REF(op2));
|
|
int32_t val = ctx->ir_base[op2].val.i32;
|
|
switch (insn->op) {
|
|
case IR_ADD:
|
|
| ASM_REG_REG_IMM_OP add, type, def_reg, op1_reg, val
|
|
break;
|
|
case IR_ADD_OV:
|
|
| ASM_REG_REG_IMM_OP adds, type, def_reg, op1_reg, val
|
|
break;
|
|
case IR_SUB:
|
|
| ASM_REG_REG_IMM_OP sub, type, def_reg, op1_reg, val
|
|
break;
|
|
case IR_SUB_OV:
|
|
| ASM_REG_REG_IMM_OP subs, type, def_reg, op1_reg, val
|
|
break;
|
|
case IR_OR:
|
|
| ASM_REG_REG_IMM_OP orr, type, def_reg, op1_reg, val
|
|
break;
|
|
case IR_AND:
|
|
| ASM_REG_REG_IMM_OP and, type, def_reg, op1_reg, val
|
|
break;
|
|
case IR_XOR:
|
|
| ASM_REG_REG_IMM_OP eor, type, def_reg, op1_reg, val
|
|
break;
|
|
default:
|
|
IR_ASSERT(0 && "NIY binary op");
|
|
break;
|
|
}
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_min_max_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = insn->type;
|
|
ir_ref op1 = insn->op1;
|
|
ir_ref op2 = insn->op2;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE && op2_reg != IR_REG_NONE);
|
|
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if ((op2_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op2)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, op2);
|
|
}
|
|
|
|
if (op1 == op2) {
|
|
return;
|
|
}
|
|
|
|
if (ir_type_size[type] == 8) {
|
|
| cmp Rx(op1_reg), Rx(op2_reg)
|
|
if (insn->op == IR_MIN) {
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| csel Rx(def_reg), Rx(op1_reg), Rx(op2_reg), le
|
|
} else {
|
|
| csel Rx(def_reg), Rx(op1_reg), Rx(op2_reg), ls
|
|
}
|
|
} else {
|
|
IR_ASSERT(insn->op == IR_MAX);
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| csel Rx(def_reg), Rx(op1_reg), Rx(op2_reg), ge
|
|
} else {
|
|
| csel Rx(def_reg), Rx(op1_reg), Rx(op2_reg), hs
|
|
}
|
|
}
|
|
} else {
|
|
| cmp Rw(op1_reg), Rw(op2_reg)
|
|
if (insn->op == IR_MIN) {
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| csel Rw(def_reg), Rw(op1_reg), Rw(op2_reg), le
|
|
} else {
|
|
| csel Rw(def_reg), Rw(op1_reg), Rw(op2_reg), ls
|
|
}
|
|
} else {
|
|
IR_ASSERT(insn->op == IR_MAX);
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| csel Rw(def_reg), Rw(op1_reg), Rw(op2_reg), ge
|
|
} else {
|
|
| csel Rw(def_reg), Rw(op1_reg), Rw(op2_reg), hs
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_overflow(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_type type = ctx->ir_base[insn->op1].type;
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
IR_ASSERT(IR_IS_TYPE_INT(type));
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
| cset Rw(def_reg), vs
|
|
} else {
|
|
| cset Rw(def_reg), cs
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_overflow_and_branch(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_insn *overflow_insn = &ctx->ir_base[insn->op2];
|
|
ir_type type = ctx->ir_base[overflow_insn->op1].type;
|
|
ir_block *bb;
|
|
uint32_t n, *p, use_block;
|
|
ir_insn *use_insn;
|
|
int true_block = 0, false_block = 0, next_block;
|
|
bool reverse = 0;
|
|
|
|
bb = &ctx->cfg_blocks[b];
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_IF_TRUE) {
|
|
true_block = ir_skip_empty_blocks(ctx, use_block);
|
|
} else if (use_insn->op == IR_IF_FALSE) {
|
|
false_block = ir_skip_empty_blocks(ctx, use_block);
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
IR_ASSERT(true_block && false_block);
|
|
|
|
next_block = b == ctx->cfg_blocks_count ? 0 : ir_skip_empty_blocks(ctx, b + 1);
|
|
if (true_block == next_block) {
|
|
reverse = 1;
|
|
true_block = false_block;
|
|
false_block = 0;
|
|
} else if (false_block == next_block) {
|
|
false_block = 0;
|
|
}
|
|
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
if (reverse) {
|
|
| bvc =>true_block
|
|
} else {
|
|
| bvs =>true_block
|
|
}
|
|
} else {
|
|
if (reverse) {
|
|
| bcc =>true_block
|
|
} else {
|
|
| bcs =>true_block
|
|
}
|
|
}
|
|
if (false_block) {
|
|
| b =>false_block
|
|
}
|
|
}
|
|
|
|
static void ir_emit_mul_div_mod_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = insn->type;
|
|
ir_ref op1 = insn->op1;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE);
|
|
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if (insn->op == IR_MUL) {
|
|
uint32_t shift = IR_LOG2(ctx->ir_base[insn->op2].val.u64);
|
|
if (shift == 1) {
|
|
| ASM_REG_REG_REG_OP add, insn->type, def_reg, op1_reg, op1_reg
|
|
} else {
|
|
| ASM_REG_REG_IMM_OP lsl, insn->type, def_reg, op1_reg, shift
|
|
}
|
|
} else if (insn->op == IR_DIV) {
|
|
uint32_t shift = IR_LOG2(ctx->ir_base[insn->op2].val.u64);
|
|
| ASM_REG_REG_IMM_OP lsr, insn->type, def_reg, op1_reg, shift
|
|
} else if (insn->op == IR_MOD) {
|
|
uint64_t mask = ctx->ir_base[insn->op2].val.u64 - 1;
|
|
| ASM_REG_REG_IMM_OP and, insn->type, def_reg, op1_reg, mask
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_shift(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = insn->type;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE && op2_reg != IR_REG_NONE);
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(insn->op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, insn->op1);
|
|
}
|
|
if (op2_reg & IR_REG_SPILL_LOAD) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
switch (insn->op) {
|
|
case IR_SHL:
|
|
| ASM_REG_REG_REG_OP lsl, insn->type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_SHR:
|
|
| ASM_REG_REG_REG_OP lsr, insn->type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_SAR:
|
|
| ASM_REG_REG_REG_OP asr, insn->type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_ROL:
|
|
//??? | ASM_REG_REG_REG_OP rol, insn->type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_ROR:
|
|
| ASM_REG_REG_REG_OP ror, insn->type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
default:
|
|
IR_ASSERT(0);
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_shift_const(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
uint32_t shift = ctx->ir_base[insn->op2].val.u64;
|
|
ir_type type = insn->type;
|
|
ir_ref op1 = insn->op1;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE);
|
|
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
switch (insn->op) {
|
|
case IR_SHL:
|
|
| ASM_REG_REG_IMM_OP lsl, insn->type, def_reg, op1_reg, shift
|
|
break;
|
|
case IR_SHR:
|
|
| ASM_REG_REG_IMM_OP lsr, insn->type, def_reg, op1_reg, shift
|
|
break;
|
|
case IR_SAR:
|
|
| ASM_REG_REG_IMM_OP asr, insn->type, def_reg, op1_reg, shift
|
|
break;
|
|
case IR_ROL:
|
|
//??? | ASM_REG_IMM_OP rol, insn->type, def_reg, op1_reg, shift
|
|
break;
|
|
case IR_ROR:
|
|
| ASM_REG_REG_IMM_OP ror, insn->type, def_reg, op1_reg, shift
|
|
break;
|
|
default:
|
|
IR_ASSERT(0);
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_op_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = insn->type;
|
|
ir_ref op1 = insn->op1;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE);
|
|
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if (insn->op == IR_NOT) {
|
|
if (insn->type == IR_BOOL) {
|
|
| ASM_REG_IMM_OP cmp, type, op1, 0
|
|
| cset Rw(def_reg), eq
|
|
} else {
|
|
| ASM_REG_REG_OP mvn, insn->type, def_reg, op1_reg
|
|
}
|
|
} else if (insn->op == IR_NEG) {
|
|
| ASM_REG_REG_OP neg, insn->type, def_reg, op1_reg
|
|
} else if (insn->op == IR_ABS) {
|
|
if (ir_type_size[type] == 8) {
|
|
| cmp Rx(op1_reg), #0
|
|
| cneg Rx(def_reg), Rx(op1_reg), lt
|
|
} else {
|
|
| cmp Rw(op1_reg), #0
|
|
| cneg Rw(def_reg), Rw(op1_reg), lt
|
|
}
|
|
} else if (insn->op == IR_BSWAP) {
|
|
| ASM_REG_REG_OP rev, insn->type, def_reg, op1_reg
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_rodata(ir_ctx *ctx)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
|.rodata
|
|
if (!data->rodata_label) {
|
|
int label = data->rodata_label = ctx->cfg_blocks_count + ctx->consts_count + 2;
|
|
|=>label:
|
|
}
|
|
}
|
|
|
|
static void ir_emit_op_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = insn->type;
|
|
ir_ref op1 = insn->op1;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE);
|
|
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if (insn->op == IR_NEG) {
|
|
if (insn->type == IR_DOUBLE) {
|
|
| fneg Rd(def_reg), Rd(op1_reg)
|
|
} else {
|
|
IR_ASSERT(insn->type == IR_FLOAT);
|
|
| fneg Rs(def_reg), Rs(op1_reg)
|
|
}
|
|
} else if (insn->op == IR_ABS) {
|
|
if (insn->type == IR_DOUBLE) {
|
|
| fabs Rd(def_reg), Rd(op1_reg)
|
|
} else {
|
|
IR_ASSERT(insn->type == IR_FLOAT);
|
|
| fabs Rs(def_reg), Rs(op1_reg)
|
|
}
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_binop_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = insn->type;
|
|
ir_ref op1 = insn->op1;
|
|
ir_ref op2 = insn->op2;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE && op2_reg != IR_REG_NONE);
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if ((op2_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op2)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
if (op1 != op2) {
|
|
ir_emit_load(ctx, type, op2_reg, op2);
|
|
}
|
|
}
|
|
switch (insn->op) {
|
|
case IR_ADD:
|
|
| ASM_FP_REG_REG_REG_OP fadd, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_SUB:
|
|
| ASM_FP_REG_REG_REG_OP fsub, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_MUL:
|
|
| ASM_FP_REG_REG_REG_OP fmul, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_DIV:
|
|
| ASM_FP_REG_REG_REG_OP fdiv, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_MIN:
|
|
| ASM_FP_REG_REG_REG_OP fmin, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
case IR_MAX:
|
|
| ASM_FP_REG_REG_REG_OP fmax, type, def_reg, op1_reg, op2_reg
|
|
break;
|
|
default:
|
|
IR_ASSERT(0 && "NIY binary op");
|
|
break;
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_cmp_int_common(ir_ctx *ctx, ir_type type, ir_reg op1_reg, ir_ref op1, ir_reg op2_reg, ir_ref op2)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
IR_ASSERT(op1_reg != IR_REG_NONE);
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (ir_type_size[type] == 8) {
|
|
| cmp Rx(op1_reg), Rx(op2_reg)
|
|
} else {
|
|
| cmp Rw(op1_reg), Rw(op2_reg)
|
|
}
|
|
} else {
|
|
IR_ASSERT(IR_IS_CONST_REF(op2));
|
|
int32_t val = ctx->ir_base[op2].val.i32;
|
|
|
|
if (ir_type_size[type] == 8) {
|
|
| cmp Rx(op1_reg), #val
|
|
} else {
|
|
| cmp Rw(op1_reg), #val
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_cmp_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = ctx->ir_base[insn->op1].type;
|
|
ir_op op = insn->op;
|
|
ir_ref op1 = insn->op1;
|
|
ir_ref op2 = insn->op2;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE);
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (op2_reg & IR_REG_SPILL_LOAD) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
if (op1 != op2) {
|
|
ir_emit_load(ctx, type, op2_reg, op2);
|
|
}
|
|
}
|
|
if (IR_IS_CONST_REF(op2)) {
|
|
ir_emit_load(ctx, type, op2_reg, op2);
|
|
}
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op2) && ctx->ir_base[insn->op2].val.u64 == 0) {
|
|
if (op == IR_ULT) {
|
|
/* always false */
|
|
ir_emit_load_imm_int(ctx, IR_BOOL, def_reg, 0);
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
return;
|
|
} else if (op == IR_UGE) {
|
|
/* always true */
|
|
ir_emit_load_imm_int(ctx, IR_BOOL, def_reg, 1);
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
return;
|
|
} else if (op == IR_ULE) {
|
|
op = IR_EQ;
|
|
} else if (op == IR_UGT) {
|
|
op = IR_NE;
|
|
}
|
|
}
|
|
ir_emit_cmp_int_common(ctx, type, op1_reg, op1, op2_reg, op2);
|
|
switch (op) {
|
|
case IR_EQ:
|
|
| cset Rw(def_reg), eq
|
|
break;
|
|
case IR_NE:
|
|
| cset Rw(def_reg), ne
|
|
break;
|
|
case IR_LT:
|
|
| cset Rw(def_reg), lt
|
|
break;
|
|
case IR_GE:
|
|
| cset Rw(def_reg), ge
|
|
break;
|
|
case IR_LE:
|
|
| cset Rw(def_reg), le
|
|
break;
|
|
case IR_GT:
|
|
| cset Rw(def_reg), gt
|
|
break;
|
|
case IR_ULT:
|
|
| cset Rw(def_reg), lo
|
|
break;
|
|
case IR_UGE:
|
|
| cset Rw(def_reg), hs
|
|
break;
|
|
case IR_ULE:
|
|
| cset Rw(def_reg), ls
|
|
break;
|
|
case IR_UGT:
|
|
| cset Rw(def_reg), hi
|
|
break;
|
|
default:
|
|
IR_ASSERT(0 && "NIY binary op");
|
|
break;
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static ir_op ir_emit_cmp_fp_common(ir_ctx *ctx, ir_ref cmp_ref, ir_insn *cmp_insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type = ctx->ir_base[cmp_insn->op1].type;
|
|
ir_op op = cmp_insn->op;
|
|
ir_ref op1, op2;
|
|
ir_reg op1_reg, op2_reg;
|
|
|
|
if (op == IR_LT || op == IR_LE) {
|
|
/* swap operands to avoid P flag check */
|
|
op ^= 3;
|
|
op1 = cmp_insn->op2;
|
|
op2 = cmp_insn->op1;
|
|
op1_reg = ctx->regs[cmp_ref][2];
|
|
op2_reg = ctx->regs[cmp_ref][1];
|
|
} else {
|
|
op1 = cmp_insn->op1;
|
|
op2 = cmp_insn->op2;
|
|
op1_reg = ctx->regs[cmp_ref][1];
|
|
op2_reg = ctx->regs[cmp_ref][2];
|
|
}
|
|
|
|
IR_ASSERT(op1_reg != IR_REG_NONE && op2_reg != IR_REG_NONE);
|
|
if ((op1_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op1)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if ((op2_reg & IR_REG_SPILL_LOAD) || IR_IS_CONST_REF(op2)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
if (op1 != op2) {
|
|
ir_emit_load(ctx, type, op2_reg, op2);
|
|
}
|
|
}
|
|
if (type == IR_DOUBLE) {
|
|
| fcmp Rd(op1_reg-IR_REG_FP_FIRST), Rd(op2_reg-IR_REG_FP_FIRST)
|
|
} else {
|
|
IR_ASSERT(type == IR_FLOAT);
|
|
| fcmp Rs(op1_reg-IR_REG_FP_FIRST), Rs(op2_reg-IR_REG_FP_FIRST)
|
|
}
|
|
return op;
|
|
}
|
|
|
|
static void ir_emit_cmp_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_op op = ir_emit_cmp_fp_common(ctx, def, insn);
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg tmp_reg = ctx->regs[def][3];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
switch (op) {
|
|
case IR_EQ:
|
|
//??? | setnp Rb(def_reg)
|
|
//??? | mov Rd(tmp_reg), 0
|
|
//??? | cmovne Rd(def_reg), Rd(tmp_reg)
|
|
break;
|
|
case IR_NE:
|
|
//??? | setp Rb(def_reg)
|
|
//??? | mov Rd(tmp_reg), 1
|
|
//??? | cmovne Rd(def_reg), Rd(tmp_reg)
|
|
break;
|
|
case IR_LT:
|
|
//??? | setnp Rb(def_reg)
|
|
//??? | mov Rd(tmp_reg), 0
|
|
//??? | cmovae Rd(def_reg), Rd(tmp_reg)
|
|
break;
|
|
case IR_GE:
|
|
//??? | setae Rb(def_reg)
|
|
break;
|
|
case IR_LE:
|
|
//??? | setnp Rb(def_reg)
|
|
//??? | mov Rd(tmp_reg), 0
|
|
//??? | cmova Rd(def_reg), Rd(tmp_reg)
|
|
break;
|
|
case IR_GT:
|
|
//??? | seta Rb(def_reg)
|
|
break;
|
|
default:
|
|
IR_ASSERT(0 && "NIY binary op");
|
|
break;
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_jmp_true(ir_ctx *ctx, int b, ir_ref def)
|
|
{
|
|
ir_block *bb;
|
|
ir_insn *use_insn;
|
|
uint32_t n, *p, use_block;
|
|
int true_block = 0, false_block = 0;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
bb = &ctx->cfg_blocks[b];
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_IF_TRUE) {
|
|
true_block = ir_skip_empty_blocks(ctx, use_block);
|
|
} else if (use_insn->op == IR_IF_FALSE) {
|
|
false_block = ir_skip_empty_blocks(ctx, use_block);
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
IR_ASSERT(true_block && false_block);
|
|
|
|
if (b == ctx->cfg_blocks_count || true_block != ir_skip_empty_blocks(ctx, b + 1)) {
|
|
| b =>true_block
|
|
}
|
|
}
|
|
|
|
static void ir_emit_jmp_false(ir_ctx *ctx, int b, ir_ref def)
|
|
{
|
|
ir_block *bb;
|
|
ir_insn *use_insn;
|
|
uint32_t n, *p, use_block;
|
|
int true_block = 0, false_block = 0;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
bb = &ctx->cfg_blocks[b];
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_IF_TRUE) {
|
|
true_block = ir_skip_empty_blocks(ctx, use_block);
|
|
} else if (use_insn->op == IR_IF_FALSE) {
|
|
false_block = ir_skip_empty_blocks(ctx, use_block);
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
IR_ASSERT(true_block && false_block);
|
|
|
|
if (b == ctx->cfg_blocks_count || true_block != ir_skip_empty_blocks(ctx, b + 1)) {
|
|
| b =>false_block
|
|
}
|
|
}
|
|
|
|
static void ir_emit_jcc(ir_ctx *ctx, uint8_t op, int b, ir_ref def, ir_insn *insn, bool int_cmp)
|
|
{
|
|
ir_block *bb;
|
|
ir_insn *use_insn;
|
|
uint32_t n, *p, use_block;
|
|
int true_block = 0, false_block = 0, next_block;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
bb = &ctx->cfg_blocks[b];
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_IF_TRUE) {
|
|
true_block = ir_skip_empty_blocks(ctx, use_block);
|
|
} else if (use_insn->op == IR_IF_FALSE) {
|
|
false_block = ir_skip_empty_blocks(ctx, use_block);
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
IR_ASSERT(true_block && false_block);
|
|
|
|
next_block = b == ctx->cfg_blocks_count ? 0 : ir_skip_empty_blocks(ctx, b + 1);
|
|
if (true_block == next_block) {
|
|
if (int_cmp || (op != IR_GT && op != IR_GE)) {
|
|
/* swap to avoid unconditional JMP if this doesn't introduce additional JP instruction */
|
|
if (op < IR_LT) {
|
|
op ^= 1; // reverse
|
|
} else {
|
|
op ^= 3; // reverse
|
|
}
|
|
true_block = false_block;
|
|
false_block = 0;
|
|
}
|
|
} else if (false_block == next_block) {
|
|
false_block = 0;
|
|
}
|
|
|
|
if (int_cmp) {
|
|
switch (op) {
|
|
case IR_EQ:
|
|
| beq =>true_block
|
|
break;
|
|
case IR_NE:
|
|
| bne =>true_block
|
|
break;
|
|
case IR_LT:
|
|
| blt =>true_block
|
|
break;
|
|
case IR_GE:
|
|
| bge =>true_block
|
|
break;
|
|
case IR_LE:
|
|
| ble =>true_block
|
|
break;
|
|
case IR_GT:
|
|
| bgt =>true_block
|
|
break;
|
|
case IR_ULT:
|
|
| blo =>true_block
|
|
break;
|
|
case IR_UGE:
|
|
| bhs =>true_block
|
|
break;
|
|
case IR_ULE:
|
|
| bls =>true_block
|
|
break;
|
|
case IR_UGT:
|
|
| bhi =>true_block
|
|
break;
|
|
default:
|
|
IR_ASSERT(0 && "NIY binary op");
|
|
break;
|
|
}
|
|
} else {
|
|
switch (op) {
|
|
case IR_EQ:
|
|
if (!false_block) {
|
|
| bvs >1
|
|
| beq =>true_block
|
|
|1:
|
|
} else {
|
|
| bvs =>false_block
|
|
| beq =>true_block
|
|
}
|
|
break;
|
|
case IR_NE:
|
|
| bne =>true_block
|
|
| bvs =>true_block
|
|
break;
|
|
case IR_LT:
|
|
if (!false_block) {
|
|
| bvs >1
|
|
| blo =>true_block
|
|
|1:
|
|
} else {
|
|
| bvs =>false_block
|
|
| blo =>true_block
|
|
}
|
|
break;
|
|
case IR_GE:
|
|
| bhs =>true_block
|
|
break;
|
|
case IR_LE:
|
|
if (!false_block) {
|
|
| bvs >1
|
|
| bls =>true_block
|
|
|1:
|
|
} else {
|
|
| bvs =>false_block
|
|
| bls =>true_block
|
|
}
|
|
break;
|
|
case IR_GT:
|
|
| bhi =>true_block
|
|
break;
|
|
// case IR_ULT: fprintf(stderr, "\tjb .LL%d\n", true_block); break;
|
|
// case IR_UGE: fprintf(stderr, "\tjae .LL%d\n", true_block); break;
|
|
// case IR_ULE: fprintf(stderr, "\tjbe .LL%d\n", true_block); break;
|
|
// case IR_UGT: fprintf(stderr, "\tja .LL%d\n", true_block); break;
|
|
default:
|
|
IR_ASSERT(0 && "NIY binary op");
|
|
break;
|
|
}
|
|
}
|
|
if (false_block) {
|
|
| b =>false_block
|
|
}
|
|
}
|
|
|
|
static void ir_emit_cmp_and_branch_int(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_insn *cmp_insn = &ctx->ir_base[insn->op2];
|
|
ir_op op = cmp_insn->op;
|
|
ir_type type = ctx->ir_base[cmp_insn->op1].type;
|
|
ir_ref op1 = cmp_insn->op1;
|
|
ir_ref op2 = cmp_insn->op2;
|
|
ir_reg op1_reg = ctx->regs[insn->op2][1];
|
|
ir_reg op2_reg = ctx->regs[insn->op2][2];
|
|
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if (op1_reg != IR_REG_NONE && IR_IS_CONST_REF(op1)) {
|
|
ir_emit_load(ctx, type, op1_reg, op1);
|
|
}
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (op2_reg & IR_REG_SPILL_LOAD) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
if (op1 != op2) {
|
|
ir_emit_load(ctx, type, op2_reg, op2);
|
|
}
|
|
}
|
|
if (IR_IS_CONST_REF(op2)) {
|
|
ir_emit_load(ctx, type, op2_reg, op2);
|
|
}
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op2) && ctx->ir_base[insn->op2].val.u64 == 0) {
|
|
if (op == IR_ULT) {
|
|
/* always false */
|
|
ir_emit_jmp_false(ctx, b, def);
|
|
return;
|
|
} else if (op == IR_UGE) {
|
|
/* always true */
|
|
ir_emit_jmp_true(ctx, b, def);
|
|
return;
|
|
} else if (op == IR_ULE) {
|
|
op = IR_EQ;
|
|
} else if (op == IR_UGT) {
|
|
op = IR_NE;
|
|
}
|
|
}
|
|
ir_emit_cmp_int_common(ctx, type, op1_reg, op1, op2_reg, op2);
|
|
ir_emit_jcc(ctx, op, b, def, insn, 1);
|
|
}
|
|
|
|
static void ir_emit_cmp_and_branch_fp(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_op op = ir_emit_cmp_fp_common(ctx, insn->op2, &ctx->ir_base[insn->op2]);
|
|
ir_emit_jcc(ctx, op, b, def, insn, 0);
|
|
}
|
|
|
|
static void ir_emit_if_int(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type type = ctx->ir_base[insn->op2].type;
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (op2_reg & IR_REG_SPILL_LOAD) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
//??? | ASM_REG_REG_OP test, type, op2_reg, op2_reg
|
|
} else if (IR_IS_CONST_REF(insn->op2)) {
|
|
IR_ASSERT(0);
|
|
} else {
|
|
//??? | ASM_MREF_IMM_OP cmp, type, insn->op2, 0
|
|
}
|
|
ir_emit_jcc(ctx, IR_NE, b, def, insn, 1);
|
|
}
|
|
|
|
static void ir_emit_return_void(ir_ctx *ctx)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
|
|
ir_emit_epilogue(ctx);
|
|
| ret
|
|
}
|
|
|
|
static void ir_emit_return_int(ir_ctx *ctx, ir_reg ref, ir_insn *insn)
|
|
{
|
|
ir_reg op2_reg = ctx->regs[ref][2];
|
|
|
|
if (op2_reg != IR_REG_INT_RET1) {
|
|
ir_type type = ctx->ir_base[insn->op2].type;
|
|
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (!(op2_reg & IR_REG_SPILL_LOAD)) {
|
|
ir_emit_mov(ctx, type, IR_REG_INT_RET1, op2_reg);
|
|
} else {
|
|
ir_emit_load(ctx, type, IR_REG_INT_RET1, insn->op2);
|
|
}
|
|
} else {
|
|
ir_emit_load(ctx, type, IR_REG_INT_RET1, insn->op2);
|
|
}
|
|
}
|
|
ir_emit_return_void(ctx);
|
|
}
|
|
|
|
static void ir_emit_return_fp(ir_ctx *ctx, ir_reg ref, ir_insn *insn)
|
|
{
|
|
ir_reg op2_reg = ctx->regs[ref][2];
|
|
ir_type type = ctx->ir_base[insn->op2].type;
|
|
|
|
if (op2_reg != IR_REG_FP_RET1) {
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (!(op2_reg & IR_REG_SPILL_LOAD)) {
|
|
ir_emit_fp_mov(ctx, type, IR_REG_FP_RET1, op2_reg);
|
|
} else {
|
|
ir_emit_load(ctx, type, IR_REG_FP_RET1, insn->op2);
|
|
}
|
|
} else {
|
|
ir_emit_load(ctx, type, IR_REG_FP_RET1, insn->op2);
|
|
}
|
|
}
|
|
ir_emit_return_void(ctx);
|
|
}
|
|
|
|
static void ir_emit_sext(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type dst_type = insn->type;
|
|
ir_type src_type = ctx->ir_base[insn->op1].type;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(IR_IS_TYPE_INT(src_type));
|
|
IR_ASSERT(IR_IS_TYPE_INT(dst_type));
|
|
IR_ASSERT(ir_type_size[dst_type] > ir_type_size[src_type]);
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, src_type, op1_reg, insn->op1);
|
|
}
|
|
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (ir_type_size[src_type] == 1) {
|
|
if (ir_type_size[dst_type] == 2) {
|
|
//??? | movsx Rw(def_reg), Rb(op1_reg)
|
|
} else if (ir_type_size[dst_type] == 4) {
|
|
//??? | movsx Rd(def_reg), Rb(op1_reg)
|
|
} else {
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movsx Rq(def_reg), Rb(op1_reg)
|
|
}
|
|
} else if (ir_type_size[src_type] == 2) {
|
|
if (ir_type_size[dst_type] == 4) {
|
|
//??? | movsx Rd(def_reg), Rw(op1_reg)
|
|
} else {
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movsx Rq(def_reg), Rw(op1_reg)
|
|
}
|
|
} else {
|
|
IR_ASSERT(ir_type_size[src_type] == 4);
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movsxd Rq(def_reg), Rd(op1_reg)
|
|
}
|
|
} else if (IR_IS_CONST_REF(insn->op1)) {
|
|
IR_ASSERT(0);
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op1);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
if (ir_type_size[src_type] == 1) {
|
|
if (ir_type_size[dst_type] == 2) {
|
|
//??? | movsx Rw(def_reg), byte [Ra(fp)+offset]
|
|
} else if (ir_type_size[dst_type] == 4) {
|
|
//??? | movsx Rd(def_reg), byte [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movsx Rq(def_reg), byte [Ra(fp)+offset]
|
|
}
|
|
} else if (ir_type_size[src_type] == 2) {
|
|
if (ir_type_size[dst_type] == 4) {
|
|
//??? | movsx Rd(def_reg), word [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movsx Rq(def_reg), word [Ra(fp)+offset]
|
|
}
|
|
} else {
|
|
IR_ASSERT(ir_type_size[src_type] == 4);
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movsxd Rq(def_reg), dword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, dst_type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_zext(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type dst_type = insn->type;
|
|
ir_type src_type = ctx->ir_base[insn->op1].type;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(IR_IS_TYPE_INT(src_type));
|
|
IR_ASSERT(IR_IS_TYPE_INT(dst_type));
|
|
IR_ASSERT(ir_type_size[dst_type] > ir_type_size[src_type]);
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, src_type, op1_reg, insn->op1);
|
|
}
|
|
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (ir_type_size[src_type] == 1) {
|
|
if (ir_type_size[dst_type] == 2) {
|
|
//??? | movzx Rw(def_reg), Rb(op1_reg)
|
|
} else if (ir_type_size[dst_type] == 4) {
|
|
//??? | movzx Rd(def_reg), Rb(op1_reg)
|
|
} else {
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movzx Rq(def_reg), Rb(op1_reg)
|
|
}
|
|
} else if (ir_type_size[src_type] == 2) {
|
|
if (ir_type_size[dst_type] == 4) {
|
|
//??? | movzx Rd(def_reg), Rw(op1_reg)
|
|
} else {
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movzx Rq(def_reg), Rw(op1_reg)
|
|
}
|
|
} else {
|
|
IR_ASSERT(ir_type_size[src_type] == 4);
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | mov Rd(def_reg), Rd(op1_reg)
|
|
}
|
|
} else if (IR_IS_CONST_REF(insn->op1)) {
|
|
IR_ASSERT(0);
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op1);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
if (ir_type_size[src_type] == 1) {
|
|
if (ir_type_size[dst_type] == 2) {
|
|
//??? | movzx Rw(def_reg), byte [Ra(fp)+offset]
|
|
} else if (ir_type_size[dst_type] == 4) {
|
|
//??? | movzx Rd(def_reg), byte [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movzx Rq(def_reg), byte [Ra(fp)+offset]
|
|
}
|
|
} else if (ir_type_size[src_type] == 2) {
|
|
if (ir_type_size[dst_type] == 4) {
|
|
//??? | movzx Rd(def_reg), word [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movzx Rq(def_reg), word [Ra(fp)+offset]
|
|
}
|
|
} else {
|
|
IR_ASSERT(ir_type_size[src_type] == 4);
|
|
IR_ASSERT(ir_type_size[dst_type] == 8);
|
|
//??? | mov Rd(def_reg), dword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, dst_type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_trunc(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type dst_type = insn->type;
|
|
ir_type src_type = ctx->ir_base[insn->op1].type;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(IR_IS_TYPE_INT(src_type));
|
|
IR_ASSERT(IR_IS_TYPE_INT(dst_type));
|
|
IR_ASSERT(ir_type_size[dst_type] < ir_type_size[src_type]);
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, src_type, op1_reg, insn->op1);
|
|
}
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (op1_reg != def_reg) {
|
|
ir_emit_mov(ctx, dst_type, def_reg, op1_reg);
|
|
}
|
|
} else {
|
|
ir_emit_load(ctx, dst_type, def_reg, insn->op1);
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, dst_type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_bitcast(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type dst_type = insn->type;
|
|
ir_type src_type = ctx->ir_base[insn->op1].type;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(ir_type_size[dst_type] == ir_type_size[src_type]);
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, src_type, op1_reg, insn->op1);
|
|
}
|
|
if (IR_IS_TYPE_INT(src_type) && IR_IS_TYPE_INT(dst_type)) {
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (op1_reg != def_reg) {
|
|
ir_emit_mov(ctx, dst_type, def_reg, op1_reg);
|
|
}
|
|
} else {
|
|
ir_emit_load(ctx, dst_type, def_reg, insn->op1);
|
|
}
|
|
} else if (IR_IS_TYPE_FP(src_type) && IR_IS_TYPE_FP(dst_type)) {
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (op1_reg != def_reg) {
|
|
ir_emit_fp_mov(ctx, dst_type, def_reg, op1_reg);
|
|
}
|
|
} else {
|
|
ir_emit_load(ctx, dst_type, def_reg, insn->op1);
|
|
}
|
|
} else if (IR_IS_TYPE_FP(src_type)) {
|
|
IR_ASSERT(IR_IS_TYPE_INT(dst_type));
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (src_type == IR_DOUBLE) {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movd Rq(def_reg), xmm(op1_reg-IR_REG_FP_FIRST)
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | movd Rd(def_reg), xmm(op1_reg-IR_REG_FP_FIRST)
|
|
}
|
|
} else if (IR_IS_CONST_REF(insn->op1)) {
|
|
ir_insn *_insn = &ctx->ir_base[insn->op1];
|
|
if (src_type == IR_DOUBLE) {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | mov64 Rq(def_reg), _insn->val.i64
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | mov Rd(def_reg), _insn->val.i32
|
|
}
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op1);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
if (src_type == IR_DOUBLE) {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | mov Rq(def_reg), qword [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | mov Rd(def_reg), dword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
} else if (IR_IS_TYPE_FP(dst_type)) {
|
|
IR_ASSERT(IR_IS_TYPE_INT(src_type));
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (dst_type == IR_DOUBLE) {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | movd xmm(def_reg-IR_REG_FP_FIRST), Rq(op1_reg)
|
|
} else {
|
|
IR_ASSERT(dst_type == IR_FLOAT);
|
|
//??? | movd xmm(def_reg-IR_REG_FP_FIRST), Rd(op1_reg)
|
|
}
|
|
} else {
|
|
//??? | ASM_FP_REG_MREF_OP movss, movsd, vmovss, vmovsd, dst_type, def_reg, insn->op1
|
|
}
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, dst_type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_int2fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type dst_type = insn->type;
|
|
ir_type src_type = ctx->ir_base[insn->op1].type;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
bool src64 = 0;
|
|
|
|
IR_ASSERT(IR_IS_TYPE_INT(src_type));
|
|
IR_ASSERT(IR_IS_TYPE_FP(dst_type));
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, src_type, op1_reg, insn->op1);
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op1)) {
|
|
IR_ASSERT(op1_reg != IR_REG_NONE);
|
|
ir_emit_load(ctx, src_type, op1_reg, insn->op1);
|
|
}
|
|
if (IR_IS_TYPE_SIGNED(src_type) ? ir_type_size[src_type] == 8 : ir_type_size[src_type] >= 4) {
|
|
// TODO: we might need to perform sign/zero integer extension to 32/64 bit integer
|
|
src64 = 1;
|
|
}
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (!src64) {
|
|
if (dst_type == IR_DOUBLE) {
|
|
//??? | cvtsi2sd xmm(def_reg-IR_REG_FP_FIRST), Rd(op1_reg)
|
|
} else {
|
|
IR_ASSERT(dst_type == IR_FLOAT);
|
|
//??? | cvtsi2ss xmm(def_reg-IR_REG_FP_FIRST), Rd(op1_reg)
|
|
}
|
|
} else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
if (dst_type == IR_DOUBLE) {
|
|
//??? | cvtsi2sd xmm(def_reg-IR_REG_FP_FIRST), Rq(op1_reg)
|
|
} else {
|
|
IR_ASSERT(dst_type == IR_FLOAT);
|
|
//??? | cvtsi2ss xmm(def_reg-IR_REG_FP_FIRST), Rq(op1_reg)
|
|
}
|
|
}
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op1);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
if (!src64) {
|
|
if (dst_type == IR_DOUBLE) {
|
|
//??? | cvtsi2sd xmm(def_reg-IR_REG_FP_FIRST), dword [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(dst_type == IR_FLOAT);
|
|
//??? | cvtsi2ss xmm(def_reg-IR_REG_FP_FIRST), dword [Ra(fp)+offset]
|
|
}
|
|
} else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
if (dst_type == IR_DOUBLE) {
|
|
//??? | cvtsi2sd xmm(def_reg-IR_REG_FP_FIRST), qword [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(dst_type == IR_FLOAT);
|
|
//??? | cvtsi2ss xmm(def_reg-IR_REG_FP_FIRST), qword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, dst_type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_fp2int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type dst_type = insn->type;
|
|
ir_type src_type = ctx->ir_base[insn->op1].type;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
bool dst64 = 0;
|
|
|
|
IR_ASSERT(IR_IS_TYPE_FP(src_type));
|
|
IR_ASSERT(IR_IS_TYPE_INT(dst_type));
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, src_type, op1_reg, insn->op1);
|
|
}
|
|
if (IR_IS_TYPE_SIGNED(dst_type) ? ir_type_size[dst_type] == 8 : ir_type_size[dst_type] >= 4) {
|
|
// TODO: we might need to perform truncation from 32/64 bit integer
|
|
dst64 = 1;
|
|
}
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (!dst64) {
|
|
if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2si Rd(def_reg), xmm(op1_reg-IR_REG_FP_FIRST)
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2si Rd(def_reg), xmm(op1_reg-IR_REG_FP_FIRST)
|
|
}
|
|
} else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2si Rq(def_reg), xmm(op1_reg-IR_REG_FP_FIRST)
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2si Rq(def_reg), xmm(op1_reg-IR_REG_FP_FIRST)
|
|
}
|
|
}
|
|
} else if (IR_IS_CONST_REF(insn->op1)) {
|
|
ir_insn *_insn = &ctx->ir_base[insn->op1];
|
|
int label = ctx->cfg_blocks_count - insn->op1;
|
|
|
|
_insn->emit_const = 1;
|
|
if (!dst64) {
|
|
if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2si Rd(def_reg), qword [=>label]
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2si Rd(def_reg), dword [=>label]
|
|
}
|
|
} else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2si Rq(def_reg), qword [=>label]
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2si Rq(def_reg), dword [=>label]
|
|
}
|
|
}
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op1);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
if (!dst64) {
|
|
if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2si Rd(def_reg), qword [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2si Rd(def_reg), dword [Ra(fp)+offset]
|
|
}
|
|
} else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2si Rq(def_reg), qword [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2si Rq(def_reg), dword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, dst_type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_fp2fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type dst_type = insn->type;
|
|
ir_type src_type = ctx->ir_base[insn->op1].type;
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(IR_IS_TYPE_FP(src_type));
|
|
IR_ASSERT(IR_IS_TYPE_FP(dst_type));
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, src_type, op1_reg, insn->op1);
|
|
}
|
|
if (op1_reg != IR_REG_NONE) {
|
|
if (src_type == dst_type) {
|
|
if (op1_reg != def_reg) {
|
|
ir_emit_fp_mov(ctx, dst_type, def_reg, op1_reg);
|
|
}
|
|
} else if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2ss xmm(def_reg-IR_REG_FP_FIRST), xmm(op1_reg-IR_REG_FP_FIRST)
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2sd xmm(def_reg-IR_REG_FP_FIRST), xmm(op1_reg-IR_REG_FP_FIRST)
|
|
}
|
|
} else if (IR_IS_CONST_REF(insn->op1)) {
|
|
ir_insn *_insn = &ctx->ir_base[insn->op1];
|
|
int label = ctx->cfg_blocks_count - insn->op1;
|
|
|
|
_insn->emit_const = 1;
|
|
if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2ss xmm(def_reg-IR_REG_FP_FIRST), qword [=>label]
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2sd xmm(def_reg-IR_REG_FP_FIRST), dword [=>label]
|
|
}
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op1);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
if (src_type == IR_DOUBLE) {
|
|
//??? | cvtsd2ss xmm(def_reg-IR_REG_FP_FIRST), qword [Ra(fp)+offset]
|
|
} else {
|
|
IR_ASSERT(src_type == IR_FLOAT);
|
|
//??? | cvtss2sd xmm(def_reg-IR_REG_FP_FIRST), dword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, dst_type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_copy_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_ref type = insn->type;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE || op1_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, insn->op1);
|
|
}
|
|
if (def_reg == op1_reg) {
|
|
/* same reg */
|
|
} else if (def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE) {
|
|
ir_emit_mov(ctx, type, def_reg, op1_reg);
|
|
} else if (def_reg != IR_REG_NONE) {
|
|
ir_emit_load(ctx, type, def_reg, insn->op1);
|
|
} else if (op1_reg != IR_REG_NONE) {
|
|
ir_emit_store(ctx, type, def, op1_reg);
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
if (def_reg != IR_REG_NONE && (ctx->regs[def][0] & IR_REG_SPILL_STORE)) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_copy_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_type type = insn->type;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
ir_reg op1_reg = ctx->regs[def][1];
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE || op1_reg != IR_REG_NONE);
|
|
if (op1_reg != IR_REG_NONE && (op1_reg & IR_REG_SPILL_LOAD)) {
|
|
op1_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op1_reg, insn->op1);
|
|
}
|
|
if (def_reg == op1_reg) {
|
|
/* same reg */
|
|
} else if (def_reg != IR_REG_NONE && op1_reg != IR_REG_NONE) {
|
|
ir_emit_mov(ctx, type, def_reg, op1_reg);
|
|
} else if (def_reg != IR_REG_NONE) {
|
|
ir_emit_load(ctx, type, def_reg, insn->op1);
|
|
} else if (op1_reg != IR_REG_NONE) {
|
|
ir_emit_store(ctx, type, def, op1_reg);
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
if (def_reg != IR_REG_NONE && (ctx->regs[def][0] & IR_REG_SPILL_STORE)) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_vaddr(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_ref type = insn->type;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
int32_t offset;
|
|
ir_reg fp;
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
offset = ir_ref_spill_slot(ctx, insn->op1);
|
|
fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
//??? | lea Ra(def_reg), aword [Ra(fp)+offset]
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_vload_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_ref type = insn->type;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
|
|
if (def_reg == IR_REG_NONE && ir_is_same_mem(ctx, insn->op2, def)) {
|
|
return; // fake load
|
|
}
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
ir_emit_load(ctx, type, def_reg, insn->op2);
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_vload_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_ref type = insn->type;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
|
|
if (def_reg == IR_REG_NONE && ir_is_same_mem(ctx, insn->op2, def)) {
|
|
return; // fake load
|
|
}
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
|
|
ir_emit_load(ctx, type, def_reg, insn->op2);
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_vstore_int(ir_ctx *ctx, ir_ref ref, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_insn *val_insn = &ctx->ir_base[insn->op3];
|
|
ir_ref type = val_insn->type;
|
|
ir_reg op3_reg = ctx->regs[ref][3];
|
|
int32_t offset;
|
|
ir_reg fp;
|
|
|
|
if ((op3_reg == IR_REG_NONE || (op3_reg & IR_REG_SPILL_LOAD))
|
|
&& !IR_IS_CONST_REF(insn->op3) && ir_is_same_mem(ctx, insn->op3, insn->op2)) {
|
|
return; // fake store
|
|
}
|
|
//??? if (IR_IS_CONST_REF(insn->op3) && IR_IS_32BIT(type, val_insn->val)) {
|
|
offset = ir_ref_spill_slot(ctx, insn->op2);
|
|
fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
//??? | ASM_MEM_IMM_OP mov, type, [Ra(fp)+offset], val_insn->val.i32
|
|
//??? } else {
|
|
IR_ASSERT(op3_reg != IR_REG_NONE);
|
|
if (op3_reg != IR_REG_NONE && (op3_reg & IR_REG_SPILL_LOAD)) {
|
|
op3_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op3_reg, insn->op3);
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op3)) {
|
|
ir_emit_load(ctx, type, op3_reg, insn->op3);
|
|
}
|
|
ir_emit_store(ctx, type, insn->op2, op3_reg);
|
|
//??? }
|
|
}
|
|
|
|
static void ir_emit_vstore_fp(ir_ctx *ctx, ir_ref ref, ir_insn *insn)
|
|
{
|
|
ir_ref type = ctx->ir_base[insn->op3].type;
|
|
ir_reg op3_reg = ctx->regs[ref][3];
|
|
|
|
if ((op3_reg == IR_REG_NONE || (op3_reg & IR_REG_SPILL_LOAD))
|
|
&& !IR_IS_CONST_REF(insn->op3) && ir_is_same_mem(ctx, insn->op3, insn->op2)) {
|
|
return; // fake store
|
|
}
|
|
IR_ASSERT(op3_reg != IR_REG_NONE);
|
|
if (op3_reg != IR_REG_NONE && (op3_reg & IR_REG_SPILL_LOAD)) {
|
|
op3_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op3_reg, insn->op3);
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op3)) {
|
|
ir_emit_load(ctx, type, op3_reg, insn->op3);
|
|
}
|
|
ir_emit_store(ctx, type, insn->op2, op3_reg);
|
|
}
|
|
|
|
static void ir_emit_load_int(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_ref type = insn->type;
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op2_reg != IR_REG_NONE && (op2_reg & IR_REG_SPILL_LOAD)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
if (op2_reg == IR_REG_NONE) {
|
|
op2_reg = def_reg;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
ir_emit_load_mem_int(ctx, type, def_reg, op2_reg, 0);
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_load_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_ref type = insn->type;
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE && op2_reg != IR_REG_NONE);
|
|
if (op2_reg != IR_REG_NONE && (op2_reg & IR_REG_SPILL_LOAD)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
ir_emit_load_mem_fp(ctx, type, def_reg, op2_reg, 0);
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, type, def, def_reg);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_store_int(ir_ctx *ctx, ir_reg ref, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_insn *val_insn = &ctx->ir_base[insn->op3];
|
|
ir_ref type = val_insn->type;
|
|
ir_reg op2_reg = ctx->regs[ref][2];
|
|
ir_reg op3_reg = ctx->regs[ref][3];
|
|
|
|
IR_ASSERT(op2_reg != IR_REG_NONE);
|
|
if (op2_reg != IR_REG_NONE && (op2_reg & IR_REG_SPILL_LOAD)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
//??? if (IR_IS_CONST_REF(insn->op3) && IR_IS_32BIT(type, val_insn->val)) {
|
|
//??? | ASM_MEM_IMM_OP mov, type, [Ra(op2_reg)], val_insn->val.i32
|
|
//??? } else {
|
|
IR_ASSERT(op3_reg != IR_REG_NONE);
|
|
if (op3_reg != IR_REG_NONE && (op3_reg & IR_REG_SPILL_LOAD)) {
|
|
op3_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op3_reg, insn->op3);
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op3)) {
|
|
ir_emit_load(ctx, type, op3_reg, insn->op3);
|
|
}
|
|
ir_emit_store_mem_int(ctx, type, op2_reg, 0, op3_reg);
|
|
//??? }
|
|
}
|
|
|
|
static void ir_emit_store_fp(ir_ctx *ctx, ir_ref ref, ir_insn *insn)
|
|
{
|
|
ir_ref type = ctx->ir_base[insn->op3].type;
|
|
ir_reg op2_reg = ctx->regs[ref][2];
|
|
ir_reg op3_reg = ctx->regs[ref][3];
|
|
|
|
IR_ASSERT(op2_reg != IR_REG_NONE && op2_reg != IR_REG_NONE);
|
|
if (op2_reg != IR_REG_NONE && (op2_reg & IR_REG_SPILL_LOAD)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
if (op3_reg != IR_REG_NONE && (op3_reg & IR_REG_SPILL_LOAD)) {
|
|
op3_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op3_reg, insn->op3);
|
|
}
|
|
if (IR_IS_CONST_REF(insn->op3)) {
|
|
ir_emit_load(ctx, type, op3_reg, insn->op3);
|
|
}
|
|
ir_emit_store_mem_fp(ctx, type, op2_reg, 0, op3_reg);
|
|
}
|
|
|
|
static void ir_emit_alloca(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
ir_insn *val = &ctx->ir_base[insn->op2];
|
|
int32_t size = val->val.i32;
|
|
|
|
IR_ASSERT(IR_IS_TYPE_INT(val->type));
|
|
IR_ASSERT(IR_IS_TYPE_UNSIGNED(val->type) || val->val.i64 > 0);
|
|
//??? IR_ASSERT(IR_IS_SIGNED_32BIT(val->val.i64));
|
|
|
|
if (ctx->flags & IR_HAS_CALLS) {
|
|
/* Stack must be 16 byte aligned */
|
|
size = IR_ALIGNED_SIZE(size, 16);
|
|
} else {
|
|
size = IR_ALIGNED_SIZE(size, 8);
|
|
}
|
|
//??? | ASM_REG_IMM_OP sub, IR_ADDR, IR_REG_RSP, size
|
|
} else {
|
|
int32_t alignment = (ctx->flags & IR_HAS_CALLS) ? 16 : 8;
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
ir_type type = ctx->ir_base[insn->op2].type;
|
|
|
|
IR_ASSERT(def_reg != IR_REG_NONE);
|
|
if (op2_reg != IR_REG_NONE && (op2_reg & IR_REG_SPILL_LOAD)) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
if (def_reg != op2_reg) {
|
|
if (op2_reg != IR_REG_NONE) {
|
|
ir_emit_mov(ctx, type, def_reg, op2_reg);
|
|
} else {
|
|
ir_emit_load(ctx, type, def_reg, insn->op2);
|
|
}
|
|
}
|
|
|
|
//??? | ASM_REG_IMM_OP add, IR_ADDR, def_reg, (alignment-1)
|
|
//??? | ASM_REG_IMM_OP and, IR_ADDR, def_reg, ~(alignment-1)
|
|
//??? | ASM_REG_REG_OP sub, IR_ADDR, IR_REG_RSP, def_reg
|
|
}
|
|
if (def_reg != IR_REG_NONE) {
|
|
//??? | mov Ra(def_reg), Ra(IR_REG_RSP)
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
} else {
|
|
ir_emit_store(ctx, IR_ADDR, def, IR_REG_STACK_POINTER);
|
|
}
|
|
}
|
|
|
|
static void ir_emit_switch(ir_ctx *ctx, int b, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_type type;
|
|
ir_block *bb;
|
|
ir_insn *use_insn, *val;
|
|
uint32_t n, *p, use_block;
|
|
int i;
|
|
int label, default_label = 0;
|
|
int count = 0;
|
|
ir_val min, max;
|
|
int64_t offset;
|
|
ir_reg op2_reg, tmp_reg;
|
|
|
|
type = ctx->ir_base[insn->op2].type;
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
min.u64 = 0x7fffffffffffffff;
|
|
max.u64 = 0x8000000000000000;
|
|
} else {
|
|
min.u64 = 0xffffffffffffffff;
|
|
max.u64 = 0x0;
|
|
}
|
|
|
|
bb = &ctx->cfg_blocks[b];
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_CASE_VAL) {
|
|
val = &ctx->ir_base[use_insn->op2];
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
IR_ASSERT(IR_IS_TYPE_SIGNED(val->type));
|
|
min.i64 = IR_MIN(min.i64, val->val.i64);
|
|
max.i64 = IR_MAX(max.i64, val->val.i64);
|
|
} else {
|
|
IR_ASSERT(!IR_IS_TYPE_SIGNED(val->type));
|
|
min.u64 = (int64_t)IR_MIN(min.u64, val->val.u64);
|
|
max.u64 = (int64_t)IR_MAX(max.u64, val->val.u64);
|
|
}
|
|
count++;
|
|
} else if (use_insn->op == IR_CASE_DEFAULT) {
|
|
default_label = ir_skip_empty_blocks(ctx, use_block);
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
op2_reg = ctx->regs[def][2];
|
|
tmp_reg = ctx->regs[def][3];
|
|
|
|
IR_ASSERT(op2_reg != IR_REG_NONE);
|
|
IR_ASSERT(tmp_reg != IR_REG_NONE || sizeof(void*) != 8);
|
|
if (op2_reg & IR_REG_SPILL_LOAD) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
} else if (IR_IS_CONST_REF(insn->op2)) {
|
|
ir_emit_load(ctx, type, op2_reg, insn->op2);
|
|
}
|
|
|
|
/* Generate a table jmp or a seqence of calls */
|
|
if ((max.i64-min.i64) < count * 8) {
|
|
int *labels = ir_mem_malloc(sizeof(int) * (max.i64 - min.i64 + 1));
|
|
|
|
for (i = 0; i <= (max.i64 - min.i64); i++) {
|
|
labels[i] = default_label;
|
|
}
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_CASE_VAL) {
|
|
val = &ctx->ir_base[use_insn->op2];
|
|
label = ir_skip_empty_blocks(ctx, use_block);
|
|
labels[val->val.i64 - min.i64] = label;
|
|
}
|
|
}
|
|
|
|
//??? if (IR_IS_32BIT(type, max)) {
|
|
//??? | ASM_REG_IMM_OP cmp, type, op2_reg, max.i32
|
|
//??? } else {
|
|
IR_ASSERT(ir_type_size[type] == 8);
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | mov64 Rq(tmp_reg), max.i64
|
|
//??? | cmp Rq(op2_reg), Rq(tmp_reg)
|
|
//??? }
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
//??? | jg =>default_label
|
|
} else {
|
|
//??? | ja =>default_label
|
|
}
|
|
|
|
//??? if (IR_IS_32BIT(type, min)) {
|
|
offset = -min.i64 * sizeof(void*);
|
|
//??? if (IR_IS_SIGNED_32BIT(offset)) {
|
|
//??? | ASM_REG_IMM_OP cmp, type, op2_reg, min.i32
|
|
//??? } else {
|
|
//??? | ASM_REG_REG_OP sub, type, op2_reg, (int32_t)offset // TODO: reg clobbering
|
|
//??? offset = 0;
|
|
//??? }
|
|
//??? } else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | mov64 Rq(tmp_reg), min.i64
|
|
//??? | ASM_REG_REG_OP sub, type, op2_reg, tmp_reg // TODO: reg clobbering
|
|
offset = 0;
|
|
//??? }
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
//??? | jl =>default_label
|
|
} else {
|
|
//??? | jb =>default_label
|
|
}
|
|
switch (ir_type_size[type]) {
|
|
case 1:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
//??? | movsx Ra(op2_reg), Rb(op2_reg)
|
|
} else {
|
|
//??? | movzx Ra(op2_reg), Rb(op2_reg)
|
|
}
|
|
break;
|
|
case 2:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
//??? | movsx Ra(op2_reg), Rw(op2_reg)
|
|
} else {
|
|
//??? | movzx Ra(op2_reg), Rw(op2_reg)
|
|
}
|
|
break;
|
|
case 4:
|
|
if (IR_IS_TYPE_SIGNED(type)) {
|
|
//??? | movsxd Ra(op2_reg), Rd(op2_reg)
|
|
} else {
|
|
//??? | mov Rd(op2_reg), Rd(op2_reg)
|
|
}
|
|
break;
|
|
case 8:
|
|
break;
|
|
default:
|
|
IR_ASSERT(0);
|
|
}
|
|
//??? | lea Ra(tmp_reg), aword [>1]
|
|
//??? | jmp aword [Ra(tmp_reg)+Ra(op2_reg)*8+(int32_t)offset]
|
|
|.jmp_table
|
|
if (!data->jmp_table_label) {
|
|
data->jmp_table_label = ctx->cfg_blocks_count + ctx->consts_count + 3;
|
|
|=>data->jmp_table_label:
|
|
}
|
|
|.align 8
|
|
|1:
|
|
for (i = 0; i <= (max.i64 - min.i64); i++) {
|
|
| .addr =>labels[i]
|
|
}
|
|
|.code
|
|
ir_mem_free(labels);
|
|
} else {
|
|
p = &ctx->cfg_edges[bb->successors];
|
|
for (n = bb->successors_count; n != 0; p++, n--) {
|
|
use_block = *p;
|
|
use_insn = &ctx->ir_base[ctx->cfg_blocks[use_block].start];
|
|
if (use_insn->op == IR_CASE_VAL) {
|
|
val = &ctx->ir_base[use_insn->op2];
|
|
label = ir_skip_empty_blocks(ctx, use_block);
|
|
//??? if (IR_IS_32BIT(type, val->val)) {
|
|
//??? | ASM_REG_IMM_OP cmp, type, op2_reg, val->val.i32
|
|
//??? } else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | mov64 Ra(tmp_reg), val->val.i64
|
|
//??? | ASM_REG_REG_OP cmp, type, op2_reg, tmp_reg
|
|
//??? }
|
|
//??? | je =>label
|
|
}
|
|
}
|
|
if (default_label) {
|
|
//??? | jmp =>default_label
|
|
}
|
|
}
|
|
}
|
|
|
|
#include <dlfcn.h>
|
|
|
|
static void *ir_resolve_sym_name(const char *name)
|
|
{
|
|
void *handle = NULL;
|
|
void *addr;
|
|
|
|
#ifdef RTLD_DEFAULT
|
|
handle = RTLD_DEFAULT;
|
|
#endif
|
|
addr = dlsym(handle, name);
|
|
IR_ASSERT(addr != NULL);
|
|
return addr;
|
|
}
|
|
|
|
typedef struct _ir_copy {
|
|
ir_type type;
|
|
ir_reg from;
|
|
ir_reg to;
|
|
} ir_copy;
|
|
|
|
static int ir_parallel_copy(ir_ctx *ctx, ir_copy *copies, int count, ir_reg tmp_reg, ir_reg tmp_fp_reg)
|
|
{
|
|
int i;
|
|
int8_t *pred, *loc, *types;
|
|
ir_reg to, from_reg;
|
|
ir_type type;
|
|
ir_regset todo, ready;
|
|
|
|
loc = ir_mem_malloc(IR_REG_NUM * 3 * sizeof(int8_t));
|
|
pred = loc + IR_REG_NUM;
|
|
types = pred + IR_REG_NUM;
|
|
memset(loc, IR_REG_NONE, IR_REG_NUM * 2 * sizeof(int8_t));
|
|
todo = IR_REGSET_EMPTY;
|
|
ready = IR_REGSET_EMPTY;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
from_reg = copies[i].from;
|
|
to = copies[i].to;
|
|
if (from_reg != to) {
|
|
loc[from_reg] = from_reg;
|
|
pred[to] = from_reg;
|
|
types[to] = copies[i].type;
|
|
IR_REGSET_INCL(todo, to);
|
|
}
|
|
}
|
|
|
|
IR_REGSET_FOREACH(todo, i) {
|
|
if (loc[i] == IR_REG_NONE) {
|
|
IR_REGSET_INCL(ready, i);
|
|
}
|
|
} IR_REGSET_FOREACH_END();
|
|
|
|
while (todo != IR_REGSET_EMPTY) {
|
|
ir_ref /*a, b,*/ c;
|
|
|
|
while (ready != IR_REGSET_EMPTY) {
|
|
to = IR_REGSET_FIRST(ready);
|
|
IR_REGSET_EXCL(ready, to);
|
|
from_reg = pred[to];
|
|
type = types[to];
|
|
c = loc[from_reg];
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
ir_emit_mov(ctx, type, to, c);
|
|
} else {
|
|
ir_emit_fp_mov(ctx, type, to, c);
|
|
}
|
|
loc[from_reg] = to;
|
|
if (from_reg == c && pred[from_reg] != IR_REG_NONE) {
|
|
IR_REGSET_INCL(ready, from_reg);
|
|
}
|
|
}
|
|
to = IR_REGSET_FIRST(todo);
|
|
IR_REGSET_EXCL(todo, to);
|
|
from_reg = pred[to];
|
|
if (to != loc[from_reg]) {
|
|
type = types[to];
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
IR_ASSERT(tmp_reg != IR_REG_NONE);
|
|
IR_ASSERT(tmp_reg >= IR_REG_GP_FIRST && tmp_reg <= IR_REG_GP_LAST);
|
|
ir_emit_mov(ctx, type, tmp_reg, to);
|
|
loc[to] = tmp_reg;
|
|
} else {
|
|
IR_ASSERT(tmp_fp_reg != IR_REG_NONE);
|
|
IR_ASSERT(tmp_fp_reg >= IR_REG_FP_FIRST && tmp_fp_reg <= IR_REG_FP_LAST);
|
|
ir_emit_fp_mov(ctx, type, tmp_fp_reg, to);
|
|
loc[to] = tmp_fp_reg;
|
|
}
|
|
IR_REGSET_INCL(ready, to);
|
|
}
|
|
}
|
|
|
|
ir_mem_free(loc);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn, bool align)
|
|
{
|
|
int j, n;
|
|
ir_type type;
|
|
int int_param = 0;
|
|
int fp_param = 0;
|
|
int int_reg_params_count = IR_REG_INT_ARGS;
|
|
int fp_reg_params_count = IR_REG_FP_ARGS;
|
|
int32_t used_stack = 0;
|
|
|
|
n = ir_input_edges_count(ctx, insn);
|
|
for (j = 3; j <= n; j++) {
|
|
type = ctx->ir_base[insn->ops[j]].type;
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (int_param >= int_reg_params_count) {
|
|
used_stack += IR_MAX(sizeof(void*), ir_type_size[type]);
|
|
}
|
|
int_param++;
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
if (fp_param >= fp_reg_params_count) {
|
|
used_stack += IR_MAX(sizeof(void*), ir_type_size[type]);
|
|
}
|
|
fp_param++;
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
if (align && used_stack % 16 != 0) {
|
|
/* Stack must be 16 byte aligned */
|
|
used_stack += sizeof(void*);
|
|
}
|
|
|
|
return used_stack;
|
|
}
|
|
|
|
static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg tmp_reg)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
int j, n;
|
|
ir_ref arg;
|
|
ir_insn *arg_insn;
|
|
uint8_t type;
|
|
ir_reg src_reg, dst_reg;
|
|
int int_param = 0;
|
|
int fp_param = 0;
|
|
int count = 0;
|
|
int int_reg_params_count = IR_REG_INT_ARGS;
|
|
int fp_reg_params_count = IR_REG_FP_ARGS;
|
|
const int8_t *int_reg_params = _ir_int_reg_params;
|
|
const int8_t *fp_reg_params = _ir_fp_reg_params;
|
|
int32_t used_stack, stack_offset = 0;
|
|
ir_copy *copies;
|
|
bool has_mem_const_args = 0;
|
|
ir_reg tmp_fp_reg = IR_REG_FP_LAST; /* Temporary register for FP loads and swap */
|
|
|
|
if (insn->op == IR_CALL && (ctx->flags & IR_PREALLOCATED_STACK)) {
|
|
// TODO: support for preallocated stack
|
|
used_stack = 0;
|
|
} else {
|
|
used_stack = ir_call_used_stack(ctx, insn, 1);
|
|
data->call_stack_size += used_stack;
|
|
if (used_stack) {
|
|
//??? | sub Ra(IR_REG_RSP), used_stack
|
|
}
|
|
}
|
|
|
|
/* 1. move all arguments that should be passed through stack
|
|
* and collect arguments that should be passed through registers */
|
|
n = ir_input_edges_count(ctx, insn);
|
|
copies = ir_mem_malloc((n - 2) * sizeof(ir_copy));
|
|
for (j = 3; j <= n; j++) {
|
|
arg = insn->ops[j];
|
|
src_reg = ctx->regs[def][j];
|
|
arg_insn = &ctx->ir_base[arg];
|
|
type = arg_insn->type;
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (int_param < int_reg_params_count) {
|
|
dst_reg = int_reg_params[int_param];
|
|
} else {
|
|
dst_reg = IR_REG_NONE; /* pass argument through stack */
|
|
}
|
|
int_param++;
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
if (fp_param < fp_reg_params_count) {
|
|
dst_reg = fp_reg_params[fp_param];
|
|
} else {
|
|
dst_reg = IR_REG_NONE; /* pass argument through stack */
|
|
}
|
|
fp_param++;
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
if (dst_reg != IR_REG_NONE && (IR_IS_CONST_REF(arg) || src_reg == IR_REG_NONE)) {
|
|
/* delay CONST->REG and MEM->REG moves to third pass */
|
|
has_mem_const_args = 1;
|
|
continue;
|
|
}
|
|
if (dst_reg != IR_REG_NONE) {
|
|
IR_ASSERT(src_reg != IR_REG_NONE);
|
|
if (src_reg & IR_REG_SPILL_LOAD) {
|
|
src_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, src_reg, arg);
|
|
}
|
|
if (src_reg != dst_reg) {
|
|
/* delay REG->REG moves to second pass */
|
|
copies[count].type = type;
|
|
copies[count].from = src_reg;
|
|
copies[count].to = dst_reg;
|
|
count++;
|
|
}
|
|
} else {
|
|
/* Pass argument through stack (REG->MEM, IMM->MEM, MEM->MEM moves) */
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (IR_IS_CONST_REF(arg)) {
|
|
ir_insn *val_insn = &ctx->ir_base[arg];
|
|
|
|
if (val_insn->op == IR_STR) {
|
|
int label = ctx->cfg_blocks_count - arg;
|
|
|
|
val_insn->emit_const = 1;
|
|
IR_ASSERT(tmp_reg != IR_REG_NONE);
|
|
//??? | lea Ra(tmp_reg), aword [=>label]
|
|
//??? | mov [Ra(IR_REG_RSP)+stack_offset], Ra(tmp_reg)
|
|
//??? } else if (IR_IS_SIGNED_32BIT(val_insn->val.i64)) {
|
|
if (ir_type_size[type] <= 4) {
|
|
//??? | mov dword [Ra(IR_REG_RSP)+stack_offset], val_insn->val.i32
|
|
} else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
//??? | mov qword [rsp+stack_offset], val_insn->val.i32
|
|
}
|
|
} else {
|
|
IR_ASSERT(sizeof(void*) == 8);
|
|
IR_ASSERT(tmp_reg != IR_REG_NONE);
|
|
//??? | mov64 Ra(tmp_reg), val_insn->val.i64
|
|
//??? | mov [rsp+stack_offset], Ra(tmp_reg)
|
|
}
|
|
} else if (src_reg != IR_REG_NONE) {
|
|
if (src_reg & IR_REG_SPILL_LOAD) {
|
|
src_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, src_reg, arg);
|
|
}
|
|
ir_emit_store_mem_int(ctx, type, IR_REG_STACK_POINTER, stack_offset, src_reg);
|
|
} else {
|
|
IR_ASSERT(tmp_reg != IR_REG_NONE);
|
|
ir_emit_load(ctx, type, tmp_reg, arg);
|
|
ir_emit_store_mem_int(ctx, type, IR_REG_STACK_POINTER, stack_offset, tmp_reg);
|
|
}
|
|
} else {
|
|
if (IR_IS_CONST_REF(arg)) {
|
|
ir_val *val = &ctx->ir_base[arg].val;
|
|
if (ir_type_size[type] == 4) {
|
|
//??? | mov dword [Ra(IR_REG_RSP)+stack_offset], val->i32
|
|
} else if (sizeof(void*) == 8) {
|
|
if (val->i64 == 0) {
|
|
//??? | mov qword [rsp+stack_offset], val->i32
|
|
} else {
|
|
IR_ASSERT(tmp_reg != IR_REG_NONE);
|
|
//??? | mov64 Rq(tmp_reg), val->i64
|
|
//??? | mov qword [rsp+stack_offset], Ra(tmp_reg)
|
|
}
|
|
} else {
|
|
ir_emit_load(ctx, type, tmp_fp_reg, arg);
|
|
ir_emit_store_mem_fp(ctx, IR_DOUBLE, IR_REG_STACK_POINTER, stack_offset, tmp_fp_reg);
|
|
}
|
|
} else if (src_reg != IR_REG_NONE) {
|
|
if (src_reg & IR_REG_SPILL_LOAD) {
|
|
src_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, src_reg, arg);
|
|
}
|
|
ir_emit_store_mem_fp(ctx, type, IR_REG_STACK_POINTER, stack_offset, src_reg);
|
|
} else {
|
|
IR_ASSERT(tmp_fp_reg != IR_REG_NONE);
|
|
ir_emit_load(ctx, type, tmp_fp_reg, arg);
|
|
ir_emit_store_mem_fp(ctx, IR_DOUBLE, IR_REG_STACK_POINTER, stack_offset, tmp_fp_reg);
|
|
}
|
|
}
|
|
stack_offset += IR_MAX(sizeof(void*), ir_type_size[type]);
|
|
}
|
|
}
|
|
|
|
/* 2. move all arguments that should be passed from one register to anouther (REG->REG movs) */
|
|
if (count) {
|
|
ir_parallel_copy(ctx, copies, count, tmp_reg, tmp_fp_reg);
|
|
}
|
|
ir_mem_free(copies);
|
|
|
|
/* 3. move the remaining memory and immediate values to registers */
|
|
if (has_mem_const_args) {
|
|
int_param = 0;
|
|
fp_param = 0;
|
|
for (j = 3; j <= n; j++) {
|
|
arg = insn->ops[j];
|
|
src_reg = ctx->regs[def][j];
|
|
arg_insn = &ctx->ir_base[arg];
|
|
type = arg_insn->type;
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (int_param < int_reg_params_count) {
|
|
dst_reg = int_reg_params[int_param];
|
|
} else {
|
|
dst_reg = IR_REG_NONE; /* argument already passed through stack */
|
|
}
|
|
int_param++;
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
if (fp_param < fp_reg_params_count) {
|
|
dst_reg = fp_reg_params[fp_param];
|
|
} else {
|
|
dst_reg = IR_REG_NONE; /* argument already passed through stack */
|
|
}
|
|
fp_param++;
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
if (dst_reg != IR_REG_NONE && (IR_IS_CONST_REF(arg) || src_reg == IR_REG_NONE)) {
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (IR_IS_CONST_REF(arg)) {
|
|
if (type == IR_ADDR) {
|
|
ir_insn *val_insn = &ctx->ir_base[arg];
|
|
|
|
if (val_insn->op == IR_STR) {
|
|
int label = ctx->cfg_blocks_count - arg;
|
|
|
|
val_insn->emit_const = 1;
|
|
//??? | lea Ra(dst_reg), aword [=>label]
|
|
continue;
|
|
}
|
|
} else if (ir_type_size[type] == 1) {
|
|
type = IR_ADDR;
|
|
}
|
|
}
|
|
ir_emit_load(ctx, type, dst_reg, arg);
|
|
} else {
|
|
ir_emit_load(ctx, type, dst_reg, arg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return used_stack;
|
|
}
|
|
|
|
static void ir_emit_call(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg def_reg;
|
|
int32_t used_stack = ir_emit_arguments(ctx, def, insn, ctx->regs[def][1]);
|
|
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
const char *name = ir_get_str(ctx, ctx->ir_base[insn->op2].val.addr);
|
|
void *addr = ir_resolve_sym_name(name);
|
|
|
|
if (sizeof(void*) == 4 /*|| IR_IS_SIGNED_32BIT(addr)*/) { // TODO: 32-bit IP relative or 64-bit absolute address
|
|
//??? | call aword &addr
|
|
} else {
|
|
//??? | mov64 rax, ((ptrdiff_t)addr) // 0x48 0xb8 <imm-64-bit>
|
|
//??? | call rax
|
|
}
|
|
} else {
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (op2_reg & IR_REG_SPILL_LOAD) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2);
|
|
}
|
|
//??? | call Ra(op2_reg)
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op2);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
//??? | call aword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
|
|
if (used_stack) {
|
|
//??? | add Ra(IR_REG_RSP), used_stack
|
|
data->call_stack_size -= used_stack;
|
|
}
|
|
|
|
if (insn->type != IR_VOID) {
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
if (def_reg != IR_REG_NONE) {
|
|
if (def_reg != IR_REG_INT_RET1) {
|
|
ir_emit_mov(ctx, insn->type, def_reg, IR_REG_INT_RET1);
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
} else {
|
|
ir_emit_store(ctx, insn->type, def, IR_REG_INT_RET1);
|
|
}
|
|
} else if (IR_IS_TYPE_FP(insn->type)) {
|
|
def_reg = IR_REG_NUM(ctx->regs[def][0]);
|
|
if (def_reg != IR_REG_NONE) {
|
|
if (def_reg != IR_REG_FP_RET1) {
|
|
ir_emit_fp_mov(ctx, insn->type, def_reg, IR_REG_FP_RET1);
|
|
}
|
|
if (ctx->regs[def][0] & IR_REG_SPILL_STORE) {
|
|
ir_emit_store(ctx, insn->type, def, def_reg);
|
|
}
|
|
} else {
|
|
ir_emit_store(ctx, insn->type, def, IR_REG_FP_RET1);
|
|
}
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
int32_t used_stack = ir_emit_arguments(ctx, def, insn, ctx->regs[def][1]);
|
|
|
|
IR_ASSERT(used_stack == 0);
|
|
|
|
ir_emit_epilogue(ctx);
|
|
|
|
if (IR_IS_CONST_REF(insn->op2)) {
|
|
const char *name = ir_get_str(ctx, ctx->ir_base[insn->op2].val.addr);
|
|
void *addr = ir_resolve_sym_name(name);
|
|
|
|
if (sizeof(void*) == 4 /*|| IR_IS_SIGNED_32BIT(addr)*/) { // TODO: 32-bit IP relative or 64-bit absolute address
|
|
//??? | jmp aword &addr
|
|
} else {
|
|
//??? | mov64 rax, ((ptrdiff_t)addr) // 0x48 0xb8 <imm-64-bit>
|
|
//??? | jmp rax
|
|
}
|
|
} else {
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (op2_reg & IR_REG_SPILL_LOAD) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2);
|
|
}
|
|
//??? | jmp Ra(op2_reg)
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op2);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
//??? | jmp aword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_ijmp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
ir_reg op2_reg = ctx->regs[def][2];
|
|
|
|
if (op2_reg != IR_REG_NONE) {
|
|
if (op2_reg & IR_REG_SPILL_LOAD) {
|
|
op2_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, IR_ADDR, op2_reg, insn->op2);
|
|
}
|
|
//??? | jmp Ra(op2_reg)
|
|
} else {
|
|
int32_t offset = ir_ref_spill_slot(ctx, insn->op2);
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
//??? | jmp aword [Ra(fp)+offset]
|
|
}
|
|
}
|
|
|
|
static int ir_emit_dessa_move(ir_ctx *ctx, uint8_t type, ir_ref from, ir_ref to)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
dasm_State **Dst = &data->dasm_state;
|
|
uint32_t from_block = data->dessa_from_block;
|
|
ir_block *from_bb = &ctx->cfg_blocks[from_block];
|
|
ir_ref ref = from_bb->end;
|
|
int8_t to_reg, from_reg;
|
|
ir_block *to_bb;
|
|
int j, k = 0;
|
|
ir_ref phi = 0;
|
|
bool spill_store = 0;
|
|
|
|
IR_ASSERT(from_bb->successors_count == 1);
|
|
to_bb = &ctx->cfg_blocks[ctx->cfg_edges[from_bb->successors]];
|
|
for (j = 0; j < to_bb->predecessors_count; j++) {
|
|
if (ctx->cfg_edges[to_bb->predecessors + j] == from_block) {
|
|
k = j + 2;
|
|
break;
|
|
}
|
|
}
|
|
IR_ASSERT(k != 0);
|
|
if (to) {
|
|
phi = to;
|
|
} else {
|
|
ir_use_list *use_list = &ctx->use_lists[from];
|
|
|
|
for (j = 0; j < use_list->count; j++) {
|
|
ir_ref use = ctx->use_edges[use_list->refs + j];
|
|
ir_insn *insn = &ctx->ir_base[use];
|
|
if (insn->op == IR_PHI && insn->ops[k] == from && insn->op1 == to_bb->start) {
|
|
phi = use;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
IR_ASSERT(phi != 0);
|
|
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
to_reg = to ? ctx->regs[phi][0] : ctx->regs[ref][0]; /* temporary register for integer swap (see ir_fix_dessa_tmps) */
|
|
from_reg = from ? ctx->regs[phi][k] : ctx->regs[ref][0]; /* temporary register for integer swap (see ir_fix_dessa_tmps) */
|
|
if (from_reg != IR_REG_NONE && (from_reg & IR_REG_SPILL_LOAD)) {
|
|
from_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, from_reg, from);
|
|
}
|
|
if (to_reg != IR_REG_NONE && (to_reg & IR_REG_SPILL_STORE)) {
|
|
to_reg &= ~IR_REG_SPILL_STORE;
|
|
spill_store = 1;
|
|
}
|
|
if (from_reg != IR_REG_NONE && to_reg != IR_REG_NONE) {
|
|
if (from_reg != to_reg) {
|
|
ir_emit_mov(ctx, type, to_reg, from_reg);
|
|
}
|
|
} else if (to_reg != IR_REG_NONE) {
|
|
ir_emit_load(ctx, type, to_reg, from);
|
|
} else if (from_reg != IR_REG_NONE) {
|
|
ir_emit_store(ctx, type, to, from_reg);
|
|
//??? } else if (IR_IS_CONST_REF(from) && (ir_type_size[type] != 8 || IR_IS_SIGNED_32BIT(ctx->ir_base[from].val.i64))) {
|
|
//??? | ASM_MREF_IMM_OP mov, type, to, ctx->ir_base[from].val.i32
|
|
//??? } else if (IR_IS_CONST_REF(from) && ir_type_size[type] == 8 && IR_IS_UNSIGNED_32BIT(ctx->ir_base[from].val.u64)) {
|
|
//??? | mov Rd(to), ctx->ir_base[from].val.u32
|
|
} else if (IR_IS_CONST_REF(from) || !ir_is_same_mem(ctx, from, to)) {
|
|
from_reg = ctx->regs[ref][1]; /* temporary register for int mem->mem (see ir_fix_dessa_tmps) */
|
|
IR_ASSERT(from_reg != IR_REG_NONE);
|
|
ir_emit_load(ctx, type, from_reg, from);
|
|
ir_emit_store(ctx, type, to, from_reg);
|
|
}
|
|
if (spill_store) {
|
|
ir_emit_store(ctx, type, to, to_reg);
|
|
}
|
|
} else {
|
|
to_reg = to ? ctx->regs[phi][0] : ctx->regs[ref][2]; /* temporary register for fp swap (see ir_fix_dessa_tmps) */
|
|
from_reg = from ? ctx->regs[phi][k] : ctx->regs[ref][2]; /* temporary register for fp swap (see ir_fix_dessa_tmps) */
|
|
if (from_reg != IR_REG_NONE && (from_reg & IR_REG_SPILL_LOAD)) {
|
|
from_reg &= ~IR_REG_SPILL_LOAD;
|
|
ir_emit_load(ctx, type, from_reg, from);
|
|
}
|
|
if (to_reg != IR_REG_NONE && (to_reg & IR_REG_SPILL_STORE)) {
|
|
to_reg &= ~IR_REG_SPILL_STORE;
|
|
spill_store = 1;
|
|
}
|
|
if (from_reg != IR_REG_NONE && to_reg != IR_REG_NONE) {
|
|
if (from_reg != to_reg) {
|
|
ir_emit_fp_mov(ctx, type, to_reg, from_reg);
|
|
}
|
|
} else if (to_reg != IR_REG_NONE) {
|
|
ir_emit_load(ctx, type, to_reg, from);
|
|
} else if (from_reg != IR_REG_NONE) {
|
|
ir_emit_store(ctx, type, to, from_reg);
|
|
} else if (IR_IS_CONST_REF(from) || !ir_is_same_mem(ctx, from, to)) {
|
|
from_reg = ctx->regs[ref][3]; /* temporary register for fp mem->mem (see ir_fix_dessa_tmps) */
|
|
IR_ASSERT(from_reg != IR_REG_NONE);
|
|
ir_emit_load(ctx, type, from_reg, from);
|
|
ir_emit_store(ctx, type, to, from_reg);
|
|
}
|
|
if (spill_store) {
|
|
ir_emit_store(ctx, type, to, to_reg);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void ir_emit_param_move(ir_ctx *ctx, uint8_t type, ir_reg from_reg, ir_reg to_reg, ir_ref to, int32_t offset)
|
|
{
|
|
ir_reg fp = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER;
|
|
|
|
IR_ASSERT(from_reg != IR_REG_NONE || to_reg != IR_REG_NONE);
|
|
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (from_reg != IR_REG_NONE) {
|
|
if (to_reg != IR_REG_NONE) {
|
|
ir_emit_mov(ctx, type, to_reg, from_reg);
|
|
} else {
|
|
ir_emit_store(ctx, type, to, from_reg);
|
|
}
|
|
} else {
|
|
ir_emit_load_mem_int(ctx, type, to_reg, fp, offset);
|
|
}
|
|
} else {
|
|
if (from_reg != IR_REG_NONE) {
|
|
if (to_reg != IR_REG_NONE) {
|
|
ir_emit_fp_mov(ctx, type, to_reg, from_reg);
|
|
} else {
|
|
ir_emit_store(ctx, type, to, from_reg);
|
|
}
|
|
} else {
|
|
ir_emit_load_mem_fp(ctx, type, to_reg, fp, offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_emit_load_params(ir_ctx *ctx)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
ir_use_list *use_list = &ctx->use_lists[1];
|
|
ir_insn *insn;
|
|
ir_ref i, n, *p, use;
|
|
int int_param_num = 0;
|
|
int fp_param_num = 0;
|
|
ir_reg src_reg;
|
|
ir_reg dst_reg;
|
|
// TODO: Calling convention specific
|
|
int int_reg_params_count = IR_REG_INT_ARGS;
|
|
int fp_reg_params_count = IR_REG_FP_ARGS;
|
|
const int8_t *int_reg_params = _ir_int_reg_params;
|
|
const int8_t *fp_reg_params = _ir_fp_reg_params;
|
|
int32_t stack_offset = 0;
|
|
|
|
if (ctx->flags & IR_USE_FRAME_POINTER) {
|
|
stack_offset = sizeof(void*) * 2; /* skip old frame pointer and return address */
|
|
} else {
|
|
stack_offset = sizeof(void*) + data->ra_data.stack_frame_size + data->call_stack_size; /* skip return address */
|
|
}
|
|
n = use_list->count;
|
|
for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) {
|
|
use = *p;
|
|
insn = &ctx->ir_base[use];
|
|
if (insn->op == IR_PARAM) {
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
if (int_param_num < int_reg_params_count) {
|
|
src_reg = int_reg_params[int_param_num];
|
|
} else {
|
|
src_reg = IR_REG_NONE;
|
|
}
|
|
int_param_num++;
|
|
} else {
|
|
if (fp_param_num < fp_reg_params_count) {
|
|
src_reg = fp_reg_params[fp_param_num];
|
|
} else {
|
|
src_reg = IR_REG_NONE;
|
|
}
|
|
fp_param_num++;
|
|
}
|
|
if (ctx->vregs[use]) {
|
|
dst_reg = IR_REG_NUM(ctx->regs[use][0]);
|
|
IR_ASSERT(src_reg != IR_REG_NONE || dst_reg != IR_REG_NONE ||
|
|
stack_offset == ctx->live_intervals[ctx->vregs[use]]->stack_spill_pos +
|
|
((ctx->flags & IR_USE_FRAME_POINTER) ? -data->ra_data.stack_frame_size : data->call_stack_size));
|
|
if (src_reg != dst_reg) {
|
|
ir_emit_param_move(ctx, insn->type, src_reg, dst_reg, use, stack_offset);
|
|
}
|
|
if (dst_reg != IR_REG_NONE && (ctx->regs[use][0] & IR_REG_SPILL_STORE)) {
|
|
ir_emit_store(ctx, insn->type, use, dst_reg);
|
|
}
|
|
}
|
|
if (src_reg == IR_REG_NONE) {
|
|
if (sizeof(void*) == 8) {
|
|
stack_offset += sizeof(void*);
|
|
} else {
|
|
stack_offset += IR_MAX(sizeof(void*), ir_type_size[insn->type]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static ir_reg ir_get_free_reg(ir_type type, ir_regset available)
|
|
{
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
available = IR_REGSET_INTERSECTION(available, IR_REGSET_GP);
|
|
} else {
|
|
IR_ASSERT(IR_IS_TYPE_FP(type));
|
|
available = IR_REGSET_INTERSECTION(available, IR_REGSET_FP);
|
|
}
|
|
IR_ASSERT(!IR_REGSET_IS_EMPTY(available));
|
|
return IR_REGSET_FIRST(available);
|
|
}
|
|
|
|
static int ir_fix_dessa_tmps(ir_ctx *ctx, uint8_t type, ir_ref from, ir_ref to)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
ir_ref ref = ctx->cfg_blocks[data->dessa_from_block].end;
|
|
|
|
if (to == 0) {
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (ctx->regs[ref][0] == IR_REG_NONE) {
|
|
ctx->regs[ref][0] = ctx->regs[ref][1] != IR_REG_X0 ? IR_REG_X0 : IR_REG_X1; //???
|
|
}
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
if (ctx->regs[ref][2] == IR_REG_NONE) {
|
|
ctx->regs[ref][2] = ctx->regs[ref][3] != IR_REG_V0 ? IR_REG_V0 : IR_REG_V1; //???
|
|
}
|
|
} else {
|
|
IR_ASSERT(0);
|
|
return 0;
|
|
}
|
|
} else if (from != 0) {
|
|
if (IR_IS_TYPE_INT(type)) {
|
|
if (ctx->regs[ref][1] == IR_REG_NONE) {
|
|
ctx->regs[ref][1] = ctx->regs[ref][0] != IR_REG_X0 ? IR_REG_X0 : IR_REG_X1; //???
|
|
}
|
|
} else if (IR_IS_TYPE_FP(type)) {
|
|
if (ctx->regs[ref][3] == IR_REG_NONE) {
|
|
ctx->regs[ref][3] = ctx->regs[ref][2] != IR_REG_V0 ? IR_REG_V0 : IR_REG_V1; //???
|
|
}
|
|
} else {
|
|
IR_ASSERT(0);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void ir_fix_param_spills(ir_ctx *ctx)
|
|
{
|
|
ir_backend_data *data = ctx->data;
|
|
ir_use_list *use_list = &ctx->use_lists[1];
|
|
ir_insn *insn;
|
|
ir_ref i, n, *p, use;
|
|
int int_param_num = 0;
|
|
int fp_param_num = 0;
|
|
ir_reg src_reg;
|
|
// TODO: Calling convention specific
|
|
int int_reg_params_count = IR_REG_INT_ARGS;
|
|
int fp_reg_params_count = IR_REG_FP_ARGS;
|
|
const int8_t *int_reg_params = _ir_int_reg_params;
|
|
const int8_t *fp_reg_params = _ir_fp_reg_params;
|
|
int32_t stack_offset = 0;
|
|
|
|
if (ctx->flags & IR_USE_FRAME_POINTER) {
|
|
/* skip old frame pointer and return address */
|
|
stack_offset = sizeof(void*) * 2 + (data->ra_data.stack_frame_size - data->stack_frame_alignment);
|
|
} else {
|
|
/* skip return address */
|
|
stack_offset = sizeof(void*) + data->ra_data.stack_frame_size;
|
|
}
|
|
n = use_list->count;
|
|
for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) {
|
|
use = *p;
|
|
insn = &ctx->ir_base[use];
|
|
if (insn->op == IR_PARAM) {
|
|
if (IR_IS_TYPE_INT(insn->type)) {
|
|
if (int_param_num < int_reg_params_count) {
|
|
src_reg = int_reg_params[int_param_num];
|
|
} else {
|
|
src_reg = IR_REG_NONE;
|
|
}
|
|
int_param_num++;
|
|
} else {
|
|
if (fp_param_num < fp_reg_params_count) {
|
|
src_reg = fp_reg_params[fp_param_num];
|
|
} else {
|
|
src_reg = IR_REG_NONE;
|
|
}
|
|
fp_param_num++;
|
|
}
|
|
if (src_reg == IR_REG_NONE) {
|
|
if (ctx->vregs[use]) {
|
|
ir_live_interval *ival = ctx->live_intervals[ctx->vregs[use]];
|
|
if ((ival->flags & IR_LIVE_INTERVAL_MEM_PARAM)
|
|
&& ival->stack_spill_pos == -1
|
|
&& (ival->next || ival->reg == IR_REG_NONE)) {
|
|
ival->stack_spill_pos = stack_offset;
|
|
ctx->regs[use][0] = IR_REG_NONE;
|
|
}
|
|
}
|
|
if (sizeof(void*) == 8) {
|
|
stack_offset += sizeof(void*);
|
|
} else {
|
|
stack_offset += IR_MAX(sizeof(void*), ir_type_size[insn->type]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_allocate_unique_spill_slots(ir_ctx *ctx)
|
|
{
|
|
int b;
|
|
ir_block *bb;
|
|
ir_insn *insn;
|
|
ir_ref i, n, j, *p;
|
|
uint32_t flags, *rule, insn_flags;
|
|
ir_backend_data *data = ctx->data;
|
|
ir_regset available = 0;
|
|
ir_tmp_reg tmp_regs[4];
|
|
uint8_t def_flags;
|
|
|
|
ctx->regs = ir_mem_malloc(sizeof(ir_regs) * ctx->insns_count);
|
|
memset(ctx->regs, IR_REG_NONE, sizeof(ir_regs) * ctx->insns_count);
|
|
|
|
ctx->live_intervals = ir_mem_calloc(ctx->vregs_count + 1 + IR_REG_NUM, sizeof(ir_live_interval*));
|
|
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
|
|
bb->flags &= ~IR_BB_MAY_SKIP;
|
|
flags = IR_BB_MAY_SKIP;
|
|
if (bb->successors_count != 1 ||
|
|
ctx->cfg_edges[bb->successors] != b + 1 ||
|
|
(bb->flags & IR_BB_DESSA_MOVES)) {
|
|
flags = 0;
|
|
}
|
|
for (i = bb->start, insn = ctx->ir_base + i, rule = ctx->rules + i; i <= bb->end;) {
|
|
switch (ctx->rules ? *rule : insn->op) {
|
|
case IR_START:
|
|
case IR_BEGIN:
|
|
case IR_END:
|
|
case IR_IF_TRUE:
|
|
case IR_IF_FALSE:
|
|
case IR_CASE_VAL:
|
|
case IR_CASE_DEFAULT:
|
|
case IR_MERGE:
|
|
case IR_LOOP_BEGIN:
|
|
case IR_LOOP_END:
|
|
case IR_LOOP_EXIT:
|
|
break;
|
|
default:
|
|
flags = 0;
|
|
case IR_VAR:
|
|
case IR_SKIP:
|
|
case IR_SKIP_REG: /* PARAM PHI PI */
|
|
/* skip */
|
|
def_flags = ir_get_def_flags(ctx, i);
|
|
if (ctx->rules && *rule != IR_CMP_AND_BRANCH_INT && *rule != IR_CMP_AND_BRANCH_FP) {
|
|
available = IR_REGSET_SCRATCH;
|
|
}
|
|
if (ctx->vregs[i]) {
|
|
ir_reg reg = ir_uses_fixed_reg(ctx, i, 0);
|
|
|
|
if (reg != IR_REG_NONE && IR_REGSET_IN(available, reg)) {
|
|
IR_REGSET_EXCL(available, reg);
|
|
ctx->regs[i][0] = reg | IR_REG_SPILL_STORE;
|
|
} else if (def_flags & IR_USE_MUST_BE_IN_REG) {
|
|
if (insn->op == IR_VLOAD
|
|
&& ctx->live_intervals[ctx->vregs[i]]
|
|
&& ctx->live_intervals[ctx->vregs[i]]->stack_spill_pos != -1) {
|
|
/* pass */
|
|
} else if (insn->op != IR_PARAM) {
|
|
reg = ir_get_free_reg(insn->type, available);
|
|
IR_REGSET_EXCL(available, reg);
|
|
ctx->regs[i][0] = reg | IR_REG_SPILL_STORE;
|
|
}
|
|
}
|
|
if (!ctx->live_intervals[ctx->vregs[i]]) {
|
|
ir_live_interval *ival = ir_mem_calloc(1, sizeof(ir_live_interval));
|
|
ctx->live_intervals[ctx->vregs[i]] = ival;
|
|
ival->type = insn->type;
|
|
ival->reg = IR_REG_NONE;
|
|
ival->vreg = ctx->vregs[i];
|
|
ival->stack_spill_pos = -1;
|
|
if (insn->op == IR_PARAM && reg == IR_REG_NONE) {
|
|
ival->flags |= IR_LIVE_INTERVAL_MEM_PARAM;
|
|
} else {
|
|
ival->stack_spill_pos = ir_allocate_spill_slot(ctx, ival->type, &data->ra_data);
|
|
}
|
|
ival->top = ival;
|
|
if (insn->op == IR_VAR) {
|
|
ir_use_list *use_list = &ctx->use_lists[i];
|
|
ir_ref i, n, *p, use;
|
|
ir_insn *use_insn;
|
|
int32_t stack_spill_pos = ival->stack_spill_pos;
|
|
|
|
n = use_list->count;
|
|
for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) {
|
|
use = *p;
|
|
use_insn = &ctx->ir_base[use];
|
|
if (use_insn->op == IR_VLOAD) {
|
|
if (ctx->vregs[use]
|
|
&& !ctx->live_intervals[ctx->vregs[use]]) {
|
|
ir_live_interval *ival = ir_mem_calloc(1, sizeof(ir_live_interval));
|
|
ctx->live_intervals[ctx->vregs[use]] = ival;
|
|
ival->type = insn->type;
|
|
ival->reg = IR_REG_NONE;
|
|
ival->vreg = ctx->vregs[use];
|
|
ival->stack_spill_pos = stack_spill_pos;
|
|
ival->top = ival;
|
|
}
|
|
} else if (use_insn->op == IR_VSTORE) {
|
|
if (!IR_IS_CONST_REF(use_insn->op3)
|
|
&& ctx->vregs[use_insn->op3]
|
|
&& !ctx->live_intervals[ctx->vregs[use_insn->op3]]) {
|
|
ir_live_interval *ival = ir_mem_calloc(1, sizeof(ir_live_interval));
|
|
ctx->live_intervals[ctx->vregs[use_insn->op3]] = ival;
|
|
ival->type = insn->type;
|
|
ival->reg = IR_REG_NONE;
|
|
ival->vreg = ctx->vregs[insn->op3];
|
|
ival->stack_spill_pos = stack_spill_pos;
|
|
ival->top = ival;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (insn->op == IR_PARAM) {
|
|
IR_ASSERT(0 && "unexpected PARAM");
|
|
return;
|
|
}
|
|
}
|
|
|
|
insn_flags = ir_op_flags[insn->op];
|
|
n = ir_input_edges_count(ctx, insn);
|
|
if (n > 0) {
|
|
ir_live_pos start, end;
|
|
ir_regset scratch = ir_get_scratch_regset(ctx, i, &start, &end);
|
|
|
|
if (!IR_REGSET_IS_EMPTY(scratch) && start == IR_LOAD_SUB_REF) {
|
|
available = IR_REGSET_DIFFERENCE(available, scratch);
|
|
}
|
|
}
|
|
for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
|
|
ir_ref input = *p;
|
|
if (IR_OPND_KIND(insn_flags, j) == IR_OPND_DATA && input > 0 && ctx->vregs[input]) {
|
|
if ((def_flags & IR_DEF_REUSES_OP1_REG) && j == 1) {
|
|
ir_reg reg = ctx->regs[i][0] & ~IR_REG_SPILL_STORE;
|
|
ctx->regs[i][1] = reg | IR_REG_SPILL_LOAD;
|
|
} else {
|
|
ir_reg reg = ir_uses_fixed_reg(ctx, i, j);
|
|
|
|
if (reg != IR_REG_NONE && IR_REGSET_IN(available, reg)) {
|
|
IR_REGSET_EXCL(available, reg);
|
|
ctx->regs[i][j] = reg | IR_REG_SPILL_LOAD;
|
|
} else if (ir_get_use_flags(ctx, i, j) & IR_USE_MUST_BE_IN_REG) {
|
|
reg = ir_get_free_reg(ctx->ir_base[input].type, available);
|
|
IR_REGSET_EXCL(available, reg);
|
|
ctx->regs[i][j] = reg | IR_REG_SPILL_LOAD;
|
|
} else if (j > 1 && input == insn->op1 && ctx->regs[i][1] != IR_REG_NONE) {
|
|
ctx->regs[i][j] = ctx->regs[i][1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
n = ir_get_temporary_regs(ctx, i, tmp_regs);
|
|
if (n) {
|
|
ir_reg reg;
|
|
|
|
do {
|
|
n--;
|
|
reg = ir_get_free_reg(tmp_regs[n].type, available);
|
|
IR_REGSET_EXCL(available, reg);
|
|
ctx->regs[i][tmp_regs[n].num] = reg;
|
|
} while (n);
|
|
}
|
|
break;
|
|
}
|
|
n = ir_operands_count(ctx, insn);
|
|
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
|
|
i += n;
|
|
insn += n;
|
|
rule += n;
|
|
}
|
|
bb->flags |= flags;
|
|
if (bb->flags & IR_BB_DESSA_MOVES) {
|
|
data->dessa_from_block = b;
|
|
ir_gen_dessa_moves(ctx, b, ir_fix_dessa_tmps);
|
|
}
|
|
}
|
|
|
|
if (ctx->flags & IR_HAS_CALLS) {
|
|
/* Stack must be 16 byte aligned */
|
|
if (ctx->flags & IR_USE_FRAME_POINTER) {
|
|
while (IR_ALIGNED_SIZE(data->ra_data.stack_frame_size + sizeof(void*) * 2, 16) != data->ra_data.stack_frame_size + sizeof(void*) * 2) {
|
|
data->ra_data.stack_frame_size += sizeof(void*);
|
|
data->stack_frame_alignment += sizeof(void*);
|
|
}
|
|
} else {
|
|
while (IR_ALIGNED_SIZE(data->ra_data.stack_frame_size + sizeof(void*), 16) != data->ra_data.stack_frame_size + sizeof(void*)) {
|
|
data->ra_data.stack_frame_size += sizeof(void*);
|
|
data->stack_frame_alignment += sizeof(void*);
|
|
}
|
|
}
|
|
}
|
|
|
|
ir_fix_param_spills(ctx);
|
|
}
|
|
|
|
static void ir_mark_empty_blocks(ir_ctx *ctx)
|
|
{
|
|
int b;
|
|
ir_block *bb;
|
|
ir_insn *insn;
|
|
ir_ref i, n;
|
|
uint32_t flags, *rule;
|
|
|
|
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
|
|
bb->flags &= ~IR_BB_MAY_SKIP;
|
|
if (bb->successors_count == 1
|
|
&& ctx->cfg_edges[bb->successors] == b + 1
|
|
&& !(bb->flags & IR_BB_DESSA_MOVES)) {
|
|
flags = IR_BB_MAY_SKIP;
|
|
for (i = bb->start, insn = ctx->ir_base + i, rule = ctx->rules + i; i < bb->end;) {
|
|
if (*rule != IR_SKIP && *rule != IR_SKIP_REG) {
|
|
flags = 0;
|
|
break;
|
|
}
|
|
n = ir_operands_count(ctx, insn);
|
|
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
|
|
i += n;
|
|
insn += n;
|
|
rule += n;
|
|
}
|
|
bb->flags |= flags;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ir_preallocate_call_stack(ir_ctx *ctx, ir_backend_data *data)
|
|
{
|
|
int call_stack_size, peak_call_stack_size = 0;
|
|
ir_ref i, n;
|
|
ir_insn *insn;
|
|
|
|
for (i = 1, insn = ctx->ir_base + 1; i < ctx->insns_count;) {
|
|
if (insn->op == IR_CALL) {
|
|
call_stack_size = ir_call_used_stack(ctx, insn, 0);
|
|
if (call_stack_size > peak_call_stack_size) {
|
|
peak_call_stack_size = call_stack_size;
|
|
}
|
|
}
|
|
n = ir_operands_count(ctx, insn);
|
|
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
|
|
i += n;
|
|
insn += n;
|
|
}
|
|
data->call_stack_size = peak_call_stack_size;
|
|
ctx->flags |= IR_PREALLOCATED_STACK;
|
|
}
|
|
|
|
static void ir_calc_stack_frame_size(ir_ctx *ctx, ir_backend_data *data)
|
|
{
|
|
int i;
|
|
ir_live_interval **p, *ival;
|
|
uint32_t additional_size = 0;
|
|
|
|
for (i = 1, p = ctx->live_intervals + i; i <= ctx->vregs_count; i++, p++) {
|
|
ival = *p;
|
|
if (ival) {
|
|
if (ival->stack_spill_pos != -1) {
|
|
if (ival->stack_spill_pos + ir_type_size[ival->type] > data->ra_data.stack_frame_size) {
|
|
data->ra_data.stack_frame_size = ival->stack_spill_pos + ir_type_size[ival->type];
|
|
}
|
|
}
|
|
if (ival->reg != IR_REG_NONE) {
|
|
if (!IR_REGSET_IN(data->used_preserved_regs, ival->reg)
|
|
&& IR_REGSET_IN(IR_REGSET_PRESERVED, ival->reg)) {
|
|
IR_REGSET_INCL(data->used_preserved_regs, ival->reg);
|
|
additional_size += sizeof(void*);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ival = ctx->live_intervals[0];
|
|
while (ival) {
|
|
if (ival->reg != IR_REG_NONE) {
|
|
if (!IR_REGSET_IN(data->used_preserved_regs, ival->reg)
|
|
&& IR_REGSET_IN(IR_REGSET_PRESERVED, ival->reg)) {
|
|
IR_REGSET_INCL(data->used_preserved_regs, ival->reg);
|
|
additional_size += sizeof(void*);
|
|
}
|
|
}
|
|
ival = ival->next;
|
|
}
|
|
|
|
data->ra_data.stack_frame_size = IR_ALIGNED_SIZE(data->ra_data.stack_frame_size, sizeof(void*));
|
|
data->ra_data.stack_frame_size += additional_size;
|
|
|
|
if (ctx->flags & IR_HAS_CALLS) {
|
|
/* Stack must be 16 byte aligned */
|
|
if (ctx->flags & IR_USE_FRAME_POINTER) {
|
|
while (IR_ALIGNED_SIZE(data->ra_data.stack_frame_size + sizeof(void*) * 2, 16) != data->ra_data.stack_frame_size + sizeof(void*) * 2) {
|
|
data->ra_data.stack_frame_size += sizeof(void*);
|
|
data->stack_frame_alignment += sizeof(void*);
|
|
}
|
|
} else {
|
|
ir_preallocate_call_stack(ctx, data);
|
|
while (IR_ALIGNED_SIZE(data->ra_data.stack_frame_size + data->call_stack_size + sizeof(void*), 16) !=
|
|
data->ra_data.stack_frame_size + data->call_stack_size + sizeof(void*)) {
|
|
data->ra_data.stack_frame_size += sizeof(void*);
|
|
data->stack_frame_alignment += sizeof(void*);
|
|
}
|
|
}
|
|
}
|
|
|
|
ir_fix_param_spills(ctx);
|
|
}
|
|
|
|
static void* dasm_labels[ir_lb_MAX];
|
|
|
|
void *ir_emit_code(ir_ctx *ctx, size_t *size)
|
|
{
|
|
int b, n, target;
|
|
ir_block *bb;
|
|
ir_ref i;
|
|
ir_insn *insn;
|
|
uint32_t *rule;
|
|
ir_backend_data data;
|
|
dasm_State **Dst;
|
|
int ret;
|
|
void *entry;
|
|
|
|
ctx->data = &data;
|
|
data.ra_data.stack_frame_size = 0;
|
|
data.ra_data.unused_slot_4 = 0;
|
|
data.ra_data.unused_slot_2 = 0;
|
|
data.ra_data.unused_slot_1 = 0;
|
|
data.stack_frame_alignment = 0;
|
|
data.call_stack_size = 0;
|
|
data.used_preserved_regs = 0;
|
|
data.rodata_label = 0;
|
|
data.jmp_table_label = 0;
|
|
|
|
if (!ctx->live_intervals) {
|
|
ir_allocate_unique_spill_slots(ctx);
|
|
} else {
|
|
ir_mark_empty_blocks(ctx);
|
|
ir_calc_stack_frame_size(ctx, &data);
|
|
}
|
|
|
|
Dst = &data.dasm_state;
|
|
data.dasm_state = NULL;
|
|
dasm_init(&data.dasm_state, DASM_MAXSECTION);
|
|
dasm_setupglobal(&data.dasm_state, dasm_labels, ir_lb_MAX);
|
|
dasm_setup(&data.dasm_state, dasm_actions);
|
|
dasm_growpc(&data.dasm_state, ctx->cfg_blocks_count + 1 + ctx->consts_count + 1 + 1 + 1);
|
|
|
|
if (ctx->flags & IR_FUNCTION) {
|
|
ir_emit_prologue(ctx);
|
|
ir_emit_load_params(ctx);
|
|
}
|
|
|
|
for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
|
|
// if (bb->flags & IR_BB_MAY_SKIP) {
|
|
// continue;
|
|
// }
|
|
// if (ir_needs_block_label(ctx, b)) {
|
|
|=>b:
|
|
// }
|
|
for (i = bb->start, insn = ctx->ir_base + i, rule = ctx->rules + i; i <= bb->end;) {
|
|
switch (*rule) {
|
|
case IR_SKIP:
|
|
case IR_SKIP_REG:
|
|
//??? case IR_SKIP_BINOP_INT:
|
|
//??? case IR_SKIP_SHIFT:
|
|
case IR_VAR:
|
|
break;
|
|
case IR_MUL_PWR2:
|
|
case IR_DIV_PWR2:
|
|
case IR_MOD_PWR2:
|
|
ir_emit_mul_div_mod_pwr2(ctx, i, insn);
|
|
break;
|
|
case IR_SHIFT:
|
|
ir_emit_shift(ctx, i, insn);
|
|
break;
|
|
case IR_SHIFT_CONST:
|
|
ir_emit_shift_const(ctx, i, insn);
|
|
break;
|
|
case IR_OP_INT:
|
|
ir_emit_op_int(ctx, i, insn);
|
|
break;
|
|
case IR_OP_FP:
|
|
ir_emit_op_fp(ctx, i, insn);
|
|
break;
|
|
case IR_BINOP_INT:
|
|
ir_emit_binop_int(ctx, i, insn);
|
|
break;
|
|
case IR_BINOP_FP:
|
|
ir_emit_binop_fp(ctx, i, insn);
|
|
break;
|
|
case IR_CMP_INT:
|
|
ir_emit_cmp_int(ctx, i, insn);
|
|
break;
|
|
case IR_CMP_FP:
|
|
ir_emit_cmp_fp(ctx, i, insn);
|
|
break;
|
|
case IR_SEXT:
|
|
ir_emit_sext(ctx, i, insn);
|
|
break;
|
|
case IR_ZEXT:
|
|
ir_emit_zext(ctx, i, insn);
|
|
break;
|
|
case IR_TRUNC:
|
|
ir_emit_trunc(ctx, i, insn);
|
|
break;
|
|
case IR_BITCAST:
|
|
ir_emit_bitcast(ctx, i, insn);
|
|
break;
|
|
case IR_INT2FP:
|
|
ir_emit_int2fp(ctx, i, insn);
|
|
break;
|
|
case IR_FP2INT:
|
|
ir_emit_fp2int(ctx, i, insn);
|
|
break;
|
|
case IR_FP2FP:
|
|
ir_emit_fp2fp(ctx, i, insn);
|
|
break;
|
|
case IR_COPY_INT:
|
|
ir_emit_copy_int(ctx, i, insn);
|
|
break;
|
|
case IR_COPY_FP:
|
|
ir_emit_copy_fp(ctx, i, insn);
|
|
break;
|
|
case IR_CMP_AND_BRANCH_INT:
|
|
ir_emit_cmp_and_branch_int(ctx, b, i, insn);
|
|
break;
|
|
case IR_CMP_AND_BRANCH_FP:
|
|
ir_emit_cmp_and_branch_fp(ctx, b, i, insn);
|
|
break;
|
|
case IR_IF_INT:
|
|
ir_emit_if_int(ctx, b, i, insn);
|
|
break;
|
|
case IR_SWITCH:
|
|
ir_emit_switch(ctx, b, i, insn);
|
|
break;
|
|
case IR_MIN_MAX_INT:
|
|
ir_emit_min_max_int(ctx, i, insn);
|
|
break;
|
|
case IR_OVERFLOW:
|
|
ir_emit_overflow(ctx, i, insn);
|
|
break;
|
|
case IR_OVERFLOW_AND_BRANCH:
|
|
ir_emit_overflow_and_branch(ctx, b, i, insn);
|
|
break;
|
|
case IR_END:
|
|
case IR_LOOP_END:
|
|
if (bb->flags & IR_BB_DESSA_MOVES) {
|
|
data.dessa_from_block = b;
|
|
ir_gen_dessa_moves(ctx, b, ir_emit_dessa_move);
|
|
}
|
|
IR_ASSERT(bb->successors_count == 1);
|
|
target = ir_skip_empty_blocks(ctx, ctx->cfg_edges[bb->successors]);
|
|
if (b == ctx->cfg_blocks_count || target != ir_skip_empty_blocks(ctx, b + 1)) {
|
|
| b =>target
|
|
}
|
|
break;
|
|
case IR_RETURN_VOID:
|
|
ir_emit_return_void(ctx);
|
|
break;
|
|
case IR_RETURN_INT:
|
|
ir_emit_return_int(ctx, i, insn);
|
|
break;
|
|
case IR_RETURN_FP:
|
|
ir_emit_return_fp(ctx, i, insn);
|
|
break;
|
|
case IR_CALL:
|
|
ir_emit_call(ctx, i, insn);
|
|
break;
|
|
case IR_TAILCALL:
|
|
ir_emit_tailcall(ctx, i, insn);
|
|
break;
|
|
case IR_IJMP:
|
|
ir_emit_ijmp(ctx, i, insn);
|
|
break;
|
|
case IR_VADDR:
|
|
ir_emit_vaddr(ctx, i, insn);
|
|
break;
|
|
case IR_VLOAD_INT:
|
|
ir_emit_vload_int(ctx, i, insn);
|
|
break;
|
|
case IR_VLOAD_FP:
|
|
ir_emit_vload_fp(ctx, i, insn);
|
|
break;
|
|
case IR_VSTORE_INT:
|
|
ir_emit_vstore_int(ctx, i, insn);
|
|
break;
|
|
case IR_VSTORE_FP:
|
|
ir_emit_vstore_fp(ctx, i, insn);
|
|
break;
|
|
case IR_LOAD_INT:
|
|
ir_emit_load_int(ctx, i, insn);
|
|
break;
|
|
case IR_LOAD_FP:
|
|
ir_emit_load_fp(ctx, i, insn);
|
|
break;
|
|
case IR_STORE_INT:
|
|
ir_emit_store_int(ctx, i, insn);
|
|
break;
|
|
case IR_STORE_FP:
|
|
ir_emit_store_fp(ctx, i, insn);
|
|
break;
|
|
case IR_ALLOCA:
|
|
ir_emit_alloca(ctx, i, insn);
|
|
break;
|
|
default:
|
|
IR_ASSERT(0 && "NIY rule/insruction");
|
|
break;
|
|
}
|
|
n = ir_operands_count(ctx, insn);
|
|
n = 1 + (n >> 2); // support for multi-word instructions like MERGE and PHI
|
|
i += n;
|
|
insn += n;
|
|
rule += n;
|
|
}
|
|
}
|
|
|
|
if (data.rodata_label) {
|
|
|.rodata
|
|
}
|
|
for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
|
|
if (insn->emit_const) {
|
|
if (IR_IS_TYPE_FP(insn->type)) {
|
|
int label = ctx->cfg_blocks_count + i;
|
|
|
|
if (!data.rodata_label) {
|
|
data.rodata_label = ctx->cfg_blocks_count + ctx->consts_count + 2;
|
|
|
|
|.rodata
|
|
|=>data.rodata_label:
|
|
}
|
|
if (insn->type == IR_DOUBLE) {
|
|
|.align 8
|
|
|=>label:
|
|
|.long insn->val.u32, insn->val.u32_hi
|
|
} else if (insn->type == IR_FLOAT) {
|
|
|.align 4
|
|
|=>label:
|
|
|.long insn->val.u32
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
} else if (insn->op == IR_STR) {
|
|
int label = ctx->cfg_blocks_count + i;
|
|
const char *str = ir_get_str(ctx, insn->val.addr);
|
|
int i = 0;
|
|
|
|
if (!data.rodata_label) {
|
|
data.rodata_label = ctx->cfg_blocks_count + ctx->consts_count + 2;
|
|
|
|
|.rodata
|
|
|=>data.rodata_label:
|
|
}
|
|
|.align 8
|
|
|=>label:
|
|
while (str[i]) {
|
|
char c = str[i];
|
|
|
|
if (c == '\\') {
|
|
if (str[i+1] == '\\') {
|
|
i++;
|
|
c = '\\';
|
|
} else if (str[i+1] == 'n') {
|
|
i++;
|
|
c = '\n';
|
|
} else if (str[i+1] == 'r') {
|
|
i++;
|
|
c = '\r';
|
|
} else if (str[i+1] == 't') {
|
|
i++;
|
|
c = '\t';
|
|
}
|
|
|
|
}
|
|
//??? |.db c
|
|
i++;
|
|
}
|
|
//??? |.db 0
|
|
|
|
} else {
|
|
IR_ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
if (data.rodata_label) {
|
|
|.code
|
|
}
|
|
|
|
ret = dasm_link(&data.dasm_state, size);
|
|
if (ret != DASM_S_OK) {
|
|
return 0;
|
|
}
|
|
|
|
entry = ir_mem_mmap(4096);
|
|
ir_mem_unprotect(entry, 4096);
|
|
|
|
ret = dasm_encode(&data.dasm_state, entry);
|
|
|
|
if (data.jmp_table_label) {
|
|
uint32_t offset = dasm_getpclabel(&data.dasm_state, data.jmp_table_label);
|
|
ctx->jmp_table_offset = offset;
|
|
} else {
|
|
ctx->jmp_table_offset = 0;
|
|
}
|
|
if (data.rodata_label) {
|
|
uint32_t offset = dasm_getpclabel(&data.dasm_state, data.rodata_label);
|
|
ctx->rodata_offset = offset;
|
|
} else {
|
|
ctx->rodata_offset = 0;
|
|
}
|
|
|
|
if (ret != DASM_S_OK) {
|
|
IR_ASSERT(0);
|
|
ir_mem_unmap(entry, 4096);
|
|
return NULL;
|
|
}
|
|
dasm_free(&data.dasm_state);
|
|
|
|
ir_mem_flush(entry, 4096);
|
|
ir_mem_protect(entry, 4096);
|
|
|
|
return entry;
|
|
}
|