Skip to content

Commit a167e04

Browse files
committed
Eliminate some reference-counting
1 parent d097dfc commit a167e04

File tree

4 files changed

+122
-17
lines changed

4 files changed

+122
-17
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 AVOID_REFCOUNTING (1<<26) /* avoid reference counting */
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_trace.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3797,7 +3797,17 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
37973797
if (ra) {
37983798
zend_jit_trace_clenup_stack(stack, opline, ssa_op, ssa, ra);
37993799
}
3800-
exit_point = zend_jit_trace_get_exit_point(opline, exit_opline, p + 1, 0);
3800+
if (op1_info & AVOID_REFCOUNTING) {
3801+
/* Temporary reset ZREG_ZVAL_TRY_ADDREF */
3802+
zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
3803+
uint32_t old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
3804+
3805+
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
3806+
exit_point = zend_jit_trace_get_exit_point(opline, exit_opline, p + 1, 0);
3807+
SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_info);
3808+
} else {
3809+
exit_point = zend_jit_trace_get_exit_point(opline, exit_opline, p + 1, 0);
3810+
}
38013811
exit_addr = zend_jit_trace_get_exit_addr(exit_point);
38023812
if (!exit_addr) {
38033813
goto jit_failure;
@@ -4088,6 +4098,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
40884098
if (opline->opcode == ZEND_FETCH_THIS
40894099
&& delayed_fetch_this) {
40904100
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_THIS);
4101+
} else if (ssa->var_info[ssa_op->result_def].type & AVOID_REFCOUNTING) {
4102+
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_TRY_ADDREF);
40914103
} else if (ra && ra[ssa_op->result_def]) {
40924104
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ra[ssa_op->result_def]->reg);
40934105
}
@@ -5053,12 +5065,20 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
50535065
fprintf(stderr, "(%s)", zend_reg_name[STACK_REG(stack, j)]);
50545066
} else if (STACK_REG(stack, j) == ZREG_THIS) {
50555067
fprintf(stderr, "(this)");
5068+
} else if (STACK_REG(stack, j) == ZREG_ZVAL_TRY_ADDREF) {
5069+
fprintf(stderr, "(zval_try_addref)");
50565070
} else {
50575071
fprintf(stderr, "(const_%d)", STACK_REG(stack, j) - ZREG_NUM);
50585072
}
50595073
}
5074+
} else if (STACK_REG(stack, j) == ZREG_ZVAL_TRY_ADDREF) {
5075+
fprintf(stderr, " ");
5076+
zend_dump_var(op_array, (j < op_array->last_var) ? IS_CV : 0, j);
5077+
fprintf(stderr, ":unknown(zval_try_addref)");
50605078
} else if (STACK_REG(stack, j) == ZREG_ZVAL_COPY_R0) {
5061-
fprintf(stderr, " zval_copy(%s)", zend_reg_name[0]);
5079+
fprintf(stderr, " ");
5080+
zend_dump_var(op_array, (j < op_array->last_var) ? IS_CV : 0, j);
5081+
fprintf(stderr, ":unknown(zval_copy(%s))", zend_reg_name[0]);
50625082
}
50635083
}
50645084
fprintf(stderr, "\n");
@@ -5523,6 +5543,8 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
55235543
ZVAL_OBJ(EX_VAR_NUM(i), obj);
55245544
} else if (STACK_REG(stack, i) == ZREG_NULL) {
55255545
ZVAL_NULL(EX_VAR_NUM(i));
5546+
} else if (STACK_REG(stack, i) == ZREG_ZVAL_TRY_ADDREF) {
5547+
Z_TRY_ADDREF_P(EX_VAR_NUM(i));
55265548
} else if (STACK_REG(stack, i) == ZREG_ZVAL_COPY_R0) {
55275549
zval *val = (zval*)regs->r[0];
55285550

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 95 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3565,6 +3565,11 @@ static int zend_jit_store_const(dasm_State **Dst, int var, zend_reg reg)
35653565
| SET_ZVAL_TYPE_INFO dst, IS_DOUBLE
35663566
} else if (reg == ZREG_NULL) {
35673567
| SET_ZVAL_TYPE_INFO dst, IS_NULL
3568+
} else if (reg == ZREG_ZVAL_TRY_ADDREF) {
3569+
| IF_NOT_ZVAL_REFCOUNTED dst, >1
3570+
| GET_ZVAL_PTR r1, dst
3571+
| GC_ADDREF r1
3572+
|1:
35683573
} else if (reg == ZREG_ZVAL_COPY_R0) {
35693574
zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
35703575

@@ -4811,14 +4816,15 @@ static int zend_jit_concat(dasm_State **Dst, const zend_op *opline, const zend_o
48114816
return zend_jit_concat_helper(Dst, opline, op_array, opline->op1_type, opline->op1, op1_addr, op1_info, opline->op2_type, opline->op2, op2_addr, op2_info, res_addr, res_info, may_throw);
48124817
}
48134818

4814-
static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_op *opline, uint32_t type, uint32_t op1_info, uint32_t op2_info, uint32_t found, uint32_t not_found, const void *found_exit_addr, const void *not_found_exit_addr)
4819+
static int zend_jit_fetch_dimension_address_inner(dasm_State **Dst, const zend_op *opline, uint32_t type, uint32_t op1_info, uint32_t op2_info, uint32_t found, uint32_t not_found, const void *found_exit_addr, const void *not_found_exit_addr, const void *exit_addr)
48154820
/* Labels: 1,2,3,4,5 */
48164821
{
48174822
zend_jit_addr op2_addr = OP2_ADDR();
48184823
zend_jit_addr res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, opline->result.var);
4819-
const void *exit_addr = NULL;
48204824

4821-
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE && (type == BP_VAR_R || type == BP_VAR_RW)) {
4825+
if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE
4826+
&& (type == BP_VAR_R || type == BP_VAR_RW)
4827+
&& !exit_addr) {
48224828
int32_t exit_point = zend_jit_trace_get_exit_point(opline, opline, NULL, ZEND_JIT_EXIT_TO_VM);
48234829
exit_addr = zend_jit_trace_get_exit_addr(exit_point);
48244830
if (!exit_addr) {
@@ -5642,7 +5648,7 @@ static int zend_jit_assign_dim(dasm_State **Dst, const zend_op *opline, const ze
56425648
uint32_t var_info = zend_array_element_type(op1_info, opline->op1_type, 0, 0);
56435649
zend_jit_addr var_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FCARG1a, 0);
56445650

5645-
if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_W, op1_info, op2_info, 8, 8, NULL, NULL)) {
5651+
if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_W, op1_info, op2_info, 8, 8, NULL, NULL, NULL)) {
56465652
return 0;
56475653
}
56485654

@@ -5869,7 +5875,7 @@ static int zend_jit_assign_dim_op(dasm_State **Dst, const zend_op *opline, const
58695875
var_info |= MAY_BE_RC1;
58705876
}
58715877

5872-
if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, 8, 8, NULL, NULL)) {
5878+
if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, 8, 8, NULL, NULL, NULL)) {
58735879
return 0;
58745880
}
58755881

@@ -10256,6 +10262,45 @@ static int zend_jit_zval_copy_deref(dasm_State **Dst, zend_jit_addr res_addr, ze
1025610262
return 1;
1025710263
}
1025810264

10265+
static zend_bool zend_jit_may_avoid_refcounting(const zend_op *opline)
10266+
{
10267+
switch (opline->opcode) {
10268+
case ZEND_FETCH_OBJ_FUNC_ARG:
10269+
if (!JIT_G(current_frame) ||
10270+
!JIT_G(current_frame) ||
10271+
!JIT_G(current_frame)->call->func ||
10272+
!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
10273+
return 0;
10274+
}
10275+
/* break missing intentionally */
10276+
case ZEND_FETCH_OBJ_R:
10277+
case ZEND_FETCH_OBJ_IS:
10278+
if (opline->op2_type == IS_CONST
10279+
&& Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_STRING
10280+
&& Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] != '\0') {
10281+
return 1;
10282+
}
10283+
break;
10284+
case ZEND_FETCH_DIM_FUNC_ARG:
10285+
if (!JIT_G(current_frame) ||
10286+
!JIT_G(current_frame) ||
10287+
!JIT_G(current_frame)->call->func ||
10288+
!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
10289+
return 0;
10290+
}
10291+
/* break missing intentionally */
10292+
case ZEND_FETCH_DIM_R:
10293+
case ZEND_FETCH_DIM_IS:
10294+
return 1;
10295+
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
10296+
if (!(opline->extended_value & ZEND_ISEMPTY)) {
10297+
return 1;
10298+
}
10299+
break;
10300+
}
10301+
return 0;
10302+
}
10303+
1025910304
static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, uint32_t op1_info, zend_jit_addr op1_addr, uint32_t op2_info, uint32_t res_info, int may_throw)
1026010305
{
1026110306
zend_jit_addr orig_op1_addr, op2_addr, res_addr;
@@ -10277,6 +10322,11 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
1027710322
}
1027810323
}
1027910324

10325+
if (op1_info & AVOID_REFCOUNTING) {
10326+
SET_STACK_REG(JIT_G(current_frame)->stack,
10327+
EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
10328+
}
10329+
1028010330
if ((res_info & MAY_BE_GUARD)
1028110331
&& JIT_G(current_frame)
1028210332
&& (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
@@ -10285,13 +10335,20 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
1028510335
zend_jit_trace_stack *stack = JIT_G(current_frame)->stack;
1028610336
int32_t exit_point;
1028710337

10288-
if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
10338+
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
10339+
&& !(op1_info & AVOID_REFCOUNTING)) {
1028910340
flags = ZEND_JIT_EXIT_FREE_OP1;
1029010341
}
1029110342
if ((opline->op2_type & (IS_VAR|IS_TMP_VAR))
1029210343
&& (op2_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))) {
1029310344
flags = ZEND_JIT_EXIT_FREE_OP2;
1029410345
}
10346+
if ((res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
10347+
&& (ssa_op+1)->op1_use == ssa_op->result_def
10348+
&& zend_jit_may_avoid_refcounting(opline+1)) {
10349+
res_info |= AVOID_REFCOUNTING;
10350+
ssa->var_info[ssa_op->result_def].type |= AVOID_REFCOUNTING;
10351+
}
1029510352
old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
1029610353
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN);
1029710354
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_R0);
@@ -10332,7 +10389,7 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
1033210389
}
1033310390
}
1033410391
| GET_ZVAL_LVAL ZREG_FCARG1a, op1_addr
10335-
if (!zend_jit_fetch_dimension_address_inner(Dst, opline, (opline->opcode != ZEND_FETCH_DIM_IS) ? BP_VAR_R : BP_VAR_IS, op1_info, op2_info, 8, 9, NULL, not_found_exit_addr)) {
10392+
if (!zend_jit_fetch_dimension_address_inner(Dst, opline, (opline->opcode != ZEND_FETCH_DIM_IS) ? BP_VAR_R : BP_VAR_IS, op1_info, op2_info, 8, 9, NULL, not_found_exit_addr, exit_addr)) {
1033610393
return 0;
1033710394
}
1033810395
}
@@ -10473,7 +10530,9 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
1047310530
| IF_NOT_ZVAL_TYPE val_addr, type, &res_exit_addr
1047410531
| // ZVAL_COPY
1047510532
| ZVAL_COPY_VALUE res_addr, -1, val_addr, res_info, ZREG_R1, ZREG_R2
10476-
| TRY_ADDREF res_info, ch, r2
10533+
if (!(res_info & AVOID_REFCOUNTING)) {
10534+
| TRY_ADDREF res_info, ch, r2
10535+
}
1047710536
} else if (op1_info & MAY_BE_ARRAY_OF_REF) {
1047810537
| // ZVAL_COPY_DEREF
1047910538
| GET_ZVAL_TYPE_INFO Rd(ZREG_R2), val_addr
@@ -10496,7 +10555,9 @@ static int zend_jit_fetch_dim_read(dasm_State **Dst, const zend_op *opline, cons
1049610555
#endif
1049710556

1049810557
| FREE_OP opline->op2_type, opline->op2, op2_info, 0, op_array, opline
10499-
| FREE_OP opline->op1_type, opline->op1, op1_info, 0, op_array, opline
10558+
if (!(op1_info & AVOID_REFCOUNTING)) {
10559+
| FREE_OP opline->op1_type, opline->op1, op1_info, 0, op_array, opline
10560+
}
1050010561

1050110562
if (may_throw) {
1050210563
if (!zend_jit_check_exception(Dst)) {
@@ -10542,7 +10603,7 @@ static int zend_jit_isset_isempty_dim(dasm_State **Dst, const zend_op *opline, c
1054210603
not_found_exit_addr = exit_addr;
1054310604
}
1054410605
}
10545-
if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_JIT_IS, op1_info, op2_info, 8, 9, found_exit_addr, not_found_exit_addr)) {
10606+
if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_JIT_IS, op1_info, op2_info, 8, 9, found_exit_addr, not_found_exit_addr, NULL)) {
1054610607
return 0;
1054710608
}
1054810609

@@ -10602,7 +10663,9 @@ static int zend_jit_isset_isempty_dim(dasm_State **Dst, const zend_op *opline, c
1060210663

1060310664
|8:
1060410665
| FREE_OP opline->op2_type, opline->op2, op2_info, 0, op_array, opline
10605-
| FREE_OP opline->op1_type, opline->op1, op1_info, 0, op_array, opline
10666+
if (!(op1_info & AVOID_REFCOUNTING)) {
10667+
| FREE_OP opline->op1_type, opline->op1, op1_info, 0, op_array, opline
10668+
}
1060610669
if (may_throw) {
1060710670
if (!zend_jit_check_exception_undef_result(Dst, opline)) {
1060810671
return 0;
@@ -10636,7 +10699,9 @@ static int zend_jit_isset_isempty_dim(dasm_State **Dst, const zend_op *opline, c
1063610699

1063710700
|9: // not found
1063810701
| FREE_OP opline->op2_type, opline->op2, op2_info, 0, op_array, opline
10639-
| FREE_OP opline->op1_type, opline->op1, op1_info, 0, op_array, opline
10702+
if (!(op1_info & AVOID_REFCOUNTING)) {
10703+
| FREE_OP opline->op1_type, opline->op1, op1_info, 0, op_array, opline
10704+
}
1064010705
if (may_throw) {
1064110706
if (!zend_jit_check_exception_undef_result(Dst, opline)) {
1064210707
return 0;
@@ -11233,6 +11298,10 @@ static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zen
1123311298
}
1123411299
}
1123511300
}
11301+
if (op1_info & AVOID_REFCOUNTING) {
11302+
SET_STACK_REG(JIT_G(current_frame)->stack,
11303+
EX_VAR_TO_NUM(opline->op1.var), ZREG_NONE);
11304+
}
1123611305
if (opline->opcode == ZEND_FETCH_OBJ_W) {
1123711306
if (Z_REG(prop_addr) != ZREG_FCARG1a || Z_OFFSET(prop_addr) != 0) {
1123811307
| LOAD_ZVAL_ADDR FCARG1a, prop_addr
@@ -11251,12 +11320,21 @@ static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zen
1125111320
zend_uchar type;
1125211321
zend_jit_addr val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0);
1125311322

11254-
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && !use_this) {
11323+
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
11324+
&& !use_this
11325+
&& !(op1_info & AVOID_REFCOUNTING)) {
1125511326
flags = ZEND_JIT_EXIT_FREE_OP1;
1125611327
}
1125711328

1125811329
| LOAD_ZVAL_ADDR r0, prop_addr
1125911330

11331+
if ((res_info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE))
11332+
&& (ssa_op+1)->op1_use == ssa_op->result_def
11333+
&& zend_jit_may_avoid_refcounting(opline+1)) {
11334+
res_info |= AVOID_REFCOUNTING;
11335+
ssa->var_info[ssa_op->result_def].type |= AVOID_REFCOUNTING;
11336+
}
11337+
1126011338
old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
1126111339
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_UNKNOWN);
1126211340
SET_STACK_REG(stack, EX_VAR_TO_NUM(opline->result.var), ZREG_ZVAL_COPY_R0);
@@ -11289,7 +11367,9 @@ static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zen
1128911367
| SET_ZVAL_TYPE_INFO res_addr, type
1129011368
} else {
1129111369
| SET_ZVAL_TYPE_INFO res_addr, edx
11292-
| TRY_ADDREF res_info, dh, r1
11370+
if (!(res_info & AVOID_REFCOUNTING)) {
11371+
| TRY_ADDREF res_info, dh, r1
11372+
}
1129311373
}
1129411374
} else {
1129511375
if (!zend_jit_zval_copy_deref(Dst, res_addr, prop_addr, ZREG_R2)) {
@@ -11375,7 +11455,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst, const zend_op *opline, const zen
1137511455
| SAVE_VALID_OPLINE opline, r0
1137611456
| EXT_CALL zend_jit_extract_helper, r0
1137711457
|1:
11378-
} else {
11458+
} else if (!(op1_info & AVOID_REFCOUNTING)) {
1137911459
| FREE_OP opline->op1_type, opline->op1, op1_info, 1, op_array, opline
1138011460
}
1138111461
}

ext/opcache/jit/zend_jit_x86.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ typedef enum _zend_reg {
7272
ZREG_LONG_MAX,
7373
ZREG_LONG_MAX_PLUS_1,
7474
ZREG_NULL,
75+
76+
ZREG_ZVAL_TRY_ADDREF,
7577
ZREG_ZVAL_COPY_R0,
7678
} zend_reg;
7779

0 commit comments

Comments
 (0)