Repository navigation
perf(ir_passes): inline small statically bound instance methods - #2045
Merged
Merged
Conversation
…d record addresses
Codegen and the inliner must agree on when PHP frames are observable, so the predicate moves from codegen::frame to Module::uses_php_backtrace and Function::keeps_backtrace_argument_snapshot; frame.rs delegates to it. Function also gains inlined_scopes, the lexical class of instructions the inliner splices in from a method body.
FunctionContext::lexical_class answers the scope the current instruction runs in: the class recorded in Function::inlined_scopes for a spliced method instruction, the function's own lexical_class otherwise. Every codegen reader of the frame's lexical class goes through it, and tail-call body cloning carries the recorded scopes along.
A method_call whose receiver class is final, whose method is final, or whose class has no vtable slot for it now reaches the small-function inliner. The splice binds $this as a borrowed argument, keeps the receiver null check (throw_error with the call's own message), renames the method's locals and records the method's class scope for each spliced instruction. Method bodies are limited to frame-independent ops (property reads on $this or packed records, locals, scalar arithmetic, buffer reads); signatures with literal false/null, by-reference, variadic or surplus-argument parameters keep their call, and so does every method call in a module that can observe backtraces. The recursion analysis now spans functions and methods.
…into perf/property-buffer-reuse
…sted Module::uses_php_backtrace also counts frames that merely collect surplus arguments, and the core prelude's ErrorException::__construct always does, so every module that keeps ErrorException disabled method inlining. The inliner now asks Module::calls_php_backtrace (direct debug_backtrace calls or the eval bridge); a backtrace reachable any other way gives every frame an argument snapshot, which already keeps that method body out of the candidates.
Buffers have no automatic cleanup, so the heap-validated example test saw the table's slot buffer still live at exit in both optimizer modes.
…f buffer properties
nahime0
marked this pull request as ready for review
October 24, 2026 08:23
Contributor
[Medium impact] The latest update appears safe to merge, subject to the required CI checks. SummaryThe PR inlines small instance methods when their target is known and preserves each copied instruction's class scope.
|
…class call A final class that inherits an accessor binds the parent's body; the spliced read of the parent's private property must keep the parent's scope although the subclass shadows the name. Assert the call disappears, the recorded scopes are Base and Leaf, and both optimizer modes print PHP's 7099.
github-merge-queue
Bot
removed this pull request from the merge queue due to invalid changes in the merge commit
Oct 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Small statically bound instance methods now participate in the EIR inliner. Calls to eligible accessors and predicates are replaced by their bodies, preserving receiver checks, private-property resolution, argument behavior and ownership.
Function::inlined_scopes; scope-sensitive codegen and body copying consume that metadata. Buffer property reuse compares the original instruction scope entries before merging reads, keeping shadowed parent and child private buffers distinct.$thisthrough an epilogue-excluded temporary and hide transplanted locals from the caller's PHP variable scope. Keep calls whose signatures, frame-dependent operations, backtrace visibility or ownership cannot be reproduced safely.This branch includes the exact #2044 head
d256479dfdb749b06b41774d437bfb3658583ca3through merge037b8538b6457b1d91804ece0e76059c08664661. The integration resolves CJ taskf3f64c3d, while the original method-inlining work addresses CJ task5ea2b764. Wait for #2044 to merge before adding this PR to the merge queue. The generic eligibility guard matches the narrow guard in #2070; its additional opcode work is separate.Regression coverage includes EIR shape on all five supported targets, receiver/null and visibility checks, private-property scopes, generic retained calls, backtraces, defaults, destructor timing and runtime behavior with IR optimization on and off. Original-ID scope matrices cover mismatched scopes and both
Some/Nonedirections, plus positive record/descriptor reuse for equal scopes. The natural shadowed-buffer fixture verifies accessor splicing, three distinct property reads and output273in both optimizer modes; borrowed receiver temporaries intentionally keep those reads outside the method-local reuse proof. Examples and compiler internals documentation are updated.The full combined diff from
f708b38dfae0cc1cd34308d023b487a690ed9753received a clean, read-only Claude review on exact head54cc37bce4fb99f8fab830097a00cdc26aa45084, delegationc8634081-0672-41e9-a2b9-c7d03b6e7780. This includes the loop fixture's missingsecond_propertyinitializer, which CI exposed on the previous head. Both helper constructors now record the original second property value.git diff --checkis clean. No local builds or tests were run, as requested. Fresh CI and Greptile must pass on this head; the priorb0c60a296review and CI results do not validate this correction.