Initial support for PHP

This commit is contained in:
Dmitry Stogov 2022-06-10 00:16:29 +03:00
parent 63ec9b4864
commit 5cafe50d36
9 changed files with 198 additions and 36 deletions

38
ir.c
View File

@ -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)

43
ir.h
View File

@ -3,6 +3,7 @@
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

23
ir_php.h Normal file
View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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;