Skip to content

runtime: preserve Windows caller identity after LTO inlining - #2773

Merged
cpunion merged 2 commits into
xgo-dev:mainfrom
zhouguangyuan0718:codex/windows-lto-funcinfo-20261010
Oct 11, 2026
Merged

cpunion merged 2 commits into
xgo-dev:mainfrom
zhouguangyuan0718:codex/windows-lto-funcinfo-20261010

Conversation

@zhouguangyuan0718

Copy link
Copy Markdown
Collaborator

Windows LTO can copy an inlined callee's function-entry record into its caller. The runtime then sorts these anchors and may resolve the caller's PC to the inlinee's name. A cross-package integer helper followed by runtime.Callers reproduces the missing caller frame on current main, without SIMD or FMV.

Normalize entry anchors to their physical PE function using the existing Windows unwind lookup and retain the first serialized record for each entry before sorting. For leaf functions without unwind metadata, retain the anchor and deduplicate equal PCs in record order. The change applies to Windows amd64/arm64; other targets retain their existing behavior.

The new cl/_testlto/caller_inline_entry fixture checks both the computation and the captured caller's identity/source location, and runs in the existing Full LTO suite.

Validation:

  • Main 686181ad6, Windows amd64 -O2 -lto=full: new fixture fails with capture frame lost its physical function identity (exit 2).
  • With the fix, Windows amd64 -O2 -lto=thin and -O2 -lto=full: fixture passes.
  • macOS arm64: go test ./cl -run '^TestRunAndTestFromTestlto$/caller_inline_entry$' -count=1 passes.
  • Independently, rebasing compiler: add early AVX2 SIMD128 multiversioning through Go APIs #2748 onto the same main and removing only these four runtime changes reproduces TestSIMDFMVInlineTraceback; restoring them makes the same Windows ThinLTO/GODEBUG test pass.

Windows execution above used MinGW cross compilation and Wine; native MSVC/MinGW CI remains to be confirmed.

Extracted from #2748 so this general Windows runtime fix can be reviewed and merged separately. #2748 will drop its overlapping runtime diff when rebased after this PR merges.

@zhouguangyuan0718

Copy link
Copy Markdown
Collaborator Author

@fennoai review

Please review this standalone Windows LTO runtime fix, extracted from #2748. Focus on correctness of PE entry normalization/deduplication on amd64 and arm64, leaf functions without unwind metadata, and regression coverage. The new cross-package scalar fixture fails on main 686181ad6 and passes with the fix under Windows ThinLTO and Full LTO; it does not require SIMD or FMV. Please distinguish concrete correctness issues from optional cleanup and do not modify the branch.

@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: Windows LTO funcinfo dedup fix

Targeted, well-reasoned fix for the Windows LTO/COFF case where an inlinee's entry-site anchor is carried into its caller and arbitrary duplicate removal would strip the caller's identity in CallersFrames. The core logic (normalize to the native PE entry, keep the first record per physical function, then feed the min-entry publication) is correct, and the explanatory comment at the dedup site matches the implementation. The unsafe PE-record dereference is correctly nil-guarded and reuses the pre-established runtimeFuncPCMayUseEntrySlack pattern — no memory-safety concerns.

Findings below are about maintainability and consistency; none are correctness blockers.

Security: No issues found.

Comment thread runtime/internal/lib/runtime/symtab.go Outdated
continue
}
pc := rtdebug.FunctionPC(unsafe.Pointer(site.pc))
if GOOS == "windows" && (GOARCH == "amd64" || GOARCH == "arm64") {

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.

[P2] Inline GOOS/GOARCH guard duplicates the build-tag platform split

This GOOS == "windows" && (GOARCH == "amd64" || GOARCH == "arm64") guard re-states the exact platform set already encoded by the build tags on funcinfo_entry_slack_windows_seh_llgo.go (//go:build windows && (amd64 || arm64)). runtimeFuncPCSiteEntry is already specialized per-platform (it returns pc unchanged everywhere except windows amd64/arm64), so the only thing this inline condition actually gates is the nativeEntries dedup map.

The amd64/arm64 knowledge now lives in two places that must stay in sync — adding a future Windows arch with PE unwind means remembering to update both the build tag and this GOARCH list. Consider pushing the dedup decision behind a single build-tagged predicate (e.g. a per-file const runtimeFuncPCDedupBySiteEntry paired with runtimeFuncPCSiteEntry), matching how runtimeFuncPCEntrySlack and runtimeFuncPCMayUseEntrySlack are already defined per file. That also keeps symtab.go platform-agnostic, consistent with the rest of the file.

*value = arith.AddSeven(*value)
return runtime.Callers(1, pcs[:])
}

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.

[P3] Test exercises the real fix path only on Windows amd64/arm64

Good, deterministic test: empty expect.txt correctly means "empty stdout, clean exit; any panic fails," matching the harness. Two transparency notes:

  • The dedup code path (symtab.go guard body) only runs on windows amd64/arm64. On Linux/macOS CI, capture already keeps its identity, so found == 1 passes without touching the fix — this is a fine regression guard but the fix itself is only validated when the suite runs on windows amd64/arm64.
  • The trigger depends on LTO actually inlining arith.AddSeven into capture. There's no assertion the inline copy was created, so a future optimizer change that stops inlining would let the test pass silently while no longer exercising its named scenario. The value != 12 check confirms the computation ran, not that it was inlined into capture.

Relatedly, the comment "Cross-package LTO copies AddSeven's entry record into this function" reads as guaranteed behavior; "is expected to copy" would set accurate expectations. Consider a brief note acknowledging the LTO-inlining dependency.

@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 (cont.): additional maintainability notes

Supplementary findings from the same review; three inline comments exceeded the title limit in the first submission and are posted here.

// loses the caller's identity. Keep the first record per physical
// PE function before sorting, and publish its true entry.
pc = runtimeFuncPCSiteEntry(pc)
if nativeEntries == nil {

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.

[P2] nativeEntries map conflicts with loop's no-alloc discipline

This loop was deliberately rewritten to avoid Go-runtime machinery that keeps stack temporaries alive — see the comments at lines 876-878 ("Keep potentially large materialization loops free of append and checked slice indexing ... can exhaust a worker thread's fixed-size stack") and 969-971 (composite assignment "used to lower through a fresh stack temporary on every loop iteration, exhausting a fixed WebAssembly goroutine stack"). The frames array is pre-sized and written via raw unsafe.Add stores specifically to avoid append.

Introducing make(map[uintptr]bool) plus per-iteration mapassign (which can trigger incremental rehash/growth and heap allocation) reintroduces exactly the kind of per-iteration runtime calls this code works to avoid. The specific wasm failure mode doesn't directly apply here (this path is windows amd64/arm64 only) and the map is bounded by distinct PE entries, so this is not a bug — but it is an inconsistency worth a short justifying comment explaining why a map is acceptable here when append/composite literals were banned a few lines above.

Also note the records already arrive in COFF order and an existing sortRuntimeFuncPCFrames + uniqueRuntimeFuncPCFrames pass runs right after. The pcline path solves the identical "equal PC, keep emission order" problem with a sequence tiebreak field and an order-preserving sort (no map) — see runtimePCLineFrameLess. If runtimeFuncPCFrame gained a sequence/order field, this pre-sort dedup map could likely be eliminated entirely.

if nativeEntries == nil {
nativeEntries = make(map[uintptr]bool)
}
if nativeEntries[pc] {

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.

[P3] Dedup keys on un-normalized anchor for leaf functions

For a leaf function (no RUNTIME_FUNCTION record), runtimeFuncPCSiteEntry returns the raw pc unchanged, so the dedup key here is the un-normalized anchor rather than a true PE function begin. For non-leaf functions pc has been normalized, so "same PC => same physical function" holds and the dedup is sound; the leaf case relies on the pre-existing COFF-fold property. The slack file documents this ("A leaf without unwind metadata keeps its anchor"), but the dedup site itself does not restate it. A one-line comment here would stop a future reader from assuming the key is always a normalized function begin.

// and must not resolve to the next densely packed function's anchor.
const runtimeFuncPCEntrySlack = 64

// runtimeFuncPCSiteEntry groups LTO inline copies by the real PE function.

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.

[P3] Doc comment overstates what runtimeFuncPCSiteEntry does

runtimeFuncPCSiteEntry only normalizes a single pc to the PE function begin address — it performs no grouping or deduplication itself. The verbs "groups LTO inline copies" and "equal-PC copies can still be deduplicated" describe behavior that actually lives in the caller (appendRuntimeFuncInfoEntryFrames's nativeEntries map). Consider rewording to make the responsibility boundary clear, e.g. "returns the real PE function entry for a pc, which the caller uses to group/dedup LTO inline copies."

@fennoai

fennoai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Review: Windows LTO funcinfo fix (focused on correctness)

Reviewed the standalone fix with the lens you asked for: PE entry normalization/dedup on amd64 + arm64, leaf functions without unwind metadata, and regression coverage. No changes made to the branch.

Verdict: the fix is correct for its stated purpose. The normalization + "keep first record per physical PE function" strategy correctly preserves the caller's identity in CallersFrames for the cross-package LTO-inline case. I found no concrete correctness defect. Below I separate the correctness reasoning from optional cleanup.

Correctness — confirmed sound

1. PE entry normalization (amd64 + arm64). runtimeFuncPCSiteEntry reads the first DWORD of the RUNTIME_FUNCTION record as BeginAddress and returns imageBase + begin. On both AMD64 (IMAGE_RUNTIME_FUNCTION_ENTRY) and ARM64 (IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY) the first field is a DWORD BeginAddress RVA, so the read is layout-correct on both arches. It reuses the exact pattern already established in runtimeFuncPCMayUseEntrySlack and windowsUnwindOne. The nil guard (entry == nil → return pc) is correct; the C wrapper llgo_windows_lookup_function_entry returns RtlLookupFunctionEntry(...) directly, so a leaf/no-metadata PC yields nil and is handled. No arch-specific hazard.

2. Dedup + ordering. Records are iterated in section (COFF) order via linear start + i*size, so "keep the first record" relies on the compiler/linker emitting the caller's own record before the carried-in inlinee copy. Given that invariant, the dedup correctly keeps the caller's funcIndex and drops the inlinee copy that shares the same native entry. This is the crux of the fix and it holds. (This emission-order invariant is the one assumption the correctness rests on — see cleanup note C.)

3. Interaction with the sort/unique passes. This dedup runs before sortRuntimeFuncPCFrames (non-stable quicksort) + uniqueRuntimeFuncPCFrames (which collapses equal-entry frames keeping the last scanned). Pre-dedup is what makes the result deterministic: without it, two equal-entry frames with different funcIndex could survive in either order and unique would arbitrarily pick one. Doing it before the unstable sort is the right placement.

4. Skipped entries[funcIndex] write for dropped records — safe via fallback. The continue at the dedup check happens before the min-entry publication (entries[funcIndex] = min(...)), so a deduplicated inlinee funcIndex gets no entry slot (left 0) on this path — new behavior for windows amd64/arm64. I traced the only consumer, funcEntryForIndex → runtimeFuncPCEntries[index], used at the pcline builder: when it returns 0 there is an explicit fallback to symbolPCFuncInfoName(...) and then addrInfoSymbol(pc). So a zero slot degrades gracefully rather than producing a wrong entry. The primary frameSymbol/FrameForPC path resolves through the frames table, where resolving an address inside the physical function to the caller (first record) is exactly the intended result. No correctness break — worth a one-line comment noting the slot is intentionally left to the fallback (optional).

5. Leaf functions without unwind metadata. For a leaf (nil lookup) runtimeFuncPCSiteEntry returns the raw anchor, so dedup keys on the un-normalized PC. Two consequences, both acceptable: (a) two distinct leaves have distinct anchors and are not collapsed — correct; (b) when the LTO caller itself is a leaf, dedup falls back to raw-PC equality rather than true-entry normalization, i.e. best-effort for that sub-case. This is unchanged from the pre-existing raw-PC behavior and is accurately described by the doc comment, so it is a documented scope limit, not a regression.

Regression coverage

The fixture is a valid, deterministic guard (empty expect.txt = clean exit; any panic fails, matching the harness). Two transparency notes, not blockers:

  • The dedup code path only executes on windows amd64/arm64. On Linux/macOS CI capture already keeps its identity, so found == 1 passes without touching the fix — the fixture validates the fix only when the suite runs on Windows amd64/arm64. You note it passes under Windows ThinLTO and Full LTO, which covers this; just flagging that other CI legs are regression guards, not validation.
  • The trigger depends on LTO actually inlining arith.AddSeven into capture; there is no assertion that the inline copy was created. If a future optimizer stops inlining, the test would pass silently while no longer exercising its named scenario. The value != 12 check proves the computation ran, not that it was inlined. A comment acknowledging this dependency would help.

Optional cleanup (non-correctness)

  • A. The inline GOOS == "windows" && (GOARCH == "amd64" || GOARCH == "arm64") guard in symtab.go restates the platform set already encoded by the build tags on funcinfo_entry_slack_*. runtimeFuncPCSiteEntry is already a per-platform no-op elsewhere, so the guard only gates the dedup map. Pushing the decision behind one build-tagged predicate would keep the amd64/arm64 knowledge in a single place.
  • B. The nativeEntries map reintroduces per-iteration mapassign/allocation into a loop deliberately rewritten to avoid append/composite literals (see the stack-exhaustion comments at lines ~876 and ~969). Not a bug here (windows-only path, bounded size), but a short comment on why a map is acceptable here would resolve the inconsistency; the pcline path solves the same "equal PC, keep emission order" problem with a sequence tiebreak and an order-preserving sort instead.
  • C. The correctness depends on the COFF emission-order invariant (caller record first). A brief note tying the fixture to that invariant would make the guard's intent explicit.
  • D. The doc comment on runtimeFuncPCSiteEntry says it "groups ... copies" / "copies can still be deduplicated," but the function only normalizes one PC — the grouping/dedup lives in the caller. Minor rewording would clarify the responsibility boundary.

These four match the inline comments posted on the earlier /review pass; none affect correctness.

@codecov

codecov Bot commented Oct 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

LLGo baseline benchmarks

7f5ce98b6f38 | 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 8568 B 0 B / +0.0% 387 B 0 B / +0.0% 640.282 ms +2.106 ms / +0.3% (worse) 1.259 ms +15.94 us / +1.3% (worse)
Linux cprintf-lto 8408 B 0 B / +0.0% 368 B 0 B / +0.0% 633.497 ms -91.05 ms / -12.6% (better) 1.228 ms -63.1 us / -4.9% (better)
Linux fmtprintf 4887480 B +240 B / +0.004911% (worse) 504614 B 0 B / +0.0% 4.482 s -194.7 ms / -4.2% (better) 3.107 ms -16.51 us / -0.5% (better)
Linux fmtprintf-lto 3607784 B +88 B / +0.002439% (worse) 442924 B 0 B / +0.0% 13.534 s +75.34 ms / +0.6% (worse) 2.942 ms +42.63 us / +1.5% (worse)
Linux println 681656 B 0 B / +0.0% 16855 B 0 B / +0.0% 656.255 ms -46.19 ms / -6.6% (better) 1.582 ms -20.87 us / -1.3% (better)
Linux println-lto 190160 B 0 B / +0.0% 14273 B 0 B / +0.0% 958.325 ms -91.19 ms / -8.7% (better) 1.609 ms +49.31 us / +3.2% (worse)
macOS cprintf 50736 B 0 B / +0.0% 4409 B 0 B / +0.0% 1.038 s +26.09 ms / +2.6% (worse) 4.095 ms -545.8 us / -11.8% (better)
macOS cprintf-lto 50496 B 0 B / +0.0% 161 B 0 B / +0.0% 1.045 s -45.03 ms / -4.1% (better) 4.710 ms +649.3 us / +16.0% (worse)
macOS fmtprintf 1774368 B 0 B / +0.0% 880665 B +48 B / +0.005451% (worse) 4.597 s -164.1 ms / -3.4% (better) 9.664 ms -1.019 ms / -9.5% (better)
macOS fmtprintf-lto 1361232 B 0 B / +0.0% 765037 B +92 B / +0.01203% (worse) 11.791 s -655.6 ms / -5.3% (better) 5.814 ms -1.677 ms / -22.4% (better)
macOS println 99344 B 0 B / +0.0% 24216 B 0 B / +0.0% 1.062 s +34.29 ms / +3.3% (worse) 3.849 ms -1.196 ms / -23.7% (better)
macOS println-lto 83664 B 0 B / +0.0% 21457 B 0 B / +0.0% 1.033 s -291.9 ms / -22.0% (better) 5.775 ms -137 us / -2.3% (better)
Windows MinGW cprintf 655872 B 0 B / +0.0% 4550 B 0 B / +0.0% 1.418 s -14.59 ms / -1.0% (better) 3.460 ms +105.2 us / +3.1% (worse)
Windows MinGW cprintf-lto 44032 B 0 B / +0.0% 4502 B 0 B / +0.0% 1.469 s +8.852 ms / +0.6% (worse) 3.454 ms -52.7 us / -1.5% (better)
Windows MinGW fmtprintf 5446144 B +1536 B / +0.02821% (worse) 604758 B +208 B / +0.03441% (worse) 5.207 s +43.53 ms / +0.8% (worse) 8.325 ms -177.6 us / -2.1% (better)
Windows MinGW fmtprintf-lto 4146688 B +1024 B / +0.0247% (worse) 551910 B +240 B / +0.0435% (worse) 12.796 s +45.09 ms / +0.4% (worse) 8.442 ms -170.2 us / -2.0% (better)
Windows MinGW println 710656 B 0 B / +0.0% 25190 B 0 B / +0.0% 1.434 s -5.29 ms / -0.4% (better) 6.351 ms -172.6 us / -2.6% (better)
Windows MinGW println-lto 208896 B 0 B / +0.0% 22070 B 0 B / +0.0% 1.711 s -4.77 ms / -0.3% (better) 8.865 ms +2.619 ms / +41.9% (worse)
Windows MinGW 386 cprintf 605184 B 0 B / +0.0% 5352 B 0 B / +0.0% 1.062 s -9.07 ms / -0.8% (better) 3.810 ms -102.2 us / -2.6% (better)
Windows MinGW 386 cprintf-lto 103424 B 0 B / +0.0% 5112 B 0 B / +0.0% 1.084 s -63.47 ms / -5.5% (better) 3.819 ms +13.6 us / +0.4% (worse)
Windows MinGW 386 fmtprintf 4750336 B +512 B / +0.01078% (worse) 472696 B 0 B / +0.0% 4.123 s +105.8 ms / +2.6% (worse) 7.741 ms -283.8 us / -3.5% (better)
Windows MinGW 386 fmtprintf-lto 4149760 B +512 B / +0.01234% (worse) 451064 B 0 B / +0.0% 9.870 s +35.71 ms / +0.4% (worse) 7.798 ms -584.1 us / -7.0% (better)
Windows MinGW 386 println 657408 B 0 B / +0.0% 21500 B 0 B / +0.0% 1.053 s -39.57 ms / -3.6% (better) 6.644 ms -163.9 us / -2.4% (better)
Windows MinGW 386 println-lto 259072 B 0 B / +0.0% 19328 B 0 B / +0.0% 1.298 s -58.87 ms / -4.3% (better) 6.918 ms +344 us / +5.2% (worse)
Windows MinGW ARM64 cprintf 666112 B 0 B / +0.0% 4436 B 0 B / +0.0% 1.662 s -38.73 ms / -2.3% (better) 6.705 ms -844.5 us / -11.2% (better)
Windows MinGW ARM64 cprintf-lto 43520 B 0 B / +0.0% 4388 B 0 B / +0.0% 1.776 s -15.84 ms / -0.9% (better) 6.517 ms -1.058 ms / -14.0% (better)
Windows MinGW ARM64 fmtprintf 5354496 B +512 B / +0.009563% (worse) 512892 B +172 B / +0.03355% (worse) 5.107 s -120 ms / -2.3% (better) 13.720 ms -264.2 us / -1.9% (better)
Windows MinGW ARM64 fmtprintf-lto 4314624 B +2048 B / +0.04749% (worse) 479104 B +176 B / +0.03675% (worse) 11.520 s -527 ms / -4.4% (better) 12.787 ms -456.6 us / -3.4% (better)
Windows MinGW ARM64 println 717824 B 0 B / +0.0% 23912 B 0 B / +0.0% 1.659 s -100.1 ms / -5.7% (better) 11.620 ms -1.194 ms / -9.3% (better)
Windows MinGW ARM64 println-lto 215552 B 0 B / +0.0% 21272 B 0 B / +0.0% 1.910 s -121.1 ms / -6.0% (better) 10.442 ms -1.829 ms / -14.9% (better)
Windows MSVC cprintf 900096 B 0 B / +0.0% 66019 B 0 B / +0.0% 1.350 s +49.53 ms / +3.8% (worse) 5.433 ms +1.97 ms / +56.9% (worse)
Windows MSVC cprintf-lto 291328 B 0 B / +0.0% 65971 B 0 B / +0.0% 1.271 s -287.3 ms / -18.4% (better) 3.349 ms -111 us / -3.2% (better)
Windows MSVC fmtprintf 5748736 B +1024 B / +0.01782% (worse) 700502 B +192 B / +0.02742% (worse) 5.592 s +704.2 ms / +14.4% (worse) 9.552 ms +735.8 us / +8.3% (worse)
Windows MSVC fmtprintf-lto 4470784 B 0 B / +0.0% 651590 B +192 B / +0.02948% (worse) 11.812 s +293.5 ms / +2.5% (worse) 8.830 ms -538.9 us / -5.8% (better)
Windows MSVC println 1023488 B 0 B / +0.0% 121062 B 0 B / +0.0% 1.280 s -60.15 ms / -4.5% (better) 7.424 ms -165.1 us / -2.2% (better)
Windows MSVC println-lto 529920 B 0 B / +0.0% 118598 B 0 B / +0.0% 1.529 s -114.6 ms / -7.0% (better) 7.118 ms -209.3 us / -2.9% (better)
Windows MSVC 386 cprintf 516608 B 0 B / +0.0% 3947 B 0 B / +0.0% 1.298 s -114.1 ms / -8.1% (better) 7.207 ms +1.453 ms / +25.3% (worse)
Windows MSVC 386 cprintf-lto 44032 B 0 B / +0.0% 3901 B 0 B / +0.0% 1.228 s -55.36 ms / -4.3% (better) 5.870 ms -388.3 us / -6.2% (better)
Windows MSVC 386 fmtprintf 4483584 B +512 B / +0.01142% (worse) 456108 B 0 B / +0.0% 5.175 s +214 ms / +4.3% (worse) 12.311 ms +838 us / +7.3% (worse)
Windows MSVC 386 fmtprintf-lto 3900928 B 0 B / +0.0% 426635 B 0 B / +0.0% 12.085 s +976.7 ms / +8.8% (worse) 12.887 ms +2.007 ms / +18.4% (worse)
Windows MSVC 386 println 571392 B 0 B / +0.0% 20356 B 0 B / +0.0% 1.223 s -19.54 ms / -1.6% (better) 9.464 ms -962 us / -9.2% (better)
Windows MSVC 386 println-lto 199168 B 0 B / +0.0% 18517 B 0 B / +0.0% 1.468 s -16.48 ms / -1.1% (better) 9.475 ms -503.9 us / -5.0% (better)
Windows MSVC ARM64 cprintf 666112 B 0 B / +0.0% 4240 B 0 B / +0.0% 1.446 s +41.62 ms / +3.0% (worse) 7.049 ms +109.5 us / +1.6% (worse)
Windows MSVC ARM64 cprintf-lto 48640 B 0 B / +0.0% 4140 B 0 B / +0.0% 1.454 s +45.77 ms / +3.2% (worse) 7.330 ms +456.6 us / +6.6% (worse)
Windows MSVC ARM64 fmtprintf 5350912 B +1024 B / +0.01914% (worse) 512836 B +160 B / +0.03121% (worse) 4.709 s +825.3 us / +0.01753% (worse) 14.139 ms +242.1 us / +1.7% (worse)
Windows MSVC ARM64 fmtprintf-lto 4322304 B +2560 B / +0.1% (worse) 479776 B +176 B / +0.0367% (worse) 11.118 s -140.7 ms / -1.2% (better) 14.197 ms -891.3 us / -5.9% (better)
Windows MSVC ARM64 println 719872 B 0 B / +0.0% 23956 B 0 B / +0.0% 1.434 s +46.17 ms / +3.3% (worse) 12.294 ms -95.9 us / -0.8% (better)
Windows MSVC ARM64 println-lto 220672 B 0 B / +0.0% 21444 B 0 B / +0.0% 1.626 s +4.921 ms / +0.3% (worse) 12.229 ms -139.3 us / -1.1% (better)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 14.630 ns/op +0.09 ns/op / +0.6% (worse)
Linux BenchmarkMergeCompilerFlags 198.500 ns/op +1.8 ns/op / +0.9% (worse)
Linux BenchmarkMergeLinkerFlags 126.400 ns/op -1.7 ns/op / -1.3% (better)
Linux BenchmarkChannelBuffered 54.520 ns/op -0.02 ns/op / -0.03667% (better)
Linux BenchmarkChannelHandoff 13194 ns/op -284 ns/op / -2.1% (better)
Linux BenchmarkDefer 45.500 ns/op -0.49 ns/op / -1.1% (better)
Linux BenchmarkDirectCall 1.167 ns/op 0 ns/op / +0.0%
Linux BenchmarkGlobalRead 1.163 ns/op -0.003 ns/op / -0.3% (better)
Linux BenchmarkGlobalWrite 7.759 ns/op +0.003 ns/op / +0.03868% (worse)
Linux BenchmarkGoroutine 24087 ns/op +119 ns/op / +0.5% (worse)
Linux BenchmarkInterfaceCall 5.831 ns/op +0.006 ns/op / +0.1% (worse)
Linux BenchmarkRuntimeGetG 2.999 ns/op -0.006 ns/op / -0.2% (better)
macOS BenchmarkLookupPCRandom 17.360 ns/op -2.68 ns/op / -13.4% (better)
macOS BenchmarkMergeCompilerFlags 156.300 ns/op +4.3 ns/op / +2.8% (worse)
macOS BenchmarkMergeLinkerFlags 90.260 ns/op -1.95 ns/op / -2.1% (better)
macOS BenchmarkChannelBuffered 39.110 ns/op +9.56 ns/op / +32.4% (worse)
macOS BenchmarkChannelHandoff 8669 ns/op -2047 ns/op / -19.1% (better)
macOS BenchmarkDefer 40.640 ns/op -2.67 ns/op / -6.2% (better)
macOS BenchmarkDirectCall 1.225 ns/op -0.093 ns/op / -7.1% (better)
macOS BenchmarkGlobalRead 2.071 ns/op -0.262 ns/op / -11.2% (better)
macOS BenchmarkGlobalWrite 1.246 ns/op -0.153 ns/op / -10.9% (better)
macOS BenchmarkGoroutine 39893 ns/op -10739 ns/op / -21.2% (better)
macOS BenchmarkInterfaceCall 4.324 ns/op -1.322 ns/op / -23.4% (better)
macOS BenchmarkRuntimeGetG 2.239 ns/op -0.577 ns/op / -20.5% (better)
Windows MinGW BenchmarkLookupPCRandom 13.140 ns/op +0.14 ns/op / +1.1% (worse)
Windows MinGW BenchmarkMergeCompilerFlags 667.200 ns/op +54.1 ns/op / +8.8% (worse)
Windows MinGW BenchmarkMergeLinkerFlags 582.200 ns/op +53.4 ns/op / +10.1% (worse)
Windows MinGW BenchmarkChannelBuffered 30.720 ns/op +0.63 ns/op / +2.1% (worse)
Windows MinGW BenchmarkChannelHandoff 916 ns/op -32.6 ns/op / -3.4% (better)
Windows MinGW BenchmarkDefer 54.320 ns/op -1.95 ns/op / -3.5% (better)
Windows MinGW BenchmarkDirectCall 1.548 ns/op +0.002 ns/op / +0.1% (worse)
Windows MinGW BenchmarkGlobalRead 1.550 ns/op -0.005 ns/op / -0.3% (better)
Windows MinGW BenchmarkGlobalWrite 2.470 ns/op 0 ns/op / +0.0%
Windows MinGW BenchmarkGoroutine 92313 ns/op -3136 ns/op / -3.3% (better)
Windows MinGW BenchmarkInterfaceCall 8.691 ns/op +0.002 ns/op / +0.02302% (worse)
Windows MinGW BenchmarkRuntimeGetG 2.170 ns/op -0.003 ns/op / -0.1% (better)
Windows MinGW 386 BenchmarkLookupPCRandom 21.570 ns/op 0 ns/op / +0.0%
Windows MinGW 386 BenchmarkMergeCompilerFlags 561 ns/op -1.2 ns/op / -0.2% (better)
Windows MinGW 386 BenchmarkMergeLinkerFlags 523.700 ns/op +5.2 ns/op / +1.0% (worse)
Windows MinGW 386 BenchmarkChannelBuffered 33.900 ns/op +0.03 ns/op / +0.1% (worse)
Windows MinGW 386 BenchmarkChannelHandoff 732.700 ns/op -34 ns/op / -4.4% (better)
Windows MinGW 386 BenchmarkDefer 34.190 ns/op -0.06 ns/op / -0.2% (better)
Windows MinGW 386 BenchmarkDirectCall 1.357 ns/op -0.001 ns/op / -0.1% (better)
Windows MinGW 386 BenchmarkGlobalRead 1.630 ns/op -0.001 ns/op / -0.1% (better)
Windows MinGW 386 BenchmarkGlobalWrite 6.982 ns/op +0.001 ns/op / +0.01432% (worse)
Windows MinGW 386 BenchmarkGoroutine 69746 ns/op +3544 ns/op / +5.4% (worse)
Windows MinGW 386 BenchmarkInterfaceCall 7.337 ns/op -0.001 ns/op / -0.01363% (better)
Windows MinGW 386 BenchmarkRuntimeGetG 1.903 ns/op +0.001 ns/op / +0.1% (worse)
Windows MinGW ARM64 BenchmarkLookupPCRandom 12.080 ns/op -0.07 ns/op / -0.6% (better)
Windows MinGW ARM64 BenchmarkMergeCompilerFlags 565.700 ns/op -2.5 ns/op / -0.4% (better)
Windows MinGW ARM64 BenchmarkMergeLinkerFlags 528.500 ns/op -2.1 ns/op / -0.4% (better)
Windows MinGW ARM64 BenchmarkChannelBuffered 37.610 ns/op -1.22 ns/op / -3.1% (better)
Windows MinGW ARM64 BenchmarkChannelHandoff 1741 ns/op -86 ns/op / -4.7% (better)
Windows MinGW ARM64 BenchmarkDefer 52.090 ns/op -2.92 ns/op / -5.3% (better)
Windows MinGW ARM64 BenchmarkDirectCall 0.737 ns/op -0.0007 ns/op / -0.1% (better)
Windows MinGW ARM64 BenchmarkGlobalRead 0.590 ns/op +0.0001 ns/op / +0.01696% (worse)
Windows MinGW ARM64 BenchmarkGlobalWrite 0.737 ns/op +0.0005 ns/op / +0.1% (worse)
Windows MinGW ARM64 BenchmarkGoroutine 64182 ns/op -376 ns/op / -0.6% (better)
Windows MinGW ARM64 BenchmarkInterfaceCall 4.234 ns/op +0.073 ns/op / +1.8% (worse)
Windows MinGW ARM64 BenchmarkRuntimeGetG 1.769 ns/op -0.002 ns/op / -0.1% (better)
Windows MSVC BenchmarkLookupPCRandom 13.050 ns/op -0.05 ns/op / -0.4% (better)
Windows MSVC BenchmarkMergeCompilerFlags 616.200 ns/op -6 ns/op / -1.0% (better)
Windows MSVC BenchmarkMergeLinkerFlags 538 ns/op -14.9 ns/op / -2.7% (better)
Windows MSVC BenchmarkChannelBuffered 30.090 ns/op +0.05 ns/op / +0.2% (worse)
Windows MSVC BenchmarkChannelHandoff 1108 ns/op +12 ns/op / +1.1% (worse)
Windows MSVC BenchmarkDefer 56.860 ns/op +0.91 ns/op / +1.6% (worse)
Windows MSVC BenchmarkDirectCall 1.549 ns/op +0.001 ns/op / +0.1% (worse)
Windows MSVC BenchmarkGlobalRead 1.860 ns/op 0 ns/op / +0.0%
Windows MSVC BenchmarkGlobalWrite 2.476 ns/op +0.006 ns/op / +0.2% (worse)
Windows MSVC BenchmarkGoroutine 89611 ns/op +525 ns/op / +0.6% (worse)
Windows MSVC BenchmarkInterfaceCall 8.700 ns/op +0.021 ns/op / +0.2% (worse)
Windows MSVC BenchmarkRuntimeGetG 2.481 ns/op -0.002 ns/op / -0.1% (better)
Windows MSVC 386 BenchmarkLookupPCRandom 26.750 ns/op +0.23 ns/op / +0.9% (worse)
Windows MSVC 386 BenchmarkMergeCompilerFlags 773 ns/op +52.1 ns/op / +7.2% (worse)
Windows MSVC 386 BenchmarkMergeLinkerFlags 705.200 ns/op +47.9 ns/op / +7.3% (worse)
Windows MSVC 386 BenchmarkChannelBuffered 39.090 ns/op +0.09 ns/op / +0.2% (worse)
Windows MSVC 386 BenchmarkChannelHandoff 924.500 ns/op -41.4 ns/op / -4.3% (better)
Windows MSVC 386 BenchmarkDefer 49.400 ns/op +1.73 ns/op / +3.6% (worse)
Windows MSVC 386 BenchmarkDirectCall 1.554 ns/op +0.004 ns/op / +0.3% (worse)
Windows MSVC 386 BenchmarkGlobalRead 1.862 ns/op +0.002 ns/op / +0.1% (worse)
Windows MSVC 386 BenchmarkGlobalWrite 7.790 ns/op +0.009 ns/op / +0.1% (worse)
Windows MSVC 386 BenchmarkGoroutine 112407 ns/op +3479 ns/op / +3.2% (worse)
Windows MSVC 386 BenchmarkInterfaceCall 8.432 ns/op +0.069 ns/op / +0.8% (worse)
Windows MSVC 386 BenchmarkRuntimeGetG 2.487 ns/op +0.002 ns/op / +0.1% (worse)
Windows MSVC ARM64 BenchmarkLookupPCRandom 12.130 ns/op +0.02 ns/op / +0.2% (worse)
Windows MSVC ARM64 BenchmarkMergeCompilerFlags 576.500 ns/op -12.1 ns/op / -2.1% (better)
Windows MSVC ARM64 BenchmarkMergeLinkerFlags 540.400 ns/op -8.6 ns/op / -1.6% (better)
Windows MSVC ARM64 BenchmarkChannelBuffered 39.490 ns/op +0.6 ns/op / +1.5% (worse)
Windows MSVC ARM64 BenchmarkChannelHandoff 3453 ns/op -240 ns/op / -6.5% (better)
Windows MSVC ARM64 BenchmarkDefer 61.500 ns/op -3.5 ns/op / -5.4% (better)
Windows MSVC ARM64 BenchmarkDirectCall 0.663 ns/op +0.0001 ns/op / +0.01507% (worse)
Windows MSVC ARM64 BenchmarkGlobalRead 0.590 ns/op +0.0001 ns/op / +0.01696% (worse)
Windows MSVC ARM64 BenchmarkGlobalWrite 3.796 ns/op 0 ns/op / +0.0%
Windows MSVC ARM64 BenchmarkGoroutine 60362 ns/op -1848 ns/op / -3.0% (better)
Windows MSVC ARM64 BenchmarkInterfaceCall 4.149 ns/op +0.001 ns/op / +0.02411% (worse)
Windows MSVC ARM64 BenchmarkRuntimeGetG 1.771 ns/op -0.032 ns/op / -1.8% (better)
Timer runtime benchmarks
Platform Operation and runtime ns/op vs base
Linux AfterFuncZeroDelivery/Go 900.200 ns/op -8.4 ns/op / -0.9% (better)
Linux AfterFuncZeroDelivery/LLGo 35800 ns/op -2932 ns/op / -7.6% (better)
Linux CreateStop/Go 289.700 ns/op +2.9 ns/op / +1.0% (worse)
Linux CreateStop/LLGo 1526 ns/op -173 ns/op / -10.2% (better)
Linux RearmStopped/Go 116 ns/op +0.1 ns/op / +0.1% (worse)
Linux RearmStopped/LLGo 1325 ns/op -222 ns/op / -14.4% (better)
Linux ResetActive/Go 68.730 ns/op +0.2 ns/op / +0.3% (worse)
Linux ResetActive/LLGo 809.800 ns/op +114.6 ns/op / +16.5% (worse)
Linux ResetHeap1024/Go 67.100 ns/op +0.03 ns/op / +0.04473% (worse)
Linux ResetHeap1024/LLGo 160.300 ns/op -10.4 ns/op / -6.1% (better)
macOS AfterFuncZeroDelivery/Go 869 ns/op +55.6 ns/op / +6.8% (worse)
macOS AfterFuncZeroDelivery/LLGo 139314 ns/op +27407 ns/op / +24.5% (worse)
macOS CreateStop/Go 236.400 ns/op +56 ns/op / +31.0% (worse)
macOS CreateStop/LLGo 903.300 ns/op +295.3 ns/op / +48.6% (worse)
macOS RearmStopped/Go 86.220 ns/op +2.46 ns/op / +2.9% (worse)
macOS RearmStopped/LLGo 439.300 ns/op -158.8 ns/op / -26.6% (better)
macOS ResetActive/Go 59.330 ns/op +2.62 ns/op / +4.6% (worse)
macOS ResetActive/LLGo 218.400 ns/op -37.4 ns/op / -14.6% (better)
macOS ResetHeap1024/Go 70 ns/op +11.55 ns/op / +19.8% (worse)
macOS ResetHeap1024/LLGo 122.500 ns/op -14.5 ns/op / -10.6% (better)
Windows MinGW AfterFuncZeroDelivery/Go 556.400 ns/op -10.6 ns/op / -1.9% (better)
Windows MinGW AfterFuncZeroDelivery/LLGo 179676 ns/op -2362 ns/op / -1.3% (better)
Windows MinGW CreateStop/Go 115.800 ns/op -9 ns/op / -7.2% (better)
Windows MinGW CreateStop/LLGo 401.500 ns/op -17.5 ns/op / -4.2% (better)
Windows MinGW RearmStopped/Go 31.510 ns/op +0.05 ns/op / +0.2% (worse)
Windows MinGW RearmStopped/LLGo 232.900 ns/op -3.4 ns/op / -1.4% (better)
Windows MinGW ResetActive/Go 20.070 ns/op +0.03 ns/op / +0.1% (worse)
Windows MinGW ResetActive/LLGo 167.900 ns/op +13 ns/op / +8.4% (worse)
Windows MinGW ResetHeap1024/Go 20.500 ns/op -0.12 ns/op / -0.6% (better)
Windows MinGW ResetHeap1024/LLGo 118.500 ns/op -0.4 ns/op / -0.3% (better)
Windows MinGW 386 AfterFuncZeroDelivery/Go 766.200 ns/op -11.9 ns/op / -1.5% (better)
Windows MinGW 386 AfterFuncZeroDelivery/LLGo 126344 ns/op +214 ns/op / +0.2% (worse)
Windows MinGW 386 CreateStop/Go 167 ns/op +0.5 ns/op / +0.3% (worse)
Windows MinGW 386 CreateStop/LLGo 376.500 ns/op +23.8 ns/op / +6.7% (worse)
Windows MinGW 386 RearmStopped/Go 56.610 ns/op -0.15 ns/op / -0.3% (better)
Windows MinGW 386 RearmStopped/LLGo 259.400 ns/op +19.7 ns/op / +8.2% (worse)
Windows MinGW 386 ResetActive/Go 32.560 ns/op -0.04 ns/op / -0.1% (better)
Windows MinGW 386 ResetActive/LLGo 802.600 ns/op -1.9 ns/op / -0.2% (better)
Windows MinGW 386 ResetHeap1024/Go 32.770 ns/op -0.07 ns/op / -0.2% (better)
Windows MinGW 386 ResetHeap1024/LLGo 140.200 ns/op +0.2 ns/op / +0.1% (worse)
Windows MinGW ARM64 AfterFuncZeroDelivery/Go 666.900 ns/op -14.9 ns/op / -2.2% (better)
Windows MinGW ARM64 AfterFuncZeroDelivery/LLGo 147041 ns/op -896 ns/op / -0.6% (better)
Windows MinGW ARM64 CreateStop/Go 207.300 ns/op +5.8 ns/op / +2.9% (worse)
Windows MinGW ARM64 CreateStop/LLGo 350.300 ns/op +19.9 ns/op / +6.0% (worse)
Windows MinGW ARM64 RearmStopped/Go 70.600 ns/op 0 ns/op / +0.0%
Windows MinGW ARM64 RearmStopped/LLGo 218.900 ns/op +4.2 ns/op / +2.0% (worse)
Windows MinGW ARM64 ResetActive/Go 31.050 ns/op +0.02 ns/op / +0.1% (worse)
Windows MinGW ARM64 ResetActive/LLGo 122.100 ns/op +9.1 ns/op / +8.1% (worse)
Windows MinGW ARM64 ResetHeap1024/Go 31.190 ns/op +0.07 ns/op / +0.2% (worse)
Windows MinGW ARM64 ResetHeap1024/LLGo 111.300 ns/op -1.6 ns/op / -1.4% (better)
Windows MSVC AfterFuncZeroDelivery/Go 562.400 ns/op -17.2 ns/op / -3.0% (better)
Windows MSVC AfterFuncZeroDelivery/LLGo 177089 ns/op +2512 ns/op / +1.4% (worse)
Windows MSVC CreateStop/Go 116.500 ns/op +1 ns/op / +0.9% (worse)
Windows MSVC CreateStop/LLGo 382.200 ns/op +5 ns/op / +1.3% (worse)
Windows MSVC RearmStopped/Go 31.570 ns/op +0.18 ns/op / +0.6% (worse)
Windows MSVC RearmStopped/LLGo 240.700 ns/op +18.1 ns/op / +8.1% (worse)
Windows MSVC ResetActive/Go 20.070 ns/op +0.01 ns/op / +0.04985% (worse)
Windows MSVC ResetActive/LLGo 152.200 ns/op -3 ns/op / -1.9% (better)
Windows MSVC ResetHeap1024/Go 20.470 ns/op +0.1 ns/op / +0.5% (worse)
Windows MSVC ResetHeap1024/LLGo 118.200 ns/op +0.7 ns/op / +0.6% (worse)
Windows MSVC 386 AfterFuncZeroDelivery/Go 963.100 ns/op +16 ns/op / +1.7% (worse)
Windows MSVC 386 AfterFuncZeroDelivery/LLGo 204659 ns/op +2721 ns/op / +1.3% (worse)
Windows MSVC 386 CreateStop/Go 202.400 ns/op +10.7 ns/op / +5.6% (worse)
Windows MSVC 386 CreateStop/LLGo 445.600 ns/op +0.1 ns/op / +0.02245% (worse)
Windows MSVC 386 RearmStopped/Go 63.580 ns/op +0.27 ns/op / +0.4% (worse)
Windows MSVC 386 RearmStopped/LLGo 306.400 ns/op +10.6 ns/op / +3.6% (worse)
Windows MSVC 386 ResetActive/Go 39.170 ns/op +0.11 ns/op / +0.3% (worse)
Windows MSVC 386 ResetActive/LLGo 934.300 ns/op +642.8 ns/op / +220.5% (worse)
Windows MSVC 386 ResetHeap1024/Go 39.770 ns/op +0.38 ns/op / +1.0% (worse)
Windows MSVC 386 ResetHeap1024/LLGo 164.900 ns/op +5.8 ns/op / +3.6% (worse)
Windows MSVC ARM64 AfterFuncZeroDelivery/Go 667.100 ns/op -3.9 ns/op / -0.6% (better)
Windows MSVC ARM64 AfterFuncZeroDelivery/LLGo 150266 ns/op +5472 ns/op / +3.8% (worse)
Windows MSVC ARM64 CreateStop/Go 199.800 ns/op -4.1 ns/op / -2.0% (better)
Windows MSVC ARM64 CreateStop/LLGo 334.800 ns/op +11.4 ns/op / +3.5% (worse)
Windows MSVC ARM64 RearmStopped/Go 70.610 ns/op -0.07 ns/op / -0.1% (better)
Windows MSVC ARM64 RearmStopped/LLGo 223.800 ns/op -3.5 ns/op / -1.5% (better)
Windows MSVC ARM64 ResetActive/Go 31.010 ns/op +0.02 ns/op / +0.1% (worse)
Windows MSVC ARM64 ResetActive/LLGo 105.500 ns/op -5.3 ns/op / -4.8% (better)
Windows MSVC ARM64 ResetHeap1024/Go 31.060 ns/op +0.03 ns/op / +0.1% (worse)
Windows MSVC ARM64 ResetHeap1024/LLGo 116 ns/op -2 ns/op / -1.7% (better)

Compared with 686181ad6f7b measured in the same runner job.

@cpunion
cpunion merged commit cb8de09 into xgo-dev:main Oct 11, 2026
93 checks passed
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.

2 participants