Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/scripts/download-bundled/jit-ir.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cd "$(dirname "$0")/../../.."
tmp_dir=/tmp/php-src-download-bundled/jit-ir
rm -rf "$tmp_dir"

revision=30a40633b0b8f3e8e66da0a25941c4199d69d38a
revision=43d1d1023a6ea811b11b39ed738f5e18696a1254

git clone --depth 1 --revision="$revision" https://github.com/dstogov/ir.git "$tmp_dir"

Expand All @@ -32,6 +32,7 @@ rm ir_load.c
rm ir_load_llvm.c
rm ir_main.c
rm ir_mem2ssa.c
rm ir_ra_fast.c

# add extra files
git restore README
30 changes: 28 additions & 2 deletions ext/opcache/jit/ir/ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,20 @@ static void ir_print_double(double v, FILE *f)
char buf[128];

if (isnan(v)) {
fprintf(f, "nan");
union {
double d;
long long i;
} u1, u2, u3;
u1.d = v;
u2.d = NAN;
u3.d = -NAN;
if (u1.i == u2.i) {
fprintf(f, "nan");
} else if (u1.i == u3.i) {
fprintf(f, "-nan");
} else {
fprintf(f, "nan(0x%llx)", u1.i);
}
} else {
snprintf(buf, sizeof(buf), "%g", v);
if (strtod(buf, NULL) != v) {
Expand All @@ -137,7 +150,20 @@ static void ir_print_float(float v, FILE *f)
char buf[128];

if (isnan(v)) {
fprintf(f, "nan");
union {
float f;
int i;
} u1, u2, u3;
u1.f = v;
u2.f = NAN;
u3.f = -NAN;
if (u1.i == u2.i) {
fprintf(f, "nan");
} else if (u1.i == u3.i) {
fprintf(f, "-nan");
} else {
fprintf(f, "nan(0x%x)", u1.i);
}
} else {
snprintf(buf, sizeof(buf), "%g", v);
if (strtod(buf, NULL) != v) {
Expand Down
3 changes: 2 additions & 1 deletion ext/opcache/jit/ir/ir.h
Original file line number Diff line number Diff line change
Expand Up @@ -968,8 +968,9 @@ int ir_coalesce(ir_ctx *ctx);
int ir_compute_dessa_moves(ir_ctx *ctx);
int ir_reg_alloc(ir_ctx *ctx);
int ir_reg_alloc_simple(ir_ctx *ctx);
int ir_reg_alloc_fast(ir_ctx *ctx);

#define ir_reg_alloc_O0(_ctx) ir_reg_alloc_simple(_ctx)
#define ir_reg_alloc_O0(_ctx) ir_reg_alloc_fast(_ctx)

int ir_regs_number(void);
bool ir_reg_is_int(int32_t reg);
Expand Down
65 changes: 39 additions & 26 deletions ext/opcache/jit/ir/ir_aarch64.dasc
Original file line number Diff line number Diff line change
Expand Up @@ -1413,8 +1413,9 @@ binop_fp:
if (IR_IS_TYPE_VECTOR(insn->type)) {
return IR_VECTOR_OP;
}
IR_FALLTHROUGH;
#endif
IR_ASSERT(IR_IS_TYPE_INT(ctx->ir_base[insn->op1].type));
IR_FALLTHROUGH;
case IR_BSWAP:
case IR_CTLZ:
case IR_CTTZ:
Expand Down Expand Up @@ -3487,29 +3488,41 @@ static void ir_emit_sdiv_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn)
}

if (ir_type_size[type] == 8) {
| cmp Rx(op1_reg), #0
if (op2_reg != IR_REG_NONE) {
| add Rx(def_reg), Rx(op1_reg), Rx(op2_reg)
if (shift == 63) {
| movz Rx(def_reg), #0x8000, lsl #48
| cmp Rx(def_reg), Rx(op1_reg)
| cset Rx(def_reg), eq
} else {
| add Rx(def_reg), Rx(op1_reg), #offset
| cmp Rx(op1_reg), #0
if (op2_reg != IR_REG_NONE) {
| add Rx(def_reg), Rx(op1_reg), Rx(op2_reg)
} else {
| add Rx(def_reg), Rx(op1_reg), #offset
}
| csel Rx(def_reg), Rx(def_reg), Rx(op1_reg), lt
| asr Rx(def_reg), Rx(def_reg), #shift
}
| csel Rx(def_reg), Rx(def_reg), Rx(op1_reg), lt
| asr Rx(def_reg), Rx(def_reg), #shift
} else {
| cmp Rw(op1_reg), #0
if (op2_reg != IR_REG_NONE) {
| add Rw(def_reg), Rw(op1_reg), Rw(op2_reg)
} else {
| add Rw(def_reg), Rw(op1_reg), #offset
}
| csel Rw(def_reg), Rw(def_reg), Rw(op1_reg), lt
if (ir_type_size[type] == 4) {
| asr Rw(def_reg), Rw(def_reg), #shift
} else if (ir_type_size[type] == 2) {
| ubfx Rw(def_reg), Rw(def_reg), #shift, #16
if (shift == 31) {
| movz Rw(def_reg), #0x8000, lsl #16
| cmp Rw(def_reg), Rw(op1_reg)
| cset Rw(def_reg), eq
} else {
IR_ASSERT(ir_type_size[type] == 1);
| ubfx Rw(def_reg), Rw(def_reg), #shift, #8
| cmp Rw(op1_reg), #0
if (op2_reg != IR_REG_NONE) {
| add Rw(def_reg), Rw(op1_reg), Rw(op2_reg)
} else {
| add Rw(def_reg), Rw(op1_reg), #offset
}
| csel Rw(def_reg), Rw(def_reg), Rw(op1_reg), lt
if (ir_type_size[type] == 4) {
| asr Rw(def_reg), Rw(def_reg), #shift
} else if (ir_type_size[type] == 2) {
| ubfx Rw(def_reg), Rw(def_reg), #shift, #16
} else {
IR_ASSERT(ir_type_size[type] == 1);
| ubfx Rw(def_reg), Rw(def_reg), #shift, #8
}
}
}
if (IR_REG_SPILLED(ctx->regs[def][0])) {
Expand Down Expand Up @@ -3965,10 +3978,10 @@ static void ir_emit_binop_fp(ir_ctx *ctx, ir_ref def, ir_insn *insn)
| 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
| ASM_FP_REG_REG_REG_OP fminnm, 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
| ASM_FP_REG_REG_REG_OP fmaxnm, type, def_reg, op1_reg, op2_reg
break;
}
if (IR_REG_SPILLED(ctx->regs[def][0])) {
Expand Down Expand Up @@ -9272,10 +9285,10 @@ static void ir_fix_param_spills(ir_ctx *ctx)
if (ival->flags & IR_LIVE_INTERVAL_MEM_PARAM) {
IR_ASSERT(ival->stack_spill_pos == -1);
ival->stack_spill_pos = stack_start + stack_offset;
if (ival->flags & IR_LIVE_INTERVAL_VAR_MEM_PARAM) {
/* Remap VAR to PARAM stack slot */
ir_remap_param_spill_slot(ctx, use, stack_start + stack_offset);
}
}
if (ival->flags & IR_LIVE_INTERVAL_VAR_MEM_PARAM) {
/* Remap VAR to PARAM stack slot */
ir_remap_param_spill_slot(ctx, use, stack_start + stack_offset);
}
}
if (sizeof(void*) == 8) {
Expand Down
32 changes: 32 additions & 0 deletions ext/opcache/jit/ir/ir_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,34 @@ static IR_NEVER_INLINE uint32_t ir_collect_irreducible_loops(ir_ctx *ctx, uint32
return loops;
}

static IR_NEVER_INLINE void ir_check_loop_entry_in_preds(ir_ctx *ctx, uint32_t hdr, ir_worklist *work)
{
ir_block *blocks = ctx->cfg_blocks;
uint32_t *edges = ctx->cfg_edges;

ir_worklist_clear(work);
ir_bitset_incl(work->visited, blocks[hdr].idom);
ir_worklist_push(work, hdr);
while (ir_worklist_len(work)) {
uint32_t b = ir_worklist_pop(work);
ir_block *bb = &blocks[b];

if (bb->flags & IR_BB_ENTRY) {
blocks[hdr].flags |= IR_BB_LOOP_WITH_ENTRY_IN_PREDS;
break;
}

uint32_t *p = &edges[bb->predecessors];
uint32_t count = bb->predecessors_count;

for (; count > 0; p++, count--) {
if (!ir_dominates(blocks, hdr, *p)) {
ir_worklist_push(work, *p);
}
}
}
}

int ir_find_loops(ir_ctx *ctx)
{
uint32_t b, j, n;
Expand Down Expand Up @@ -1295,6 +1323,10 @@ int ir_find_loops(ir_ctx *ctx)
b = loops;
do {
bb = &blocks[b];
if (ctx->entries_count
&& (bb->predecessors_count > 2 || (bb->flags & IR_BB_IRREDUCIBLE_LOOP))) {
ir_check_loop_entry_in_preds(ctx, b, &work);
}
b = bb->next_loop;
IR_ASSERT(bb->flags & IR_BB_LOOP_HEADER);
bb->loop_depth = (bb->loop_header) ? blocks[bb->loop_header].loop_depth + 1 : 1;
Expand Down
20 changes: 13 additions & 7 deletions ext/opcache/jit/ir/ir_emit.c
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,9 @@ static IR_NEVER_INLINE void ir_emit_osr_entry_loads(ir_ctx *ctx, int b, ir_block
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;
int8_t *loc; /* array of the current locations of the original source registers: initially loc[from] = from */
int8_t *pred; /* array of original source registers that must be copied to destinations: pred[to] = from */
int8_t *types; /* array of the types of the orginal source registers: types[from] = typeof(from) */
ir_reg to, from;
ir_type type;
ir_regset todo, ready, srcs;
Expand Down Expand Up @@ -625,18 +627,19 @@ static int ir_parallel_copy(ir_ctx *ctx, ir_copy *copies, int count, ir_reg tmp_
IR_ASSERT(tmp_reg == IR_REG_NONE || !IR_REGSET_IN(srcs, tmp_reg) || pred[loc[tmp_reg]] == tmp_reg);
IR_ASSERT(tmp_fp_reg == IR_REG_NONE || !IR_REGSET_IN(srcs, tmp_fp_reg) || pred[loc[tmp_fp_reg]] == tmp_fp_reg);

/* now we resolve all "windmill axles" - cycles (this reuires temporary registers) */
/* now we resolve all "windmill axles" - cycles (this requires temporary registers) */
while (todo != IR_REGSET_EMPTY) {
to = ir_regset_pop_first(&todo);
to = ir_regset_pop_first(&todo); /* select a register to break the cycle */
from = pred[to];
IR_ASSERT(to != loc[from]);
type = types[from];
type = types[to]; /* type of the register that needs to be moved into a temporary register */
if (IR_IS_TYPE_INT(type)) {
#ifdef IR_HAVE_SWAP_INT
if (pred[from] == to) {
if (ir_type_size[types[to]] > ir_type_size[type]) {
type = types[to];
if (ir_type_size[types[from]] > ir_type_size[type]) {
type = types[from];
}
/* resolve the cycle with 2 registers using SWAP */
ir_emit_swap(ctx, type, to, from);
IR_REGSET_EXCL(todo, from);
loc[to] = from;
Expand All @@ -646,11 +649,13 @@ static int ir_parallel_copy(ir_ctx *ctx, ir_copy *copies, int count, ir_reg tmp_
#endif
IR_ASSERT(tmp_reg != IR_REG_NONE);
IR_ASSERT(tmp_reg >= IR_REG_GP_FIRST && tmp_reg <= IR_REG_GP_LAST);
/* break cycle, moving one of its register into a temporary register */
ir_emit_mov(ctx, type, tmp_reg, to);
loc[to] = tmp_reg;
} else {
#ifdef IR_HAVE_SWAP_FP
if (pred[from] == to && types[to] == type) {
if (pred[from] == to && types[from] == type) {
/* resolve the cycle with 2 registers using SWAP */
ir_emit_swap_fp(ctx, type, to, from);
IR_REGSET_EXCL(todo, from);
loc[to] = from;
Expand All @@ -660,6 +665,7 @@ static int ir_parallel_copy(ir_ctx *ctx, ir_copy *copies, int count, ir_reg tmp_
#endif
IR_ASSERT(tmp_fp_reg != IR_REG_NONE);
IR_ASSERT(tmp_fp_reg >= IR_REG_FP_FIRST && tmp_fp_reg <= IR_REG_FP_LAST);
/* break cycle, moving one of its register into a temporary register */
ir_emit_fp_mov(ctx, type, tmp_fp_reg, to);
loc[to] = tmp_fp_reg;
}
Expand Down
5 changes: 3 additions & 2 deletions ext/opcache/jit/ir/ir_fold.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ IR_FOLD(EQ(C_DOUBLE, C_DOUBLE))

IR_FOLD(EQ(C_FLOAT, C_FLOAT))
{
IR_FOLD_BOOL(op1_insn->val.d == op2_insn->val.d);
IR_FOLD_BOOL(op1_insn->val.f == op2_insn->val.f);
}

IR_FOLD(NE(C_BOOL, C_BOOL))
Expand Down Expand Up @@ -1001,7 +1001,8 @@ IR_FOLD(MUL_OV(C_I64, C_I64))
int64_t res;
IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type);
res = op1_insn->val.u64 * op2_insn->val.u64;
if ((op1_insn->val.i64 != 0 && res / op1_insn->val.i64 != op2_insn->val.i64) || res < min || res > max) {
if ((op1_insn->val.i64 == -1 && res == INT64_MIN)
|| (op1_insn->val.i64 != 0 && res / op1_insn->val.i64 != op2_insn->val.i64) || res < min || res > max) {
IR_FOLD_NEXT;
}
IR_FOLD_CONST_U(res);
Expand Down
4 changes: 2 additions & 2 deletions ext/opcache/jit/ir/ir_gcm.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ static uint32_t ir_gcm_select_best_block(ir_ctx *ctx, ir_ref ref, uint32_t lca)
#endif

flags = (bb->flags & IR_BB_LOOP_HEADER) ? bb->flags : ctx->cfg_blocks[bb->loop_header].flags;
if ((flags & IR_BB_LOOP_WITH_ENTRY)
if ((flags & (IR_BB_LOOP_WITH_ENTRY|IR_BB_LOOP_WITH_ENTRY_IN_PREDS))
&& !(ctx->binding && ir_binding_find(ctx, ref))) {
/* Don't move loop invariant code across an OSR ENTRY if we can't restore it */
return lca;
Expand Down Expand Up @@ -135,7 +135,7 @@ static uint32_t ir_gcm_select_best_block(ir_ctx *ctx, ir_ref ref, uint32_t lca)
break;
}
flags = (bb->flags & IR_BB_LOOP_HEADER) ? bb->flags : ctx->cfg_blocks[bb->loop_header].flags;
if ((flags & IR_BB_LOOP_WITH_ENTRY)
if ((flags & (IR_BB_LOOP_WITH_ENTRY|IR_BB_LOOP_WITH_ENTRY_IN_PREDS))
&& !(ctx->binding && ir_binding_find(ctx, ref))) {
break;
}
Expand Down
1 change: 1 addition & 0 deletions ext/opcache/jit/ir/ir_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,7 @@ void ir_iter_cleanup(ir_ctx *ctx);
#define IR_BB_PREV_EMPTY_ENTRY (1<<8)
#define IR_BB_OSR_ENTRY_LOADS (1<<9) /* OSR Entry-point with register LOADs */
#define IR_BB_LOOP_WITH_ENTRY (1<<10) /* set together with LOOP_HEADER if there is an ENTRY in the loop */
#define IR_BB_LOOP_WITH_ENTRY_IN_PREDS (1<<18) /* set together with LOOP_HEADER if an ENTRY reaches the loop bypassing its immediate dominator */

/* The following flags are set by GCM */
#define IR_BB_HAS_PHI (1<<11)
Expand Down
6 changes: 6 additions & 0 deletions ext/opcache/jit/ir/ir_ra.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,9 @@ int ir_compute_live_ranges(ir_ctx *ctx)
IR_ASSERT(use_insn->op3 == ref);
/* skip VSTORE (VAR is going to be remapped to PARAM) */
param_ival->flags |= IR_LIVE_INTERVAL_VAR_MEM_PARAM;
if (ctx->use_lists[ref].count != 1) {
param_ival->flags &= ~IR_LIVE_INTERVAL_MEM_PARAM;
}
param_ival->use_pos = param_ival->use_pos->next;
ctx->rules[use] = IR_SKIPPED | IR_NOP;
}
Expand Down Expand Up @@ -1530,6 +1533,9 @@ int ir_compute_live_ranges(ir_ctx *ctx)
IR_ASSERT(use_insn->op3 == ref);
/* skip VSTORE (VAR is going to be remapped to PARAM) */
param_ival->flags |= IR_LIVE_INTERVAL_VAR_MEM_PARAM;
if (ctx->use_lists[ref].count != 1) {
param_ival->flags &= ~IR_LIVE_INTERVAL_MEM_PARAM;
}
param_ival->use_pos = param_ival->use_pos->next;
ctx->rules[use] = IR_SKIPPED | IR_NOP;
}
Expand Down
11 changes: 10 additions & 1 deletion ext/opcache/jit/ir/ir_ra_simple.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* IR - Lightweight JIT Compilation Framework
* Simple Local Register Allocation (used for -O0)
* Simple Local Register Allocation (not used anymore)
* This file is part of the IR Project distributed under the MIT-style LICENSE.
* Authors: Dmitry Stogov <dmitry@php.net>
*
Expand Down Expand Up @@ -131,6 +131,15 @@ static ir_reg _get_free_reg2(ir_ctx *ctx, ir_type type, ir_reg_alloc_simple_data
return reg;
}

/* The following checks for the safety of VAR slot reuse are incomplete.
* Some of the unsupported edge cases are demonstrated at https://github.com/dstogov/ir/pull/232
* and https://github.com/dstogov/ir/pull/230 (there are may be other problems).
* Unfortunatly, the attempts to fix the problems break the simplicity of this allocator and make it ridiculous.
* The complete removing of VAR slot reuse makes the already bad generated code significanly worse.
*
* A better appoach is implemented in ir_ra_fast.c
*/

static bool ir_load_may_reuse_var_slot(ir_ctx *ctx, ir_block *bb, ir_ref var, ir_ref load)
{
ir_use_list *use_list = &ctx->use_lists[load];
Expand Down
Loading
Loading