From 2369f48092a15a42731a4c1d1d8a1c770517a780 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 25 Aug 2020 18:28:23 +0300 Subject: [PATCH] Infer information about packed/hash arrays and use it for JIT --- Zend/zend_type_info.h | 15 +++- ext/opcache/Optimizer/zend_func_info.c | 4 +- ext/opcache/Optimizer/zend_inference.c | 4 +- ext/opcache/Optimizer/zend_inference.h | 3 + ext/opcache/jit/zend_jit_x86.dasc | 104 ++++++++++++++----------- 5 files changed, 75 insertions(+), 55 deletions(-) diff --git a/Zend/zend_type_info.h b/Zend/zend_type_info.h index 9dc90d142b..a0fc698ebe 100644 --- a/Zend/zend_type_info.h +++ b/Zend/zend_type_info.h @@ -56,11 +56,18 @@ #define MAY_BE_ARRAY_OF_ANY (MAY_BE_ANY << MAY_BE_ARRAY_SHIFT) #define MAY_BE_ARRAY_OF_REF (MAY_BE_REF << MAY_BE_ARRAY_SHIFT) -#define MAY_BE_ARRAY_KEY_LONG (1<<21) -#define MAY_BE_ARRAY_KEY_STRING (1<<22) +#define MAY_BE_ARRAY_PACKED (1<<21) +#define MAY_BE_ARRAY_HASH (1<<22) /* hash with numeric keys */ + +#define MAY_BE_ARRAY_KEY_LONG (MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_HASH) +#define MAY_BE_ARRAY_KEY_STRING (1<<23) #define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING) -#define MAY_BE_CLASS (1<<23) -#define MAY_BE_INDIRECT (1<<24) +#define MAY_BE_CLASS (1<<24) +#define MAY_BE_INDIRECT (1<<25) + + +#define MAY_BE_ANY_ARRAY \ + (MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF) #endif /* ZEND_TYPE_INFO_H */ diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c index 47c4af5115..a387942919 100644 --- a/ext/opcache/Optimizer/zend_func_info.c +++ b/ext/opcache/Optimizer/zend_func_info.c @@ -64,7 +64,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline, &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]); uint32_t t3 = 0; - uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG; + uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED; if (call_info->num_args == 3) { t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline, @@ -86,7 +86,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa return tmp; } else { /* May throw */ - return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; + return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING; } } diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index ab8b751acc..9a66c91d64 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -2454,7 +2454,7 @@ static zend_always_inline int _zend_update_type_info( if (t1 & MAY_BE_OBJECT) { tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; } else { - tmp |= ((t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT) | ((t1 & MAY_BE_ANY)? MAY_BE_ARRAY_KEY_LONG : 0); + tmp |= ((t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT) | ((t1 & MAY_BE_ANY) ? MAY_BE_ARRAY_PACKED : 0); } } UPDATE_SSA_TYPE(tmp, ssa_op->result_def); @@ -3474,7 +3474,7 @@ static zend_always_inline int _zend_update_type_info( UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def); break; case ZEND_FUNC_GET_ARGS: - UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN| MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY, ssa_op->result_def); + UPDATE_SSA_TYPE(MAY_BE_RC1|MAY_BE_RCN| MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_ANY, ssa_op->result_def); break; case ZEND_GET_CLASS: case ZEND_GET_CALLED_CLASS: diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h index 0708d5df94..e4c8598ce4 100644 --- a/ext/opcache/Optimizer/zend_inference.h +++ b/ext/opcache/Optimizer/zend_inference.h @@ -178,6 +178,9 @@ static zend_always_inline uint32_t _const_op_type(const zval *zv) { } tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT); } ZEND_HASH_FOREACH_END(); + if (HT_IS_PACKED(ht)) { + tmp &= ~MAY_BE_ARRAY_HASH; + } return tmp; } else { uint32_t tmp = (1 << Z_TYPE_P(zv)); diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index f655c1616d..f761c851bf 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -4947,21 +4947,26 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o } if (op2_info & MAY_BE_LONG) { + zend_bool op2_loaded = 0; + if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_LONG)) { | // if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) | IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3 } - if (type == BP_VAR_W || type == BP_VAR_RW) { + if (type == BP_VAR_W) { | // hval = Z_LVAL_P(dim); | GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr + op2_loaded = 1; } - if (op1_info & MAY_BE_ARRAY_KEY_LONG) { + if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_PACKED)) { if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { zend_long val = Z_LVAL_P(Z_ZV(op2_addr)); if (val >= 0 && val < HT_MAX_SIZE) { | // ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef); - | test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED - | jz >4 // HASH_FIND + if (op1_info & MAY_BE_ARRAY_HASH) { + | test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED + | jz >4 // HASH_FIND + } | // if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed)) |.if X64 | movsxd r0, dword [FCARG1a + offsetof(zend_array, nNumUsed)] @@ -4998,13 +5003,16 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o } } } else { - if (type != BP_VAR_W && type != BP_VAR_RW) { + if (!op2_loaded) { | // hval = Z_LVAL_P(dim); | GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr + op2_loaded = 1; } | // ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef); - | test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED - | jz >4 // HASH_FIND + if (op1_info & MAY_BE_ARRAY_HASH) { + | test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED + | jz >4 // HASH_FIND + } | // if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed)) |.if X64 | movsxd r0, dword [FCARG1a + offsetof(zend_array, nNumUsed)] @@ -5042,60 +5050,58 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o } switch (type) { case BP_JIT_IS: - if (op1_info & MAY_BE_ARRAY_KEY_LONG) { + if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_HASH)) { |4: - } - if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { - | // hval = Z_LVAL_P(dim); - | GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr - } - | EXT_CALL _zend_hash_index_find, r0 - | test r0, r0 - if (not_found_exit_addr) { - | jz ¬_found_exit_addr + if (!op2_loaded) { + | // hval = Z_LVAL_P(dim); + | GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr + } + | EXT_CALL _zend_hash_index_find, r0 + | test r0, r0 + if (not_found_exit_addr) { + | jz ¬_found_exit_addr + } else { + | jz >9 // NOT_FOUND + } + if (op2_info & MAY_BE_STRING) { + | jmp >5 + } + } else if (not_found_exit_addr) { + | jmp ¬_found_exit_addr } else { - | jz >9 // NOT_FOUND - } - if (op2_info & MAY_BE_STRING) { - | jmp >5 + | jmp >9 // NOT_FOUND } break; case BP_VAR_R: case BP_VAR_IS: case BP_VAR_UNSET: - if (op1_info & MAY_BE_ARRAY_KEY_LONG) { - if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { - zend_long val = Z_LVAL_P(Z_ZV(op2_addr)); - if (val >= 0 && val < HT_MAX_SIZE) { - if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { - | jmp &exit_addr - } else if (type == BP_VAR_IS && not_found_exit_addr) { - | jmp ¬_found_exit_addr - } else { - | jmp >2 // NOT_FOUND - } - } - } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { + if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) || + !(op1_info & MAY_BE_ARRAY_HASH) || + Z_MODE(op2_addr) != IS_CONST_ZVAL || + (Z_LVAL_P(Z_ZV(op2_addr)) >= 0 && Z_LVAL_P(Z_ZV(op2_addr)) < HT_MAX_SIZE)) { + if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { | jmp &exit_addr } else if (type == BP_VAR_IS && not_found_exit_addr) { | jmp ¬_found_exit_addr } else { | jmp >2 // NOT_FOUND } - |4: } - if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { - | // hval = Z_LVAL_P(dim); - | GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr - } - | EXT_CALL _zend_hash_index_find, r0 - | test r0, r0 - if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { - | jz &exit_addr - } else if (type == BP_VAR_IS && not_found_exit_addr) { - | jz ¬_found_exit_addr - } else { - | jz >2 // NOT_FOUND + if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_HASH)) { + |4: + if (!op2_loaded) { + | // hval = Z_LVAL_P(dim); + | GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr + } + | EXT_CALL _zend_hash_index_find, r0 + | test r0, r0 + if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) { + | jz &exit_addr + } else if (type == BP_VAR_IS && not_found_exit_addr) { + | jz ¬_found_exit_addr + } else { + | jz >2 // NOT_FOUND + } } |.cold_code |2: @@ -5124,6 +5130,10 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o case BP_VAR_RW: |2: |4: + if (!op2_loaded) { + | // hval = Z_LVAL_P(dim); + | GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr + } | SAVE_VALID_OPLINE opline, r0 | EXT_CALL zend_jit_hash_index_lookup_rw, r0 | test r0, r0 -- 2.40.0