Uh oh!
There was an error while loading. Please reload this page.
preload time monomorphization of functions - #12
Conversation
Signed-off-by: azjezz <azjezz@protonmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com> fix inheritance chaining Signed-off-by: Robert Landers <landers.robert@gmail.com> fix inheritance chaining Signed-off-by: Robert Landers <landers.robert@gmail.com>
Specialize concrete turbofish calls at compile time: Graph\dfs::<string,int>() compiles to a direct by-name call to the mangled monomorph and drops the per-call ZEND_VERIFY_GENERIC_ARGUMENTS opcode, making it byte-identical to a non-generic call. Monomorphs are synthesized and registered on first reference; the runtime verify/bind path remains as the fallback for non-concrete / inference-only call sites. Also: persist the generic value-check plan into opcache SHM, and gate destroy_op_array's monomorph-table scan on ZEND_ACC2_HAS_GENERIC_CALL_OPS so non-generic code is not taxed. PSL\Graph bench (aarch64, opcache, two-point Ir/iter): reified-vs-erased overhead 6.51%->4.14% (all) / 5.72%->4.19% (query); wall 9.04%->5.89% (no-JIT). Erased baseline unchanged; generics tax -37%. Digests identical across no-JIT/tracing/-n, generics .phpt 0 new regressions, valgrind-clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Turbofish monomorphs are shallow copies of their base op_array that share the opcode buffer (zend_synthesize_function_monomorph). The tracing JIT keys compiled code and patched opline handlers on opcode addresses and bakes op_array-specific constants (arg_info for RECV / VERIFY_RETURN_TYPE, inferred operand types, cached literals), which are valid for only one substituted arg_info layout. A trace compiled for one monomorph was therefore reused for a sibling carrying a different binding, enforcing the wrong type: e.g. nestedGen::<Fooable&Barable>($fb) was checked against an earlier nestedGen::<int|string> binding and wrongly threw "must be of type string|int" under opcache.jit=tracing (the interpreter and jit=disable were correct). The interpreter avoids this via the TRAIT_CLONE slow-path RECV; the JIT has no equivalent and the hazard is not limited to RECV, so keep monomorphizable generics and their monomorphs interpreted until per-monomorph JIT state exists. Signed-off-by: Robert Landers <landers.robert@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a bare type-parameter leaf (`T $x`) is bound to a concrete generic instantiation via turbofish (e.g. `id::<DBox<L2<int>>>($v)`), the binding arrives as a pre-erasure named-with-args type. zend_substitute_leaf_type_param_origin returned it verbatim, so the synthesized monomorph's arg_info (and return type) kept the named-with-args payload. The reified RECV check then reached zend_fetch_ce_from_type(), which reads ZEND_TYPE_NAME() and interpreted that payload as a zend_string: at depth >= 2 it dereferenced a garbage length and tried a multi-terabyte allocation; at depth 1 it raised a spurious TypeError. Fold a fully concrete named-with-args binding to a plain CLASS reference to the monomorph's canonical class name, mirroring the named-with-args branch in the same function. That is the erased shape the runtime arg / property / return checks already understand, and it matches the class the value itself carries (so inference and turbofish now agree). Reproduces in the interpreter and under opcache; not JIT-specific. Signed-off-by: Robert Landers <landers.robert@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
zend_try_synthesize_monomorph_by_name() backs the class-existence lookup path
(class_exists(), interface_exists(), is-a probes, the autoloader). It raised in
two cases: a non-generic base carrying type arguments ("Plain<int>") threw, and
an out-of-bound argument ("Box<int>" for Box<T : object>) reached
zend_synthesize_monomorph(), whose bound check fatals via zend_error_noreturn.
So class_exists('Box<int>') aborted with a fatal instead of returning false.
A name whose arguments are invalid simply names a class that does not exist, so
the lookup now reports "not found" (NULL): the non-generic case returns NULL
directly, and bounds are validated silently up front, returning NULL on
violation before synthesis. The throwing enforcement stays on the `new` /
type-declaration paths (compile-time and zend_synthesize_monomorph), where a
bound violation is a genuine error — verified that `new Box::<int>()` and a
`Plain<int>` type position still fatal.
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Signed-off-by: Robert Landers <landers.robert@gmail.com>
…t happen to have the same name Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
… also make monomorphs share static props Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Signed-off-by: Robert Landers <landers.robert@gmail.com>
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
3335ce1 to
10d21f7Compare
No description provided.