Skip to content

Commit 2369f48

Browse files
committed
Infer information about packed/hash arrays and use it for JIT
1 parent 647fb38 commit 2369f48

File tree

5 files changed

+75
-55
lines changed

5 files changed

+75
-55
lines changed

Zend/zend_type_info.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,18 @@
5656
#define MAY_BE_ARRAY_OF_ANY (MAY_BE_ANY << MAY_BE_ARRAY_SHIFT)
5757
#define MAY_BE_ARRAY_OF_REF (MAY_BE_REF << MAY_BE_ARRAY_SHIFT)
5858

59-
#define MAY_BE_ARRAY_KEY_LONG (1<<21)
60-
#define MAY_BE_ARRAY_KEY_STRING (1<<22)
59+
#define MAY_BE_ARRAY_PACKED (1<<21)
60+
#define MAY_BE_ARRAY_HASH (1<<22) /* hash with numeric keys */
61+
62+
#define MAY_BE_ARRAY_KEY_LONG (MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_HASH)
63+
#define MAY_BE_ARRAY_KEY_STRING (1<<23)
6164
#define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING)
6265

63-
#define MAY_BE_CLASS (1<<23)
64-
#define MAY_BE_INDIRECT (1<<24)
66+
#define MAY_BE_CLASS (1<<24)
67+
#define MAY_BE_INDIRECT (1<<25)
68+
69+
70+
#define MAY_BE_ANY_ARRAY \
71+
(MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF)
6572

6673
#endif /* ZEND_TYPE_INFO_H */

ext/opcache/Optimizer/zend_func_info.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa
6464
uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
6565
&ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]);
6666
uint32_t t3 = 0;
67-
uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG;
67+
uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED;
6868

6969
if (call_info->num_args == 3) {
7070
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
8686
return tmp;
8787
} else {
8888
/* May throw */
89-
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;
89+
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;
9090
}
9191
}
9292

ext/opcache/Optimizer/zend_inference.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,7 +2454,7 @@ static zend_always_inline int _zend_update_type_info(
24542454
if (t1 & MAY_BE_OBJECT) {
24552455
tmp |= MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
24562456
} else {
2457-
tmp |= ((t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT) | ((t1 & MAY_BE_ANY)? MAY_BE_ARRAY_KEY_LONG : 0);
2457+
tmp |= ((t1 & MAY_BE_ANY) << MAY_BE_ARRAY_SHIFT) | ((t1 & MAY_BE_ANY) ? MAY_BE_ARRAY_PACKED : 0);
24582458
}
24592459
}
24602460
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
@@ -3474,7 +3474,7 @@ static zend_always_inline int _zend_update_type_info(
34743474
UPDATE_SSA_TYPE(MAY_BE_LONG, ssa_op->result_def);
34753475
break;
34763476
case ZEND_FUNC_GET_ARGS:
3477-
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);
3477+
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);
34783478
break;
34793479
case ZEND_GET_CLASS:
34803480
case ZEND_GET_CALLED_CLASS:

ext/opcache/Optimizer/zend_inference.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ static zend_always_inline uint32_t _const_op_type(const zval *zv) {
178178
}
179179
tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
180180
} ZEND_HASH_FOREACH_END();
181+
if (HT_IS_PACKED(ht)) {
182+
tmp &= ~MAY_BE_ARRAY_HASH;
183+
}
181184
return tmp;
182185
} else {
183186
uint32_t tmp = (1 << Z_TYPE_P(zv));

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 57 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4947,21 +4947,26 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
49474947
}
49484948

49494949
if (op2_info & MAY_BE_LONG) {
4950+
zend_bool op2_loaded = 0;
4951+
49504952
if (op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - MAY_BE_LONG)) {
49514953
| // if (EXPECTED(Z_TYPE_P(dim) == IS_LONG))
49524954
| IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3
49534955
}
4954-
if (type == BP_VAR_W || type == BP_VAR_RW) {
4956+
if (type == BP_VAR_W) {
49554957
| // hval = Z_LVAL_P(dim);
49564958
| GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr
4959+
op2_loaded = 1;
49574960
}
4958-
if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
4961+
if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_PACKED)) {
49594962
if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
49604963
zend_long val = Z_LVAL_P(Z_ZV(op2_addr));
49614964
if (val >= 0 && val < HT_MAX_SIZE) {
49624965
| // ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
4963-
| test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
4964-
| jz >4 // HASH_FIND
4966+
if (op1_info & MAY_BE_ARRAY_HASH) {
4967+
| test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
4968+
| jz >4 // HASH_FIND
4969+
}
49654970
| // if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed))
49664971
|.if X64
49674972
| 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
49985003
}
49995004
}
50005005
} else {
5001-
if (type != BP_VAR_W && type != BP_VAR_RW) {
5006+
if (!op2_loaded) {
50025007
| // hval = Z_LVAL_P(dim);
50035008
| GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr
5009+
op2_loaded = 1;
50045010
}
50055011
| // ZEND_HASH_INDEX_FIND(ht, hval, retval, num_undef);
5006-
| test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
5007-
| jz >4 // HASH_FIND
5012+
if (op1_info & MAY_BE_ARRAY_HASH) {
5013+
| test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
5014+
| jz >4 // HASH_FIND
5015+
}
50085016
| // if (EXPECTED((zend_ulong)(_h) < (zend_ulong)(_ht)->nNumUsed))
50095017
|.if X64
50105018
| 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
50425050
}
50435051
switch (type) {
50445052
case BP_JIT_IS:
5045-
if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
5053+
if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_HASH)) {
50465054
|4:
5047-
}
5048-
if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5049-
| // hval = Z_LVAL_P(dim);
5050-
| GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr
5051-
}
5052-
| EXT_CALL _zend_hash_index_find, r0
5053-
| test r0, r0
5054-
if (not_found_exit_addr) {
5055-
| jz &not_found_exit_addr
5055+
if (!op2_loaded) {
5056+
| // hval = Z_LVAL_P(dim);
5057+
| GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr
5058+
}
5059+
| EXT_CALL _zend_hash_index_find, r0
5060+
| test r0, r0
5061+
if (not_found_exit_addr) {
5062+
| jz &not_found_exit_addr
5063+
} else {
5064+
| jz >9 // NOT_FOUND
5065+
}
5066+
if (op2_info & MAY_BE_STRING) {
5067+
| jmp >5
5068+
}
5069+
} else if (not_found_exit_addr) {
5070+
| jmp &not_found_exit_addr
50565071
} else {
5057-
| jz >9 // NOT_FOUND
5058-
}
5059-
if (op2_info & MAY_BE_STRING) {
5060-
| jmp >5
5072+
| jmp >9 // NOT_FOUND
50615073
}
50625074
break;
50635075
case BP_VAR_R:
50645076
case BP_VAR_IS:
50655077
case BP_VAR_UNSET:
5066-
if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
5067-
if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5068-
zend_long val = Z_LVAL_P(Z_ZV(op2_addr));
5069-
if (val >= 0 && val < HT_MAX_SIZE) {
5070-
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5071-
| jmp &exit_addr
5072-
} else if (type == BP_VAR_IS && not_found_exit_addr) {
5073-
| jmp &not_found_exit_addr
5074-
} else {
5075-
| jmp >2 // NOT_FOUND
5076-
}
5077-
}
5078-
} else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5078+
if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) ||
5079+
!(op1_info & MAY_BE_ARRAY_HASH) ||
5080+
Z_MODE(op2_addr) != IS_CONST_ZVAL ||
5081+
(Z_LVAL_P(Z_ZV(op2_addr)) >= 0 && Z_LVAL_P(Z_ZV(op2_addr)) < HT_MAX_SIZE)) {
5082+
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
50795083
| jmp &exit_addr
50805084
} else if (type == BP_VAR_IS && not_found_exit_addr) {
50815085
| jmp &not_found_exit_addr
50825086
} else {
50835087
| jmp >2 // NOT_FOUND
50845088
}
5085-
|4:
50865089
}
5087-
if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
5088-
| // hval = Z_LVAL_P(dim);
5089-
| GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr
5090-
}
5091-
| EXT_CALL _zend_hash_index_find, r0
5092-
| test r0, r0
5093-
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5094-
| jz &exit_addr
5095-
} else if (type == BP_VAR_IS && not_found_exit_addr) {
5096-
| jz &not_found_exit_addr
5097-
} else {
5098-
| jz >2 // NOT_FOUND
5090+
if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_HASH)) {
5091+
|4:
5092+
if (!op2_loaded) {
5093+
| // hval = Z_LVAL_P(dim);
5094+
| GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr
5095+
}
5096+
| EXT_CALL _zend_hash_index_find, r0
5097+
| test r0, r0
5098+
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
5099+
| jz &exit_addr
5100+
} else if (type == BP_VAR_IS && not_found_exit_addr) {
5101+
| jz &not_found_exit_addr
5102+
} else {
5103+
| jz >2 // NOT_FOUND
5104+
}
50995105
}
51005106
|.cold_code
51015107
|2:
@@ -5124,6 +5130,10 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
51245130
case BP_VAR_RW:
51255131
|2:
51265132
|4:
5133+
if (!op2_loaded) {
5134+
| // hval = Z_LVAL_P(dim);
5135+
| GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr
5136+
}
51275137
| SAVE_VALID_OPLINE opline, r0
51285138
| EXT_CALL zend_jit_hash_index_lookup_rw, r0
51295139
| test r0, r0

0 commit comments

Comments
 (0)