Skip to content

ssa: use static itabs for known T2I conversions - #2703

Open
visualfc wants to merge 1 commit into
xgo-dev:mainfrom
visualfc:fix/static-itab
Open

visualfc wants to merge 1 commit into
xgo-dev:mainfrom
visualfc:fix/static-itab

Conversation

@visualfc

@visualfc visualfc commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Known concrete-to-interface (T2I) conversions emit a read-only _llgo_itab$ global ({Inter, Type, Hash, Fun[]}) with weak_odr linkage, matching cmd/compile's go:itab symbols. Identification is by type: the method set is complete and types.AssignableTo holds.

Ordinary builds use that global as the runtime vtable. After function bodies compile, path.init$itabs registers those itabs with RegisterStaticItab, after runtime.init and before package init (gc itabsinit). LTO and deadcode-drop keep calling NewItab so unused interface methods can be dropped (Fun[] would pin them). LTO still emits the static template for de-virt.

NewItab remains for I2I and for types only known at runtime. Hash is copied from the type descriptor when present. interequal compares the (inter, _type) pair so a static itab and a dynamically allocated itab for the same conversion compare equal.

Stack: first of four. Independent of the later PRs.

Test plan

  • go test ./ssa
  • cl/_testrt/staticitab (including any(T).(I) equality)
  • go test ./cl -run TestRunAndTestFromTestmeta
  • go test -tags=dev LTO plugin tests (TestBuildAndCheckSymbolsFromTestltoLTOPlugin, TestLTOPluginAggregateStaticItabDevirt)
  • go test -tags=dev -run TestBuildAndCheckSymbolsFromTestdrop ./cl (including interface_match)
  • TestTypeHashFromGlobal, TestRuntimeStaticItabUsesVtableAndInitItabs, TestDeclareStaticItabInits

@fennoai fennoai Bot left a comment

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.

Review: static itab for known T2I conversions

This is a clean, well-scoped change. staticItab now emits a compile-time itab global for known T2I conversions (interface with methods + assignable concrete type) and unsafeInterface uses it directly instead of always calling runtime.NewItab. Because I2I still goes through NewItab, the two representations of the same dynamic type can hold different tab pointers, so interequal correctly gains a type-pair fallback.

Correctness verified:

  • interequal fast path (x.tab == y.tab) is preserved; the new branch runs only on pointer mismatch and is nil-safe (the x.tab == nil || y.tab == nil guard precedes the field access at alg.go:265).
  • The fallback compares canonical descriptor pointers (inter, _type). abiType/abiIntf globals are name-canonicalized with WeakODRLinkage, so the static itab's inter/_type are the same pointers NewItab stores — pointer equality holds within a binary. It still calls ifaceeq, so data comparison is retained.
  • interhash (alg.go:103) and TypeAssert (interface.go:422) key on tab._type, not the itab pointer, so map hashing and type assertions stay consistent across both representations.
  • The static itab hash field matches abiCommonFields (sha256(typeName)[:4]), so type-switch/hash paths agree — the added comment at interface.go:88 is accurate.

Findings (all non-blocking):

  1. Test coverage gap for the interequal fallback — inline on cl/_testrt/staticitab/in.go. The new fallback branch (alg.go:262-265) is the reason for this PR, but the new test only exercises the fast path.

  2. Stale comment in cl/_testlto/globaldce_static_itab_devirt/in.go:50-52 (outside the diff hunk, so noted here). The comment still reads:

    // Interface equality relies on canonical runtime itab identity. The static template is analysis-only; the direct conversion must still agree with an interface assembled through reflection.
    Both claims now describe the old behavior. Equality no longer relies on itab-pointer identity (the whole point of the interequal change), and the static itab is used directly for T2I rather than being "analysis-only". This is the same stale wording removed elsewhere in the PR ("compile-time certificate", "template disappears before GlobalDCE") — this occurrence was missed. The adjacent SYMBOL check on line 8 was correctly flipped from SYMBOL-NOT to SYMBOL-DAG: _llgo_itab, so only the prose is out of date.

  3. Minor (compile-time only): in staticItab, the two abi.TypeName lookups + sha256 + base64 that build the global name run on every call before the VarOf(name) dedup check (interface.go:67-73). For repeated T2I sites of the same (interface, type) pair this is redundant work per site. Optional: key a fast in-memory map on (rawIntf, concrete) before hashing. No runtime or binary-size impact.

No correctness or security defects found.

panic(i.M())
}
j := boxed(T(41))
if i != j {

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.

The interequal change in runtime/internal/runtime/alg.go adds a fallback branch (comparing inter/_type when itab pointers differ) specifically to handle a T2I static-itab value compared against an I2I NewItab value. But this test never exercises that branch: both i (var i I = T(41)) and j (boxed(T(41))) are T2I conversions, and the static-itab global name is a hash of (intfName, typeName), so both resolve to the same global. x.tab == y.tab holds and the comparison takes the fast path — a regression in the fallback would still pass here.

Consider adding a case where one operand is produced via I2I (e.g. through ChangeInterface / assigning via a second interface type so unsafeInterface is reached with a nil concrete and routes to newItab) so the two operands have different tab pointers, plus an inequality check against a different concrete type.

@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.80952% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
ssa/interface.go 97.67% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

LLGo baseline benchmarks

83362687fdb4 | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Text size vs base Build vs base Run vs base
Linux cprintf 7280 B +120 B / +1.7% (worse) 393 B +6 B / +1.6% (worse) 531.431 ms -25.93 ms / -4.7% (better) 1.285 ms +14.05 us / +1.1% (worse)
Linux cprintf-lto 7112 B +200 B / +2.9% (worse) 374 B +6 B / +1.6% (worse) 520.959 ms -2.231 ms / -0.4% (better) 1.249 ms -8.648 us / -0.7% (better)
Linux fmtprintf 1774624 B +112352 B / +6.8% (worse) 552540 B +54181 B / +10.9% (worse) 3.492 s -170.9 ms / -4.7% (better) 3.686 ms +452.3 us / +14.0% (worse)
Linux fmtprintf-lto 1595864 B +95480 B / +6.4% (worse) 480619 B +44506 B / +10.2% (worse) 11.438 s +647.5 ms / +6.0% (worse) 2.888 ms +1.053 us / +0.03648% (worse)
Linux println 69256 B +264 B / +0.4% (worse) 16810 B +27 B / +0.2% (worse) 532.841 ms -7.675 ms / -1.4% (better) 1.605 ms -2.601 us / -0.2% (better)
Linux println-lto 60016 B +168 B / +0.3% (worse) 14215 B +16 B / +0.1% (worse) 792.058 ms -5.649 ms / -0.7% (better) 1.627 ms -117.7 us / -6.7% (better)
macOS cprintf 68096 B +32 B / +0.04701% (worse) 4437 B +8 B / +0.2% (worse) 700.956 ms -254.3 ms / -26.6% (better) 2.595 ms -1.563 ms / -37.6% (better)
macOS cprintf-lto 84288 B 0 B / +0.0% 193 B 0 B / +0.0% 705.073 ms -125.2 ms / -15.1% (better) 2.604 ms -670.3 us / -20.5% (better)
macOS fmtprintf 1628448 B +123776 B / +8.2% (worse) 933972 B +59400 B / +6.8% (worse) 2.776 s -779.3 ms / -21.9% (better) 4.971 ms -909.3 us / -15.5% (better)
macOS fmtprintf-lto 1275136 B +82432 B / +6.9% (worse) 900720 B +52504 B / +6.2% (worse) 7.932 s -1.072 s / -11.9% (better) 4.351 ms -586.7 us / -11.9% (better)
macOS println 117248 B +112 B / +0.1% (worse) 37473 B +52 B / +0.1% (worse) 681.857 ms -219.7 ms / -24.4% (better) 3.375 ms -1.035 ms / -23.5% (better)
macOS println-lto 119472 B 0 B / +0.0% 34856 B +32 B / +0.1% (worse) 867.327 ms -310.3 ms / -26.3% (better) 3.347 ms -208.1 us / -5.9% (better)
Windows MinGW cprintf 19968 B +512 B / +2.6% (worse) 4566 B +16 B / +0.4% (worse) 1.239 s -66.15 ms / -5.1% (better) 3.400 ms -116 us / -3.3% (better)
Windows MinGW cprintf-lto 17920 B 0 B / +0.0% 4486 B 0 B / +0.0% 1.295 s -78.68 ms / -5.7% (better) 3.428 ms -537.4 us / -13.6% (better)
Windows MinGW fmtprintf 2096128 B +163328 B / +8.5% (worse) 692006 B +93056 B / +15.5% (worse) 4.098 s -177.8 ms / -4.2% (better) 7.722 ms -474.2 us / -5.8% (better)
Windows MinGW fmtprintf-lto 2112512 B +155136 B / +7.9% (worse) 625430 B +78112 B / +14.3% (worse) 10.753 s +736.3 ms / +7.4% (worse) 7.822 ms -1.359 ms / -14.8% (better)
Windows MinGW println 75776 B 0 B / +0.0% 25158 B +16 B / +0.1% (worse) 1.265 s -73.54 ms / -5.5% (better) 6.556 ms -1.161 ms / -15.0% (better)
Windows MinGW println-lto 69632 B +512 B / +0.7% (worse) 21990 B 0 B / +0.0% 1.473 s -103.1 ms / -6.5% (better) 6.620 ms -1.288 ms / -16.3% (better)
Windows MinGW 386 cprintf 43520 B 0 B / +0.0% 5342 B +16 B / +0.3% (worse) 957.113 ms -32.32 ms / -3.3% (better) 3.861 ms -112.9 us / -2.8% (better)
Windows MinGW 386 cprintf-lto 20992 B 0 B / +0.0% 5094 B 0 B / +0.0% 988.644 ms -28.62 ms / -2.8% (better) 3.785 ms -198.4 us / -5.0% (better)
Windows MinGW 386 fmtprintf 2017792 B +121344 B / +6.4% (worse) 532350 B +59936 B / +12.7% (worse) 3.355 s -187.5 ms / -5.3% (better) 8.406 ms +684.6 us / +8.9% (worse)
Windows MinGW 386 fmtprintf-lto 2387968 B +207360 B / +9.5% (worse) 504818 B +53560 B / +11.9% (worse) 8.381 s +651.7 ms / +8.4% (worse) 8.555 ms +822 us / +10.6% (worse)
Windows MinGW 386 println 96256 B 0 B / +0.0% 21474 B +16 B / +0.1% (worse) 1.038 s +45.46 ms / +4.6% (worse) 7.257 ms +399.5 us / +5.8% (worse)
Windows MinGW 386 println-lto 74752 B +512 B / +0.7% (worse) 19318 B +4 B / +0.02071% (worse) 1.185 s -86.14 ms / -6.8% (better) 6.807 ms +271.6 us / +4.2% (worse)
Windows MinGW ARM64 cprintf 19456 B +512 B / +2.7% (worse) 4424 B +16 B / +0.4% (worse) 1.437 s -47.44 ms / -3.2% (better) 5.904 ms -410.5 us / -6.5% (better)
Windows MinGW ARM64 cprintf-lto 17920 B 0 B / +0.0% 4340 B 0 B / +0.0% 1.498 s -28.09 ms / -1.8% (better) 6.260 ms +309.3 us / +5.2% (worse)
Windows MinGW ARM64 fmtprintf 1954816 B +135680 B / +7.5% (worse) 577896 B +68288 B / +13.4% (worse) 4.144 s -21.01 ms / -0.5% (better) 12.099 ms -224.1 us / -1.8% (better)
Windows MinGW ARM64 fmtprintf-lto 2024960 B +145920 B / +7.8% (worse) 536728 B +60576 B / +12.7% (worse) 10.097 s +726.4 ms / +7.8% (worse) 12.530 ms -223 us / -1.7% (better)
Windows MinGW ARM64 println 72192 B 0 B / +0.0% 23912 B +36 B / +0.2% (worse) 1.463 s -49.48 ms / -3.3% (better) 10.240 ms -758 us / -6.9% (better)
Windows MinGW ARM64 println-lto 68608 B 0 B / +0.0% 21240 B +16 B / +0.1% (worse) 1.676 s -48.56 ms / -2.8% (better) 10.503 ms -567.2 us / -5.1% (better)
Windows MSVC cprintf 120320 B 0 B / +0.0% 65814 B +16 B / +0.02432% (worse) 1.110 s -139.1 ms / -11.1% (better) 3.534 ms +60.5 us / +1.7% (worse)
Windows MSVC cprintf-lto 119808 B 0 B / +0.0% 65734 B 0 B / +0.0% 1.076 s -6.242 ms / -0.6% (better) 3.543 ms +200 us / +6.0% (worse)
Windows MSVC fmtprintf 1774592 B +131584 B / +8.0% (worse) 787542 B +93040 B / +13.4% (worse) 3.857 s -249.4 ms / -6.1% (better) 10.240 ms -379.2 us / -3.6% (better)
Windows MSVC fmtprintf-lto 1762304 B +127488 B / +7.8% (worse) 725718 B +78656 B / +12.2% (worse) 9.751 s -198.5 ms / -2.0% (better) 8.976 ms -1.282 ms / -12.5% (better)
Windows MSVC println 195072 B +512 B / +0.3% (worse) 120838 B +16 B / +0.01324% (worse) 1.096 s +17.81 ms / +1.7% (worse) 7.492 ms +334.6 us / +4.7% (worse)
Windows MSVC println-lto 192512 B 0 B / +0.0% 118358 B +16 B / +0.01352% (worse) 1.260 s -7.093 ms / -0.6% (better) 7.620 ms +407.9 us / +5.7% (worse)
Windows MSVC 386 cprintf 9728 B 0 B / +0.0% 3931 B 0 B / +0.0% 1.088 s -13.21 ms / -1.2% (better) 5.562 ms -96.9 us / -1.7% (better)
Windows MSVC 386 cprintf-lto 9216 B 0 B / +0.0% 3853 B 0 B / +0.0% 1.294 s -29.4 ms / -2.2% (better) 5.577 ms -207.7 us / -3.6% (better)
Windows MSVC 386 fmtprintf 1286144 B +81920 B / +6.8% (worse) 515740 B +59936 B / +13.1% (worse) 3.943 s +117.8 ms / +3.1% (worse) 11.468 ms +375.2 us / +3.4% (worse)
Windows MSVC 386 fmtprintf-lto 1336320 B +95232 B / +7.7% (worse) 479211 B +52016 B / +12.2% (worse) 9.571 s +729.1 ms / +8.2% (worse) 11.400 ms -503.4 us / -4.2% (better)
Windows MSVC 386 println 36352 B 0 B / +0.0% 20340 B +16 B / +0.1% (worse) 1.103 s -16.3 ms / -1.5% (better) 9.807 ms -434 us / -4.2% (better)
Windows MSVC 386 println-lto 35328 B -512 B / -1.4% (better) 18549 B 0 B / +0.0% 1.309 s -12.59 ms / -1.0% (better) 9.060 ms -900.4 us / -9.0% (better)
Windows MSVC ARM64 cprintf 11776 B 0 B / +0.0% 4208 B +16 B / +0.4% (worse) 1.137 s -46.71 ms / -3.9% (better) 6.117 ms -377.8 us / -5.8% (better)
Windows MSVC ARM64 cprintf-lto 10752 B 0 B / +0.0% 4084 B 0 B / +0.0% 1.157 s -34.37 ms / -2.9% (better) 6.064 ms -273.4 us / -4.3% (better)
Windows MSVC ARM64 fmtprintf 1490432 B +103936 B / +7.5% (worse) 577848 B +68304 B / +13.4% (worse) 3.634 s -65.05 ms / -1.8% (better) 13.374 ms +614.8 us / +4.8% (worse)
Windows MSVC ARM64 fmtprintf-lto 1521664 B +116736 B / +8.3% (worse) 537444 B +60624 B / +12.7% (worse) 8.988 s +465.2 ms / +5.5% (worse) 13.507 ms +400.6 us / +3.1% (worse)
Windows MSVC ARM64 println 45056 B 0 B / +0.0% 23940 B +32 B / +0.1% (worse) 1.141 s -28.4 ms / -2.4% (better) 11.153 ms +26.7 us / +0.2% (worse)
Windows MSVC ARM64 println-lto 42496 B 0 B / +0.0% 21396 B +16 B / +0.1% (worse) 1.308 s -37.38 ms / -2.8% (better) 10.882 ms +36.4 us / +0.3% (worse)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 14.520 ns/op +0.12 ns/op / +0.8% (worse)
Linux BenchmarkMergeCompilerFlags 198.300 ns/op -6.2 ns/op / -3.0% (better)
Linux BenchmarkMergeLinkerFlags 127.500 ns/op -5.7 ns/op / -4.3% (better)
Linux BenchmarkChannelBuffered 55.130 ns/op +0.24 ns/op / +0.4% (worse)
Linux BenchmarkChannelHandoff 12871 ns/op -135 ns/op / -1.0% (better)
Linux BenchmarkDefer 47.350 ns/op -1.56 ns/op / -3.2% (better)
Linux BenchmarkDirectCall 1.558 ns/op +0.004 ns/op / +0.3% (worse)
Linux BenchmarkGlobalRead 1.164 ns/op +0.001 ns/op / +0.1% (worse)
Linux BenchmarkGlobalWrite 7.756 ns/op +0.001 ns/op / +0.01289% (worse)
Linux BenchmarkGoroutine 24019 ns/op -156 ns/op / -0.6% (better)
Linux BenchmarkInterfaceCall 5.822 ns/op -0.046 ns/op / -0.8% (better)
Linux BenchmarkRuntimeGetG 3.002 ns/op +0.013 ns/op / +0.4% (worse)
macOS BenchmarkLookupPCRandom 11.770 ns/op -0.62 ns/op / -5.0% (better)
macOS BenchmarkMergeCompilerFlags 94.220 ns/op -16.48 ns/op / -14.9% (better)
macOS BenchmarkMergeLinkerFlags 67.150 ns/op +0.69 ns/op / +1.0% (worse)
macOS BenchmarkChannelBuffered 26.030 ns/op -0.48 ns/op / -1.8% (better)
macOS BenchmarkChannelHandoff 6999 ns/op -2939 ns/op / -29.6% (better)
macOS BenchmarkDefer 36.140 ns/op -0.09 ns/op / -0.2% (better)
macOS BenchmarkDirectCall 1.074 ns/op 0 ns/op / +0.0%
macOS BenchmarkGlobalRead 1.085 ns/op -0.014 ns/op / -1.3% (better)
macOS BenchmarkGlobalWrite 1.079 ns/op -0.004 ns/op / -0.4% (better)
macOS BenchmarkGoroutine 39572 ns/op +2194 ns/op / +5.9% (worse)
macOS BenchmarkInterfaceCall 3.941 ns/op +0.028 ns/op / +0.7% (worse)
macOS BenchmarkRuntimeGetG 2.415 ns/op +0.273 ns/op / +12.7% (worse)
Windows MinGW BenchmarkLookupPCRandom 13.170 ns/op +0.18 ns/op / +1.4% (worse)
Windows MinGW BenchmarkMergeCompilerFlags 613.600 ns/op +4 ns/op / +0.7% (worse)
Windows MinGW BenchmarkMergeLinkerFlags 537.500 ns/op -3 ns/op / -0.6% (better)
Windows MinGW BenchmarkChannelBuffered 30.810 ns/op +0.79 ns/op / +2.6% (worse)
Windows MinGW BenchmarkChannelHandoff 821.200 ns/op -40 ns/op / -4.6% (better)
Windows MinGW BenchmarkDefer 55.910 ns/op -1.62 ns/op / -2.8% (better)
Windows MinGW BenchmarkDirectCall 1.548 ns/op -0.001 ns/op / -0.1% (better)
Windows MinGW BenchmarkGlobalRead 1.547 ns/op -0.311 ns/op / -16.7% (better)
Windows MinGW BenchmarkGlobalWrite 2.470 ns/op +0.027 ns/op / +1.1% (worse)
Windows MinGW BenchmarkGoroutine 92512 ns/op -1095 ns/op / -1.2% (better)
Windows MinGW BenchmarkInterfaceCall 8.061 ns/op -0.308 ns/op / -3.7% (better)
Windows MinGW BenchmarkRuntimeGetG 2.476 ns/op +0.311 ns/op / +14.4% (worse)
Windows MinGW 386 BenchmarkLookupPCRandom 21.540 ns/op +0.01 ns/op / +0.04645% (worse)
Windows MinGW 386 BenchmarkMergeCompilerFlags 553.700 ns/op +4.6 ns/op / +0.8% (worse)
Windows MinGW 386 BenchmarkMergeLinkerFlags 541.700 ns/op +27.6 ns/op / +5.4% (worse)
Windows MinGW 386 BenchmarkChannelBuffered 33.250 ns/op -0.64 ns/op / -1.9% (better)
Windows MinGW 386 BenchmarkChannelHandoff 821 ns/op +18.8 ns/op / +2.3% (worse)
Windows MinGW 386 BenchmarkDefer 35.720 ns/op -0.05 ns/op / -0.1% (better)
Windows MinGW 386 BenchmarkDirectCall 1.628 ns/op +0.272 ns/op / +20.1% (worse)
Windows MinGW 386 BenchmarkGlobalRead 1.356 ns/op 0 ns/op / +0.0%
Windows MinGW 386 BenchmarkGlobalWrite 6.983 ns/op +0.002 ns/op / +0.02865% (worse)
Windows MinGW 386 BenchmarkGoroutine 71746 ns/op +770 ns/op / +1.1% (worse)
Windows MinGW 386 BenchmarkInterfaceCall 7.125 ns/op -0.215 ns/op / -2.9% (better)
Windows MinGW 386 BenchmarkRuntimeGetG 1.634 ns/op +0.002 ns/op / +0.1% (worse)
Windows MinGW ARM64 BenchmarkLookupPCRandom 12.120 ns/op +0.02 ns/op / +0.2% (worse)
Windows MinGW ARM64 BenchmarkMergeCompilerFlags 571.700 ns/op +13.3 ns/op / +2.4% (worse)
Windows MinGW ARM64 BenchmarkMergeLinkerFlags 543.200 ns/op +28.6 ns/op / +5.6% (worse)
Windows MinGW ARM64 BenchmarkChannelBuffered 38.740 ns/op +1.42 ns/op / +3.8% (worse)
Windows MinGW ARM64 BenchmarkChannelHandoff 2320 ns/op -32 ns/op / -1.4% (better)
Windows MinGW ARM64 BenchmarkDefer 56.860 ns/op +2.42 ns/op / +4.4% (worse)
Windows MinGW ARM64 BenchmarkDirectCall 0.589 ns/op 0 ns/op / +0.0%
Windows MinGW ARM64 BenchmarkGlobalRead 0.663 ns/op -0.0002 ns/op / -0.03014% (better)
Windows MinGW ARM64 BenchmarkGlobalWrite 0.663 ns/op -0.0001 ns/op / -0.01508% (better)
Windows MinGW ARM64 BenchmarkGoroutine 62490 ns/op +394 ns/op / +0.6% (worse)
Windows MinGW ARM64 BenchmarkInterfaceCall 4.143 ns/op -0.003 ns/op / -0.1% (better)
Windows MinGW ARM64 BenchmarkRuntimeGetG 1.801 ns/op +0.031 ns/op / +1.8% (worse)
Windows MSVC BenchmarkLookupPCRandom 13.050 ns/op -0.03 ns/op / -0.2% (better)
Windows MSVC BenchmarkMergeCompilerFlags 663 ns/op +39.6 ns/op / +6.4% (worse)
Windows MSVC BenchmarkMergeLinkerFlags 595.700 ns/op +42.7 ns/op / +7.7% (worse)
Windows MSVC BenchmarkChannelBuffered 29.850 ns/op -2.28 ns/op / -7.1% (better)
Windows MSVC BenchmarkChannelHandoff 1006 ns/op -77 ns/op / -7.1% (better)
Windows MSVC BenchmarkDefer 55.690 ns/op +0.56 ns/op / +1.0% (worse)
Windows MSVC BenchmarkDirectCall 1.550 ns/op +0.003 ns/op / +0.2% (worse)
Windows MSVC BenchmarkGlobalRead 1.862 ns/op +0.313 ns/op / +20.2% (worse)
Windows MSVC BenchmarkGlobalWrite 2.455 ns/op -0.014 ns/op / -0.6% (better)
Windows MSVC BenchmarkGoroutine 91713 ns/op +248 ns/op / +0.3% (worse)
Windows MSVC BenchmarkInterfaceCall 9.023 ns/op +0.636 ns/op / +7.6% (worse)
Windows MSVC BenchmarkRuntimeGetG 2.172 ns/op +0.314 ns/op / +16.9% (worse)
Windows MSVC 386 BenchmarkLookupPCRandom 26.470 ns/op -0.19 ns/op / -0.7% (better)
Windows MSVC 386 BenchmarkMergeCompilerFlags 753.800 ns/op -13.3 ns/op / -1.7% (better)
Windows MSVC 386 BenchmarkMergeLinkerFlags 673.800 ns/op -27.9 ns/op / -4.0% (better)
Windows MSVC 386 BenchmarkChannelBuffered 38.500 ns/op -6.39 ns/op / -14.2% (better)
Windows MSVC 386 BenchmarkChannelHandoff 879 ns/op +55 ns/op / +6.7% (worse)
Windows MSVC 386 BenchmarkDefer 46.250 ns/op 0 ns/op / +0.0%
Windows MSVC 386 BenchmarkDirectCall 1.547 ns/op -0.002 ns/op / -0.1% (better)
Windows MSVC 386 BenchmarkGlobalRead 1.550 ns/op -0.307 ns/op / -16.5% (better)
Windows MSVC 386 BenchmarkGlobalWrite 7.780 ns/op 0 ns/op / +0.0%
Windows MSVC 386 BenchmarkGoroutine 110921 ns/op +3001 ns/op / +2.8% (worse)
Windows MSVC 386 BenchmarkInterfaceCall 8.103 ns/op +0.015 ns/op / +0.2% (worse)
Windows MSVC 386 BenchmarkRuntimeGetG 1.924 ns/op -0.563 ns/op / -22.6% (better)
Windows MSVC ARM64 BenchmarkLookupPCRandom 12.040 ns/op -0.04 ns/op / -0.3% (better)
Windows MSVC ARM64 BenchmarkMergeCompilerFlags 560.900 ns/op +17.8 ns/op / +3.3% (worse)
Windows MSVC ARM64 BenchmarkMergeLinkerFlags 532.800 ns/op +2.5 ns/op / +0.5% (worse)
Windows MSVC ARM64 BenchmarkChannelBuffered 39.470 ns/op -0.12 ns/op / -0.3% (better)
Windows MSVC ARM64 BenchmarkChannelHandoff 2614 ns/op +765 ns/op / +41.4% (worse)
Windows MSVC ARM64 BenchmarkDefer 57.200 ns/op +0.02 ns/op / +0.03498% (worse)
Windows MSVC ARM64 BenchmarkDirectCall 0.590 ns/op +0.0001 ns/op / +0.01696% (worse)
Windows MSVC ARM64 BenchmarkGlobalRead 0.885 ns/op +0.2215 ns/op / +33.4% (worse)
Windows MSVC ARM64 BenchmarkGlobalWrite 3.758 ns/op -0.005 ns/op / -0.1% (better)
Windows MSVC ARM64 BenchmarkGoroutine 54006 ns/op -2169 ns/op / -3.9% (better)
Windows MSVC ARM64 BenchmarkInterfaceCall 4.151 ns/op +0.016 ns/op / +0.4% (worse)
Windows MSVC ARM64 BenchmarkRuntimeGetG 1.804 ns/op +0.033 ns/op / +1.9% (worse)
Timer runtime benchmarks
Platform Operation and runtime ns/op vs base
Linux AfterFuncZeroDelivery/Go 893.700 ns/op -8.4 ns/op / -0.9% (better)
Linux AfterFuncZeroDelivery/LLGo 37484 ns/op -8488 ns/op / -18.5% (better)
Linux CreateStop/Go 289.100 ns/op -1.4 ns/op / -0.5% (better)
Linux CreateStop/LLGo 1681 ns/op -201 ns/op / -10.7% (better)
Linux RearmStopped/Go 115.700 ns/op +1 ns/op / +0.9% (worse)
Linux RearmStopped/LLGo 1087 ns/op -371 ns/op / -25.4% (better)
Linux ResetActive/Go 68.480 ns/op +0.78 ns/op / +1.2% (worse)
Linux ResetActive/LLGo 827.100 ns/op +95.9 ns/op / +13.1% (worse)
Linux ResetHeap1024/Go 67.050 ns/op -0.18 ns/op / -0.3% (better)
Linux ResetHeap1024/LLGo 175.700 ns/op -0.8 ns/op / -0.5% (better)
macOS AfterFuncZeroDelivery/Go 409.200 ns/op -26.4 ns/op / -6.1% (better)
macOS AfterFuncZeroDelivery/LLGo 80619 ns/op -6532 ns/op / -7.5% (better)
macOS CreateStop/Go 126 ns/op -21.5 ns/op / -14.6% (better)
macOS CreateStop/LLGo 445.500 ns/op -12.7 ns/op / -2.8% (better)
macOS RearmStopped/Go 55.210 ns/op -7.01 ns/op / -11.3% (better)
macOS RearmStopped/LLGo 380.800 ns/op -59.3 ns/op / -13.5% (better)
macOS ResetActive/Go 40.610 ns/op -4.81 ns/op / -10.6% (better)
macOS ResetActive/LLGo 172.100 ns/op -26.9 ns/op / -13.5% (better)
macOS ResetHeap1024/Go 45.830 ns/op +2.76 ns/op / +6.4% (worse)
macOS ResetHeap1024/LLGo 90.630 ns/op -8.22 ns/op / -8.3% (better)
Windows MinGW AfterFuncZeroDelivery/Go 551 ns/op -8.4 ns/op / -1.5% (better)
Windows MinGW AfterFuncZeroDelivery/LLGo 179089 ns/op -1252 ns/op / -0.7% (better)
Windows MinGW CreateStop/Go 116.200 ns/op +0.9 ns/op / +0.8% (worse)
Windows MinGW CreateStop/LLGo 452 ns/op +0.4 ns/op / +0.1% (worse)
Windows MinGW RearmStopped/Go 31.380 ns/op -0.03 ns/op / -0.1% (better)
Windows MinGW RearmStopped/LLGo 281.400 ns/op +4.8 ns/op / +1.7% (worse)
Windows MinGW ResetActive/Go 20.100 ns/op -0.01 ns/op / -0.04973% (better)
Windows MinGW ResetActive/LLGo 166.600 ns/op -4.6 ns/op / -2.7% (better)
Windows MinGW ResetHeap1024/Go 20.630 ns/op +0.27 ns/op / +1.3% (worse)
Windows MinGW ResetHeap1024/LLGo 124 ns/op +0.2 ns/op / +0.2% (worse)
Windows MinGW 386 AfterFuncZeroDelivery/Go 768 ns/op -0.2 ns/op / -0.02603% (better)
Windows MinGW 386 AfterFuncZeroDelivery/LLGo 129816 ns/op +209 ns/op / +0.2% (worse)
Windows MinGW 386 CreateStop/Go 167.400 ns/op -1.9 ns/op / -1.1% (better)
Windows MinGW 386 CreateStop/LLGo 373.100 ns/op -6.1 ns/op / -1.6% (better)
Windows MinGW 386 RearmStopped/Go 57.690 ns/op +0.97 ns/op / +1.7% (worse)
Windows MinGW 386 RearmStopped/LLGo 274.500 ns/op +5.9 ns/op / +2.2% (worse)
Windows MinGW 386 ResetActive/Go 32.590 ns/op -0.04 ns/op / -0.1% (better)
Windows MinGW 386 ResetActive/LLGo 909 ns/op +4.6 ns/op / +0.5% (worse)
Windows MinGW 386 ResetHeap1024/Go 32.830 ns/op +0.02 ns/op / +0.1% (worse)
Windows MinGW 386 ResetHeap1024/LLGo 151.100 ns/op +2.4 ns/op / +1.6% (worse)
Windows MinGW ARM64 AfterFuncZeroDelivery/Go 672.900 ns/op -1.6 ns/op / -0.2% (better)
Windows MinGW ARM64 AfterFuncZeroDelivery/LLGo 140624 ns/op -2480 ns/op / -1.7% (better)
Windows MinGW ARM64 CreateStop/Go 204.700 ns/op +9.3 ns/op / +4.8% (worse)
Windows MinGW ARM64 CreateStop/LLGo 352.400 ns/op -9.9 ns/op / -2.7% (better)
Windows MinGW ARM64 RearmStopped/Go 70.640 ns/op +0.01 ns/op / +0.01416% (worse)
Windows MinGW ARM64 RearmStopped/LLGo 252.100 ns/op -0.5 ns/op / -0.2% (better)
Windows MinGW ARM64 ResetActive/Go 30.970 ns/op +0.07 ns/op / +0.2% (worse)
Windows MinGW ARM64 ResetActive/LLGo 118.300 ns/op -5.9 ns/op / -4.8% (better)
Windows MinGW ARM64 ResetHeap1024/Go 31.080 ns/op +0.05 ns/op / +0.2% (worse)
Windows MinGW ARM64 ResetHeap1024/LLGo 126.200 ns/op -0.6 ns/op / -0.5% (better)
Windows MSVC AfterFuncZeroDelivery/Go 558.500 ns/op -5.2 ns/op / -0.9% (better)
Windows MSVC AfterFuncZeroDelivery/LLGo 172943 ns/op -2985 ns/op / -1.7% (better)
Windows MSVC CreateStop/Go 118 ns/op 0 ns/op / +0.0%
Windows MSVC CreateStop/LLGo 420 ns/op -2.3 ns/op / -0.5% (better)
Windows MSVC RearmStopped/Go 31.560 ns/op -0.03 ns/op / -0.1% (better)
Windows MSVC RearmStopped/LLGo 256.400 ns/op -10.3 ns/op / -3.9% (better)
Windows MSVC ResetActive/Go 20.020 ns/op -0.11 ns/op / -0.5% (better)
Windows MSVC ResetActive/LLGo 143.400 ns/op -11.5 ns/op / -7.4% (better)
Windows MSVC ResetHeap1024/Go 20.590 ns/op -0.01 ns/op / -0.04854% (better)
Windows MSVC ResetHeap1024/LLGo 123 ns/op -3.2 ns/op / -2.5% (better)
Windows MSVC 386 AfterFuncZeroDelivery/Go 954.400 ns/op +6.6 ns/op / +0.7% (worse)
Windows MSVC 386 AfterFuncZeroDelivery/LLGo 202474 ns/op +2056 ns/op / +1.0% (worse)
Windows MSVC 386 CreateStop/Go 191.800 ns/op -1.6 ns/op / -0.8% (better)
Windows MSVC 386 CreateStop/LLGo 468.200 ns/op -30.5 ns/op / -6.1% (better)
Windows MSVC 386 RearmStopped/Go 63.550 ns/op +0.15 ns/op / +0.2% (worse)
Windows MSVC 386 RearmStopped/LLGo 322.900 ns/op +3 ns/op / +0.9% (worse)
Windows MSVC 386 ResetActive/Go 38.950 ns/op -0.1 ns/op / -0.3% (better)
Windows MSVC 386 ResetActive/LLGo 903.600 ns/op -85.1 ns/op / -8.6% (better)
Windows MSVC 386 ResetHeap1024/Go 39.440 ns/op +0.14 ns/op / +0.4% (worse)
Windows MSVC 386 ResetHeap1024/LLGo 172.400 ns/op -0.9 ns/op / -0.5% (better)
Windows MSVC ARM64 AfterFuncZeroDelivery/Go 674.300 ns/op +6.5 ns/op / +1.0% (worse)
Windows MSVC ARM64 AfterFuncZeroDelivery/LLGo 136372 ns/op +2377 ns/op / +1.8% (worse)
Windows MSVC ARM64 CreateStop/Go 199.100 ns/op +0.3 ns/op / +0.2% (worse)
Windows MSVC ARM64 CreateStop/LLGo 469.500 ns/op +15 ns/op / +3.3% (worse)
Windows MSVC ARM64 RearmStopped/Go 70.640 ns/op +0.01 ns/op / +0.01416% (worse)
Windows MSVC ARM64 RearmStopped/LLGo 276.700 ns/op +2.4 ns/op / +0.9% (worse)
Windows MSVC ARM64 ResetActive/Go 30.880 ns/op -0.03 ns/op / -0.1% (better)
Windows MSVC ARM64 ResetActive/LLGo 150.900 ns/op -0.3 ns/op / -0.2% (better)
Windows MSVC ARM64 ResetHeap1024/Go 31.040 ns/op +0.04 ns/op / +0.1% (worse)
Windows MSVC ARM64 ResetHeap1024/LLGo 133.900 ns/op -3.1 ns/op / -2.3% (better)

Compared with 653957f840b5 measured in the same runner job.

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

LLGo WebAssembly build benchmarks

83362687fdb4 | workflow run | long-term charts

WebAssembly output sizes
Example, profile and compiler Wasm module vs base Generated JS glue vs base
cprintf/j32-emscripten/LLGo 147053 B +80 B / +0.1% (worse) 74890 B 0 B / +0.0%
cprintf/j32-goos-js/LLGo 145487 B +80 B / +0.1% (worse) 73165 B 0 B / +0.0%
cprintf/j64-emscripten-memory64/LLGo 134980 B +464 B / +0.3% (worse) 78778 B 0 B / +0.0%
cprintf/w32-goos-wasip1/LLGo 142071 B +115 B / +0.1% (worse) 0 B 0 B / 0.0%
cprintf/w32-wasi/LLGo 141819 B +115 B / +0.1% (worse) 0 B 0 B / 0.0%
fmtprintf/j32-emscripten/LLGo 3459966 B +256589 B / +8.0% (worse) 118590 B 0 B / +0.0%
fmtprintf/j32-goos-js/Go 2526852 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/j32-goos-js/LLGo 3438343 B +255567 B / +8.0% (worse) 101818 B +183 B / +0.2% (worse)
fmtprintf/j64-emscripten-memory64/LLGo 3166279 B +225960 B / +7.7% (worse) 125433 B 0 B / +0.0%
fmtprintf/w32-goos-wasip1/Go 2500019 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/w32-goos-wasip1/LLGo 3194896 B +359108 B / +12.7% (worse) 0 B 0 B / 0.0%
fmtprintf/w32-wasi/LLGo 3056844 B +354623 B / +13.1% (worse) 0 B 0 B / 0.0%
j32-emscripten/LLGo 146286 B +81 B / +0.1% (worse) 74890 B 0 B / +0.0%
j32-goos-js/Go 1895533 B 0 B / +0.0% 0 B 0 B / 0.0%
j32-goos-js/LLGo 144956 B +80 B / +0.1% (worse) 73165 B 0 B / +0.0%
j64-emscripten-memory64/LLGo 134311 B +464 B / +0.3% (worse) 78778 B 0 B / +0.0%
reflectcall/j32-emscripten/LLGo 1575604 B +41519 B / +2.7% (worse) 92056 B 0 B / +0.0%
reflectcall/j32-goos-js/Go 2191221 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/j32-goos-js/LLGo 1578055 B +41225 B / +2.7% (worse) 90331 B 0 B / +0.0%
reflectcall/j64-emscripten-memory64/LLGo 1458887 B +39630 B / +2.8% (worse) 98119 B +330 B / +0.3% (worse)
reflectcall/w32-goos-wasip1/Go 2205707 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/w32-goos-wasip1/LLGo 1583725 B +43057 B / +2.8% (worse) 0 B 0 B / 0.0%
reflectcall/w32-wasi/LLGo 1507468 B +41257 B / +2.8% (worse) 0 B 0 B / 0.0%
w32-goos-wasip1/Go 1909947 B 0 B / +0.0% 0 B 0 B / 0.0%
w32-goos-wasip1/LLGo 141287 B +117 B / +0.1% (worse) 0 B 0 B / 0.0%
w32-wasi/LLGo 141106 B +117 B / +0.1% (worse) 0 B 0 B / 0.0%
LLGo WebAssembly build measurements
Example and profile Build vs base
j32-emscripten 5.716 s +90.29 ms / +1.6% (worse)
j32-goos-js 5.989 s +181 ms / +3.1% (worse)
j64-emscripten-memory64 5.059 s +128.9 ms / +2.6% (worse)
reflectcall/w32-wasi 27.019 s +696.3 ms / +2.6% (worse)
w32-goos-wasip1 4.880 s +309.7 ms / +6.8% (worse)
w32-wasi 4.810 s +385.8 ms / +8.7% (worse)

Compared with 15732a0d63d9 measured in the same runner job.

@visualfc
visualfc force-pushed the fix/static-itab branch 2 times, most recently from 22abddd to 916df42 Compare September 30, 2026 02:32
Known concrete-to-interface conversions emit a read-only _llgo_itab$
global (Inter, Type, Hash, Fun) with weak_odr linkage, matching
cmd/compile's go:itab symbols.

Ordinary builds use that global as the runtime vtable. After function
bodies compile, path.init$itabs registers those itabs with
RegisterStaticItab, after runtime.init and before package init (gc
itabsinit). LTO and deadcode-drop keep calling NewItab so unused
interface methods can be dropped; Fun[] would pin them. LTO still
emits the static template for de-virt.

Hash is copied from the type descriptor when present. interequal
compares the (inter, _type) pair so a static itab and a dynamically
allocated itab for the same conversion compare equal.
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