This can occur on opcache OOM conditions, where the function/class
names are not interned and the script does not get cached. In
that case the functions/classes get transferred from the persistent
script to the global tables, without incrementing the key refcount.
To mirror that, we should also not try to free the keys when freeing
the persistent script. For this by setting the number of elements
to zero, which will free only the hashtable structure itself.
offsetSet did not account for the fact that the array may no longer exist after
the field is overwritten. This fixes that.
Add test of resizing both to the empty array and a smaller array - there should
be no valgrind warnings with a proper fix.
Alternate approach to #7486 (described in https://bugs.php.net/bug.php?id=81429)
The evaluation of the initializer may throw. This could be refined
by checking whether the initializer is a constant AST. For now
just fix the miscompile.
The do_fcall opcode may have been optimized away if an opcode like
exit is present in the arguments. In that case the opcode scan
would go past the end of the op array.
Ensure current_observed_frame always points to an actually observed frame.
This solution has a caveat of being O(stack size), with the worst case occurring if there are a lot of frames between the current and previous observed frames.
An O(1) solution would require keeping track of the previous observed frame, which would require some additional frame attached metadata, which is best not attempted in an already released version.
Permanent opcache interned strings could have ce_cache pointing to
non-permanent map_ptr slots. On reset, those would be left dangling.
Clear any non-permanent ce_cache slots when the interned string
state is reset.
This was fun to debug...
We should report the undefined variable here and convert it to
null. Passing on undef is particularly insidious here, because
a write_dimension handler may insert it into a hash table
(observed with WeakMap).
PHP JIT supports three configurations: HYRBID, CALL with global register
variables feature(CALL+GRV for short), and CALL+noGRV.
CALL+GRV mode can be built with the following commands:
```
php Zend/zend_vm_gen.php --with-vm-kind=CALL
./buildconf -f; ./configure; make
```
About 230 test cases failed for tracing JIT under CALL+GRV mode on both
x86 and arm64 machines.
For CALL+GRV mode, the condition to determine whether the execution of
an oparray is finished, is "opline == NULL". See function execute_ex()
around line "if (UNEXPECTED(!OPLINE)) {".
However, such cleanup operation is missing for the JIT wrapper
zend_jit_trace_counter_helper(), and the trace_halt stub function.
Tests:
1. test cases: all .phpt test cases under "Zend/tests/ tests/
ext/opcache/tests/jit/".
2. both JIT/x86 and JIT/arm64: function JIT, tracing JIT and tracing JIT
with "--repeat 3"
3. execution modes: NTS/ZTS, HYBRID/CALL+GRV/CALL+noGRV
In my local test, these test cases passed under all JIT configrations.
We'd have usually converted it into a PRE_INC if there is no use,
but that's not guaranteed. If there is no use at this point, make
sure we don't try to use the sentinel value.
zend_jit_long_math_helper() implicitly assumes that the operands
MAY_BE_LONG (but can also have additional types). It will normally
only be called if this is guaranteed. However, for compound
array/object assignment ops this was not check. Generalize the
existing check for assign_op to apply to these as well.
Of course, we could also make the code support this correctly,
but I don't think it makes sense to JIT these if the type we're
specializing for is not present.
Closes GH-7481.