While technically legal, this may cause unexpected situations
(in this example, setting an FE_FREE operand to constant null)
and is suboptimal anyway. It's better to preserve the vacuous type
and drop it later (though we currently don't implement this).
This can happen in degenerate cases where we know that the
SWITCH_STRING argument is not refcounted. We should be treating it
in the same way as SWITCH_LONG here.
The mod_by_zero and negative_shift helper may also be used by
ASSIGN_OP, in which case there is not necessarily a result operand.
If the stars aligned just right, this used to clobber other parts
of the call frame.
For these two helpers, check whether the result_type is TMP/VAR
before setting to UNDEF:
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.
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.
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.
The IS_UNSERIALIZED check here does not work if the string is
interned (serialized with file_cache_only=0) but unserialization
happens with file_cache_only=1. In this case the unserializde
string will be in the str area after mem, which is not included
in the script size, and which is also not accessible at this
point without threading through more information. Work around
the problem by checking for the serialized representation instead.