From 6c366fad414db96e46ee14670201eeadc0404ce4 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 14 May 2024 21:40:54 +0300 Subject: [PATCH] JIT: Implement CPU register usage for ASSIGN_OP --- ext/opcache/jit/zend_jit.c | 15 ++++++++--- ext/opcache/jit/zend_jit_ir.c | 45 ++++++++++++++++++++++++-------- ext/opcache/jit/zend_jit_trace.c | 16 +++++++++--- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 2905a59d54921..15705533dd621 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -106,6 +106,7 @@ static void zend_jit_trace_add_code(const void *start, uint32_t size); static zend_string *zend_jit_func_name(const zend_op_array *op_array); static bool zend_jit_needs_arg_dtor(const zend_function *func, uint32_t arg_num, zend_call_info *call_info); +static bool zend_jit_supported_binary_op(uint8_t op, uint32_t op1_info, uint32_t op2_info); static bool dominates(const zend_basic_block *blocks, int a, int b) { while (blocks[b].level > blocks[a].level) { @@ -1273,7 +1274,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op bool recv_emitted = 0; /* emitted at least one RECV opcode */ uint8_t smart_branch_opcode; uint32_t target_label, target_label2; - uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info; + uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_mem_info; zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr; zend_class_entry *ce; bool ce_is_instanceof; @@ -1667,10 +1668,18 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op opline->extended_value, op1_info, op2_info)) { break; } + op1_addr = OP1_REG_ADDR(); + op1_mem_info = -1; + if (Z_MODE(op1_addr) != IS_REG + || Z_LOAD(op1_addr) + || Z_STORE(op1_addr)) { + op1_mem_info = op1_info; + } op1_def_info = OP1_DEF_INFO(); if (!zend_jit_assign_op(&ctx, opline, - op1_info, op1_def_info, OP1_RANGE(), - op2_info, OP2_RANGE(), + op1_info, op1_addr, OP1_RANGE(), + op1_def_info, OP1_DEF_REG_ADDR(), op1_mem_info, + op2_info, OP2_REG_ADDR(), OP2_RANGE(), (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa), zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index b7e5e2baaa9db..deebd6e401418 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -5858,19 +5858,26 @@ static int zend_jit_concat(zend_jit_ctx *jit, const zend_op *opline, uint32_t op return zend_jit_concat_helper(jit, opline, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, res_addr, may_throw); } -static int zend_jit_assign_op(zend_jit_ctx *jit, const zend_op *opline, uint32_t op1_info, uint32_t op1_def_info, zend_ssa_range *op1_range, uint32_t op2_info, zend_ssa_range *op2_range, int may_overflow, int may_throw) +static int zend_jit_assign_op(zend_jit_ctx *jit, + const zend_op *opline, + uint32_t op1_info, + zend_jit_addr op1_addr, + zend_ssa_range *op1_range, + uint32_t op1_def_info, + zend_jit_addr op1_def_addr, + uint32_t op1_mem_info, + uint32_t op2_info, + zend_jit_addr op2_addr, + zend_ssa_range *op2_range, + int may_overflow, + int may_throw) { int result = 1; - zend_jit_addr op1_addr, op2_addr; ir_ref slow_path = IR_UNUSED; - ZEND_ASSERT(opline->op1_type == IS_CV && opline->result_type == IS_UNUSED); ZEND_ASSERT(!(op1_info & MAY_BE_UNDEF) && !(op2_info & MAY_BE_UNDEF)); - op1_addr = OP1_ADDR(); - op2_addr = OP2_ADDR(); - if (op1_info & MAY_BE_REF) { ir_ref ref, ref2, arg2, op1_noref_path; ir_ref if_op1_ref = IR_UNUSED; @@ -5887,7 +5894,15 @@ static int zend_jit_assign_op(zend_jit_ctx *jit, const zend_op *opline, uint32_t if_op1_typed = jit_if_TYPED_REF(jit, ref2); ir_IF_TRUE_cold(if_op1_typed); - arg2 = jit_ZVAL_ADDR(jit, op2_addr); + if (Z_MODE(op2_addr) == IS_REG) { + zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->op2.var); + if (!zend_jit_spill_store_inv(jit, op2_addr, real_addr, op2_info)) { + return 0; + } + arg2 = jit_ZVAL_ADDR(jit, real_addr); + } else { + arg2 = jit_ZVAL_ADDR(jit, op2_addr); + } jit_SET_EX_OPLINE(jit, opline); if ((opline->op2_type & (IS_TMP_VAR|IS_VAR)) && (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) { @@ -5905,7 +5920,8 @@ static int zend_jit_assign_op(zend_jit_ctx *jit, const zend_op *opline, uint32_t ir_MERGE_WITH(op1_noref_path); ref = ir_PHI_2(IR_ADDR, ref2, ref); - op1_addr = ZEND_ADDR_REF_ZVAL(ref); + ZEND_ASSERT(op1_addr == op1_def_addr); + op1_def_addr = op1_addr = ZEND_ADDR_REF_ZVAL(ref); } switch (opline->extended_value) { @@ -5913,7 +5929,7 @@ static int zend_jit_assign_op(zend_jit_ctx *jit, const zend_op *opline, uint32_t case ZEND_SUB: case ZEND_MUL: case ZEND_DIV: - result = zend_jit_math_helper(jit, opline, opline->extended_value, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, opline->op1.var, op1_addr, op1_def_info, op1_info, may_overflow, may_throw); + result = zend_jit_math_helper(jit, opline, opline->extended_value, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, opline->op1.var, op1_def_addr, op1_def_info, op1_mem_info, may_overflow, may_throw); break; case ZEND_BW_OR: case ZEND_BW_AND: @@ -5924,10 +5940,10 @@ static int zend_jit_assign_op(zend_jit_ctx *jit, const zend_op *opline, uint32_t result = zend_jit_long_math_helper(jit, opline, opline->extended_value, opline->op1_type, opline->op1, op1_addr, op1_info, op1_range, opline->op2_type, opline->op2, op2_addr, op2_info, op2_range, - opline->op1.var, op1_addr, op1_def_info, op1_info, may_throw); + opline->op1.var, op1_def_addr, op1_def_info, op1_mem_info, may_throw); break; case ZEND_CONCAT: - result = zend_jit_concat_helper(jit, opline, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, op1_addr, may_throw); + result = zend_jit_concat_helper(jit, opline, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, op1_def_addr, may_throw); break; default: ZEND_UNREACHABLE(); @@ -16487,6 +16503,13 @@ static bool zend_jit_opline_supports_reg(const zend_op_array *op_array, zend_ssa return (op_array->type != ZEND_EVAL_CODE && op_array->function_name); case ZEND_ASSIGN: return (opline->op1_type == IS_CV); + case ZEND_ASSIGN_OP: + if (opline->op1_type != IS_CV || opline->result_type != IS_UNUSED) { + return 0; + } + op1_info = OP1_INFO(); + op2_info = OP2_INFO(); + return zend_jit_supported_binary_op(opline->extended_value, op1_info, op2_info); case ZEND_ADD: case ZEND_SUB: case ZEND_MUL: diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 8e5b20337ce50..6e4fb885b601f 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -3984,7 +3984,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par const zend_op_array *op_arrays[ZEND_JIT_TRACE_MAX_FUNCS]; uint8_t smart_branch_opcode; const void *exit_addr; - uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_data_info; + uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_data_info, op1_mem_info; bool send_result = 0; bool skip_comparison; zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr; @@ -4562,14 +4562,24 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par opline->extended_value, op1_info, op2_info)) { break; } + op1_addr = OP1_REG_ADDR(); + if (Z_MODE(op1_addr) != IS_REG + || Z_LOAD(op1_addr) + || Z_STORE(op1_addr)) { + op1_mem_info = op1_info; + } else { + op1_mem_info = zend_jit_trace_type_to_info( + STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var))); + } op1_def_info = OP1_DEF_INFO(); if (op1_def_info & MAY_BE_GUARD && !has_concrete_type(op1_def_info)) { op1_def_info &= ~MAY_BE_GUARD; } if (!zend_jit_assign_op(&ctx, opline, - op1_info, op1_def_info, OP1_RANGE(), - op2_info, OP2_RANGE(), + op1_info, op1_addr, OP1_RANGE(), + op1_def_info, OP1_DEF_REG_ADDR(), op1_mem_info, + op2_info, OP2_REG_ADDR(), OP2_RANGE(), (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & (MAY_BE_DOUBLE|MAY_BE_GUARD)) && zend_may_overflow(opline, ssa_op, op_array, ssa), zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure;