diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 92042ea8cbb5..0d6eeb8b84df 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -1085,6 +1085,33 @@ static IR_NEVER_INLINE void ir_collect_irreducible_loops(ir_ctx *ctx, uint32_t * } } +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)) { + ir_block *bb = &blocks[ir_worklist_pop(work)]; + + 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; @@ -1284,6 +1311,12 @@ int ir_find_loops(ir_ctx *ctx) } } } + bb = &blocks[b]; + if (ctx->entries_count + && (bb->flags & (IR_BB_LOOP_HEADER|IR_BB_LOOP_WITH_ENTRY)) == IR_BB_LOOP_HEADER + && (bb->predecessors_count > 2 || (bb->flags & IR_BB_IRREDUCIBLE_LOOP))) { + ir_check_loop_entry_in_preds(ctx, b, &work); + } } } diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index cbe049be932c..8b8530956f28 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -891,7 +891,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); @@ -3333,7 +3334,7 @@ IR_FOLD(MIN(MIN, _)) IR_FOLD(MAX(MAX, _)) { if (op1_insn->op1 == op2 || op1_insn->op2 == op2) { - IR_FOLD_COPY(op2); + IR_FOLD_COPY(op1); } IR_FOLD_NEXT; } diff --git a/ext/opcache/jit/ir/ir_gcm.c b/ext/opcache/jit/ir/ir_gcm.c index b194eeb81770..5bec546129a4 100644 --- a/ext/opcache/jit/ir/ir_gcm.c +++ b/ext/opcache/jit/ir/ir_gcm.c @@ -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; @@ -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; } diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index 6d8f31a8b7ed..25edb4bc41a7 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -1125,6 +1125,7 @@ void ir_iter_cleanup(ir_ctx *ctx); #define IR_BB_PREV_EMPTY_ENTRY (1<<7) #define IR_BB_OSR_ENTRY_LOADS (1<<8) /* OSR Entry-point with register LOADs */ #define IR_BB_LOOP_WITH_ENTRY (1<<9) /* 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<<10) diff --git a/ext/opcache/tests/jit/bw_and_or_fold.phpt b/ext/opcache/tests/jit/bw_and_or_fold.phpt new file mode 100644 index 000000000000..654da1a0a5ee --- /dev/null +++ b/ext/opcache/tests/jit/bw_and_or_fold.phpt @@ -0,0 +1,24 @@ +--TEST-- +JIT: IR folding of (a op b) op a for BW_AND and BW_OR +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=64M +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +int(7) +int(7) +int(2) +int(2) diff --git a/ext/opcache/tests/jit/gh25037.phpt b/ext/opcache/tests/jit/gh25037.phpt new file mode 100644 index 000000000000..cfb3b47cf5c8 --- /dev/null +++ b/ext/opcache/tests/jit/gh25037.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-25037 (Function JIT: "ENTRY - live var" for a namespaced function call with a recursive self:: call argument) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=32M +opcache.jit=1205 +--EXTENSIONS-- +opcache +mbstring +--FILE-- + +--EXPECT-- +string(8) "f09f8d9e"