From 5cafe50d360ef4c53292be960032201f07f379b1 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 10 Jun 2022 00:16:29 +0300 Subject: [PATCH] Initial support for PHP --- ir.c | 38 +++++++++++++++++++++++++++++++++- ir.h | 43 ++++++++++++++++++++++++-------------- ir_aarch64.dasc | 49 +++++++++++++++++++++++++++++++++++++++---- ir_cfg.c | 4 ++-- ir_main.c | 2 +- ir_php.h | 23 +++++++++++++++++++++ ir_private.h | 11 +++++----- ir_ra.c | 9 +++++++- ir_x86.dasc | 55 +++++++++++++++++++++++++++++++++++++++++++------ 9 files changed, 198 insertions(+), 36 deletions(-) create mode 100644 ir_php.h diff --git a/ir.c b/ir.c index a7896cf..4e72f41 100644 --- a/ir.c +++ b/ir.c @@ -41,6 +41,9 @@ const char *ir_type_cname[IR_LAST_TYPE] = { const char *ir_op_name[IR_LAST_OP] = { IR_OPS(IR_OP_NAME) +#ifdef IR_PHP + IR_PHP_OPS(IR_OP_NAME) +#endif }; void ir_print_const(ir_ctx *ctx, ir_insn *insn, FILE *f) @@ -70,7 +73,11 @@ void ir_print_const(ir_ctx *ctx, ir_insn *insn, FILE *f) fprintf(f, "%" PRIu64, insn->val.u64); break; case IR_ADDR: - fprintf(f, "%" PRIxPTR, insn->val.addr); + if (insn->val.addr) { + fprintf(f, "0x%" PRIxPTR, insn->val.addr); + } else { + fprintf(f, "0"); + } break; case IR_CHAR: if (insn->val.c == '\\') { @@ -136,14 +143,39 @@ void ir_print_const(ir_ctx *ctx, ir_insn *insn, FILE *f) #define ir_op_flag_c0X2 (ir_op_flag_c | 0 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_c1 (ir_op_flag_c | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_c1X1 (ir_op_flag_c | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_c1X2 (ir_op_flag_c | 1 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_c2 (ir_op_flag_c | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_c2X1 (ir_op_flag_c | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_c3 (ir_op_flag_c | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_cN (ir_op_flag_c | 4 | (4 << IR_OP_FLAG_OPERANDS_SHIFT)) // MERGE (number of operands encoded in op1) +#define ir_op_flag_B (IR_OP_FLAG_CONTROL|IR_OP_FLAG_BB_BEGIN) +#define ir_op_flag_B0 ir_op_flag_B +#define ir_op_flag_B0X1 (ir_op_flag_B | 0 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_B0X2 (ir_op_flag_B | 0 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_B1 (ir_op_flag_B | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_B1X1 (ir_op_flag_B | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_B1X2 (ir_op_flag_B | 1 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_B2 (ir_op_flag_B | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_B2X1 (ir_op_flag_B | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_B3 (ir_op_flag_B | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_BN (ir_op_flag_B | 4 | (4 << IR_OP_FLAG_OPERANDS_SHIFT)) // MERGE (number of operands encoded in op1) +#define ir_op_flag_E (IR_OP_FLAG_CONTROL|IR_OP_FLAG_BB_END) +#define ir_op_flag_E0 ir_op_flag_B +#define ir_op_flag_E0X1 (ir_op_flag_E | 0 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_E0X2 (ir_op_flag_E | 0 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_E1 (ir_op_flag_E | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_E1X1 (ir_op_flag_E | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_E1X2 (ir_op_flag_E | 1 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_E2 (ir_op_flag_E | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_E2X1 (ir_op_flag_E | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_E3 (ir_op_flag_E | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_EN (ir_op_flag_E | 4 | (4 << IR_OP_FLAG_OPERANDS_SHIFT)) // MERGE (number of operands encoded in op1) #define ir_op_flag_l (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_LOAD) #define ir_op_flag_l0 ir_op_flag_l #define ir_op_flag_l1 (ir_op_flag_l | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_l1X1 (ir_op_flag_l | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_l2 (ir_op_flag_l | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_l2X1 (ir_op_flag_l | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_l3 (ir_op_flag_l | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_s (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_STORE) #define ir_op_flag_s0 ir_op_flag_s @@ -160,6 +192,7 @@ void ir_print_const(ir_ctx *ctx, ir_insn *insn, FILE *f) #define ir_op_kind_reg IR_OPND_CONTROL_DEP #define ir_op_kind_beg IR_OPND_CONTROL_REF #define ir_op_kind_ret IR_OPND_CONTROL_REF +#define ir_op_kind_ent IR_OPND_CONTROL_REF #define ir_op_kind_str IR_OPND_STR #define ir_op_kind_num IR_OPND_NUM #define ir_op_kind_fld IR_OPND_STR @@ -171,6 +204,9 @@ void ir_print_const(ir_ctx *ctx, ir_insn *insn, FILE *f) const uint32_t ir_op_flags[IR_LAST_OP] = { IR_OPS(_IR_OP_FLAGS) +#ifdef IR_PHP + IR_PHP_OPS(_IR_OP_FLAGS) +#endif }; static void ir_grow_bottom(ir_ctx *ctx) diff --git a/ir.h b/ir.h index 6a6d996..a241637 100644 --- a/ir.h +++ b/ir.h @@ -3,6 +3,7 @@ #include #include +#include #include #define IR_VERSION "0.0.1" @@ -43,7 +44,9 @@ # error "Unknown byte order" #endif -typedef uint8_t bool; +#ifdef IR_PHP +# include "ir_php.h" +#endif /* IR Type flags (low 4 bits are used for type size) */ #define IR_TYPE_SIGNED (1<<4) @@ -94,6 +97,8 @@ typedef enum _ir_type { * d - data IR_OP_FLAG_DATA * r - ref IR_OP_FLAG_DATA alias * c - control IR_OP_FLAG_CONTROL + * B - control IR_OP_FLAG_CONTROL + IR_OP_FLAG_BB_BEGIN + * E - control IR_OP_FLAG_CONTROL + IR_OP_FLAG_BB_END * l - load IR_OP_FLAG_MEM + IR_OP_FLAG_MEM_LOAD * s - store IR_OP_FLAG_MEM + IR_OP_FLAG_STORE * x - call IR_OP_FLAG_MEM + IR_OP_FLAG_CALL @@ -115,6 +120,7 @@ typedef enum _ir_type { * reg - data-control dependency on region (PHI, VAR, PARAM) * beg - reference to a LOOP_BEGIN region (LOOP_END, LOOP_EXIT) * ret - reference to a previous RETURN instruction (RETURN) + * ent - reference to a previous ENTRY instruction (ENTRY) * str - string: variable/argument name (VAR, PARAM, CALL, TAILCALL) * num - number: argument number (PARAM) * prb - branch probability 1-99 (0 - unspecified): (IF_TRUE, IF_FALSE, CASE_VAL, CASE_DEFAULT) @@ -223,27 +229,29 @@ typedef enum _ir_type { _(VADDR, d1, var, ___, ___) /* load address of local var */ \ _(VLOAD, l2, src, var, ___) /* load value of local var */ \ _(VSTORE, s3, src, var, def) /* store value to local var */ \ + _(RLOAD, l1X1, src, num, ___) /* load value from register */ \ + _(RSTORE, l2X1, src, def, num) /* store value into register */ \ _(LOAD, l2, src, ref, ___) /* load from memory */ \ _(STORE, s3, src, ref, def) /* store to memory */ \ /* memory reference ops (A, H, U, S, TMP, STR, NEW, X, V) ??? */ \ \ /* control-flow nodes */ \ - _(START, c0X1, ret, ___, ___) /* function start */ \ - _(RETURN, c2X1, src, def, ret) /* function return */ \ - _(UNREACHABLE, c2X1, src, def, ret) /* unreachable (tailcall, etc) */ \ - _(BEGIN, c1, src, ___, ___) /* block start */ \ - _(END, c1, src, ___, ___) /* block end */ \ - _(IF, c2, src, def, ___) /* conditional control split */ \ - _(IF_TRUE, c1X1, src, prb, ___) /* IF TRUE proj. */ \ - _(IF_FALSE, c1X1, src, prb, ___) /* IF FALSE proj. */ \ - _(SWITCH, c2, src, def, ___) /* multi-way control split */ \ - _(CASE_VAL, c2X1, src, def, prb) /* switch proj. */ \ - _(CASE_DEFAULT, c1X1, src, prb, ___) /* switch proj. */ \ - _(MERGE, cN, src, src, src) /* control merge */ \ - _(LOOP_BEGIN, c2, src, src, ___) /* loop start */ \ - _(LOOP_END, c1X1, src, beg, ___) /* loop end */ \ + _(START, B0X2, ret, ent, ___) /* function start */ \ + _(RETURN, E2X1, src, def, ret) /* function return */ \ + _(UNREACHABLE, E2X1, src, def, ret) /* unreachable (tailcall, etc) */ \ + _(BEGIN, B1, src, ___, ___) /* block start */ \ + _(END, E1, src, ___, ___) /* block end */ \ + _(IF, E2, src, def, ___) /* conditional control split */ \ + _(IF_TRUE, B1X1, src, prb, ___) /* IF TRUE proj. */ \ + _(IF_FALSE, B1X1, src, prb, ___) /* IF FALSE proj. */ \ + _(SWITCH, E2, src, def, ___) /* multi-way control split */ \ + _(CASE_VAL, B2X1, src, def, prb) /* switch proj. */ \ + _(CASE_DEFAULT, B1X1, src, prb, ___) /* switch proj. */ \ + _(MERGE, BN, src, src, src) /* control merge */ \ + _(LOOP_BEGIN, B2, src, src, ___) /* loop start */ \ + _(LOOP_END, E1X1, src, beg, ___) /* loop end */ \ _(LOOP_EXIT, c1X1, src, beg, ___) /* loop exit */ \ - _(IJMP, c2X1, src, def, ret) /* computed goto */ \ + _(IJMP, E2X1, src, def, ret) /* computed goto */ \ \ /* guards (floating or not) ??? */ \ _(GUARD_TRUE, c2, src, def, ___) /* IF without second successor */ \ @@ -253,6 +261,9 @@ typedef enum _ir_type { typedef enum _ir_op { IR_OPS(IR_OP_ENUM) +#ifdef IR_PHP + IR_PHP_OPS(IR_OP_ENUM) +#endif IR_LAST_OP } ir_op; diff --git a/ir_aarch64.dasc b/ir_aarch64.dasc index 99cd357..cf05a05 100644 --- a/ir_aarch64.dasc +++ b/ir_aarch64.dasc @@ -405,6 +405,7 @@ uint8_t ir_get_use_flags(ir_ctx *ctx, ir_ref ref, int op_num) case IR_BITCAST: case IR_RETURN_INT: case IR_RETURN_FP: + case IR_RSTORE: case IR_SKIP_REG: /* PARAM PHI PI */ return IR_USE_SHOULD_BE_IN_REG; } @@ -856,6 +857,7 @@ binop_fp: case IR_PHI: case IR_PI: case IR_PARAM: + case IR_RLOAD: return IR_SKIP_REG; case IR_CALL: @@ -2934,6 +2936,28 @@ static void ir_emit_store_fp(ir_ctx *ctx, ir_ref ref, ir_insn *insn) ir_emit_store_mem_fp(ctx, type, op2_reg, 0, op3_reg); } +static void ir_emit_rstore(ir_ctx *ctx, ir_ref ref, ir_insn *insn) +{ + ir_ref type = ctx->ir_base[insn->op2].type; + ir_reg op2_reg = ctx->regs[ref][2]; + ir_reg dst_reg = insn->op3; + + 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); + } + if (IR_IS_TYPE_INT(type)) { + ir_emit_mov(ctx, type, dst_reg, op2_reg); + } else { + IR_ASSERT(IR_IS_TYPE_FP(type)); + ir_emit_fp_mov(ctx, type, dst_reg, op2_reg); + } + } else { + ir_emit_load(ctx, type, dst_reg, insn->op2); + } +} + static void ir_emit_alloca(ir_ctx *ctx, ir_ref def, ir_insn *insn) { ir_backend_data *data = ctx->data; @@ -3445,8 +3469,15 @@ static void ir_emit_call(ir_ctx *ctx, ir_ref def, ir_insn *insn) 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); + ir_insn *addr_insn = &ctx->ir_base[insn->op2]; + void *addr; + + IR_ASSERT(addr_insn->type == IR_ADDR); + if (addr_insn->op == IR_FUNC) { + addr = ir_resolve_sym_name(ir_get_str(ctx, addr_insn->val.addr)); + } else { + addr = (void*)addr_insn->val.addr; + } | bl &addr } else { @@ -3507,8 +3538,15 @@ static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn) 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); + ir_insn *addr_insn = &ctx->ir_base[insn->op2]; + void *addr; + + IR_ASSERT(addr_insn->type == IR_ADDR); + if (addr_insn->op == IR_FUNC) { + addr = ir_resolve_sym_name(ir_get_str(ctx, addr_insn->val.addr)); + } else { + addr = (void*)addr_insn->val.addr; + } | b &addr } else { @@ -4321,6 +4359,9 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size) case IR_VSTORE: ir_emit_vstore(ctx, i, insn); break; + case IR_RSTORE: + ir_emit_rstore(ctx, i, insn); + break; case IR_LOAD_INT: ir_emit_load_int(ctx, i, insn); break; diff --git a/ir_cfg.c b/ir_cfg.c index e5d9ef0..b06b263 100644 --- a/ir_cfg.c +++ b/ir_cfg.c @@ -29,7 +29,7 @@ int ir_build_cfg(ir_ctx *ctx) while (1) { insn = &ctx->ir_base[ref]; _blocks[ref] = bb_count; - if (IR_IS_BB_START(insn->op)) { + if (IR_IS_BB_BEGIN(insn->op)) { ir_bitset_incl(worklist.visited, ref); break; } @@ -62,7 +62,7 @@ int ir_build_cfg(ir_ctx *ctx) _blocks[ref] = n; bb = &blocks[n]; insn = &ctx->ir_base[ref]; - if (IR_IS_BB_START(insn->op)) { + if (IR_IS_BB_BEGIN(insn->op)) { bb->start = ref; } else { bb->end = ref; diff --git a/ir_main.c b/ir_main.c index 71d3dc4..7577770 100644 --- a/ir_main.c +++ b/ir_main.c @@ -397,7 +397,7 @@ int main(int argc, char **argv) ir_perf_map_register("test", entry, size); ir_perf_jitdump_open(); - ir_perf_jitdump_register("test", entry, size); + ir_perf_jitdump_register("test", entry, size); ir_mem_unprotect(entry, 4096); ir_gdb_register("test", entry, size, 0, 0); diff --git a/ir_php.h b/ir_php.h new file mode 100644 index 0000000..99ce2f7 --- /dev/null +++ b/ir_php.h @@ -0,0 +1,23 @@ +#ifndef IR_PHP_H +#define IR_PHP_H + +#define IR_PHP_OPS(_) \ + _(PHP_ENTRY, B0X2, num, ent, ___) /* PHP Code Entry */ \ + +#if defined(IR_TARGET_X86) +# define IR_REG_PHP_FP IR_REG_RSI +# define IR_REG_PHP_IP IR_REG_RDI +#elif defined(IR_TARGET_X64) +# define IR_REG_PHP_FP IR_REG_R14 +# define IR_REG_PHP_IP IR_REG_R15 +#elif defined(IR_TARGET_AARCH64) +# define IR_REG_PHP_FP IR_REG_X27 +# define IR_REG_PHP_IP IR_REG_X28 +#else +# error "Unknown IR target" +#endif + +#define IR_REGSET_PHP_FIXED \ + (IR_REGSET(IR_REG_PHP_FP) | IR_REGSET(IR_REG_PHP_IP)) + +#endif /* IR_PHP_H */ diff --git a/ir_private.h b/ir_private.h index c89a5fc..ab63033 100644 --- a/ir_private.h +++ b/ir_private.h @@ -544,6 +544,8 @@ extern const char *ir_op_name[IR_LAST_OP]; #define IR_OP_FLAG_CONTROL (1<<9) #define IR_OP_FLAG_MEM (1<<10) #define IR_OP_FLAG_COMMUTATIVE (1<<11) +#define IR_OP_FLAG_BB_BEGIN (1<<12) +#define IR_OP_FLAG_BB_END (1<<13) #define IR_OP_FLAG_MEM_LOAD ((0<<6)|(0<<7)) #define IR_OP_FLAG_MEM_STORE ((0<<6)|(1<<7)) @@ -616,16 +618,14 @@ struct _ir_use_list { }; /*** IR Basic Blocks info ***/ -#define IR_IS_BB_START(op) \ - ((op) == IR_START || (op) == IR_BEGIN || (op) == IR_MERGE || (op) == IR_LOOP_BEGIN || \ - (op) == IR_IF_TRUE || (op) == IR_IF_FALSE || (op) == IR_CASE_VAL || (op) == IR_CASE_DEFAULT) +#define IR_IS_BB_BEGIN(op) \ + ((ir_op_flags[op] & IR_OP_FLAG_BB_BEGIN) != 0) #define IR_IS_BB_MERGE(op) \ ((op) == IR_MERGE || (op) == IR_LOOP_BEGIN) #define IR_IS_BB_END(op) \ - ((op) == IR_RETURN || (op) == IR_END || (op) == IR_LOOP_END || (op) == IR_IF || \ - (op) == IR_SWITCH || (op) == IR_IJMP || (op) == IR_UNREACHABLE) + ((ir_op_flags[op] & IR_OP_FLAG_BB_END) != 0) #define IR_BB_UNREACHABLE (1<<0) #define IR_BB_LOOP_HEADER (1<<1) @@ -723,6 +723,7 @@ struct _ir_live_range { #define IR_LIVE_INTERVAL_HAS_HINTS (1<<6) #define IR_LIVE_INTERVAL_MEM_PARAM (1<<7) #define IR_LIVE_INTERVAL_MEM_LOAD (1<<8) +#define IR_LIVE_INTERVAL_REG_LOAD (1<<9) struct _ir_live_interval { uint8_t type; diff --git a/ir_ra.c b/ir_ra.c index 30b38b4..8e393ad 100644 --- a/ir_ra.c +++ b/ir_ra.c @@ -406,7 +406,12 @@ int ir_compute_live_ranges(ir_ctx *ctx) if ((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) { if (ctx->vregs[i]) { if (ir_bitset_in(live, ctx->vregs[i])) { - if (insn->op != IR_PHI) { + if (insn->op == IR_RLOAD) { + ir_fix_live_range(ctx, ctx->vregs[i], + IR_START_LIVE_POS_FROM_REF(bb->start), IR_DEF_LIVE_POS_FROM_REF(i)); + ctx->live_intervals[ctx->vregs[i]]->flags = IR_LIVE_INTERVAL_REG_LOAD; + ctx->live_intervals[ctx->vregs[i]]->reg = insn->op2; + } else if (insn->op != IR_PHI) { ir_live_pos def_pos; ir_ref hint_ref = 0; @@ -2051,6 +2056,8 @@ static int ir_linear_scan(ir_ctx *ctx) if (ival->stack_spill_pos == -1) { ival->stack_spill_pos = ir_allocate_spill_slot(ctx, ival->type, &data); } + } else if (ival->flags & IR_LIVE_INTERVAL_REG_LOAD) { + /* pre-allocated fixed register */ } else if (!(ival->flags & (IR_LIVE_INTERVAL_MEM_PARAM|IR_LIVE_INTERVAL_MEM_LOAD)) || !ir_ival_spill_for_fuse_load(ctx, ival, &data)) { ir_add_to_unhandled(&unhandled, ival); diff --git a/ir_x86.dasc b/ir_x86.dasc index 5d021f4..3cf3321 100644 --- a/ir_x86.dasc +++ b/ir_x86.dasc @@ -875,6 +875,7 @@ uint8_t ir_get_use_flags(ir_ctx *ctx, ir_ref ref, int op_num) case IR_RETURN_INT: case IR_RETURN_FP: case IR_IJMP: + case IR_RSTORE: case IR_SKIP_REG: /* PARAM PHI PI */ return IR_USE_SHOULD_BE_IN_REG; // case IR_VLOAD: @@ -1414,6 +1415,7 @@ binop_fp: case IR_PHI: case IR_PI: case IR_PARAM: + case IR_RLOAD: return IR_SKIP_REG; case IR_CALL: @@ -3411,7 +3413,9 @@ 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); + if (ctx->flags & IR_FUNCTION) { + ir_emit_epilogue(ctx); + } | ret } @@ -4324,6 +4328,28 @@ static void ir_emit_store_fp(ir_ctx *ctx, ir_ref ref, ir_insn *insn) ir_emit_store_mem_fp(ctx, type, op2_reg, 0, op3_reg); } +static void ir_emit_rstore(ir_ctx *ctx, ir_ref ref, ir_insn *insn) +{ + ir_ref type = ctx->ir_base[insn->op2].type; + ir_reg op2_reg = ctx->regs[ref][2]; + ir_reg dst_reg = insn->op3; + + 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); + } + if (IR_IS_TYPE_INT(type)) { + ir_emit_mov(ctx, type, dst_reg, op2_reg); + } else { + IR_ASSERT(IR_IS_TYPE_FP(type)); + ir_emit_fp_mov(ctx, type, dst_reg, op2_reg); + } + } else { + ir_emit_load(ctx, type, dst_reg, insn->op2); + } +} + static void ir_emit_alloca(ir_ctx *ctx, ir_ref def, ir_insn *insn) { ir_backend_data *data = ctx->data; @@ -4941,9 +4967,15 @@ static void ir_emit_call(ir_ctx *ctx, ir_ref def, ir_insn *insn) 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); + ir_insn *addr_insn = &ctx->ir_base[insn->op2]; + void *addr; + IR_ASSERT(addr_insn->type == IR_ADDR); + if (addr_insn->op == IR_FUNC) { + addr = ir_resolve_sym_name(ir_get_str(ctx, addr_insn->val.addr)); + } else { + addr = (void*)addr_insn->val.addr; + } if (sizeof(void*) == 4 /*|| IR_IS_SIGNED_32BIT(addr)*/) { // TODO: 32-bit IP relative or 64-bit absolute address | call aword &addr } else { @@ -5016,12 +5048,20 @@ static void ir_emit_tailcall(ir_ctx *ctx, ir_ref def, ir_insn *insn) IR_ASSERT(used_stack == 0); - ir_emit_epilogue(ctx); + if (ctx->flags & IR_FUNCTION) { + 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); + ir_insn *addr_insn = &ctx->ir_base[insn->op2]; + void *addr; + IR_ASSERT(addr_insn->type == IR_ADDR); + if (addr_insn->op == IR_FUNC) { + addr = ir_resolve_sym_name(ir_get_str(ctx, addr_insn->val.addr)); + } else { + addr = (void*)addr_insn->val.addr; + } if (sizeof(void*) == 4 /*|| IR_IS_SIGNED_32BIT(addr)*/) { // TODO: 32-bit IP relative or 64-bit absolute address | jmp aword &addr } else { @@ -6363,6 +6403,9 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size) case IR_VSTORE_FP: ir_emit_vstore_fp(ctx, i, insn); break; + case IR_RSTORE: + ir_emit_rstore(ctx, i, insn); + break; case IR_LOAD_INT: ir_emit_load_int(ctx, i, insn); break;