Skip to content

[Based on #2255] runtime/ffi: use direct ffi_call_go where compatible - #136

Closed
cpunion wants to merge 15 commits into
codex/closure-env-2170-v2from
codex/closure-env-ffi-call-go
Closed

[Based on #2255] runtime/ffi: use direct ffi_call_go where compatible#136
cpunion wants to merge 15 commits into
codex/closure-env-2170-v2from
codex/closure-env-ffi-call-go

Conversation

@cpunion

@cpunion cpunion commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Based on xgo-dev#2255. This is an isolated CI branch and does not update the existing PR head.

Selection rule

The closure entry ABI is independent of the libffi call path. Native targets pass env through LLVM nest or swiftself; WebAssembly retains explicit env. Windows already follows the architecture-selected ABI even though LLGo does not support the OS yet, avoiding a later closure ABI change.

For reflection/FFI calls, the semantic CIF never contains a hidden env. The final hop is selected as follows:

  1. Use direct ffi_call_go when libffi's Go static chain and LLVM's hidden attribute use the same physical register.
  2. If libffi marshals the arguments correctly but uses a different context register, use ffi_call_go plus a small register bridge.
  3. Otherwise use public ffi_call plus a TLS trampoline that restores the marshalled arguments and installs the LLVM hidden register.

Every hidden-env architecture must select exactly one of these paths or fail at compile time. FFI_GO_CLOSURES is a libffi target capability, not a closure-ABI switch.

Platform selection

OS Arch Env transport libffi path Compatibility / fallback reason
Any x86_64 / i386 nest direct ffi_call_go LLVM and libffi use the same static-chain register; x86 requires FFI_GO_CLOSURES; Windows runtime support remains future work
All supported RISC-V 32/64 nest/T2 direct ffi_call_go libffi also uses T2/X7
All supported ARM32 swiftself/R10 ffi_call_go + register bridge libffi uses IP/R12; LLVM ARM32 nest is an ordinary first argument, so the bridge moves R12 to hidden R10 without TLS or argument saves
Non-Apple, non-Android, non-Windows AArch64 nest/X18 direct ffi_call_go libffi also uses X18
Apple AArch64 swiftself/X20 public ffi_call + TLS trampoline X18 is reserved and upstream/Homebrew libffi does not export ffi_call_go; unlike ARM32, no context register reaches a small bridge, so TLS carries {fn, env}
Android AArch64 swiftself/X20 public ffi_call + TLS trampoline libffi's X18 transport is unavailable or incompatible with LLGo's X20 ABI
Windows, not yet supported by LLGo ARM / AArch64 swiftself compile-time TODO X18 is reserved on AArch64; the Windows TLS final hop is not implemented or validated yet
Any Wasm explicit env ordinary FFI path no compatible hidden-register transport; a future one will be an intentional target ABI upgrade
Any Other unvalidated architectures explicit env ordinary FFI path no hidden-env register mapping and final hop have been validated

Both libffi headers and linker flags come exclusively from pkg-config libffi. macOS CI explicitly installs pkg-config and Homebrew libffi and verifies the resolved prefix. macOS x86 compilation rejects Apple SDK libffi when FFI_GO_CLOSURES is absent. Apple AArch64 deliberately keeps the X20 TLS path.

This keeps libffi unmodified while removing the redundant RISC-V trampoline, the ARM argument-save trampoline, and the entire x86 TLS trampoline.

Focused verification

  • Ubuntu 24.04 amd64/libffi 3.4.6 direct probe: result 42; wrapper object references only ffi_call_go
  • macOS arm64 public-call/TLS path: closure and reflection tests pass; wrapper references ffi_call, not ffi_call_go
  • x86 direct path: wrapper references only ffi_call_go; compiling against Apple SDK-style headers without Go closures fails with the intended diagnostic
  • Windows x86_64 COFF direct-path compile: wrapper references ffi_call_go; Windows ARM64 reports the explicit final-hop TODO
  • ARMv7 hard-float/Thumb probe against unmodified libffi 3.4.6 under QEMU: result 42
  • closure object matrix covers nest, swiftself, Windows x86/AArch64 selection, Wasm explicit env, Go/C functions, captured and empty closures, nil receivers, and interface method values
  • closure/reflection runtime tests: closureall, closureenv, reflectfn, reflectmkfn, reflectclosureenv

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

3d73ab8dad17 | workflow run | long-term charts

Program measurements

Platform Workload File size vs main Build vs main Run vs main
Linux cprintf 18344 B -1.1% (better) 335.269 ms -7.1% (better) 1.293 ms +3.7% (worse)
Linux fmtprintf 1830840 B -17.4% (better) 3.316 s -1.8% (better) 3.117 ms +24.9% (worse)
Linux println 67720 B -5.3% (better) 338.022 ms -7.2% (better) 1.564 ms -2.7% (better)
macOS cprintf 84672 B +0.0% 384.620 ms +18.3% (worse) 2.526 ms -0.2% (better)
macOS fmtprintf 1869328 B -20.8% (better) 2.873 s +10.3% (worse) 12.578 ms -24.3% (better)
macOS println 121200 B -3.6% (better) 317.225 ms +4.7% (worse) 4.551 ms +39.9% (worse)
Core language and compiler benchmarks
Platform Benchmark ns/op vs main
Linux BenchmarkLookupPCRandom 13.430 ns/op +1.3% (worse)
Linux BenchmarkMergeCompilerFlags 150.500 ns/op -0.6% (better)
Linux BenchmarkMergeLinkerFlags 94.440 ns/op +0.2% (worse)
Linux BenchmarkChannelBuffered 40.660 ns/op +19.4% (worse)
Linux BenchmarkChannelHandoff 27487 ns/op -1.5% (better)
Linux BenchmarkDefer 46.070 ns/op -2.5% (better)
Linux BenchmarkDirectCall 1.556 ns/op -0.3% (better)
Linux BenchmarkGlobalRead 1.868 ns/op -0.2% (better)
Linux BenchmarkGlobalWrite 2.487 ns/op +0.0% (worse)
Linux BenchmarkGoroutine 32222 ns/op +0.7% (worse)
Linux BenchmarkInterfaceCall 8.088 ns/op -0.1% (better)
Linux BenchmarkRuntimeGetG 1.869 ns/op -0.1% (better)
macOS BenchmarkLookupPCRandom 11.200 ns/op +4.2% (worse)
macOS BenchmarkMergeCompilerFlags 113.900 ns/op +14.2% (worse)
macOS BenchmarkMergeLinkerFlags 66.100 ns/op -27.0% (better)
macOS BenchmarkChannelBuffered 20.700 ns/op -7.1% (better)
macOS BenchmarkChannelHandoff 6909 ns/op +10.2% (worse)
macOS BenchmarkDefer 25.350 ns/op -20.1% (better)
macOS BenchmarkDirectCall 0.981 ns/op -13.4% (better)
macOS BenchmarkGlobalRead 1.040 ns/op -5.3% (better)
macOS BenchmarkGlobalWrite 1.033 ns/op -12.8% (better)
macOS BenchmarkGoroutine 27882 ns/op +20.5% (worse)
macOS BenchmarkInterfaceCall 4.133 ns/op -11.1% (better)
macOS BenchmarkRuntimeGetG 2.037 ns/op -7.1% (better)

Compared only with the latest matching platform in the main series.

@cpunion

cpunion commented Aug 2, 2026

Copy link
Copy Markdown
Owner Author

该实现已 rebase 并完整纳入 xgo-dev/llgo#2255,当前 PR 不再单独推进,现关闭。

@cpunion cpunion closed this Aug 2, 2026
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