Skip to content

Commit d4383be

Browse files
committed
Use guard to check if array is packed or hash
1 parent 5948a66 commit d4383be

File tree

4 files changed

+113
-24
lines changed

4 files changed

+113
-24
lines changed

ext/opcache/Optimizer/zend_inference.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
/* Bitmask for type inference (zend_ssa_var_info.type) */
2727
#include "zend_type_info.h"
2828

29+
#define MAY_BE_PACKED_GUARD (1<<27) /* needs packed array guard */
2930
#define MAY_BE_CLASS_GUARD (1<<27) /* needs class guard */
3031
#define MAY_BE_GUARD (1<<28) /* needs type guard */
3132
#define MAY_BE_IN_REG (1<<29) /* value allocated in CPU register */

ext/opcache/jit/zend_jit_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ typedef enum _zend_jit_trace_stop {
210210
#define ZEND_JIT_EXIT_POLYMORPHISM (1<<4) /* exit because of polymorphic call */
211211
#define ZEND_JIT_EXIT_FREE_OP1 (1<<5)
212212
#define ZEND_JIT_EXIT_FREE_OP2 (1<<6)
213+
#define ZEND_JIT_EXIT_PACKED_GUARD (1<<7)
213214

214215
typedef union _zend_op_trace_info {
215216
zend_op dummy; /* the size of this structure must be the same as zend_op */

ext/opcache/jit/zend_jit_trace.c

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,12 +1358,12 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
13581358
level = 0;
13591359
for (;;p++) {
13601360
if (p->op == ZEND_JIT_TRACE_VM) {
1361-
uint8_t op1_type, op2_type, op3_type;
1361+
uint8_t orig_op1_type, op1_type, op2_type, op3_type;
13621362

13631363
// TODO: range inference ???
13641364
opline = p->opline;
13651365

1366-
op1_type = p->op1_type;
1366+
op1_type = orig_op1_type = p->op1_type;
13671367
op2_type = p->op2_type;
13681368
op3_type = p->op3_type;
13691369
if (op1_type & (IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)) {
@@ -1425,8 +1425,6 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
14251425
case ZEND_IS_IDENTICAL:
14261426
case ZEND_IS_NOT_IDENTICAL:
14271427
case ZEND_CASE_STRICT:
1428-
case ZEND_FETCH_DIM_R:
1429-
case ZEND_FETCH_DIM_IS:
14301428
case ZEND_BW_OR:
14311429
case ZEND_BW_AND:
14321430
case ZEND_BW_XOR:
@@ -1505,8 +1503,38 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin
15051503
// TODO: support for empty() ???
15061504
break;
15071505
}
1506+
/* break missing intentionally */
1507+
case ZEND_FETCH_DIM_R:
1508+
case ZEND_FETCH_DIM_IS:
15081509
ADD_OP1_TRACE_GUARD();
15091510
ADD_OP2_TRACE_GUARD();
1511+
1512+
if (opline->op1_type != IS_CONST
1513+
&& op1_type == IS_ARRAY
1514+
&& ((opline->op2_type == IS_CONST
1515+
&& Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_LONG)
1516+
|| (opline->op2_type != IS_CONST
1517+
&& op2_type == IS_LONG))) {
1518+
1519+
zend_ssa_var_info *info = &tssa->var_info[tssa->ops[idx].op1_use];
1520+
1521+
if ((info->type & MAY_BE_ARRAY_PACKED)
1522+
&& (info->type & MAY_BE_ARRAY_HASH)
1523+
&& orig_op1_type != IS_UNKNOWN
1524+
&& !(orig_op1_type & IS_TRACE_REFERENCE)) {
1525+
/* setup "packed" guards only for loop invariant or reused variables */
1526+
if ((trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
1527+
&& tssa->ops[idx].op1_use < trace_buffer->op_array->last_var)
1528+
|| tssa->ops[idx].op1_use_chain >= 0) {
1529+
info->type |= MAY_BE_PACKED_GUARD;
1530+
if (orig_op1_type & IS_TRACE_PACKED) {
1531+
info->type &= ~MAY_BE_ARRAY_HASH;
1532+
} else {
1533+
info->type &= ~MAY_BE_ARRAY_PACKED;
1534+
}
1535+
}
1536+
}
1537+
}
15101538
break;
15111539
case ZEND_SEND_VAL_EX:
15121540
case ZEND_SEND_VAR_EX:
@@ -3023,6 +3051,16 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
30233051
} else {
30243052
SET_STACK_TYPE(stack, i, IS_UNKNOWN);
30253053
}
3054+
3055+
if ((info & MAY_BE_PACKED_GUARD) != 0
3056+
&& trace_buffer->stop == ZEND_JIT_TRACE_STOP_LOOP
3057+
&& ssa->vars[i].use_chain != -1) {
3058+
if (!zend_jit_packed_guard(&dasm_state, opline, EX_NUM_TO_VAR(i), info)) {
3059+
goto jit_failure;
3060+
}
3061+
info &= ~MAY_BE_PACKED_GUARD;
3062+
ssa->var_info[i].type = info;
3063+
}
30263064
}
30273065

30283066
if (parent_trace) {
@@ -4023,6 +4061,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
40234061
avoid_refcounting =
40244062
ssa_op->op1_use >= 0 &&
40254063
ssa->var_info[ssa_op->op1_use].avoid_refcounting;
4064+
if (op1_info & MAY_BE_PACKED_GUARD) {
4065+
ssa->var_info[ssa_op->op1_use].type &= ~MAY_BE_PACKED_GUARD;
4066+
}
40264067
if (!zend_jit_fetch_dim_read(&dasm_state, opline, ssa, ssa_op,
40274068
op1_info, op1_addr, avoid_refcounting,
40284069
op2_info, res_info, RES_REG_ADDR(),
@@ -4090,6 +4131,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
40904131
avoid_refcounting =
40914132
ssa_op->op1_use >= 0 &&
40924133
ssa->var_info[ssa_op->op1_use].avoid_refcounting;
4134+
if (op1_info & MAY_BE_PACKED_GUARD) {
4135+
ssa->var_info[ssa_op->op1_use].type &= ~MAY_BE_PACKED_GUARD;
4136+
}
40934137
if (!zend_jit_isset_isempty_dim(&dasm_state, opline,
40944138
op1_info, op1_addr, avoid_refcounting,
40954139
op2_info,

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3234,6 +3234,27 @@ static int zend_jit_type_guard(dasm_State **Dst, const zend_op *opline, uint32_t
32343234
return 1;
32353235
}
32363236

3237+
static int zend_jit_packed_guard(dasm_State **Dst, const zend_op *opline, uint32_t var, uint32_t op_info)
3238+
{
3239+
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
3240+
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
3241+
3242+
if (!exit_addr) {
3243+
return 0;
3244+
}
3245+
3246+
| GET_ZVAL_LVAL ZREG_FCARG1a, ZEND_ADDR_MEM_ZVAL(ZREG_FP, var)
3247+
if (op_info & MAY_BE_ARRAY_PACKED) {
3248+
| test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
3249+
| jz &exit_addr
3250+
} else {
3251+
| test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
3252+
| jnz &exit_addr
3253+
}
3254+
3255+
return 1;
3256+
}
3257+
32373258
static int zend_jit_trace_handler(dasm_State **Dst, const zend_op_array *op_array, const zend_op *opline, int may_throw, zend_jit_trace_rec *trace)
32383259
{
32393260
zend_jit_op_array_trace_extension *jit_extension =
@@ -4953,12 +4974,27 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
49534974
| // if (EXPECTED(Z_TYPE_P(dim) == IS_LONG))
49544975
| IF_NOT_ZVAL_TYPE op2_addr, IS_LONG, >3
49554976
}
4977+
if (op1_info & MAY_BE_PACKED_GUARD) {
4978+
int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_PACKED_GUARD);
4979+
const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point);
4980+
4981+
if (!exit_addr) {
4982+
return 0;
4983+
}
4984+
if (op1_info & MAY_BE_ARRAY_PACKED) {
4985+
| test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
4986+
| jz &exit_addr
4987+
} else {
4988+
| test dword [FCARG1a + offsetof(zend_array, u.flags)], HASH_FLAG_PACKED
4989+
| jnz &exit_addr
4990+
}
4991+
}
49564992
if (type == BP_VAR_W) {
49574993
| // hval = Z_LVAL_P(dim);
49584994
| GET_ZVAL_LVAL ZREG_FCARG2a, op2_addr
49594995
op2_loaded = 1;
49604996
}
4961-
if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_PACKED)) {
4997+
if (op1_info & MAY_BE_ARRAY_PACKED) {
49624998
if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
49634999
zend_long val = Z_LVAL_P(Z_ZV(op2_addr));
49645000
if (val >= 0 && val < HT_MAX_SIZE) {
@@ -5050,7 +5086,7 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
50505086
}
50515087
switch (type) {
50525088
case BP_JIT_IS:
5053-
if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_HASH)) {
5089+
if (op1_info & MAY_BE_ARRAY_HASH) {
50545090
|4:
50555091
if (!op2_loaded) {
50565092
| // hval = Z_LVAL_P(dim);
@@ -5076,9 +5112,9 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
50765112
case BP_VAR_IS:
50775113
case BP_VAR_UNSET:
50785114
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)) {
5115+
((op1_info & MAY_BE_ARRAY_PACKED) &&
5116+
(Z_MODE(op2_addr) != IS_CONST_ZVAL ||
5117+
(Z_LVAL_P(Z_ZV(op2_addr)) >= 0 && Z_LVAL_P(Z_ZV(op2_addr)) < HT_MAX_SIZE)))) {
50825118
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && type == BP_VAR_R) {
50835119
| jmp &exit_addr
50845120
} else if (type == BP_VAR_IS && not_found_exit_addr) {
@@ -5087,7 +5123,7 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
50875123
| jmp >2 // NOT_FOUND
50885124
}
50895125
}
5090-
if ((op1_info & MAY_BE_ARRAY_KEY_LONG) && (op1_info & MAY_BE_ARRAY_HASH)) {
5126+
if (op1_info & MAY_BE_ARRAY_HASH) {
50915127
|4:
50925128
if (!op2_loaded) {
50935129
| // hval = Z_LVAL_P(dim);
@@ -5140,20 +5176,27 @@ static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_o
51405176
| jz >9
51415177
break;
51425178
case BP_VAR_W:
5143-
|2:
5144-
| //retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
5145-
|.if X64
5146-
| LOAD_ADDR_ZTS CARG3, executor_globals, uninitialized_zval
5147-
|.else
5148-
| sub r4, 12
5149-
| PUSH_ADDR_ZTS executor_globals, uninitialized_zval, r0
5150-
|.endif
5151-
| EXT_CALL zend_hash_index_add_new, r0
5152-
|.if not(X64)
5153-
| add r4, 12
5154-
|.endif
5155-
if (op1_info & MAY_BE_ARRAY_KEY_LONG) {
5156-
| jmp >8
5179+
if (!(op1_info & MAY_BE_ARRAY_KEY_LONG) ||
5180+
((op1_info & MAY_BE_ARRAY_PACKED) &&
5181+
(Z_MODE(op2_addr) != IS_CONST_ZVAL ||
5182+
(Z_LVAL_P(Z_ZV(op2_addr)) >= 0 && Z_LVAL_P(Z_ZV(op2_addr)) < HT_MAX_SIZE)))) {
5183+
|2:
5184+
| //retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval));
5185+
|.if X64
5186+
| LOAD_ADDR_ZTS CARG3, executor_globals, uninitialized_zval
5187+
|.else
5188+
| sub r4, 12
5189+
| PUSH_ADDR_ZTS executor_globals, uninitialized_zval, r0
5190+
|.endif
5191+
| EXT_CALL zend_hash_index_add_new, r0
5192+
|.if not(X64)
5193+
| add r4, 12
5194+
|.endif
5195+
if (op1_info & MAY_BE_ARRAY_HASH) {
5196+
| jmp >8
5197+
}
5198+
}
5199+
if (op1_info & MAY_BE_ARRAY_HASH) {
51575200
|4:
51585201
| EXT_CALL zend_jit_hash_index_lookup_w, r0
51595202
}

0 commit comments

Comments
 (0)