From: Dmitry Stogov Date: Tue, 16 Jun 2020 14:59:04 +0000 (+0300) Subject: Tracing JIT support for PHP references in array related instructions X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8425214c1d51414cce901e03e25992ca839a13ab;p=php Tracing JIT support for PHP references in array related instructions --- diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 286ad6644c..31e421424a 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2393,7 +2393,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op break; } if (!zend_jit_assign_dim_op(&dasm_state, opline, op_array, - OP1_INFO(), OP1_DEF_INFO(), OP2_INFO(), + OP1_INFO(), OP1_DEF_INFO(), OP1_REG_ADDR(), OP2_INFO(), OP1_DATA_INFO(), OP1_DATA_RANGE(), zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; @@ -2407,7 +2407,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op break; } if (!zend_jit_assign_dim(&dasm_state, opline, op_array, - OP1_INFO(), OP2_INFO(), OP1_DATA_INFO(), + OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), OP1_DATA_INFO(), zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; } @@ -2721,7 +2721,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op break; } if (!zend_jit_fetch_dim_read(&dasm_state, opline, op_array, - OP1_INFO(), OP2_INFO(), RES_INFO(), + OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), RES_INFO(), zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; } @@ -2750,7 +2750,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op target_label = target_label2 = (uint32_t)-1; } if (!zend_jit_isset_isempty_dim(&dasm_state, opline, op_array, - OP1_INFO(), OP2_INFO(), + OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), zend_may_throw(opline, ssa_op, op_array, ssa), smart_branch_opcode, target_label, target_label2, NULL)) { diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 7f32a02c4a..a8d42cddfe 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2847,6 +2847,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par uint8_t op1_type = p->op1_type; uint8_t op2_type = p->op2_type; uint8_t op3_type = p->op3_type; + uint8_t orig_op1_type = op1_type; opline = p->opline; if (op1_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) { @@ -3152,14 +3153,22 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par break; } op1_info = OP1_INFO(); - CHECK_OP1_TRACE_TYPE(); + op1_addr = OP1_REG_ADDR(); + if (orig_op1_type != IS_UNKNOWN + && (orig_op1_type & IS_TRACE_REFERENCE)) { + if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr)) { + goto jit_failure; + } + } else { + CHECK_OP1_TRACE_TYPE(); + } op2_info = OP2_INFO(); CHECK_OP2_TRACE_TYPE(); op1_data_info = OP1_DATA_INFO(); CHECK_OP1_DATA_TRACE_TYPE(); op1_def_info = OP1_DEF_INFO(); if (!zend_jit_assign_dim_op(&dasm_state, opline, op_array, - op1_info, op1_def_info, op2_info, + op1_info, op1_def_info, op1_addr, op2_info, op1_data_info, OP1_DATA_RANGE(), zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; @@ -3170,13 +3179,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par break; } op1_info = OP1_INFO(); - CHECK_OP1_TRACE_TYPE(); + op1_addr = OP1_REG_ADDR(); + if (orig_op1_type != IS_UNKNOWN + && (orig_op1_type & IS_TRACE_REFERENCE)) { + if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr)) { + goto jit_failure; + } + } else { + CHECK_OP1_TRACE_TYPE(); + } op2_info = OP2_INFO(); CHECK_OP2_TRACE_TYPE(); op1_data_info = OP1_DATA_INFO(); CHECK_OP1_DATA_TRACE_TYPE(); if (!zend_jit_assign_dim(&dasm_state, opline, op_array, - op1_info, op2_info, op1_data_info, + op1_info, op1_addr, op2_info, op1_data_info, zend_may_throw(opline, ssa_op, op_array, ssa))) { goto jit_failure; } @@ -3596,12 +3613,20 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par case ZEND_FETCH_DIM_R: case ZEND_FETCH_DIM_IS: op1_info = OP1_INFO(); - CHECK_OP1_TRACE_TYPE(); + op1_addr = OP1_REG_ADDR(); + if (orig_op1_type != IS_UNKNOWN + && (orig_op1_type & IS_TRACE_REFERENCE)) { + if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr)) { + goto jit_failure; + } + } else { + CHECK_OP1_TRACE_TYPE(); + } op2_info = OP2_INFO(); CHECK_OP2_TRACE_TYPE(); res_info = RES_INFO(); if (!zend_jit_fetch_dim_read(&dasm_state, opline, op_array, - op1_info, op2_info, res_info, + op1_info, op1_addr, op2_info, res_info, ( (op1_info & MAY_BE_ANY) != MAY_BE_ARRAY || (op2_info & (MAY_BE_ANY - (MAY_BE_LONG|MAY_BE_STRING))) != 0 || @@ -3615,14 +3640,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par goto jit_failure; } goto done; - goto done; case ZEND_ISSET_ISEMPTY_DIM_OBJ: if ((opline->extended_value & ZEND_ISEMPTY)) { // TODO: support for empty() ??? break; } op1_info = OP1_INFO(); - CHECK_OP1_TRACE_TYPE(); + op1_addr = OP1_REG_ADDR(); + if (orig_op1_type != IS_UNKNOWN + && (orig_op1_type & IS_TRACE_REFERENCE)) { + if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr)) { + goto jit_failure; + } + } else { + CHECK_OP1_TRACE_TYPE(); + } op2_info = OP2_INFO(); CHECK_OP2_TRACE_TYPE(); if ((opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) { @@ -3644,7 +3676,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par exit_addr = NULL; } if (!zend_jit_isset_isempty_dim(&dasm_state, opline, op_array, - op1_info, op2_info, + op1_info, op1_addr, op2_info, zend_may_throw(opline, ssa_op, op_array, ssa), smart_branch_opcode, -1, -1, exit_addr)) { diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index fb634378f2..115f932909 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -5378,13 +5378,12 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, return 1; } -static int zend_jit_assign_dim(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, uint32_t op2_info, uint32_t val_info, int may_throw) +static int zend_jit_assign_dim(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, uint32_t val_info, int may_throw) { - zend_jit_addr op1_addr, op2_addr, op3_addr, res_addr; + zend_jit_addr op2_addr, op3_addr, res_addr; ZEND_ASSERT(opline->op1_type == IS_CV); - op1_addr = OP1_ADDR(); op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0; op3_addr = OP1_DATA_ADDR(); if (opline->result_type == IS_UNUSED) { @@ -5586,13 +5585,12 @@ static int zend_jit_assign_dim(dasm_State **Dst, const zend_op *opline, const ze return 1; } -static int zend_jit_assign_dim_op(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, uint32_t op1_def_info, uint32_t op2_info, uint32_t op1_data_info, zend_ssa_range *op1_data_range, int may_throw) +static int zend_jit_assign_dim_op(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, uint32_t op1_def_info, zend_jit_addr op1_addr, uint32_t op2_info, uint32_t op1_data_info, zend_ssa_range *op1_data_range, int may_throw) { - zend_jit_addr op1_addr, op2_addr, op3_addr, var_addr; + zend_jit_addr op2_addr, op3_addr, var_addr; ZEND_ASSERT(opline->op1_type == IS_CV && opline->result_type == IS_UNUSED); - op1_addr = OP1_ADDR(); op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0; op3_addr = OP1_DATA_ADDR(); @@ -10064,12 +10062,12 @@ static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, ze return 1; } -static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, uint32_t op2_info, uint32_t res_info, int may_throw) +static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, uint32_t res_info, int may_throw) { - zend_jit_addr op1_addr, orig_op1_addr, op2_addr, res_addr; + zend_jit_addr orig_op1_addr, op2_addr, res_addr; const void *exit_addr = NULL; - op1_addr = orig_op1_addr = OP1_ADDR(); + orig_op1_addr = OP1_ADDR(); op2_addr = OP2_ADDR(); res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); @@ -10263,14 +10261,13 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons return 1; } -static int zend_jit_isset_isempty_dim(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, uint32_t op2_info, int may_throw, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr) +static int zend_jit_isset_isempty_dim(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, int may_throw, zend_uchar smart_branch_opcode, uint32_t target_label, uint32_t target_label2, const void *exit_addr) { - zend_jit_addr op1_addr, op2_addr, res_addr; + zend_jit_addr op2_addr, res_addr; // TODO: support for empty() ??? ZEND_ASSERT(!(opline->extended_value & ZEND_ISEMPTY)); - op1_addr = OP1_ADDR(); op2_addr = OP2_ADDR(); res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var); @@ -11349,6 +11346,52 @@ static zend_bool zend_jit_verify_return_type(dasm_State **Dst, const zend_op *op return 1; } +static zend_bool zend_jit_fetch_reference(dasm_State **Dst, const zend_op *opline, uint8_t var_type, uint32_t *var_info_ptr, zend_jit_addr *var_addr_ptr) +{ + zend_jit_addr var_addr = *var_addr_ptr; + uint32_t var_info = *var_info_ptr; + int32_t exit_point = zend_jit_trace_get_exit_point(opline, opline, NULL, ZEND_JIT_EXIT_TO_VM); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + + | IF_NOT_ZVAL_TYPE var_addr, IS_REFERENCE, &exit_addr + | GET_ZVAL_PTR FCARG1a, var_addr + | add FCARG1a, offsetof(zend_reference, val) + + var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0); + *var_addr_ptr = var_addr; + + var_type &= ~IS_TRACE_REFERENCE; + if (var_type != IS_UNKNOWN + && (var_info & (MAY_BE_ANY|MAY_BE_UNDEF)) != (1 << var_type)) { + exit_point = zend_jit_trace_get_exit_point(opline, opline, NULL, 0); + exit_addr = zend_jit_trace_get_exit_addr(exit_point); + + if (!exit_addr) { + return 0; + } + + | IF_NOT_Z_TYPE FCARG1a, var_type, &exit_addr + + //var_info = zend_jit_trace_type_to_info_ex(var_type, var_info); + ZEND_ASSERT(var_info & (1 << var_type)); + if (var_type < IS_STRING) { + var_info = (1 << var_type); + } else if (var_type != IS_ARRAY) { + var_info = (1 << var_type) | (var_info & (MAY_BE_RC1|MAY_BE_RCN)); + } else { + var_info = MAY_BE_ARRAY | (var_info & (MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_ARRAY_KEY_ANY|MAY_BE_RC1|MAY_BE_RCN)); + } + + *var_info_ptr = var_info; + } + + return 1; +} + static zend_bool zend_jit_may_reuse_reg(const zend_op *opline, const zend_ssa_op *ssa_op, zend_ssa *ssa, int def_var, int use_var) { if ((ssa->var_info[def_var].type & ~MAY_BE_GUARD) != (ssa->var_info[use_var].type & ~MAY_BE_GUARD)) {