diff --git a/ext/opcache/jit/ir/ir_gcm.c b/ext/opcache/jit/ir/ir_gcm.c index be8744ef198fd..396ba2d7f7c7b 100644 --- a/ext/opcache/jit/ir/ir_gcm.c +++ b/ext/opcache/jit/ir/ir_gcm.c @@ -401,9 +401,10 @@ static bool ir_split_partially_dead_node(ir_ctx *ctx, ir_ref ref, uint32_t b) for (i = 1; i < clones_count; i++) { clones[i].ref = clone = ir_emit(ctx, insn->optx, insn->op1, insn->op2, insn->op3); insn = &ctx->ir_base[ref]; - if (insn->op1 > 0) ir_use_list_add(ctx, insn->op1, clone); - if (insn->op2 > 0) ir_use_list_add(ctx, insn->op2, clone); - if (insn->op3 > 0) ir_use_list_add(ctx, insn->op3, clone); + /* Depending on the flags in IR_OPS, these can be references or data. */ + if (insn->op1 > 0 && insn->inputs_count >= 1) ir_use_list_add(ctx, insn->op1, clone); + if (insn->op2 > 0 && insn->inputs_count >= 2) ir_use_list_add(ctx, insn->op2, clone); + if (insn->op3 > 0 && insn->inputs_count >= 3) ir_use_list_add(ctx, insn->op3, clone); } /* Reconstruct IR: Update DEF->USE lists, CFG mapping and etc */ diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 4f364849390a2..b4f2257744145 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -3067,7 +3067,7 @@ static void ir_iter_optimize_if(ir_ctx *ctx, ir_ref ref, ir_insn *insn, ir_bitqu static void ir_iter_optimize_guard(ir_ctx *ctx, ir_ref ref, ir_insn *insn, ir_bitqueue *worklist) { - bool swap; + bool swap = 0; ir_ref condition = ir_iter_optimize_condition(ctx, insn->op1, insn->op2, &swap); if (swap) { diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 9afe38f701c43..7a423ce6a6529 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1425,7 +1425,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op uint32_t target_label, target_label2; 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; + zend_class_entry *ce = NULL; bool ce_is_instanceof; bool on_this; @@ -2444,11 +2444,6 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_IS: case ZEND_FETCH_OBJ_W: - if (opline->op2_type != IS_CONST - || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING - || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { - break; - } ce = NULL; ce_is_instanceof = 0; on_this = 0; @@ -2478,6 +2473,11 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } } + if (opline->op2_type != IS_CONST + || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING + || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { + break; + } if (!zend_jit_fetch_obj(&ctx, opline, op_array, ssa, ssa_op, op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL, RES_REG_ADDR(), IS_UNKNOWN, @@ -2847,6 +2847,36 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op /* We skip over the DO_FCALL, so decrement call_level ourselves. */ call_level--; } + break; + case ZEND_FETCH_OBJ_R: + if (!zend_jit_handler(&ctx, opline, + zend_may_throw(opline, ssa_op, op_array, ssa))) { + goto jit_failure; + } + + /* Cache slot is only used for IS_CONST op2, so only that can result in hook fast path. */ + if (opline->op2_type == IS_CONST) { + if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) { + if (opline->op1_type == IS_UNUSED) { + ce = op_array->scope; + } else { + ce = NULL; + } + } + + if (!ce || !(ce->ce_flags & ZEND_ACC_FINAL) || ce->num_hooked_props > 0) { + /* If a simple hook is called, exit to the VM. */ + ir_ref if_hook_enter = ir_IF(jit_CMP_IP(jit, IR_EQ, opline + 1)); + ir_IF_FALSE(if_hook_enter); + if (GCC_GLOBAL_REGS) { + ir_TAILCALL(IR_VOID, ir_LOAD_A(jit_IP(jit))); + } else { + ir_RETURN(ir_CONST_I32(1)); /* ZEND_VM_ENTER */ + } + ir_IF_TRUE(if_hook_enter); + } + } + break; default: if (!zend_jit_handler(&ctx, opline, diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 4ef32415abe76..ca3ac174e5db0 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -9355,6 +9355,16 @@ static int zend_jit_init_static_method_call(zend_jit_ctx *jit, (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF)) { if (op_array->fn_flags & ZEND_ACC_STATIC) { scope_ref = ir_LOAD_A(jit_EX(This.value.ref)); + } else if (op_array->fn_flags & ZEND_ACC_CLOSURE) { + ir_ref if_object, values = IR_UNUSED; + + if_object = ir_IF(ir_EQ(jit_Z_TYPE_ref(jit, jit_EX(This)), ir_CONST_U8(IS_OBJECT))); + ir_IF_TRUE(if_object); + ir_END_PHI_list(values, + ir_LOAD_A(ir_ADD_OFFSET(ir_LOAD_A(jit_EX(This.value.ref)), offsetof(zend_object, ce)))); + ir_IF_FALSE(if_object); + ir_END_PHI_list(values, ir_LOAD_A(jit_EX(This.value.ref))); + ir_PHI_list(values); } else { scope_ref = ir_LOAD_A(ir_ADD_OFFSET(ir_LOAD_A(jit_EX(This.value.ref)), offsetof(zend_object, ce))); } diff --git a/ext/opcache/tests/jit/gh15834.phpt b/ext/opcache/tests/jit/gh15834.phpt new file mode 100644 index 0000000000000..a8cc6ce7a15d6 --- /dev/null +++ b/ext/opcache/tests/jit/gh15834.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-15834 (Segfault with hook "simple get" cache slot and minimal JIT) +--EXTENSIONS-- +opcache +--INI-- +opcache.jit=1111 +--FILE-- + $this->_prop; + } +} + +$a = new A; +for ($i=0;$i<5;$i++) { + echo $a->prop; + $a->_prop++; +} +?> +--EXPECT-- +12345 diff --git a/ext/opcache/tests/jit/gh17966.phpt b/ext/opcache/tests/jit/gh17966.phpt new file mode 100644 index 0000000000000..f9983ad4c2b36 --- /dev/null +++ b/ext/opcache/tests/jit/gh17966.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-17966 (Symfony JIT 1205 assertion failure) +--EXTENSIONS-- +opcache +--INI-- +opcache.jit=1205 +--FILE-- + +--EXPECT-- +float(2.5) +float(1.25) diff --git a/ext/opcache/tests/jit/init_static_method_call_001.phpt b/ext/opcache/tests/jit/init_static_method_call_001.phpt new file mode 100644 index 0000000000000..b6f1b95cc1e23 --- /dev/null +++ b/ext/opcache/tests/jit/init_static_method_call_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +JIT INIT_STATIC_METHOD_CALL: 001 Invalid scope +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +int(15) diff --git a/ext/standard/array.c b/ext/standard/array.c index a1890b05b57d3..8462492651310 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -6591,7 +6591,13 @@ PHP_FUNCTION(array_filter) /* }}} */ /* {{{ Internal function to find an array element for a user closure. */ -static zend_result php_array_find(const HashTable *array, zend_fcall_info fci, zend_fcall_info_cache fci_cache, zval *result_key, zval *result_value, bool negate_condition) +enum php_array_find_result { + PHP_ARRAY_FIND_EXCEPTION = -1, + PHP_ARRAY_FIND_NONE = 0, + PHP_ARRAY_FIND_SOME = 1, +}; + +static enum php_array_find_result php_array_find(const HashTable *array, zend_fcall_info fci, zend_fcall_info_cache fci_cache, zval *result_key, zval *result_value, bool negate_condition) { zend_ulong num_key; zend_string *str_key; @@ -6599,16 +6605,8 @@ static zend_result php_array_find(const HashTable *array, zend_fcall_info fci, z zval args[2]; zval *operand; - if (result_value != NULL) { - ZVAL_UNDEF(result_value); - } - - if (result_key != NULL) { - ZVAL_UNDEF(result_key); - } - if (zend_hash_num_elements(array) == 0) { - return SUCCESS; + return PHP_ARRAY_FIND_NONE; } ZEND_ASSERT(ZEND_FCI_INITIALIZED(fci)); @@ -6631,7 +6629,7 @@ static zend_result php_array_find(const HashTable *array, zend_fcall_info fci, z ZEND_ASSERT(result == SUCCESS); if (UNEXPECTED(Z_ISUNDEF(retval))) { - return FAILURE; + return PHP_ARRAY_FIND_EXCEPTION; } bool retval_true = zend_is_true(&retval); @@ -6649,103 +6647,75 @@ static zend_result php_array_find(const HashTable *array, zend_fcall_info fci, z ZVAL_COPY(result_key, &args[1]); } - break; + return PHP_ARRAY_FIND_SOME; } } ZEND_HASH_FOREACH_END(); - return SUCCESS; + return PHP_ARRAY_FIND_NONE; } /* }}} */ /* {{{ Search within an array and returns the first found element value. */ PHP_FUNCTION(array_find) { - zval *array = NULL; + HashTable *array; zend_fcall_info fci; zend_fcall_info_cache fci_cache = empty_fcall_info_cache; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY(array) + Z_PARAM_ARRAY_HT(array) Z_PARAM_FUNC(fci, fci_cache) ZEND_PARSE_PARAMETERS_END(); - if (php_array_find(Z_ARR_P(array), fci, fci_cache, NULL, return_value, false) != SUCCESS) { - RETURN_THROWS(); - } - - if (Z_TYPE_P(return_value) == IS_UNDEF) { - RETURN_NULL(); - } + php_array_find(array, fci, fci_cache, NULL, return_value, false); } /* }}} */ /* {{{ Search within an array and returns the first found element key. */ PHP_FUNCTION(array_find_key) { - zval *array = NULL; + HashTable *array; zend_fcall_info fci; zend_fcall_info_cache fci_cache = empty_fcall_info_cache; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY(array) + Z_PARAM_ARRAY_HT(array) Z_PARAM_FUNC(fci, fci_cache) ZEND_PARSE_PARAMETERS_END(); - if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, false) != SUCCESS) { - RETURN_THROWS(); - } - - if (Z_TYPE_P(return_value) == IS_UNDEF) { - RETURN_NULL(); - } + php_array_find(array, fci, fci_cache, return_value, NULL, false); } /* }}} */ /* {{{ Checks if at least one array element satisfies a callback function. */ PHP_FUNCTION(array_any) { - zval *array = NULL; + HashTable *array; zend_fcall_info fci; zend_fcall_info_cache fci_cache = empty_fcall_info_cache; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY(array) + Z_PARAM_ARRAY_HT(array) Z_PARAM_FUNC(fci, fci_cache) ZEND_PARSE_PARAMETERS_END(); - if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, false) != SUCCESS) { - RETURN_THROWS(); - } - - bool retval = !Z_ISUNDEF_P(return_value); - if (Z_TYPE_P(return_value) == IS_STRING) { - zval_ptr_dtor_str(return_value); - } - RETURN_BOOL(retval); + RETURN_BOOL(php_array_find(array, fci, fci_cache, NULL, NULL, false) == PHP_ARRAY_FIND_SOME); } /* }}} */ /* {{{ Checks if all array elements satisfy a callback function. */ PHP_FUNCTION(array_all) { - zval *array = NULL; + HashTable *array; zend_fcall_info fci; zend_fcall_info_cache fci_cache = empty_fcall_info_cache; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_ARRAY(array) + Z_PARAM_ARRAY_HT(array) Z_PARAM_FUNC(fci, fci_cache) ZEND_PARSE_PARAMETERS_END(); - if (php_array_find(Z_ARR_P(array), fci, fci_cache, return_value, NULL, true) != SUCCESS) { - RETURN_THROWS(); - } - - bool retval = Z_ISUNDEF_P(return_value); - if (Z_TYPE_P(return_value) == IS_STRING) { - zval_ptr_dtor_str(return_value); - } - RETURN_BOOL(retval); + RETURN_BOOL(php_array_find(array, fci, fci_cache, NULL, NULL, true) == PHP_ARRAY_FIND_NONE); } /* }}} */