|
| 1 | +# Zend Engine |
| 2 | + |
| 3 | +## Zend memory manager |
| 4 | + |
| 5 | +### General |
| 6 | + |
| 7 | +The goal of the new memory manager (available since PHP 5.2) is to reduce memory |
| 8 | +allocation overhead and speedup memory management. |
| 9 | + |
| 10 | +### Debugging |
| 11 | + |
| 12 | +Normal: |
| 13 | + |
| 14 | +```bash |
| 15 | +sapi/cli/php -r 'leak();' |
| 16 | +``` |
| 17 | + |
| 18 | +Zend MM disabled: |
| 19 | + |
| 20 | +```bash |
| 21 | +USE_ZEND_ALLOC=0 valgrind --leak-check=full sapi/cli/php -r 'leak();' |
| 22 | +``` |
| 23 | + |
| 24 | +### Shared extensions |
| 25 | + |
| 26 | +Since PHP 5.3.11 it is possible to prevent shared extensions from unloading so |
| 27 | +that valgrind can correctly track the memory leaks in shared extensions. For |
| 28 | +this there is the `ZEND_DONT_UNLOAD_MODULES` environment variable. If set, then |
| 29 | +`DL_UNLOAD()` is skipped during the shutdown of shared extensions. |
| 30 | + |
| 31 | +## ZEND_VM |
| 32 | + |
| 33 | +`ZEND_VM` architecture allows specializing opcode handlers according to |
| 34 | +`op_type` fields and using different execution methods (call threading, switch |
| 35 | +threading and direct threading). As a result ZE2 got more than 20% speedup on |
| 36 | +raw PHP code execution (with specialized executor and direct threading execution |
| 37 | +method). As in most PHP applications raw execution speed isn't the limiting |
| 38 | +factor but system calls and database calls are, your mileage with this patch |
| 39 | +will vary. |
| 40 | + |
| 41 | +Most parts of the old zend_execute.c go into `zend_vm_def.h`. Here you can find |
| 42 | +opcode handlers and helpers. The typical opcode handler template looks like |
| 43 | +this: |
| 44 | + |
| 45 | +```c |
| 46 | +ZEND_VM_HANDLER(<OPCODE-NUMBER>, <OPCODE>, <OP1_TYPES>, <OP2_TYPES>) |
| 47 | +{ |
| 48 | + <HANDLER'S CODE> |
| 49 | +} |
| 50 | +``` |
| 51 | +
|
| 52 | +`<OPCODE-NUMBER>` is a opcode number (0, 1, ...) |
| 53 | +`<OPCODE>` is an opcode name (ZEN_NOP, ZEND_ADD, :) |
| 54 | +`<OP1_TYPES>` and `<OP2_TYPES>` are masks for allowed operand op_types. |
| 55 | +Specializer will generate code only for defined combination of types. You can |
| 56 | +use any combination of the following op_types UNUSED, CONST, VAR, TMP and CV |
| 57 | +also you can use ANY mask to disable specialization according operand's op_type. |
| 58 | +`<HANDLER'S CODE>` is a handler's code itself. For most handlers it stills the |
| 59 | +same as in old `zend_execute.c`, but now it uses macros to access opcode |
| 60 | +operands and some internal executor data. |
| 61 | +
|
| 62 | +You can see the conformity of new macros to old code in the following list: |
| 63 | +
|
| 64 | +```c |
| 65 | +EXECUTE_DATA |
| 66 | + execute_data |
| 67 | +ZEND_VM_DISPATCH_TO_HANDLER(<OP>) |
| 68 | + return <OP>_helper(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) |
| 69 | +ZEND_VM_DISPATCH_TO_HELPER(<NAME>) |
| 70 | + return <NAME>(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) |
| 71 | +ZEND_VM_DISPATCH_TO_HELPER_EX(<NAME>,<PARAM>,<VAL>) |
| 72 | + return <NAME>(<VAL>, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) |
| 73 | +ZEND_VM_CONTINUE() |
| 74 | + return 0 |
| 75 | +ZEND_VM_NEXT_OPCODE() |
| 76 | + NEXT_OPCODE() |
| 77 | +ZEND_VM_SET_OPCODE(<TARGET> |
| 78 | + SET_OPCODE(<TARGET> |
| 79 | +ZEND_VM_INC_OPCODE() |
| 80 | + INC_OPCOD() |
| 81 | +ZEND_VM_RETURN_FROM_EXECUTE_LOOP() |
| 82 | + RETURN_FROM_EXECUTE_LOOP() |
| 83 | +ZEND_VM_C_LABEL(<LABEL>): |
| 84 | + <LABEL>: |
| 85 | +ZEND_VM_C_GOTO(<LABEL>) |
| 86 | + goto <LABEL> |
| 87 | +OP<X>_TYPE |
| 88 | + opline->op<X>.op_type |
| 89 | +GET_OP<X>_ZVAL_PTR(<TYPE>) |
| 90 | + get_zval_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>) |
| 91 | +GET_OP<X>_ZVAL_PTR_PTR(<TYPE>) |
| 92 | + get_zval_ptr_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>) |
| 93 | +GET_OP<X>_OBJ_ZVAL_PTR(<TYPE>) |
| 94 | + get_obj_zval_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>) |
| 95 | +GET_OP<X>_OBJ_ZVAL_PTR_PTR(<TYPE>) |
| 96 | + get_obj_zval_ptr_ptr(&opline->op<X>, EX(Ts), &free_op<X>, <TYPE>) |
| 97 | +IS_OP<X>_TMP_FREE() |
| 98 | + IS_TMP_FREE(free_op<X>) |
| 99 | +FREE_OP<X>() |
| 100 | + FREE_OP(free_op<X>) |
| 101 | +FREE_OP<X>_IF_VAR() |
| 102 | + FREE_VAR(free_op<X>) |
| 103 | +FREE_OP<X>_VAR_PTR() |
| 104 | + FREE_VAR_PTR(free_op<X>) |
| 105 | +``` |
| 106 | + |
| 107 | +Executor's helpers can be defined without parameters or with one parameter. This |
| 108 | +is done with the following constructs: |
| 109 | + |
| 110 | +```c |
| 111 | +ZEND_VM_HELPER(<HELPER-NAME>, <OP1_TYPES>, <OP2_TYPES>) |
| 112 | +{ |
| 113 | + <HELPER'S CODE> |
| 114 | +} |
| 115 | + |
| 116 | +ZEND_VM_HELPER_EX(<HELPER-NAME>, <OP1_TYPES>, <OP2_TYPES>, <PARAM_SPEC>) |
| 117 | +{ |
| 118 | + <HELPER'S CODE> |
| 119 | +} |
| 120 | +``` |
| 121 | +
|
| 122 | +Executor's code is generated by PHP script zend_vm_gen.php it uses |
| 123 | +`zend_vm_def.h` and `zend_vm_execute.skl` as input and produces |
| 124 | +`zend_vm_opcodes.h` and `zend_vm_execute.h`. The first file is a list of opcode |
| 125 | +definitions. It is included from `zend_compile.h`. The second one is an executor |
| 126 | +code itself. It is included from `zend_execute.c`. |
| 127 | +
|
| 128 | +`zend_vm_gen.php` can produce different kind of executors. You can select |
| 129 | +different opcode threading model using `--with-vm-kind=CALL|SWITCH|GOTO`. You |
| 130 | +can disable opcode specialization using `--without-specializer`. You can include |
| 131 | +or exclude old executor together with specialized one using |
| 132 | +`--without-old-executor`. At last you can debug executor using original |
| 133 | +`zend_vm_def.h` or generated file `zend_vm_execute.h`. Debugging with original |
| 134 | +file requires `--with-lines` option. By default ZE2 uses the following command |
| 135 | +to generate executor: |
| 136 | +
|
| 137 | +```bash |
| 138 | +php zend_vm_gen.php --with-vm-kind=CALL |
| 139 | +``` |
0 commit comments