Skip to content

Commit 9480c89

Browse files
committed
Alternative implementation for short-circuiting
1 parent b45fafe commit 9480c89

File tree

2 files changed

+48
-97
lines changed

2 files changed

+48
-97
lines changed

Zend/zend_compile.c

Lines changed: 47 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,6 @@ typedef struct _zend_loop_var {
6060
uint32_t try_catch_offset;
6161
} zend_loop_var;
6262

63-
typedef struct _zend_short_circuiting_chain {
64-
zend_stack jmp_null_opnums;
65-
uint32_t flags;
66-
} zend_short_circuiting_chain;
67-
6863
static inline uint32_t zend_alloc_cache_slots(unsigned count) {
6964
if (count == 0) {
7065
return (uint32_t) -1;
@@ -373,15 +368,14 @@ void zend_init_compiler_data_structures(void) /* {{{ */
373368
{
374369
zend_stack_init(&CG(loop_var_stack), sizeof(zend_loop_var));
375370
zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op));
376-
zend_stack_init(&CG(short_circuiting_chains), sizeof(zend_short_circuiting_chain));
371+
zend_stack_init(&CG(short_circuiting_opnums), sizeof(uint32_t));
377372
CG(active_class_entry) = NULL;
378373
CG(in_compilation) = 0;
379374
CG(skip_shebang) = 0;
380375

381376
CG(encoding_declared) = 0;
382377
CG(memoized_exprs) = NULL;
383378
CG(memoize_mode) = 0;
384-
CG(in_short_circuiting_chain) = 0;
385379
}
386380
/* }}} */
387381

@@ -428,7 +422,7 @@ void shutdown_compiler(void) /* {{{ */
428422
{
429423
zend_stack_destroy(&CG(loop_var_stack));
430424
zend_stack_destroy(&CG(delayed_oplines_stack));
431-
zend_stack_destroy(&CG(short_circuiting_chains));
425+
zend_stack_destroy(&CG(short_circuiting_opnums));
432426
zend_hash_destroy(&CG(filenames_table));
433427
zend_arena_destroy(CG(arena));
434428

@@ -2233,15 +2227,7 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
22332227
}
22342228
/* }}} */
22352229

2236-
#define ZEND_WAS_IN_SHORT_CIRCUITING_CHAIN (1 << 0)
2237-
#define ZEND_SHORT_CIRCUITING_CHAIN_WAS_CREATED (1 << 1)
2238-
2239-
static zend_short_circuiting_chain *zend_current_short_circuiting_chain()
2240-
{
2241-
return zend_stack_top(&CG(short_circuiting_chains));
2242-
}
2243-
2244-
static zend_bool zend_ast_kind_is_short_circuited(uint32_t ast_kind)
2230+
static zend_bool zend_ast_kind_is_short_circuited(zend_ast_kind ast_kind)
22452231
{
22462232
switch (ast_kind) {
22472233
case ZEND_AST_DIM:
@@ -2276,67 +2262,56 @@ static zend_bool zend_ast_is_short_circuited(const zend_ast *ast)
22762262
return 0;
22772263
}
22782264

2279-
static uint32_t zend_begin_short_circuiting_chain(uint32_t ast_kind, zend_bool force)
2280-
{
2281-
zend_bool was_in_short_circuiting_chain = CG(in_short_circuiting_chain);
2282-
zend_bool ast_kind_is_short_circuited = zend_ast_kind_is_short_circuited(ast_kind);
2283-
2284-
uint32_t flags = 0;
2285-
if (was_in_short_circuiting_chain) {
2286-
flags |= ZEND_WAS_IN_SHORT_CIRCUITING_CHAIN;
2287-
}
2265+
/* Mark nodes that are an inner part of a short-circuiting chain.
2266+
* We should not perform a "commit" on them, as it will be performed by the outer-most node.
2267+
* We do this to avoid passing down an argument in various compile functions. */
22882268

2289-
if (force || (!was_in_short_circuiting_chain && ast_kind_is_short_circuited)) {
2290-
flags |= ZEND_SHORT_CIRCUITING_CHAIN_WAS_CREATED;
2269+
#define ZEND_SHORT_CIRCUITING_INNER 0x8000
22912270

2292-
CG(in_short_circuiting_chain) = 1;
2293-
2294-
zend_short_circuiting_chain chain;
2295-
zend_stack_init(&chain.jmp_null_opnums, sizeof(uint32_t));
2296-
chain.flags = 0;
2297-
zend_stack_push(&CG(short_circuiting_chains), &chain);
2298-
}
2299-
2300-
if (!force && !ast_kind_is_short_circuited) {
2301-
CG(in_short_circuiting_chain) = 0;
2271+
static void zend_short_circuiting_mark_inner(zend_ast *ast) {
2272+
if (zend_ast_kind_is_short_circuited(ast->kind)) {
2273+
ast->attr |= ZEND_SHORT_CIRCUITING_INNER;
23022274
}
2303-
2304-
return flags;
23052275
}
23062276

2307-
static void zend_end_short_circuiting_chain(uint32_t flags, znode *result)
2277+
static uint32_t zend_short_circuiting_checkpoint()
23082278
{
2309-
zend_bool was_in_short_circuiting_chain = (flags & ZEND_WAS_IN_SHORT_CIRCUITING_CHAIN) != 0;
2310-
zend_bool short_circuiting_chain_was_created = (flags & ZEND_SHORT_CIRCUITING_CHAIN_WAS_CREATED) != 0;
2311-
2312-
if (short_circuiting_chain_was_created) {
2313-
zend_short_circuiting_chain *chain = zend_current_short_circuiting_chain();
2314-
zend_stack *jmp_null_opnums = &chain->jmp_null_opnums;
2279+
return zend_stack_count(&CG(short_circuiting_opnums));
2280+
}
23152281

2316-
while (!zend_stack_is_empty(jmp_null_opnums)) {
2317-
uint32_t jmp_null_opnum = *(uint32_t *)zend_stack_top(jmp_null_opnums);
2318-
zend_op *jmp_null_opline = &CG(active_op_array)->opcodes[jmp_null_opnum];
2319-
jmp_null_opline->op2.opline_num = get_next_op_number();
2320-
SET_NODE(jmp_null_opline->result, result);
2321-
jmp_null_opline->extended_value = chain->flags;
2322-
zend_stack_del_top(jmp_null_opnums);
2323-
}
2282+
static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, zend_ast *ast)
2283+
{
2284+
zend_bool is_short_circuited = zend_ast_kind_is_short_circuited(ast->kind)
2285+
|| ast->kind == ZEND_AST_ISSET || ast->kind == ZEND_AST_EMPTY;
2286+
if (!is_short_circuited) {
2287+
ZEND_ASSERT(zend_stack_count(&CG(short_circuiting_opnums)) == checkpoint
2288+
&& "Short circuiting stack should be empty");
2289+
return;
2290+
}
23242291

2325-
zend_stack_destroy(jmp_null_opnums);
2326-
zend_stack_del_top(&CG(short_circuiting_chains));
2292+
if (ast->attr & ZEND_SHORT_CIRCUITING_INNER) {
2293+
/* Outer-most node will commit. */
2294+
return;
23272295
}
23282296

2329-
CG(in_short_circuiting_chain) = was_in_short_circuiting_chain;
2297+
while (zend_stack_count(&CG(short_circuiting_opnums)) != checkpoint) {
2298+
uint32_t opnum = *(uint32_t *) zend_stack_top(&CG(short_circuiting_opnums));
2299+
zend_op *opline = &CG(active_op_array)->opcodes[opnum];
2300+
opline->op2.opline_num = get_next_op_number();
2301+
SET_NODE(opline->result, result);
2302+
opline->extended_value =
2303+
ast->kind == ZEND_AST_ISSET ? ZEND_SHORT_CIRCUITING_CHAIN_ISSET :
2304+
ast->kind == ZEND_AST_EMPTY ? ZEND_SHORT_CIRCUITING_CHAIN_EMPTY :
2305+
ZEND_SHORT_CIRCUITING_CHAIN_EXPR;
2306+
zend_stack_del_top(&CG(short_circuiting_opnums));
2307+
}
23302308
}
23312309

23322310
static void zend_emit_jmp_null(znode *obj_node)
23332311
{
23342312
uint32_t jmp_null_opnum = get_next_op_number();
23352313
zend_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
2336-
2337-
zend_short_circuiting_chain *short_circuiting_chain = zend_current_short_circuiting_chain();
2338-
zend_stack *jmp_null_opnums = &short_circuiting_chain->jmp_null_opnums;
2339-
zend_stack_push(jmp_null_opnums, &jmp_null_opnum);
2314+
zend_stack_push(&CG(short_circuiting_opnums), &jmp_null_opnum);
23402315
}
23412316

23422317
#define ZEND_MEMOIZE_NONE 0
@@ -2658,10 +2633,7 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint
26582633
znode name_node;
26592634
zend_op *opline;
26602635

2661-
zend_bool was_in_short_circuiting_chain = CG(in_short_circuiting_chain);
2662-
CG(in_short_circuiting_chain) = 0;
26632636
zend_compile_expr(&name_node, name_ast);
2664-
CG(in_short_circuiting_chain) = was_in_short_circuiting_chain;
26652637
if (name_node.op_type == IS_CONST) {
26662638
convert_to_string(&name_node.u.constant);
26672639
}
@@ -2761,6 +2733,7 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t
27612733

27622734
znode var_node, dim_node;
27632735

2736+
zend_short_circuiting_mark_inner(var_ast);
27642737
opline = zend_delayed_compile_var(&var_node, var_ast, type, 0);
27652738
if (opline && type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) {
27662739
opline->extended_value |= ZEND_FETCH_DIM_WRITE;
@@ -2777,10 +2750,7 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t
27772750
}
27782751
dim_node.op_type = IS_UNUSED;
27792752
} else {
2780-
zend_bool was_in_short_circuiting_chain = CG(in_short_circuiting_chain);
2781-
CG(in_short_circuiting_chain) = 0;
27822753
zend_compile_expr(&dim_node, dim_ast);
2783-
CG(in_short_circuiting_chain) = was_in_short_circuiting_chain;
27842754
}
27852755

27862756
opline = zend_delayed_emit_op(result, ZEND_FETCH_DIM_R, &var_node, &dim_node);
@@ -2818,6 +2788,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
28182788
}
28192789
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
28202790
} else {
2791+
zend_short_circuiting_mark_inner(obj_ast);
28212792
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, 0);
28222793
zend_separate_if_call_and_write(&obj_node, obj_ast, type);
28232794
}
@@ -2826,10 +2797,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
28262797
zend_emit_jmp_null(&obj_node);
28272798
}
28282799

2829-
zend_bool was_in_short_circuiting_chain = CG(in_short_circuiting_chain);
2830-
CG(in_short_circuiting_chain) = 0;
28312800
zend_compile_expr(&prop_node, prop_ast);
2832-
CG(in_short_circuiting_chain) = was_in_short_circuiting_chain;
28332801

28342802
opline = zend_delayed_emit_op(result, ZEND_FETCH_OBJ_R, &obj_node, &prop_node);
28352803
if (opline->op2_type == IS_CONST) {
@@ -2862,12 +2830,10 @@ zend_op *zend_compile_static_prop(znode *result, zend_ast *ast, uint32_t type, i
28622830
znode class_node, prop_node;
28632831
zend_op *opline;
28642832

2833+
zend_short_circuiting_mark_inner(class_ast);
28652834
zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
28662835

2867-
zend_bool was_in_short_circuiting_chain = CG(in_short_circuiting_chain);
2868-
CG(in_short_circuiting_chain) = 0;
28692836
zend_compile_expr(&prop_node, prop_ast);
2870-
CG(in_short_circuiting_chain) = was_in_short_circuiting_chain;
28712837

28722838
if (delayed) {
28732839
opline = zend_delayed_emit_op(result, ZEND_FETCH_STATIC_PROP_R, &prop_node, NULL);
@@ -3325,9 +3291,6 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
33253291
zend_bool uses_arg_unpack = 0;
33263292
uint32_t arg_count = 0; /* number of arguments not including unpacks */
33273293

3328-
zend_bool was_in_short_circuiting_chain = CG(in_short_circuiting_chain);
3329-
CG(in_short_circuiting_chain) = 0;
3330-
33313294
for (i = 0; i < args->children; ++i) {
33323295
zend_ast *arg = args->child[i];
33333296
uint32_t arg_num = i + 1;
@@ -3448,8 +3411,6 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
34483411
opline->result.var = EX_NUM_TO_VAR(arg_num - 1);
34493412
}
34503413

3451-
CG(in_short_circuiting_chain) = was_in_short_circuiting_chain;
3452-
34533414
return arg_count;
34543415
}
34553416
/* }}} */
@@ -4286,6 +4247,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
42864247
}
42874248
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
42884249
} else {
4250+
zend_short_circuiting_mark_inner(obj_ast);
42894251
zend_compile_expr(&obj_node, obj_ast);
42904252
}
42914253

@@ -4360,12 +4322,10 @@ void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type) /* {{
43604322
zend_op *opline;
43614323
zend_function *fbc = NULL;
43624324

4325+
zend_short_circuiting_mark_inner(class_ast);
43634326
zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION);
43644327

4365-
zend_bool was_in_short_circuiting_chain = CG(in_short_circuiting_chain);
4366-
CG(in_short_circuiting_chain) = 0;
43674328
zend_compile_expr(&method_node, method_ast);
4368-
CG(in_short_circuiting_chain) = was_in_short_circuiting_chain;
43694329

43704330
if (method_node.op_type == IS_CONST) {
43714331
zval *name = &method_node.u.constant;
@@ -8689,17 +8649,12 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
86898649

86908650
ZEND_ASSERT(ast->kind == ZEND_AST_ISSET || ast->kind == ZEND_AST_EMPTY);
86918651

8692-
uint32_t begin_short_circuiting_chain_flags = zend_begin_short_circuiting_chain(ast->kind, 1);
8693-
zend_current_short_circuiting_chain()->flags = ast->kind == ZEND_AST_ISSET
8694-
? ZEND_SHORT_CIRCUITING_CHAIN_ISSET
8695-
: ZEND_SHORT_CIRCUITING_CHAIN_EMPTY;
8696-
8652+
zend_short_circuiting_mark_inner(var_ast);
86978653
if (!zend_is_variable(var_ast)) {
86988654
if (ast->kind == ZEND_AST_EMPTY) {
86998655
/* empty(expr) can be transformed to !expr */
87008656
zend_ast *not_ast = zend_ast_create_ex(ZEND_AST_UNARY_OP, ZEND_BOOL_NOT, var_ast);
87018657
zend_compile_expr(result, not_ast);
8702-
zend_end_short_circuiting_chain(begin_short_circuiting_chain_flags, result);
87038658
return;
87048659
} else {
87058660
zend_error_noreturn(E_COMPILE_ERROR,
@@ -8740,8 +8695,6 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
87408695
if (!(ast->kind == ZEND_AST_ISSET)) {
87418696
opline->extended_value |= ZEND_ISEMPTY;
87428697
}
8743-
8744-
zend_end_short_circuiting_chain(begin_short_circuiting_chain_flags, result);
87458698
}
87468699
/* }}} */
87478700

@@ -9606,9 +9559,9 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
96069559

96079560
void zend_compile_expr(znode *result, zend_ast *ast)
96089561
{
9609-
uint32_t begin_short_circuiting_chain_flags = zend_begin_short_circuiting_chain(ast->kind, 0);
9562+
uint32_t checkpoint = zend_short_circuiting_checkpoint();
96109563
zend_compile_expr_inner(result, ast);
9611-
zend_end_short_circuiting_chain(begin_short_circuiting_chain_flags, result);
9564+
zend_short_circuiting_commit(checkpoint, result, ast);
96129565
}
96139566

96149567
static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t type, int by_ref)
@@ -9651,10 +9604,9 @@ static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t ty
96519604

96529605
zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, int by_ref) /* {{{ */
96539606
{
9654-
uint32_t begin_short_circuiting_chain_flags = zend_begin_short_circuiting_chain(ast->kind, 0);
9607+
uint32_t checkpoint = zend_short_circuiting_checkpoint();
96559608
zend_op *opcode = zend_compile_var_inner(result, ast, type, by_ref);
9656-
zend_end_short_circuiting_chain(begin_short_circuiting_chain_flags, result);
9657-
9609+
zend_short_circuiting_commit(checkpoint, result, ast);
96589610
return opcode;
96599611
}
96609612

Zend/zend_globals.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ struct _zend_compiler_globals {
131131

132132
uint32_t rtd_key_counter;
133133

134-
zend_bool in_short_circuiting_chain;
135-
zend_stack short_circuiting_chains;
134+
zend_stack short_circuiting_opnums;
136135
};
137136

138137

0 commit comments

Comments
 (0)