2019-05-19 02:39:18 +08:00
|
|
|
# Zend Engine
|
|
|
|
|
|
|
|
## Zend memory manager
|
|
|
|
|
|
|
|
### General
|
|
|
|
|
|
|
|
The goal of the new memory manager (available since PHP 5.2) is to reduce memory
|
|
|
|
allocation overhead and speedup memory management.
|
|
|
|
|
|
|
|
### Debugging
|
|
|
|
|
|
|
|
Normal:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
sapi/cli/php -r 'leak();'
|
|
|
|
```
|
|
|
|
|
|
|
|
Zend MM disabled:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
USE_ZEND_ALLOC=0 valgrind --leak-check=full sapi/cli/php -r 'leak();'
|
|
|
|
```
|
|
|
|
|
|
|
|
### Shared extensions
|
|
|
|
|
|
|
|
Since PHP 5.3.11 it is possible to prevent shared extensions from unloading so
|
|
|
|
that valgrind can correctly track the memory leaks in shared extensions. For
|
|
|
|
this there is the `ZEND_DONT_UNLOAD_MODULES` environment variable. If set, then
|
|
|
|
`DL_UNLOAD()` is skipped during the shutdown of shared extensions.
|
|
|
|
|
|
|
|
## ZEND_VM
|
|
|
|
|
|
|
|
`ZEND_VM` architecture allows specializing opcode handlers according to
|
|
|
|
`op_type` fields and using different execution methods (call threading, switch
|
|
|
|
threading and direct threading). As a result ZE2 got more than 20% speedup on
|
|
|
|
raw PHP code execution (with specialized executor and direct threading execution
|
|
|
|
method). As in most PHP applications raw execution speed isn't the limiting
|
|
|
|
factor but system calls and database calls are, your mileage with this patch
|
|
|
|
will vary.
|
|
|
|
|
|
|
|
Most parts of the old zend_execute.c go into `zend_vm_def.h`. Here you can find
|
|
|
|
opcode handlers and helpers. The typical opcode handler template looks like
|
|
|
|
this:
|
|
|
|
|
|
|
|
```c
|
|
|
|
ZEND_VM_HANDLER(<OPCODE-NUMBER>, <OPCODE>, <OP1_TYPES>, <OP2_TYPES>)
|
|
|
|
{
|
|
|
|
<HANDLER'S CODE>
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
`<OPCODE-NUMBER>` is a opcode number (0, 1, ...)
|
|
|
|
`<OPCODE>` is an opcode name (ZEN_NOP, ZEND_ADD, :)
|
|
|
|
`<OP1_TYPES>` and `<OP2_TYPES>` are masks for allowed operand op_types.
|
|
|
|
Specializer will generate code only for defined combination of types. You can
|
|
|
|
use any combination of the following op_types UNUSED, CONST, VAR, TMP and CV
|
|
|
|
also you can use ANY mask to disable specialization according operand's op_type.
|
|
|
|
`<HANDLER'S CODE>` is a handler's code itself. For most handlers it stills the
|
|
|
|
same as in old `zend_execute.c`, but now it uses macros to access opcode
|
|
|
|
operands and some internal executor data.
|
|
|
|
|
|
|
|
You can see the conformity of new macros to old code in the following list:
|
|
|
|
|
|
|
|
```c
|
|
|
|
EXECUTE_DATA
|
|
|
|
execute_data
|
|
|
|
ZEND_VM_DISPATCH_TO_HANDLER(<OP>)
|
|
|
|
return <OP>_helper(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
|
|
|
|
ZEND_VM_DISPATCH_TO_HELPER(<NAME>)
|
|
|
|
return <NAME>(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
|
|
|
|
ZEND_VM_DISPATCH_TO_HELPER_EX(<NAME>,<PARAM>,<VAL>)
|
|
|
|
return <NAME>(<VAL>, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
|
|
|
|
ZEND_VM_CONTINUE()
|
|
|
|
return 0
|
|
|
|
ZEND_VM_NEXT_OPCODE()
|
|
|
|
NEXT_OPCODE()
|
|
|
|
ZEND_VM_SET_OPCODE(<TARGET>
|
|
|
|
SET_OPCODE(<TARGET>
|
|
|
|
ZEND_VM_INC_OPCODE()
|
|
|
|
INC_OPCOD()
|
|
|
|
ZEND_VM_RETURN_FROM_EXECUTE_LOOP()
|
|
|
|
RETURN_FROM_EXECUTE_LOOP()
|
|
|
|
ZEND_VM_C_LABEL(<LABEL>):
|
|
|
|
<LABEL>:
|
|
|
|
ZEND_VM_C_GOTO(<LABEL>)
|
|
|
|
goto <LABEL>
|
|
|
|
OP<X>_TYPE
|
|
|
|
opline->op<X>.op_type
|
|
|
|
GET_OP<X>_ZVAL_PTR(<TYPE>)
|
|
|
|
get_zval_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
|
|
|
|
GET_OP<X>_ZVAL_PTR_PTR(<TYPE>)
|
|
|
|
get_zval_ptr_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
|
|
|
|
GET_OP<X>_OBJ_ZVAL_PTR(<TYPE>)
|
|
|
|
get_obj_zval_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
|
|
|
|
GET_OP<X>_OBJ_ZVAL_PTR_PTR(<TYPE>)
|
|
|
|
get_obj_zval_ptr_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>)
|
|
|
|
IS_OP<X>_TMP_FREE()
|
|
|
|
IS_TMP_FREE(free_op<X>)
|
|
|
|
FREE_OP<X>()
|
|
|
|
FREE_OP(free_op<X>)
|
|
|
|
FREE_OP<X>_IF_VAR()
|
|
|
|
FREE_VAR(free_op<X>)
|
|
|
|
FREE_OP<X>_VAR_PTR()
|
|
|
|
FREE_VAR_PTR(free_op<X>)
|
|
|
|
```
|
|
|
|
|
|
|
|
Executor's helpers can be defined without parameters or with one parameter. This
|
|
|
|
is done with the following constructs:
|
|
|
|
|
|
|
|
```c
|
|
|
|
ZEND_VM_HELPER(<HELPER-NAME>, <OP1_TYPES>, <OP2_TYPES>)
|
|
|
|
{
|
|
|
|
<HELPER'S CODE>
|
|
|
|
}
|
|
|
|
|
|
|
|
ZEND_VM_HELPER_EX(<HELPER-NAME>, <OP1_TYPES>, <OP2_TYPES>, <PARAM_SPEC>)
|
|
|
|
{
|
|
|
|
<HELPER'S CODE>
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2022-01-29 16:12:32 +08:00
|
|
|
The executors code is generated by the PHP script `zend_vm_gen.php`. It uses
|
2019-05-19 02:39:18 +08:00
|
|
|
`zend_vm_def.h` and `zend_vm_execute.skl` as input and produces
|
|
|
|
`zend_vm_opcodes.h` and `zend_vm_execute.h`. The first file is a list of opcode
|
|
|
|
definitions. It is included from `zend_compile.h`. The second one is an executor
|
|
|
|
code itself. It is included from `zend_execute.c`.
|
|
|
|
|
2022-01-29 16:12:32 +08:00
|
|
|
`zend_vm_gen.php` can produce different kind of executors. You can select a
|
|
|
|
different opcode threading model using `--with-vm-kind=CALL|SWITCH|GOTO|HYBRID`.
|
|
|
|
You can disable opcode specialization using `--without-specializer`.
|
|
|
|
At last you can debug the executor using the original `zend_vm_def.h` or the
|
|
|
|
generated `zend_vm_execute.h` file. Debugging with the original file requires
|
2022-02-08 17:38:33 +08:00
|
|
|
the `--with-lines` option. By default, Zend Engine uses the following
|
2022-01-29 16:12:32 +08:00
|
|
|
command to generate the executor:
|
2019-05-19 02:39:18 +08:00
|
|
|
|
|
|
|
```bash
|
2022-01-29 16:12:32 +08:00
|
|
|
# Default VM kind is HYBRID
|
|
|
|
php zend_vm_gen.php --with-vm-kind=HYBRID
|
2019-05-19 02:39:18 +08:00
|
|
|
```
|