Skip to content

cl/ssa: split invoke through reusable thunk#1669

Open
luoliwoshang wants to merge 1 commit intogoplus:mainfrom
luoliwoshang:codex/invoke-thunk-split-pr
Open

cl/ssa: split invoke through reusable thunk#1669
luoliwoshang wants to merge 1 commit intogoplus:mainfrom
luoliwoshang:codex/invoke-thunk-split-pr

Conversation

@luoliwoshang
Copy link
Copy Markdown
Member

@luoliwoshang luoliwoshang commented Mar 2, 2026

Summary

  • route non-variadic interface invoke calls through a generated invoke thunk instead of emitting direct itab-slot loads at each call site
  • add reusable thunk generation in ssa/interface.go with weak_odr linkage and stable naming (__llgo_invoke.<iface>$m<idx>.<method>)
  • keep variadic invoke on the legacy Imethod path for now to avoid varargs forwarding regressions
  • regenerate .ll expectations with gentest

Validation

  • go test ./ssa -run 'TestIfaceMethodClosureCallIR|TestInterfaceHelpers' -count=1
  • go test ./ssa -run 'TestFromTestgo/(abimethod|invoke|ifaceprom)|TestFromTestrt/(tpmethod|vamethod)' -count=1
  • go test ./ssa -run 'TestFromTestgo/(interface|reader)|TestFromTestrt/(mapclosure)' -count=1
  • go run ./chore/gentests
  • go install ./cmd/llgo

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the internal handling of interface method calls within the LLGo compiler. By introducing reusable thunk functions, the system moves towards a more standardized and potentially optimized approach for dispatching non-variadic interface methods, streamlining the generated LLVM IR and improving code generation consistency.

Highlights

  • Interface Method Invocation Refactoring: The pull request introduces a new mechanism for invoking interface methods by generating reusable thunk functions. This replaces the previous pattern of directly extracting method pointers and data from interface values.
  • Thunk Function Generation: New internal functions like invokeThunkName, invokeThunk, and InvokeThunk have been added to ssa/interface.go to create and manage these __llgo_invoke thunk functions. These thunks handle the details of extracting method information and calling the underlying concrete method.
  • Conditional Thunk Usage: The cl/instr.go file has been updated to utilize these new InvokeThunk functions for non-variadic interface method calls. Variadic interface method calls will continue to use the legacy closure-based invocation path for now.
  • Extensive Test File Updates: Numerous LLVM IR output files (.ll) across various test directories (_testdata, _testgo, _testrt) have been updated to reflect the new thunk-based interface method invocation pattern, demonstrating the broad impact of this change.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • cl/_testdata/embedunexport/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testdata/geometry1370/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testgo/abimethod/out.ll
    • Replaced direct interface method calls with calls to __llgo_invoke thunks in multiple test cases.
    • Added definitions for several __llgo_invoke thunk functions for different method signatures.
  • cl/_testgo/closureall/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testgo/embedunexport-1598/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testgo/errors/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testgo/ifaceprom/out.ll
    • Replaced direct interface method calls with calls to __llgo_invoke thunks in multiple test cases.
    • Added definitions for several __llgo_invoke thunk functions for different method signatures.
  • cl/_testgo/interface/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testgo/invoke/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testgo/reader/out.ll
    • Replaced direct interface method calls with calls to __llgo_invoke thunks in multiple test cases.
    • Added definitions for several __llgo_invoke thunk functions for different method signatures.
  • cl/_testgo/reflect/out.ll
    • Replaced direct interface method calls with calls to __llgo_invoke thunks in multiple test cases.
    • Added definitions for several __llgo_invoke thunk functions for different method signatures.
  • cl/_testgo/tpinst/out.ll
    • Replaced direct interface method calls with calls to __llgo_invoke thunks in multiple test cases.
    • Added definitions for several __llgo_invoke thunk functions for different method signatures.
  • cl/_testrt/mapclosure/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testrt/tpabi/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testrt/tpmethod/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/_testrt/vamethod/out.ll
    • Updated interface method calls to use the new __llgo_invoke thunk.
  • cl/instr.go
    • Modified interface method call generation to use InvokeThunk for non-variadic calls.
    • Preserved legacy closure path for variadic interface method calls.
  • ssa/interface.go
    • Imported go/token and strconv packages.
    • Introduced invokeThunkPrefix constant.
    • Added invokeMethodSig function to determine the signature for invoke thunks.
    • Implemented invokeThunkName to generate unique names for invoke thunks.
    • Created invokeThunk function to generate and cache reusable thunk functions for interface methods.
    • Added InvokeThunk method to Builder to retrieve or create an invoke thunk for a given interface method.
Activity
  • The author luoliwoshang initiated this pull request.
  • The changes primarily involve refactoring the LLVM IR generation for interface method calls.
  • Numerous test output files were updated, indicating a significant change in the generated code structure.
  • Core compiler logic was adjusted to support the new thunk-based invocation strategy.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors interface method calls to use reusable thunks, which is a good optimization for reducing code size. The implementation introduces weak_odr LLVM functions for non-variadic interface method invocations. The logic for creating these thunks and modifying the call sites appears correct and is well-structured. The changes are consistently applied across the codebase, as reflected in the updated test outputs. The decision to handle variadic functions separately for now is a reasonable incremental approach. Overall, this is a solid improvement.

Comment on lines 70 to +84
@@ -79,6 +80,59 @@ func (b Builder) Imethod(intf Expr, method *types.Func) Expr {
sig = types.NewSignatureType(nil, nil, nil, types.NewTuple(vars...), sig.Results(), sig.Variadic())
}
}
return sig
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invokeMethodSig performs a non-obvious transformation (stripping the implicit interface receiver from the first parameter position when there is no explicit Recv). A brief doc comment would help future readers understand the contract — particularly when the signature is returned unmodified (method has a receiver, or first param doesn't match the interface).

This function is now shared between both Imethod and InvokeThunk, making it an important piece of the interface dispatch logic.

Comment on lines +91 to +100
func (p Package) invokeThunk(rawIntf *types.Interface, method *types.Func, sig *types.Signature) Function {
methodName := method.Name()
methodIdx := iMethodOf(rawIntf, methodName)
if methodIdx < 0 {
panic("invokeThunk: interface method not found: " + methodName)
}
name := p.invokeThunkName(rawIntf, methodName, methodIdx)
if thunk := p.FuncOf(name); thunk != nil {
return thunk
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the cache-hit path, invokeThunk still runs iMethodOf (linear scan) at line 93, invokeThunkName at line 97 (which calls InterfaceName → SHA-256 hash + base64 encode), and string concatenation — all before the FuncOf lookup at line 98 discards the result because the thunk already exists.

For hot interfaces invoked at many call sites (e.g., io.Reader, error), this repeated work could add up at compile time. Consider caching by a cheaper key (e.g., (*types.Interface, string) composite) or caching the InterfaceName result.

Comment on lines +695 to 696
args = append([]llssa.Expr{o}, args...)
ret = b.Do(act, fn, args...)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: append([]llssa.Expr{o}, args...) allocates a 1-element slice then grows it. Since args is freshly allocated by compileValues, you could pre-allocate with the right capacity:

Suggested change
args = append([]llssa.Expr{o}, args...)
ret = b.Do(act, fn, args...)
fullArgs := make([]llssa.Expr, 0, len(args)+1)
fullArgs = append(append(fullArgs, o), args...)
ret = b.Do(act, fn, fullArgs...)

@xgopilot
Copy link
Copy Markdown
Contributor

xgopilot bot commented Mar 2, 2026

Clean refactoring that replaces per-call-site itab dispatch with a shared weak_odr thunk, reducing generated IR verbosity. The variadic carve-out is sensible. The thunk caching via FuncOf is correct, and naming scheme (SHA-256 hash + index + name) prevents collisions. No security concerns found.

Main suggestions: (1) add a doc comment to invokeMethodSig explaining the receiver-stripping contract, (2) consider a cheaper cache-lookup path for invokeThunk to avoid redundant SHA-256 + linear scan on every cache hit, (3) minor allocation nit in cl/instr.go. See inline comments.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 2, 2026

Codecov Report

❌ Patch coverage is 95.34884% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.36%. Comparing base (c18be41) to head (159a0ec).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
ssa/interface.go 94.59% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1669      +/-   ##
==========================================
+ Coverage   91.35%   91.36%   +0.01%     
==========================================
  Files          47       47              
  Lines       12681    12720      +39     
==========================================
+ Hits        11585    11622      +37     
- Misses        906      907       +1     
- Partials      190      191       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant