Skip to content

Commit dee8c7f

Browse files
committed
Tweak lineno compilation
zend_compile_expr() and zend_compile_var() now restore CG(zend_lineno) after compilation of the node has finished. This makes the compiled lineno more predictable. Fixes GH-18985
1 parent 6fe3482 commit dee8c7f

6 files changed

Lines changed: 77 additions & 16 deletions

File tree

Zend/Optimizer/zend_optimizer.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,10 @@ static void zend_optimize(zend_op_array *op_array,
10521052
}
10531053

10541054
if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
1055-
zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL);
1055+
uint32_t additional_dump_flags = (ctx->debug_level & ZEND_DUMP_LINE_NUMBERS_PASSTHRU)
1056+
? ZEND_DUMP_LINE_NUMBERS
1057+
: 0;
1058+
zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES|additional_dump_flags, "before optimizer", NULL);
10561059
}
10571060

10581061
/* pass 1 (Simple local optimizations)

Zend/Optimizer/zend_optimizer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#define ZEND_DUMP_DFA_SSA (1<<27)
8181
#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
8282
#define ZEND_DUMP_SCCP (1<<29)
83+
#define ZEND_DUMP_LINE_NUMBERS_PASSTHRU (1<<30)
8384

8485
typedef struct _zend_script {
8586
zend_string *filename;

Zend/zend_compile.c

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7125,6 +7125,8 @@ static void zend_compile_match(znode *result, zend_ast *ast)
71257125
zend_ast *arm_ast = arms->child[i];
71267126
zend_ast *body_ast = arm_ast->child[1];
71277127

7128+
CG(zend_lineno) = zend_ast_get_lineno(arm_ast);
7129+
71287130
if (arm_ast->child[0] != NULL) {
71297131
zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
71307132

@@ -10728,6 +10730,8 @@ static void zend_compile_binary_op(znode *result, zend_ast *ast) /* {{{ */
1072810730
zend_compile_expr(&left_node, left_ast);
1072910731
zend_compile_expr(&right_node, right_ast);
1073010732

10733+
CG(zend_lineno) = ast->lineno;
10734+
1073110735
if (left_node.op_type == IS_CONST && right_node.op_type == IS_CONST) {
1073210736
if (zend_try_ct_eval_binary_op(&result->u.constant, opcode,
1073310737
&left_node.u.constant, &right_node.u.constant)
@@ -12327,9 +12331,6 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1232712331

1232812332
static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
1232912333
{
12330-
/* CG(zend_lineno) = ast->lineno; */
12331-
CG(zend_lineno) = zend_ast_get_lineno(ast);
12332-
1233312334
if (CG(memoize_mode) != ZEND_MEMOIZE_NONE) {
1233412335
zend_compile_memoized_expr(result, ast, BP_VAR_R);
1233512336
return;
@@ -12468,6 +12469,9 @@ static void zend_compile_expr(znode *result, zend_ast *ast)
1246812469
{
1246912470
zend_check_stack_limit();
1247012471

12472+
uint32_t prev_lineno = CG(zend_lineno);
12473+
CG(zend_lineno) = zend_ast_get_lineno(ast);
12474+
1247112475
uint32_t checkpoint = zend_short_circuiting_checkpoint();
1247212476
zend_compile_expr_inner(result, ast);
1247312477
zend_short_circuiting_commit(checkpoint, result, ast);
@@ -12477,12 +12481,12 @@ static void zend_compile_expr(znode *result, zend_ast *ast)
1247712481
ZEND_ASSERT(result->op_type != IS_VAR);
1247812482
}
1247912483
#endif
12484+
12485+
CG(zend_lineno) = prev_lineno;
1248012486
}
1248112487

1248212488
static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t type, bool by_ref)
1248312489
{
12484-
CG(zend_lineno) = zend_ast_get_lineno(ast);
12485-
1248612490
if (CG(memoize_mode) != ZEND_MEMOIZE_NONE) {
1248712491
switch (ast->kind) {
1248812492
case ZEND_AST_CALL:
@@ -12543,6 +12547,9 @@ static zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, bo
1254312547
{
1254412548
zend_check_stack_limit();
1254512549

12550+
uint32_t prev_lineno = CG(zend_lineno);
12551+
CG(zend_lineno) = zend_ast_get_lineno(ast);
12552+
1254612553
uint32_t checkpoint = zend_short_circuiting_checkpoint();
1254712554
zend_op *opcode = zend_compile_var_inner(result, ast, type, by_ref);
1254812555
zend_short_circuiting_commit(checkpoint, result, ast);
@@ -12556,32 +12563,47 @@ static zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, bo
1255612563
ZEND_ASSERT(result->op_type != IS_VAR);
1255712564
}
1255812565
#endif
12566+
12567+
CG(zend_lineno) = prev_lineno;
12568+
1255912569
return opcode;
1256012570
}
1256112571

1256212572
static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_ref) /* {{{ */
1256312573
{
1256412574
zend_check_stack_limit();
1256512575

12576+
uint32_t prev_lineno = CG(zend_lineno);
12577+
CG(zend_lineno) = zend_ast_get_lineno(ast);
12578+
12579+
zend_op *opline;
1256612580
switch (ast->kind) {
1256712581
case ZEND_AST_VAR:
12568-
return zend_compile_simple_var(result, ast, type, true);
12582+
opline = zend_compile_simple_var(result, ast, type, true);
12583+
break;
1256912584
case ZEND_AST_DIM:
12570-
return zend_delayed_compile_dim(result, ast, type, by_ref);
12585+
opline = zend_delayed_compile_dim(result, ast, type, by_ref);
12586+
break;
1257112587
case ZEND_AST_PROP:
1257212588
case ZEND_AST_NULLSAFE_PROP:
1257312589
{
12574-
zend_op *opline = zend_delayed_compile_prop(result, ast, type);
12590+
opline = zend_delayed_compile_prop(result, ast, type);
1257512591
if (by_ref) {
1257612592
opline->extended_value |= ZEND_FETCH_REF;
1257712593
}
12578-
return opline;
12594+
break;
1257912595
}
1258012596
case ZEND_AST_STATIC_PROP:
12581-
return zend_compile_static_prop(result, ast, type, by_ref, true);
12597+
opline = zend_compile_static_prop(result, ast, type, by_ref, true);
12598+
break;
1258212599
default:
12583-
return zend_compile_var(result, ast, type, false);
12600+
opline = zend_compile_var(result, ast, type, false);
12601+
break;
1258412602
}
12603+
12604+
CG(zend_lineno) = prev_lineno;
12605+
12606+
return opline;
1258512607
}
1258612608
/* }}} */
1258712609

ext/opcache/tests/gh18985.phpt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
GH-18985: Wrong lineno for multiline expressions
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
opcache.opt_debug_level=0x40010000
8+
--FILE--
9+
<?php
10+
11+
echo match(15) {
12+
13 => "A",
13+
15 => "B",
14+
default => "C",
15+
};
16+
17+
?>
18+
--EXPECTF--
19+
$_main:
20+
; (lines=9, args=0, vars=0, tmps=%s)
21+
; (before optimizer)
22+
; %sgh18985.php:1-10
23+
; return [] RANGE[0..0]
24+
L0003 0000 MATCH int(15) 13: 0001, 15: 0003, default: 0005
25+
L0004 0001 T1 = QM_ASSIGN string("A")
26+
L0004 0002 JMP 0007
27+
L0005 0003 T1 = QM_ASSIGN string("B")
28+
L0005 0004 JMP 0007
29+
L0006 0005 T1 = QM_ASSIGN string("C")
30+
L0006 0006 JMP 0007
31+
L0003 0007 ECHO T1
32+
L0010 0008 RETURN int(1)
33+
LIVE RANGES:
34+
1: 0006 - 0007 (tmp/var)
35+
B

ext/opcache/tests/jit/shift_right_004.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ Warning: Undefined array key 0 in %sshift_right_004.php on line 7
3030

3131
Deprecated: Implicit conversion from float %f to int loses precision in %sshift_right_004.php on line 8
3232

33-
Warning: A non-numeric value encountered in %sshift_right_004.php on line 7
33+
Warning: A non-numeric value encountered in %sshift_right_004.php on line 6
3434

35-
Warning: A non-numeric value encountered in %sshift_right_004.php on line 7
35+
Warning: A non-numeric value encountered in %sshift_right_004.php on line 6
3636

3737
Fatal error: Uncaught ArithmeticError: Bit shift by negative number in %sshift_right_004.php:8
3838
Stack trace:

ext/opcache/tests/jit/switch_001.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ foo();
1616
?>
1717
DONE
1818
--EXPECTF--
19-
Warning: Undefined variable $y in %sswitch_001.php on line 4
19+
Warning: Undefined variable $y in %sswitch_001.php on line 3
2020

21-
Warning: Undefined variable $y in %sswitch_001.php on line 5
21+
Warning: Undefined variable $y in %sswitch_001.php on line 3
2222
DONE

0 commit comments

Comments
 (0)