Skip to content

Commit b4b5734

Browse files
committed
JIT for FETCH_OBJ_W
1 parent b461e6b commit b4b5734

File tree

5 files changed

+418
-21
lines changed

5 files changed

+418
-21
lines changed

ext/opcache/jit/zend_jit.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
19691969
zend_bool send_result;
19701970
zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr;
19711971
zend_class_entry *ce;
1972+
zend_bool ce_is_instanceof;
19721973

19731974
if (JIT_G(bisect_limit)) {
19741975
jit_bisect_pos++;
@@ -2759,16 +2760,19 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
27592760
goto done;
27602761
case ZEND_FETCH_OBJ_R:
27612762
case ZEND_FETCH_OBJ_IS:
2763+
case ZEND_FETCH_OBJ_W:
27622764
if (opline->op2_type != IS_CONST
27632765
|| Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
27642766
|| Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
27652767
break;
27662768
}
27672769
ce = NULL;
2770+
ce_is_instanceof = 0;
27682771
if (opline->op1_type == IS_UNUSED) {
27692772
op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
27702773
op1_addr = 0;
27712774
ce = op_array->scope;
2775+
ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
27722776
} else {
27732777
op1_info = OP1_INFO();
27742778
if (!(op1_info & MAY_BE_OBJECT)) {
@@ -2779,14 +2783,15 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
27792783
zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
27802784
if (ssa_op->op1_use >= 0) {
27812785
zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
2782-
if (op1_ssa->ce && !op1_ssa->is_instanceof && !op1_ssa->ce->create_object) {
2786+
if (op1_ssa->ce && !op1_ssa->ce->create_object) {
27832787
ce = op1_ssa->ce;
2788+
ce_is_instanceof = op1_ssa->is_instanceof;
27842789
}
27852790
}
27862791
}
27872792
}
2788-
if (!zend_jit_fetch_obj_read(&dasm_state, opline, op_array,
2789-
op1_info, op1_addr, ce,
2793+
if (!zend_jit_fetch_obj(&dasm_state, opline, op_array,
2794+
op1_info, op1_addr, 0, ce, ce_is_instanceof,
27902795
zend_may_throw(opline, ssa_op, op_array, ssa))) {
27912796
goto jit_failure;
27922797
}

ext/opcache/jit/zend_jit_disasm_x86.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,10 @@ static int zend_jit_disasm_init(void)
432432
REGISTER_HELPER(zend_jit_fetch_obj_r_dynamic);
433433
REGISTER_HELPER(zend_jit_fetch_obj_is_slow);
434434
REGISTER_HELPER(zend_jit_fetch_obj_is_dynamic);
435+
REGISTER_HELPER(zend_jit_fetch_obj_w_slow);
436+
REGISTER_HELPER(zend_jit_check_array_promotion);
437+
REGISTER_HELPER(zend_jit_create_typed_ref);
438+
REGISTER_HELPER(zend_jit_extract_helper);
435439
REGISTER_HELPER(zend_jit_vm_stack_free_args_helper);
436440
REGISTER_HELPER(zend_jit_copy_extra_args_helper);
437441
REGISTER_HELPER(zend_jit_deprecated_helper);
@@ -447,6 +451,7 @@ static int zend_jit_disasm_init(void)
447451
REGISTER_HELPER(zend_jit_only_vars_by_reference);
448452
REGISTER_HELPER(zend_jit_invalid_array_access);
449453
REGISTER_HELPER(zend_jit_invalid_property_read);
454+
REGISTER_HELPER(zend_jit_invalid_property_write);
450455
REGISTER_HELPER(zend_jit_prepare_assign_dim_ref);
451456
REGISTER_HELPER(zend_jit_pre_inc);
452457
REGISTER_HELPER(zend_jit_pre_dec);

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,193 @@ static void ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic(zend_object *zobj, intpt
13451345
zend_jit_fetch_obj_is_slow(zobj, offset, result, cache_slot);
13461346
}
13471347

1348+
static zend_always_inline zend_bool promotes_to_array(zval *val) {
1349+
return Z_TYPE_P(val) <= IS_FALSE
1350+
|| (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE);
1351+
}
1352+
1353+
static zend_always_inline zend_bool check_type_array_assignable(zend_type type) {
1354+
if (!ZEND_TYPE_IS_SET(type)) {
1355+
return 1;
1356+
}
1357+
return (ZEND_TYPE_FULL_MASK(type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) != 0;
1358+
}
1359+
1360+
static zend_property_info *zend_object_fetch_property_type_info(
1361+
zend_object *obj, zval *slot)
1362+
{
1363+
if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(obj->ce))) {
1364+
return NULL;
1365+
}
1366+
1367+
/* Not a declared property */
1368+
if (UNEXPECTED(slot < obj->properties_table ||
1369+
slot >= obj->properties_table + obj->ce->default_properties_count)) {
1370+
return NULL;
1371+
}
1372+
1373+
return zend_get_typed_property_info_for_slot(obj, slot);
1374+
}
1375+
1376+
static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) {
1377+
zend_string *type_str = zend_type_to_string(prop->type);
1378+
zend_type_error(
1379+
"Cannot auto-initialize an %s inside property %s::$%s of type %s",
1380+
type,
1381+
ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name),
1382+
ZSTR_VAL(type_str)
1383+
);
1384+
zend_string_release(type_str);
1385+
}
1386+
1387+
static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error(
1388+
zend_property_info *prop) {
1389+
zend_throw_error(NULL,
1390+
"Cannot access uninitialized non-nullable property %s::$%s by reference",
1391+
ZSTR_VAL(prop->ce->name),
1392+
zend_get_unmangled_property_name(prop->name));
1393+
}
1394+
1395+
static zend_never_inline zend_bool zend_handle_fetch_obj_flags(
1396+
zval *result, zval *ptr, zend_object *obj, zend_property_info *prop_info, uint32_t flags)
1397+
{
1398+
switch (flags) {
1399+
case ZEND_FETCH_DIM_WRITE:
1400+
if (promotes_to_array(ptr)) {
1401+
if (!prop_info) {
1402+
prop_info = zend_object_fetch_property_type_info(obj, ptr);
1403+
if (!prop_info) {
1404+
break;
1405+
}
1406+
}
1407+
if (!check_type_array_assignable(prop_info->type)) {
1408+
zend_throw_auto_init_in_prop_error(prop_info, "array");
1409+
if (result) ZVAL_ERROR(result);
1410+
return 0;
1411+
}
1412+
}
1413+
break;
1414+
case ZEND_FETCH_REF:
1415+
if (Z_TYPE_P(ptr) != IS_REFERENCE) {
1416+
if (!prop_info) {
1417+
prop_info = zend_object_fetch_property_type_info(obj, ptr);
1418+
if (!prop_info) {
1419+
break;
1420+
}
1421+
}
1422+
if (Z_TYPE_P(ptr) == IS_UNDEF) {
1423+
if (!ZEND_TYPE_ALLOW_NULL(prop_info->type)) {
1424+
zend_throw_access_uninit_prop_by_ref_error(prop_info);
1425+
if (result) ZVAL_ERROR(result);
1426+
return 0;
1427+
}
1428+
ZVAL_NULL(ptr);
1429+
}
1430+
1431+
ZVAL_NEW_REF(ptr, ptr);
1432+
ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(ptr), prop_info);
1433+
}
1434+
break;
1435+
EMPTY_SWITCH_DEFAULT_CASE()
1436+
}
1437+
return 1;
1438+
}
1439+
1440+
static void ZEND_FASTCALL zend_jit_fetch_obj_w_slow(zend_object *zobj, zval *offset, zval *result, uint32_t cache_slot)
1441+
{
1442+
zval *retval;
1443+
zend_execute_data *execute_data = EG(current_execute_data);
1444+
const zend_op *opline = execute_data->opline;
1445+
zend_string *name, *tmp_name;
1446+
1447+
name = zval_get_tmp_string(offset, &tmp_name);
1448+
retval = zobj->handlers->get_property_ptr_ptr(zobj, name, BP_VAR_W, CACHE_ADDR(cache_slot));
1449+
if (NULL == retval) {
1450+
retval = zobj->handlers->read_property(zobj, name, BP_VAR_W, CACHE_ADDR(cache_slot), result);
1451+
if (retval == result) {
1452+
if (UNEXPECTED(Z_ISREF_P(retval) && Z_REFCOUNT_P(retval) == 1)) {
1453+
ZVAL_UNREF(retval);
1454+
}
1455+
goto end;
1456+
}
1457+
} else if (UNEXPECTED(Z_ISERROR_P(retval))) {
1458+
ZVAL_ERROR(result);
1459+
goto end;
1460+
}
1461+
1462+
ZVAL_INDIRECT(result, retval);
1463+
1464+
/* Support for typed properties */
1465+
do {
1466+
uint32_t flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS;
1467+
1468+
if (flags) {
1469+
zend_property_info *prop_info = NULL;
1470+
1471+
if (opline->op2_type == IS_CONST) {
1472+
prop_info = CACHED_PTR_EX(CACHE_ADDR(cache_slot) + 2);
1473+
if (!prop_info) {
1474+
break;
1475+
}
1476+
}
1477+
if (UNEXPECTED(!zend_handle_fetch_obj_flags(result, retval, zobj, prop_info, flags))) {
1478+
goto end;
1479+
}
1480+
}
1481+
} while (0);
1482+
1483+
if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
1484+
ZVAL_NULL(retval);
1485+
}
1486+
1487+
end:
1488+
zend_tmp_string_release(tmp_name);
1489+
}
1490+
1491+
static void ZEND_FASTCALL zend_jit_check_array_promotion(zval *val, zend_property_info *prop)
1492+
{
1493+
zend_execute_data *execute_data = EG(current_execute_data);
1494+
const zend_op *opline = execute_data->opline;
1495+
zval *result = EX_VAR(opline->result.var);
1496+
1497+
if (((Z_TYPE_P(val) <= IS_FALSE
1498+
|| (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE))
1499+
&& ZEND_TYPE_IS_SET(prop->type)
1500+
&& ZEND_TYPE_FULL_MASK(prop->type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)) == 0) {
1501+
zend_string *type_str = zend_type_to_string(prop->type);
1502+
zend_type_error(
1503+
"Cannot auto-initialize an array inside property %s::$%s of type %s",
1504+
ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name),
1505+
ZSTR_VAL(type_str)
1506+
);
1507+
zend_string_release(type_str);
1508+
ZVAL_ERROR(result);
1509+
} else {
1510+
ZVAL_INDIRECT(result, val);
1511+
}
1512+
}
1513+
1514+
static void ZEND_FASTCALL zend_jit_create_typed_ref(zval *val, zend_property_info *prop, zval *result)
1515+
{
1516+
if (!Z_ISREF_P(val)) {
1517+
ZVAL_NEW_REF(val, val);
1518+
ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(val), prop);
1519+
}
1520+
ZVAL_INDIRECT(result, val);
1521+
}
1522+
1523+
static void ZEND_FASTCALL zend_jit_extract_helper(zend_refcounted *garbage)
1524+
{
1525+
zend_execute_data *execute_data = EG(current_execute_data);
1526+
const zend_op *opline = execute_data->opline;
1527+
zval *zv = EX_VAR(opline->result.var);
1528+
1529+
if (EXPECTED(Z_TYPE_P(zv) == IS_INDIRECT)) {
1530+
ZVAL_COPY(zv, Z_INDIRECT_P(zv));
1531+
}
1532+
rc_dtor_func(garbage);
1533+
}
1534+
13481535
static void ZEND_FASTCALL zend_jit_vm_stack_free_args_helper(zend_execute_data *call)
13491536
{
13501537
zend_vm_stack_free_args(call);
@@ -1508,6 +1695,13 @@ static void ZEND_FASTCALL zend_jit_invalid_property_read(zval *container, const
15081695
zend_error(E_WARNING, "Attempt to read property '%s' on %s", property_name, zend_zval_type_name(container));
15091696
}
15101697

1698+
static void ZEND_FASTCALL zend_jit_invalid_property_write(zval *container, const char *property_name)
1699+
{
1700+
zend_throw_error(NULL,
1701+
"Attempt to modify property '%s' on %s",
1702+
property_name, zend_zval_type_name(container));
1703+
}
1704+
15111705
static zval * ZEND_FASTCALL zend_jit_prepare_assign_dim_ref(zval *ref) {
15121706
zval *val = Z_REFVAL_P(ref);
15131707
if (Z_TYPE_P(val) <= IS_FALSE) {

ext/opcache/jit/zend_jit_trace.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,7 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
15571557
/* break missing intentionally */
15581558
case ZEND_FETCH_OBJ_R:
15591559
case ZEND_FETCH_OBJ_IS:
1560+
case ZEND_FETCH_OBJ_W:
15601561
if (opline->op2_type != IS_CONST
15611562
|| Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
15621563
|| Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
@@ -2632,6 +2633,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
26322633
zend_bool send_result = 0;
26332634
zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr;
26342635
zend_class_entry *ce;
2636+
zend_bool ce_is_instanceof;
26352637
uint32_t i;
26362638
zend_jit_trace_stack_frame *frame, *top, *call;
26372639
zend_jit_trace_stack *stack;
@@ -2865,6 +2867,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
28652867
uint8_t op2_type = p->op2_type;
28662868
uint8_t op3_type = p->op3_type;
28672869
uint8_t orig_op1_type = op1_type;
2870+
zend_bool op1_indirect;
28682871

28692872
opline = p->opline;
28702873
if (op1_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) {
@@ -3728,22 +3731,36 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
37283731
/* break missing intentionally */
37293732
case ZEND_FETCH_OBJ_R:
37303733
case ZEND_FETCH_OBJ_IS:
3734+
case ZEND_FETCH_OBJ_W:
37313735
if (opline->op2_type != IS_CONST
37323736
|| Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
37333737
|| Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
37343738
break;
37353739
}
37363740
ce = NULL;
3741+
ce_is_instanceof = 0;
3742+
op1_indirect = 0;
37373743
if (opline->op1_type == IS_UNUSED) {
37383744
op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
37393745
ce = op_array->scope;
3746+
ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
37403747
op1_addr = 0;
37413748
} else {
37423749
op1_info = OP1_INFO();
37433750
if (!(op1_info & MAY_BE_OBJECT)) {
37443751
break;
37453752
}
37463753
op1_addr = OP1_REG_ADDR();
3754+
if (opline->op1_type == IS_VAR
3755+
&& opline->opcode == ZEND_FETCH_OBJ_W) {
3756+
if (orig_op1_type != IS_UNKNOWN
3757+
&& (orig_op1_type & IS_TRACE_INDIRECT)) {
3758+
op1_indirect = 1;
3759+
if (!zend_jit_fetch_indirect_var(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr)) {
3760+
goto jit_failure;
3761+
}
3762+
}
3763+
}
37473764
if (orig_op1_type != IS_UNKNOWN
37483765
&& (orig_op1_type & IS_TRACE_REFERENCE)) {
37493766
if (!zend_jit_fetch_reference(&dasm_state, opline, orig_op1_type, &op1_info, &op1_addr, 1)) {
@@ -3755,14 +3772,15 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
37553772
if (ssa->var_info && ssa->ops) {
37563773
if (ssa_op->op1_use >= 0) {
37573774
zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
3758-
if (op1_ssa->ce && !op1_ssa->is_instanceof && !op1_ssa->ce->create_object) {
3775+
if (op1_ssa->ce && !op1_ssa->ce->create_object) {
37593776
ce = op1_ssa->ce;
3777+
ce_is_instanceof = op1_ssa->is_instanceof;
37603778
}
37613779
}
37623780
}
37633781
}
3764-
if (!zend_jit_fetch_obj_read(&dasm_state, opline, op_array,
3765-
op1_info, op1_addr, ce,
3782+
if (!zend_jit_fetch_obj(&dasm_state, opline, op_array,
3783+
op1_info, op1_addr, op1_indirect, ce, ce_is_instanceof,
37663784
zend_may_throw(opline, ssa_op, op_array, ssa))) {
37673785
goto jit_failure;
37683786
}

0 commit comments

Comments
 (0)