Category
Foundation: · generated-code size / compile time · child of #36188
Problem
JitValueBox::copyBetweenPointers() (lib/JIT/JitValueBox.php:623-800) appends value_copy_{string,hashtable,object,long,double,bool,null,done} plus seven after_* blocks — 15 basic blocks, tagged by a static counter so nothing is shared — at every boxed assignment site (176 copy* call sites in lib/). The same pattern repeats for error paths: each op gets its own __compiler_jit_raise_type_error + abort block.
Measured on the committed IR dump build/micro/m_loop.ll (4-line typed loop, 111,127 IR lines, 485 defined functions):
| function |
IR lines |
of which |
PHPCompiler_ext_standard_IniJitHelper__inicfgget |
16,508 |
1,176 __value__valueDelref, 759 __compiler_jit_raise_type_error + abort pairs, 167 __string__separate |
…IniJitHelper__iniget |
12,187 |
(51 KB of .text in the linked binary) |
…PendingHeadersJitHelper__statuscodefromhttpversionline |
— |
51 KB of .text for a status-line parser |
user function loop |
40 |
|
The user program is 0.04 % of the module. Everything else is helper corpus re-lowered per unit, and its size is set by this open-coding. It costs LLVM verify/codegen time on every build, unit.o size (see the helper-unit duplication issue), and makes any optimisation pass 25x more expensive than the program warrants.
php-src reference
- Zend/zend_variables.h —
ZVAL_COPY / ZVAL_COPY_VALUE are one inline function each; the type switch lives in zval_add_ref, not at every call site.
PHP implementation target
- Emit the body of
copyBetweenPointers once per module as __value__copy(%__value__* dst, %__value__* src) (and __value__assign = delref old + copy) with alwaysinline so that the minimal pass pipeline (sibling issue) can still inline where profitable; call sites become one call.
- Outline the cold error paths: one
__jit_type_error_abort(msg, code) noreturn function per module; call sites br to a single shared cold block per function.
- Same treatment for
JitValueNumeric::emitBoxedNumericResult (lib/JIT/JitValueNumeric.php:373-448): load each operand's tag once; valueIsDouble/valueIsString/valueBoxToDouble re-load it 4x today.
- Measure with
PHP_COMPILER_DUMP_IR=1 (writes /tmp/phpc-last.ll): IR lines, define count, and .text of the hello-world binary before/after.
Repro
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && printf "<?php echo 1;\n" > build/one.php \
&& PHP_COMPILER_DUMP_IR=1 php bin/compile.php -o build/one build/one.php && wc -l /tmp/phpc-last.ll \
&& grep -c "value_copy_string_" /tmp/phpc-last.ll && size build/one'
Done when
Category
Foundation:· generated-code size / compile time · child of #36188Problem
JitValueBox::copyBetweenPointers()(lib/JIT/JitValueBox.php:623-800) appendsvalue_copy_{string,hashtable,object,long,double,bool,null,done}plus sevenafter_*blocks — 15 basic blocks, tagged by a static counter so nothing is shared — at every boxed assignment site (176copy*call sites inlib/). The same pattern repeats for error paths: each op gets its own__compiler_jit_raise_type_error+abortblock.Measured on the committed IR dump
build/micro/m_loop.ll(4-line typed loop, 111,127 IR lines, 485 defined functions):PHPCompiler_ext_standard_IniJitHelper__inicfgget__value__valueDelref, 759__compiler_jit_raise_type_error+abortpairs, 167__string__separate…IniJitHelper__iniget…PendingHeadersJitHelper__statuscodefromhttpversionlineloopThe user program is 0.04 % of the module. Everything else is helper corpus re-lowered per unit, and its size is set by this open-coding. It costs LLVM verify/codegen time on every build,
unit.osize (see the helper-unit duplication issue), and makes any optimisation pass 25x more expensive than the program warrants.php-src reference
ZVAL_COPY/ZVAL_COPY_VALUEare one inline function each; the type switch lives inzval_add_ref, not at every call site.PHP implementation target
copyBetweenPointersonce per module as__value__copy(%__value__* dst, %__value__* src)(and__value__assign= delref old + copy) withalwaysinlineso that the minimal pass pipeline (sibling issue) can still inline where profitable; call sites become onecall.__jit_type_error_abort(msg, code)noreturnfunction per module; call sitesbrto a single shared cold block per function.JitValueNumeric::emitBoxedNumericResult(lib/JIT/JitValueNumeric.php:373-448): load each operand's tag once;valueIsDouble/valueIsString/valueBoxToDoublere-load it 4x today.PHP_COMPILER_DUMP_IR=1(writes/tmp/phpc-last.ll): IR lines,definecount, and.textof the hello-world binary before/after.Repro
Done when
echo 1module drops ≥ 40 %;value_copy_string_labels ≤ 1 per module.text(size) drops accordingly; committed helper-unit cache refreshed once (make helper-runtime-prelink-refresh) and its total size recorded in the PRscript/aot-smoke.sh8/8;script/differential-sweep.sh --aot --repeat 3failing-name set unchanged;PHP_COMPILER_OPT_LEVEL=2build ofAck(3,9)still 4093