Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions ext/opcache/jit/zend_jit_vm_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,11 +680,20 @@ static int zend_jit_trace_record_fake_init_call(zend_execute_data *call, zend_ji
return zend_jit_trace_record_fake_init_call_ex(call, trace_buffer, idx, is_megamorphic, 0);
}

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)
static int zend_jit_trace_subtrace(zend_execute_data *call, zend_jit_trace_rec *trace_buffer, int start, int end, uint8_t event, const zend_op_array *op_array, const zend_op *opline)
{
int idx;

TRACE_START(ZEND_JIT_TRACE_START, event, op_array, opline);
if (call) {
idx = zend_jit_trace_record_fake_init_call(call, trace_buffer, idx, 0);
if (idx < 0) {
return idx;
}
}
if (idx + (end - start) >= JIT_G(max_trace_length) - 2) {
return -1;
}
memmove(trace_buffer + idx, trace_buffer + start, (end - start) * sizeof(zend_jit_trace_rec));
return idx + (end - start);
}
Expand Down Expand Up @@ -1352,8 +1361,13 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex,

if (opline == last_loop_opline
&& level == last_loop_level) {
idx = zend_jit_trace_subtrace(trace_buffer,
int ret = zend_jit_trace_subtrace(EX(call), trace_buffer,
last_loop, idx, ZEND_JIT_TRACE_START_LOOP, op_array, opline);
if (ret < 0) {
stop = ZEND_JIT_TRACE_STOP_TOO_LONG;
break;
}
idx = ret;
start = ZEND_JIT_TRACE_START_LOOP;
stop = ZEND_JIT_TRACE_STOP_LOOP;
ret_level = 0;
Expand Down
40 changes: 40 additions & 0 deletions ext/opcache/tests/jit/array_map_loop_in_call_region.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
JIT: array_map() foreach optimization: loop trace inside a call region must not clobber EX(call)
--EXTENSIONS--
opcache
--FILE--
<?php

class C {
static function f($x) { return $x + 1; }
}

function sink(array $a) { return array_sum($a); }

/* array_map() is compiled to a foreach loop, so both loop headers sit between
* the INIT_FCALL of sink() and its DO_UCALL. The side trace recorded at the
* exit of the first loop runs into the header of the second one and is turned
* into a loop trace rooted there; that conversion must not lose track of the
* sink() frame that is still under construction, otherwise DO_FCALL of the
* callback stores NULL into EX(call) and the following SEND_VAL crashes.
*
* $o::f(...) is used so that INIT_STATIC_METHOD_CALL falls back to the VM
* handler, which is what makes the JIT emit the EX(call) store at DO_FCALL. */
function test(array $a, $o) {
$n = sink(array_map($o::f(...), $a));
$n += sink(array_map($o::f(...), $a));
return $n;
}

$a = [1, 2, 3];
$o = new C();

for ($i = 0; $i < 2; $i++) {
$r = test($a, $o);
}

var_dump($r);

?>
--EXPECT--
int(18)
Loading