]> granicus.if.org Git - php/commitdiff
JIT for FETCH_DIM_W/RW insructions
authorDmitry Stogov <dmitry@zend.com>
Tue, 8 Sep 2020 23:41:22 +0000 (02:41 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 8 Sep 2020 23:41:22 +0000 (02:41 +0300)
ext/opcache/jit/zend_jit.c
ext/opcache/jit/zend_jit_disasm_x86.c
ext/opcache/jit/zend_jit_helpers.c
ext/opcache/jit/zend_jit_trace.c
ext/opcache/jit/zend_jit_x86.dasc

index 718117a56a7bde551da0dc53d2afec731a9488eb..5243907365cf22aec42efa46cffec611e743410d 100644 (file)
@@ -2853,6 +2853,22 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
                                                        goto jit_failure;
                                                }
                                                goto done;
+                                       case ZEND_FETCH_DIM_W:
+                                       case ZEND_FETCH_DIM_RW:
+//                                     case ZEND_FETCH_DIM_UNSET:
+                                       case ZEND_FETCH_LIST_W:
+                                               if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
+                                                       break;
+                                               }
+                                               if (opline->op1_type != IS_CV) {
+                                                       break;
+                                               }
+                                               if (!zend_jit_fetch_dim(&dasm_state, opline,
+                                                               OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), RES_REG_ADDR(),
+                                                               zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info))) {
+                                                       goto jit_failure;
+                                               }
+                                               goto done;
                                        case ZEND_ISSET_ISEMPTY_DIM_OBJ:
                                                if ((opline->extended_value & ZEND_ISEMPTY)) {
                                                        // TODO: support for empty() ???
index bd737fa669732549a91442ae8a08bdc2819dab5c..b044d632357a8b609ca3fbfc2c0ad3749296f33b 100644 (file)
@@ -420,6 +420,9 @@ static int zend_jit_disasm_init(void)
        REGISTER_HELPER(zend_jit_fetch_dim_obj_is_helper);
        REGISTER_HELPER(zend_jit_fetch_dim_rw_helper);
        REGISTER_HELPER(zend_jit_fetch_dim_w_helper);
+       REGISTER_HELPER(zend_jit_fetch_dim_obj_rw_helper);
+       REGISTER_HELPER(zend_jit_fetch_dim_obj_w_helper);
+//     REGISTER_HELPER(zend_jit_fetch_dim_obj_unset_helper);
        REGISTER_HELPER(zend_jit_assign_dim_helper);
        REGISTER_HELPER(zend_jit_assign_dim_op_helper);
        REGISTER_HELPER(zend_jit_fast_assign_concat_helper);
index 5b6b0e4b7f6a767f3d35ad8337bef33bb7aff120..42d98e383ef7ab5c58b77380cb3c010aa6f11462 100644 (file)
@@ -690,7 +690,8 @@ try_again:
                        /* For BC reasons we allow errors so that we can warn on leading numeric string */
                        if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL,
                                        /* allow errors */ true, NULL, &trailing_data)) {
-                               if (UNEXPECTED(trailing_data) /*&& type != BP_VAR_UNSET*/) {
+                               if (UNEXPECTED(trailing_data)
+                                && EG(current_execute_data)->opline->opcode != ZEND_FETCH_DIM_UNSET) {
                                        zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim));
                                }
                                return offset;
@@ -850,6 +851,7 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void)
                case ZEND_FETCH_DIM_RW:
                case ZEND_FETCH_DIM_FUNC_ARG:
                case ZEND_FETCH_DIM_UNSET:
+               case ZEND_FETCH_LIST_W:
                        /* TODO: Encode the "reason" into opline->extended_value??? */
                        var = opline->result.var;
                        opline++;
@@ -858,9 +860,21 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void)
                        while (opline < end) {
                                if (opline->op1_type == IS_VAR && opline->op1.var == var) {
                                        switch (opline->opcode) {
+                                               case ZEND_FETCH_OBJ_W:
+                                               case ZEND_FETCH_OBJ_RW:
+                                               case ZEND_FETCH_OBJ_FUNC_ARG:
+                                               case ZEND_FETCH_OBJ_UNSET:
+                                               case ZEND_ASSIGN_OBJ:
                                                case ZEND_ASSIGN_OBJ_OP:
+                                               case ZEND_ASSIGN_OBJ_REF:
                                                        msg = "Cannot use string offset as an object";
                                                        break;
+                                               case ZEND_FETCH_DIM_W:
+                                               case ZEND_FETCH_DIM_RW:
+                                               case ZEND_FETCH_DIM_FUNC_ARG:
+                                               case ZEND_FETCH_DIM_UNSET:
+                                               case ZEND_FETCH_LIST_W:
+                                               case ZEND_ASSIGN_DIM:
                                                case ZEND_ASSIGN_DIM_OP:
                                                        msg = "Cannot use string offset as an array";
                                                        break;
@@ -878,20 +892,6 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void)
                                                case ZEND_POST_DEC:
                                                        msg = "Cannot increment/decrement string offsets";
                                                        break;
-                                               case ZEND_FETCH_DIM_W:
-                                               case ZEND_FETCH_DIM_RW:
-                                               case ZEND_FETCH_DIM_FUNC_ARG:
-                                               case ZEND_FETCH_DIM_UNSET:
-                                               case ZEND_ASSIGN_DIM:
-                                                       msg = "Cannot use string offset as an array";
-                                                       break;
-                                               case ZEND_FETCH_OBJ_W:
-                                               case ZEND_FETCH_OBJ_RW:
-                                               case ZEND_FETCH_OBJ_FUNC_ARG:
-                                               case ZEND_FETCH_OBJ_UNSET:
-                                               case ZEND_ASSIGN_OBJ:
-                                                       msg = "Cannot use string offset as an object";
-                                                       break;
                                                case ZEND_ASSIGN_REF:
                                                case ZEND_ADD_ARRAY_ELEMENT:
                                                case ZEND_INIT_ARRAY:
@@ -914,6 +914,9 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(void)
                                                case ZEND_SEND_FUNC_ARG:
                                                        msg = "Only variables can be passed by reference";
                                                        break;
+                                               case ZEND_FE_RESET_RW:
+                                                       msg = "Cannot iterate on string offsets by reference";
+                                                       break;
                                                EMPTY_SWITCH_DEFAULT_CASE();
                                        }
                                        break;
@@ -1014,6 +1017,75 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
        }
 }
 
+static zend_always_inline void ZEND_FASTCALL zend_jit_fetch_dim_obj_helper(zval *object_ptr, zval *dim, zval *result, int type)
+{
+       zval *retval;
+
+       if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
+               retval = Z_OBJ_HT_P(object_ptr)->read_dimension(Z_OBJ_P(object_ptr), dim, type, result);
+               if (UNEXPECTED(retval == &EG(uninitialized_zval))) {
+                       zend_class_entry *ce = Z_OBJCE_P(object_ptr);
+
+                       ZVAL_NULL(result);
+                       zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
+               } else if (EXPECTED(retval && Z_TYPE_P(retval) != IS_UNDEF)) {
+                       if (!Z_ISREF_P(retval)) {
+                               if (result != retval) {
+                                       ZVAL_COPY(result, retval);
+                                       retval = result;
+                               }
+                               if (Z_TYPE_P(retval) != IS_OBJECT) {
+                                       zend_class_entry *ce = Z_OBJCE_P(object_ptr);
+                                       zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name));
+                               }
+                       } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) {
+                               ZVAL_UNREF(retval);
+                       }
+                       if (result != retval) {
+                               ZVAL_INDIRECT(result, retval);
+                       }
+               } else {
+                       ZEND_ASSERT(EG(exception) && "read_dimension() returned NULL without exception");
+                       ZVAL_UNDEF(result);
+               }
+       } else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING)) {
+               if (!dim) {
+                       zend_throw_error(NULL, "[] operator not supported for strings");
+               } else {
+                       if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) {
+                               zend_check_string_offset(dim/*, BP_VAR_RW*/);
+                       }
+                       if (!EG(exception)) {
+                               zend_wrong_string_offset();
+                       }
+               }
+               ZVAL_UNDEF(result);
+       } else {
+               if (type == BP_VAR_UNSET) {
+                       zend_throw_error(NULL, "Cannot unset offset in a non-array variable");
+                       ZVAL_UNDEF(result);
+               } else {
+                       zend_throw_error(NULL, "Cannot use a scalar value as an array");
+                       ZVAL_UNDEF(result);
+               }
+       }
+}
+
+static void ZEND_FASTCALL zend_jit_fetch_dim_obj_w_helper(zval *object_ptr, zval *dim, zval *result)
+{
+       zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_W);
+}
+
+static void ZEND_FASTCALL zend_jit_fetch_dim_obj_rw_helper(zval *object_ptr, zval *dim, zval *result)
+{
+       zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_RW);
+}
+
+//static void ZEND_FASTCALL zend_jit_fetch_dim_obj_unset_helper(zval *object_ptr, zval *dim, zval *result)
+//{
+//     zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_UNSET);
+//}
+
 static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim, zval *value, zval *result)
 {
        if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) {
index 8da6e899dfe7777b05447e9d37132338a5eca69c..9eee964126b80b88cc44093ec38a9176c45c4c0a 100644 (file)
@@ -1558,6 +1558,16 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
                                                }
                                        }
                                        break;
+                               case ZEND_FETCH_DIM_W:
+                               case ZEND_FETCH_DIM_RW:
+//                             case ZEND_FETCH_DIM_UNSET:
+                               case ZEND_FETCH_LIST_W:
+                                       if (opline->op1_type != IS_CV) {
+                                               break;
+                                       }
+                                       ADD_OP1_TRACE_GUARD();
+                                       ADD_OP2_TRACE_GUARD();
+                                       break;
                                case ZEND_SEND_VAL_EX:
                                case ZEND_SEND_VAR_EX:
                                case ZEND_SEND_VAR_NO_REF_EX:
@@ -4311,6 +4321,37 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
                                                        goto jit_failure;
                                                }
                                                goto done;
+                                       case ZEND_FETCH_DIM_W:
+                                       case ZEND_FETCH_DIM_RW:
+//                                     case ZEND_FETCH_DIM_UNSET:
+                                       case ZEND_FETCH_LIST_W:
+                                               if (opline->op1_type != IS_CV) {
+                                                       break;
+                                               }
+                                               op1_info = OP1_INFO();
+                                               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,
+                                                                       !ssa->var_info[ssa_op->op1_use].guarded_reference, 1)) {
+                                                               goto jit_failure;
+                                                       }
+                                                       if (opline->op1_type == IS_CV
+                                                        && zend_jit_var_may_alias(op_array, op_array_ssa, EX_VAR_TO_NUM(opline->op1.var)) == NO_ALIAS) {
+                                                               ssa->var_info[ssa_op->op1_use].guarded_reference = 1;
+                                                       }
+                                               } else {
+                                                       CHECK_OP1_TRACE_TYPE();
+                                               }
+                                               op2_info = OP2_INFO();
+                                               CHECK_OP2_TRACE_TYPE();
+                                               op1_def_info = OP1_DEF_INFO();
+                                               if (!zend_jit_fetch_dim(&dasm_state, opline,
+                                                               op1_info, op1_addr, op2_info, RES_REG_ADDR(),
+                                                               zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info))) {
+                                                       goto jit_failure;
+                                               }
+                                               goto done;
                                        case ZEND_ISSET_ISEMPTY_DIM_OBJ:
                                                if ((opline->extended_value & ZEND_ISEMPTY)) {
                                                        // TODO: support for empty() ???
index f44a472b95a8ada542e5083d0aa33f57c5f9c5fd..1962b23a4733a49d57c336e5eab496bd7c0acd38 100644 (file)
@@ -10950,6 +10950,199 @@ static int zend_jit_fetch_dim_read(dasm_State        **Dst,
        return 1;
 }
 
+static int zend_jit_fetch_dim(dasm_State    **Dst,
+                              const zend_op  *opline,
+                              uint32_t        op1_info,
+                              zend_jit_addr   op1_addr,
+                              uint32_t        op2_info,
+                              zend_jit_addr   res_addr,
+                              int             may_throw)
+{
+       zend_jit_addr op2_addr;
+
+       ZEND_ASSERT(opline->op1_type == IS_CV);
+
+       op2_addr = (opline->op2_type != IS_UNUSED) ? OP2_ADDR() : 0;
+
+       if (op1_info & MAY_BE_REF) {
+               |       LOAD_ZVAL_ADDR FCARG1a, op1_addr
+               |       IF_NOT_Z_TYPE FCARG1a, IS_REFERENCE, >1
+               |       GET_Z_PTR FCARG2a, FCARG1a
+               |       IF_NOT_TYPE byte [FCARG2a + offsetof(zend_reference, val) + offsetof(zval, u1.v.type)], IS_ARRAY, >2
+               |       lea FCARG1a, [FCARG2a + offsetof(zend_reference, val)]
+               |       jmp >3
+               |.cold_code
+               |2:
+               |       SET_EX_OPLINE opline, r0
+               |       EXT_CALL zend_jit_prepare_assign_dim_ref, r0
+               |       test r0, r0
+               |       mov FCARG1a, r0
+               |       jne >1
+               |       jmp ->exception_handler_undef
+               |.code
+               |1:
+               op1_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0);
+       }
+
+       if (op1_info & MAY_BE_ARRAY) {
+               if (op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_ARRAY)) {
+                       |       IF_NOT_ZVAL_TYPE op1_addr, IS_ARRAY, >7
+               }
+               |3:
+               |       SEPARATE_ARRAY op1_addr, op1_info, 1
+       }
+       if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) {
+               if (op1_info & MAY_BE_ARRAY) {
+                       |.cold_code
+                       |7:
+               }
+               if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+                       |       CMP_ZVAL_TYPE op1_addr, IS_FALSE
+                       |       jg >7
+               }
+               if ((op1_info & MAY_BE_UNDEF)
+                && opline->opcode == ZEND_FETCH_DIM_RW) {
+                       if (op1_info & (MAY_BE_NULL|MAY_BE_FALSE)) {
+                               |       IF_NOT_ZVAL_TYPE op1_addr, IS_UNDEF, >1
+                       }
+                       |       SET_EX_OPLINE opline, r0
+                       |       mov FCARG1a, opline->op1.var
+                       |       EXT_CALL zend_jit_undefined_op_helper, r0
+                       |1:
+               }
+               |       // ZVAL_ARR(container, zend_new_array(8));
+               if (Z_REG(op1_addr) != ZREG_FP) {
+                       |       mov T1, Ra(Z_REG(op1_addr)) // save
+               }
+               |       EXT_CALL _zend_new_array_0, r0
+               if (Z_REG(op1_addr) != ZREG_FP) {
+                       |       mov Ra(Z_REG(op1_addr)), T1 // restore
+               }
+               |       SET_ZVAL_LVAL op1_addr, r0
+               |       SET_ZVAL_TYPE_INFO op1_addr, IS_ARRAY_EX
+               |       mov FCARG1a, r0
+               if (op1_info & MAY_BE_ARRAY) {
+                       |       jmp >1
+                       |.code
+                       |1:
+               }
+       }
+
+       if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+               |6:
+               if (opline->op2_type == IS_UNUSED) {
+                       |       // var_ptr = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval));
+                       |       LOAD_ADDR_ZTS FCARG2a, executor_globals, uninitialized_zval
+                       |       EXT_CALL zend_hash_next_index_insert, r0
+                       |       // if (UNEXPECTED(!var_ptr)) {
+                       |       test r0, r0
+                       |       jz >1
+                       |.cold_code
+                       |1:
+                       |       // zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
+                       |       CANNOT_ADD_ELEMENT opline
+                       |       SET_ZVAL_TYPE_INFO res_addr, IS_UNDEF
+                       |       //ZEND_VM_C_GOTO(assign_dim_op_ret_null);
+                       |       jmp >8
+                       |.code
+                       |       SET_ZVAL_PTR res_addr, r0
+                       |       SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
+               } else {
+                       uint32_t type;
+
+                       switch (opline->opcode) {
+                               case ZEND_FETCH_DIM_W:
+                               case ZEND_FETCH_LIST_W:
+                                       type = BP_VAR_W;
+                                       break;
+                               case ZEND_FETCH_DIM_RW:
+                                       type = BP_VAR_RW;
+                                       break;
+                               case ZEND_FETCH_DIM_UNSET:
+                                       type = BP_VAR_UNSET;
+                                       break;
+                               default:
+                                       ZEND_UNREACHABLE();
+                       }
+
+                       if (!zend_jit_fetch_dimension_address_inner(Dst, opline, type, op1_info, op2_info, NULL, NULL, NULL)) {
+                               return 0;
+                       }
+
+                       |8:
+                       |       SET_ZVAL_PTR res_addr, r0
+                       |       SET_ZVAL_TYPE_INFO res_addr, IS_INDIRECT
+
+                       if (type == BP_VAR_RW || (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) {
+                               |.cold_code
+                               |9:
+                               |       SET_ZVAL_TYPE_INFO res_addr, IS_NULL
+                               |       jmp >8
+                               |.code
+                       }
+               }
+       }
+
+       if (op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY))) {
+               if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+                       |.cold_code
+                       |7:
+               }
+
+               |       SET_EX_OPLINE opline, r0
+               if (Z_REG(op1_addr) != ZREG_FCARG1a || Z_OFFSET(op1_addr) != 0) {
+                       |       LOAD_ZVAL_ADDR FCARG1a, op1_addr
+               }
+           if (opline->op2_type == IS_UNUSED) {
+                       |       xor FCARG2a, FCARG2a
+               } else if (opline->op2_type == IS_CONST && Z_EXTRA_P(RT_CONSTANT(opline, opline->op2)) == ZEND_EXTRA_VALUE) {
+                       ZEND_ASSERT(Z_MODE(op2_addr) == IS_CONST_ZVAL);
+                       |       LOAD_ADDR FCARG2a, (Z_ZV(op2_addr) + 1)
+               } else {
+                       |       LOAD_ZVAL_ADDR FCARG2a, op2_addr
+               }
+               |.if X64
+                       |       LOAD_ZVAL_ADDR CARG3, res_addr
+               |.else
+                       |       sub r4, 12
+                       |       PUSH_ZVAL_ADDR res_addr, r0
+               |.endif
+               switch (opline->opcode) {
+                       case ZEND_FETCH_DIM_W:
+                       case ZEND_FETCH_LIST_W:
+                               |       EXT_CALL zend_jit_fetch_dim_obj_w_helper, r0
+                               break;
+                       case ZEND_FETCH_DIM_RW:
+                               |       EXT_CALL zend_jit_fetch_dim_obj_rw_helper, r0
+                               break;
+//                     case ZEND_FETCH_DIM_UNSET:
+//                             |       EXT_CALL zend_jit_fetch_dim_obj_unset_helper, r0
+//                             break;
+                       default:
+                               ZEND_UNREACHABLE();
+                       }
+               |.if not(X64)
+               |       add r4, 12
+               |.endif
+
+               if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_ARRAY)) {
+                       |       jmp >8 // END
+                       |.code
+               }
+       }
+
+       |8:
+       |       FREE_OP opline->op2_type, opline->op2, op2_info, 0, opline
+
+       if (may_throw) {
+               if (!zend_jit_check_exception(Dst)) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
 static int zend_jit_isset_isempty_dim(dasm_State    **Dst,
                                       const zend_op  *opline,
                                       uint32_t        op1_info,