Skip to content

Commit 95c0dfc

Browse files
committed
JIT: Fixed megamorphic call detection
1 parent 38626db commit 95c0dfc

File tree

3 files changed

+70
-33
lines changed

3 files changed

+70
-33
lines changed

ext/opcache/jit/zend_jit_vm_helpers.c

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,14 @@ static uint8_t zend_jit_trace_bad_stop_event(const zend_op *opline, int count)
439439
return 0;
440440
}
441441

442-
static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx, uint32_t is_megamorphic, uint32_t *megamorphic, uint32_t level, uint32_t init_level, uint32_t *call_level)
442+
/* Workaround for PHP-8.0 */
443+
#ifndef ZEND_CALL_JIT_RESERVED
444+
# define ZEND_CALL_JIT_RESERVED (1<<29)
445+
#endif
446+
447+
#define ZEND_CALL_MEGAMORPHIC ZEND_CALL_JIT_RESERVED
448+
449+
static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx, uint32_t is_megamorphic, uint32_t init_level)
443450
{
444451
zend_jit_trace_stop stop ZEND_ATTRIBUTE_UNUSED = ZEND_JIT_TRACE_STOP_ERROR;
445452

@@ -448,7 +455,7 @@ static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend
448455
zend_jit_op_array_trace_extension *jit_extension;
449456

450457
if (call->prev_execute_data) {
451-
idx = zend_jit_trace_record_fake_init_call_ex(call->prev_execute_data, trace_buffer, idx, is_megamorphic, megamorphic, level, init_level + 1, call_level);
458+
idx = zend_jit_trace_record_fake_init_call_ex(call->prev_execute_data, trace_buffer, idx, is_megamorphic, init_level + 1);
452459
if (idx < 0) {
453460
return idx;
454461
}
@@ -479,32 +486,16 @@ static int zend_jit_trace_record_fake_init_call_ex(zend_execute_data *call, zend
479486
&& ((ZEND_CALL_INFO(call) & ZEND_CALL_DYNAMIC)
480487
|| func->common.scope)) {
481488
func = NULL;
482-
*megamorphic |= (1 << (level + *call_level));
483-
} else {
484-
*megamorphic &= ~(1 << (level + *call_level));
489+
ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_MEGAMORPHIC);
485490
}
486-
(*call_level)++;
487491
TRACE_RECORD(ZEND_JIT_TRACE_INIT_CALL, ZEND_JIT_TRACE_FAKE_INFO(init_level), func);
488492
} while (0);
489493
return idx;
490494
}
491495

492-
static int zend_jit_trace_record_fake_init_call(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx, uint32_t is_megamorphic, uint32_t *megamorphic, uint32_t level)
496+
static int zend_jit_trace_record_fake_init_call(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int idx, uint32_t is_megamorphic)
493497
{
494-
uint32_t call_level = 0;
495-
496-
return zend_jit_trace_record_fake_init_call_ex(call, trace_buffer, idx, is_megamorphic, megamorphic, level, 0, &call_level);
497-
}
498-
499-
static int zend_jit_trace_call_level(const zend_execute_data *call)
500-
{
501-
int call_level = 0;
502-
503-
while (call->prev_execute_data) {
504-
call_level++;
505-
call = call->prev_execute_data;
506-
}
507-
return call_level;
498+
return zend_jit_trace_record_fake_init_call_ex(call, trace_buffer, idx, is_megamorphic, 0);
508499
}
509500

510501
static int zend_jit_trace_subtrace(zend_jit_trace_rec *trace_buffer, int start, int end, uint8_t event, const zend_op_array *op_array, const zend_op *opline)
@@ -558,7 +549,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
558549
zend_jit_trace_stop halt = 0;
559550
int level = 0;
560551
int ret_level = 0;
561-
int call_level;
562552
zend_vm_opcode_handler_t handler;
563553
const zend_op_array *op_array;
564554
zend_jit_op_array_trace_extension *jit_extension;
@@ -575,7 +565,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
575565
int last_loop = -1;
576566
int last_loop_level = -1;
577567
const zend_op *last_loop_opline = NULL;
578-
uint32_t megamorphic = 0;
579568
const zend_op_array *unrolled_calls[ZEND_JIT_TRACE_MAX_CALL_DEPTH + ZEND_JIT_TRACE_MAX_RET_DEPTH];
580569
#ifdef HAVE_GCC_GLOBAL_REGS
581570
zend_execute_data *prev_execute_data = ex;
@@ -613,7 +602,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
613602
}
614603

615604
if (prev_call) {
616-
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, is_megamorphic, &megamorphic, ret_level + level);
605+
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, is_megamorphic);
617606
if (ret < 0) {
618607
TRACE_END(ZEND_JIT_TRACE_END, ZEND_JIT_TRACE_STOP_BAD_FUNC, opline);
619608
#ifdef HAVE_GCC_GLOBAL_REGS
@@ -723,8 +712,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
723712
|| opline->opcode == ZEND_DO_ICALL
724713
|| opline->opcode == ZEND_DO_UCALL
725714
|| opline->opcode == ZEND_DO_FCALL_BY_NAME) {
726-
call_level = zend_jit_trace_call_level(EX(call));
727-
if (megamorphic & (1 << (ret_level + level + call_level))) {
715+
if (ZEND_CALL_INFO(EX(call)) & ZEND_CALL_MEGAMORPHIC) {
728716
stop = ZEND_JIT_TRACE_STOP_INTERPRETER;
729717
break;
730718
}
@@ -852,7 +840,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
852840
last_loop_opline = NULL;
853841

854842
if (prev_call) {
855-
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, 0, &megamorphic, ret_level + level);
843+
int ret = zend_jit_trace_record_fake_init_call(prev_call, trace_buffer, idx, 0);
856844
if (ret < 0) {
857845
stop = ZEND_JIT_TRACE_STOP_BAD_FUNC;
858846
break;
@@ -935,12 +923,8 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,
935923
&& trace_buffer[1].opline == opline - 1) {
936924
func = NULL;
937925
}
938-
call_level = zend_jit_trace_call_level(EX(call));
939-
ZEND_ASSERT(ret_level + level + call_level < 32);
940-
if (func) {
941-
megamorphic &= ~(1 << (ret_level + level + call_level));
942-
} else {
943-
megamorphic |= (1 << (ret_level + level + call_level));
926+
if (!func) {
927+
ZEND_ADD_CALL_FLAG(EX(call), ZEND_CALL_MEGAMORPHIC);
944928
}
945929
TRACE_RECORD(ZEND_JIT_TRACE_INIT_CALL, 0, func);
946930
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
JIT INIT_FCALL: 001 too deep nesting level
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.file_update_protection=0
7+
opcache.jit_buffer_size=1M
8+
--FILE--
9+
<?php
10+
ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(
11+
ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(
12+
ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(
13+
ini_set(ini_set(ini_set(ini_set(ini_set(ini_set(
14+
)))))))))))))))))))))))))))))))));
15+
?>
16+
--EXPECTF--
17+
Fatal error: Uncaught ArgumentCountError: ini_set() expects exactly 2 arguments, 0 given in %sinit_fcall_001.php:5
18+
Stack trace:
19+
#0 %sinit_fcall_001.php(5): ini_set()
20+
#1 {main}
21+
thrown in %sinit_fcall_001.php on line 5
22+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
JIT INIT_FCALL: 002 incorrect megamorphic call detection
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.file_update_protection=0
7+
opcache.jit_buffer_size=1M
8+
opcache.jit=tracing
9+
opcache.jit_max_polymorphic_calls=0
10+
--FILE--
11+
<?php
12+
class C {
13+
function foo($x) {
14+
return $x;
15+
}
16+
}
17+
function foo($x) {
18+
return $x;
19+
}
20+
function test2($x) {
21+
return foo(foo($x));
22+
}
23+
function test1() {
24+
$x = new C;
25+
foo(foo($x->foo(foo(test2($x)))));
26+
}
27+
test1();
28+
?>
29+
DONE
30+
--EXPECT--
31+
DONE

0 commit comments

Comments
 (0)