Skip to content

[depends on #2327] target: add COFF linking and Go architecture tuning - #2335

Open
cpunion wants to merge 27 commits into
xgo-dev:mainfrom
cpunion:codex/windows-coff-linker-flags-20260815
Open

[depends on #2327] target: add COFF linking and Go architecture tuning#2335
cpunion wants to merge 27 commits into
xgo-dev:mainfrom
cpunion:codex/windows-coff-linker-flags-20260815

Conversation

@cpunion

@cpunion cpunion commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Part of #2325, the MSVC-first Windows support proposal.

Depends on #2327, which establishes the MSVC target triples and native Windows host smoke foundation.

Summary

  • translate native Windows linker diagnostics and ICF controls to COFF/lld-link syntax (/errorlimit:0, /opt:noicf)
  • map LLGo optimization levels to lld-link ThinLTO levels (/opt:lldlto=0..3), enable per-function/data sections, and use /opt:ref
  • parse and normalize GO386, GOAMD64, GOARM, and GOARM64 with Go-compatible defaults, valid values, extension ordering, and diagnostics
  • carry the normalized architecture setting through typed build configuration, Go package selection, child Go commands, native clang/linker triples, Plan 9 assembly modules, and LLVM target CPU/features
  • separate package-cache fingerprints by the active architecture setting
  • keep named -target configurations unchanged

LLVM mapping

  • GO386=sse2 preserves the existing Pentium 4/SSE2 baseline; softfloat enables LLVM software floating point and disables SSE/x87 floating-point instructions
  • GOAMD64=v1..v4 maps to LLVM x86-64, x86-64-v2, x86-64-v3, and x86-64-v4
  • GOARM=5/6/7 selects the matching ARM ISA; ,softfloat and ,hardfloat select Go-compatible floating-point behavior, including ARMv5's soft-float default
  • GOARM64=v8.0..v9.5 maps to the corresponding AArch64 architecture feature; lse and crypto are propagated, with LSE enabled automatically from v8.1 as in Go

The architecture environment parsing is shared by native operating systems because these are Go architecture semantics, while the COFF linker translation remains Windows-specific.

Scope

This PR covers native target configuration and clang/lld flag generation. It does not add the Windows runtime, SDK discovery, FFI, C ABI, GC, goroutine, panic/recover, or debug backends.

Because this is stacked on #2327, GitHub will temporarily show the foundation changes in this PR diff until #2327 merges.

Validation

  • go test -cover ./internal/meta ./internal/goarch ./internal/xtool/llvm (internal/goarch: 98.3% statement coverage)
  • go test ./ssa -count=1 (full suite, 162 seconds on the current head)
  • go test ./internal/build -count=1 (full suite, 339 seconds on the current head)
  • focused internal/build tests cover explicit/default/environment values, child-command environment, invalid values, and cache separation for all four architecture variables
  • focused ssa tests validate LLVM CPU/features and emit real objects for 386 soft-float, amd64 v4, ARM soft/hard-float, and ARM64 v9.5+crypto target machines
  • focused crosscompile and Plan 9 assembly tests verify GOARM reaches every native triple consumer
  • go vet ./internal/meta ./internal/goarch ./internal/xtool/llvm ./internal/crosscompile ./internal/plan9asm ./internal/build ./ssa
  • Windows host smoke passed on the current head with the pinned LLVM 19.1.7 toolchain
  • earlier linker validation produced PE32 i686 and PE32+ x86-64/AArch64 binaries, including ThinLTO with /opt:lldlto=2

The PR is Ready for review. FennoAI found no blocking design issue; all review follow-ups are addressed on the current head.

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

a89914749179 | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Build vs base Run vs base
Linux cprintf 19248 B +0.0% 327.037 ms +0.9% (worse) 1.276 ms +0.1% (worse)
Linux fmtprintf 1879904 B +0.0% 2.678 s +2.9% (worse) 3.248 ms -2.0% (better)
Linux println 68616 B +0.0% 342.470 ms +4.6% (worse) 1.607 ms -2.0% (better)
macOS cprintf 84624 B +0.0% 436.693 ms -5.6% (better) 3.709 ms +9.5% (worse)
macOS fmtprintf 1891424 B +0.0% 2.843 s -14.6% (better) 14.470 ms +17.6% (worse)
macOS println 121168 B +0.0% 406.845 ms -5.6% (better) 4.733 ms +12.7% (worse)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 13.350 ns/op +0.1% (worse)
Linux BenchmarkMergeCompilerFlags 151.200 ns/op -0.3% (better)
Linux BenchmarkMergeLinkerFlags 94.410 ns/op -0.5% (better)
Linux BenchmarkChannelBuffered 35.220 ns/op -0.2% (better)
Linux BenchmarkChannelHandoff 28326 ns/op -3.5% (better)
Linux BenchmarkDefer 45.590 ns/op -5.8% (better)
Linux BenchmarkDirectCall 1.557 ns/op -0.1% (better)
Linux BenchmarkGlobalRead 1.558 ns/op +0.1% (worse)
Linux BenchmarkGlobalWrite 2.482 ns/op +0.1% (worse)
Linux BenchmarkGoroutine 38945 ns/op +24.1% (worse)
Linux BenchmarkInterfaceCall 8.102 ns/op +0.0% (worse)
Linux BenchmarkRuntimeGetG 2.183 ns/op +0.1% (worse)
macOS BenchmarkLookupPCRandom 11.960 ns/op -1.6% (better)
macOS BenchmarkMergeCompilerFlags 137.500 ns/op +30.2% (worse)
macOS BenchmarkMergeLinkerFlags 80.640 ns/op -1.3% (better)
macOS BenchmarkChannelBuffered 24.080 ns/op -15.6% (better)
macOS BenchmarkChannelHandoff 7333 ns/op -18.8% (better)
macOS BenchmarkDefer 30.300 ns/op -12.3% (better)
macOS BenchmarkDirectCall 1.039 ns/op -6.6% (better)
macOS BenchmarkGlobalRead 1.142 ns/op +6.4% (worse)
macOS BenchmarkGlobalWrite 1.036 ns/op -20.5% (better)
macOS BenchmarkGoroutine 29112 ns/op -49.7% (better)
macOS BenchmarkInterfaceCall 5.536 ns/op -12.6% (better)
macOS BenchmarkRuntimeGetG 2.150 ns/op -14.8% (better)

Compared with 7f954d6182d1 measured in the same runner job.

@codecov

codecov Bot commented Aug 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.37445% with 21 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/crosscompile/crosscompile.go 84.15% 14 Missing and 2 partials ⚠️
internal/meta/meta.go 96.39% 2 Missing and 2 partials ⚠️
internal/crosscompile/fetch.go 88.88% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@cpunion cpunion changed the title [depends on #2327] crosscompile: use COFF linker flags on Windows [depends on #2327] target: add COFF linking and Go architecture tuning Aug 15, 2026
@cpunion
cpunion marked this pull request as ready for review August 15, 2026 11:29
@cpunion

cpunion commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

@fennoai please review the current head, including the Go-compatible GO386/GOAMD64/GOARM64 parsing, LLVM CPU/feature mapping, build-cache isolation, and Windows COFF linker behavior.

@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 COFF linker flags & GOARCH build config

Solid, well-tested PR. The platform split for file locking (filelock_*.go) and mmap (mmap_*.go) is clean, the new internal/goarch package nicely separates Go semantics from LLVM mapping, and the test coverage across the new resolvers, triple builder, and target features is thorough. I verified the areas most prone to subtle bugs and they are correct:

  • goarchEnv emitting empty GO386=/GOAMD64=/GOARM64= entries — intended and correct. withEnv treats each entry's key as an override, so the empty entries deliberately clear inherited parent-env arch values for child go invocations.
  • releaseLock no longer removing the lock file — correct improvement; unlinking a lock file while another holder has it open by inode is a classic mutual-exclusion race. Retention is the standard flock pattern and the accumulation is bounded to one file per fixed path.
  • meta.Close clearing mmap/raw — double-Close is now a safe no-op (covered by the new test).
  • Target.Spec() re-resolving arch config — build.go passes already-normalized values and the resolvers are idempotent on their canonical output, so the double-resolution is safe.
  • Windows mmap / unsafe.Slice — the new fi.Size() bound in Open prevents negative/overflow lengths reaching unsafe.Slice, the CreateFileMapping handle is released via defer, and unmapFile guards len(raw)==0. No leak.

A few minor, non-blocking items are inline below.

Behavioral note (intentional, but reviewer-visible): this changes the default GOOS=windows target ABI from GNU/MinGW (*-windows-gnu) to MSVC (*-pc-windows-msvc). That is a meaningful default change for existing Windows users — worth calling out in release notes so anyone relying on the MinGW runtime is aware.

Additional findings

  • ssa/target.go:160: [P3] Dead llvmarch assignments after triple refactor: After moving triple construction into GetTargetTripleWithGOARM (line 161), the llvmarch values computed here for 386/amd64/arm64/wasm/default are dead — only the arm branch's llvmarch is still consumed (in the switch llvmarch at the arm feature block below). Consider reducing this first switch to compute llvmarch only for the arm case (or inline it into the arm feature switch), so it no longer misleadingly appears to feed the triple.

Comment thread internal/meta/meta.go Outdated
Comment thread internal/xtool/llvm/llvm.go
Comment thread .github/workflows/windows.yml
@fennoai

fennoai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

FennoAI is processing your request, please wait a moment...

@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: COFF linking and Go architecture tuning

Reviewed the four requested areas at head 6e70577. This PR is stacked on #2327, so I scoped the review to the architecture-tuning and COFF-linker changes and treated the inherited crosscompile/meta foundation as context.

Overall: solid, well-tested, and no blocking issues. The four areas verified as correct:

  • GO386/GOAMD64/GOARM64 parsing (internal/goarch/config.go) — strict allow-lists, Go-compatible defaults (sse2/v1/v8.0), extension ordering, LSE-forced-from-v8.1, and Go-matching error messages. Round-trips stably through ARM64.String().
  • LLVM CPU/feature mapping (ssa/target.go) — GO386=softfloat disables SSE/x87, GOAMD64 vN maps to x86-64[-vN], GOARM64 version→arch feature with +lse/+crypto and -fmv on non-darwin. Correct.
  • Build-cache isolation (fingerprint.go + collect.go) — distinct GO386/GOAMD64/GOARM64 produce distinct fingerprints; normalized empty↔default configs still hit cache; inactive-arch fields are zeroed so no cross-arch leakage. Verified against goarch_test.go.
  • Windows COFF linker (crosscompile.go) — /errorlimit:0, /opt:noicf (justified by Go's distinct-PC requirement, since lld-link's /opt:ref enables ICF by default), /opt:lldlto=0..3, and /opt:ref with per-function/data sections. coffLTOLevel correctly clamps to lld-link's 0–3 range.

No security findings. Env values are validated before reaching LLVM feature/linker strings; linker flags are built from constants and passed as argv (no shell); the Windows CI workflow interpolates no untrusted ${{ }} into run: blocks.

The inline comments below are minor maintainability/documentation notes, not defects.

Informational (no reliable inline location)

  • internal/xtool/llvm/llvm.go — the new goarm parameter of GetTargetTripleWithGOARM is only wired through ssa/target.go. Production callers crosscompile.go:273 and plan9asm/translate.go:108 still call GetTargetTriple (goarm=""→armv7), so GOARM triple tuning is unreachable there. This does not affect the arm64/386/amd64 tuning that is this PR's focus (those apply via Spec() CPU/features), but worth noting for future GOARM work.

Comment thread internal/goarch/config.go
Comment thread ssa/target.go Outdated
Comment thread internal/crosscompile/crosscompile.go
Comment thread ssa/target.go Outdated
@cpunion
cpunion force-pushed the codex/windows-coff-linker-flags-20260815 branch from 6e70577 to 9ba511d Compare August 15, 2026 11:46
@cpunion

cpunion commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the remaining FennoAI informational note in 9a11290: the resolved GOARM value now reaches the native clang/linker triple and Plan 9 assembly module triple, while named targets keep their configured triple. Focused crosscompile, Plan 9 assembly, build, SSA, and vet checks pass. I will request a final re-review after the current CI and Codecov uploads finish.

@cpunion

cpunion commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator Author

@fennoai All checks now pass on the current head, including Ubuntu/macOS tests and coverage, and the prior review threads have been resolved. Could you please take a final review?

@fennoai

fennoai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

FennoAI is processing your request, please wait a moment...

@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.

Final review: COFF linking and Go architecture tuning

Re-reviewed at head ad3c7dd, focusing on the delta since my prior review (6e70577): 6 new commits touching 18 files.

All prior review findings were addressed:

  • Dual-return (default + error) contract is now documented on Resolve386/ResolveAMD64/ParseARM/ParseARM64 (internal/goarch/config.go).
  • The swallowed _ errors in Spec() now carry an explaining comment about relying on Build-time validation plus the Go-default fallback for hand-built Targets (ssa/target.go).
  • The repeated reset-then-resolve boilerplate is extracted into the goArchitectureSetting helper.
  • My informational note about GOARM triple tuning being unreachable is now fully resolved: GOARM is threaded through config resolution, fingerprint, child env, crosscompile.UseWithGOARM, and plan9asm translate options.

New changes reviewed and verified sound:

  • ParseARM (GOARM) — Go-compatible: ARMv5 defaults to softfloat, v6/v7 to hardfloat, ,softfloat/,hardfloat suffixes honored, GOARM=5,hardfloat correctly forces VFPv2 features. Covered by TestParseARM (incl. the invalid 7,softfloat,hardfloat case) and TestTranslateGOARMTargetTriple.
  • meta: reject malformed package headers (internal/meta/meta.go) — solid hardening. Section-offset decoding now validates monotonicity (off >= prev), bounds (off <= len(raw)), 4-byte alignment, a minimum file size (< headerSize), and a minimum symbols-section size before reading nsyms. I confirmed these checks are consistent with the writer in builder.go (monotonic non-decreasing offsets from headerSize, section sizes are 4-byte multiples). This closes potential out-of-bounds reads on corrupt/truncated .meta files.
  • COFF linker — the ELF-vs-COFF LTO-level asymmetry I noted now has the clarifying comment near the --lto branch.

Verification (this environment):

  • go build, go test, and go vet pass for the pure-Go packages internal/goarch and internal/meta (the packages carrying the genuinely new logic).
  • LLVM-cgo packages (ssa, crosscompile, anything importing xgo-dev/llvm) could not be built here because the LLVM C headers are not installed in this sandbox — an environment limitation, not a code issue. Per the PR, Ubuntu/macOS CI (tests + coverage) is green on this head.

Conclusion: LGTM. No blocking or non-blocking findings. The architecture-tuning parsing, LLVM CPU/feature mapping, build-cache isolation, GOARM propagation, and Windows COFF linker behavior are correct and well-tested; the meta-header hardening is a welcome robustness improvement.

Note: I cannot approve PRs — this is a non-blocking review. A human maintainer's approval is still required to merge.

@cpunion
cpunion force-pushed the codex/windows-coff-linker-flags-20260815 branch 2 times, most recently from a43d0d5 to 801303c Compare August 15, 2026 23:47
@cpunion

cpunion commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

The rebased head 801303cd2 is now fully green across the complete CI matrix, including LLVM 19 Windows host smoke, Go 1.26.5 and Go 1.24.2 compatibility tests, Linux/macOS coverage, benchmarks, and release artifacts. Codecov reports 99.69% patch coverage.

The delta since the prior reviewed head is limited to the dependency rebase plus aligning the Plan 9 assembly GOARM=6,softfloat expectation with the shared Go-compatible triple resolver.

@fennoai, could you please re-review the current head?

@fennoai

fennoai Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

FennoAI is processing your request, please wait a moment...

@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.

Re-review: COFF linking and Go architecture tuning (rebased head 801303c)

Re-reviewed at 801303c. I computed the true delta against my last reviewed head (ad3c7dd — still reachable in the object store) rather than trusting branch names.

Scope note: the delta is a bit larger than "rebase + one Plan 9 test alignment." Alongside the dependency rebase and the GOARM=6,softfloat triple test fix, there are several substantive code changes, which I reviewed fresh:

New changes reviewed and verified sound:

  • COMDAT for shared COFF definitions (ssa/decl.go, ssa/abitype.go, ssa/python.go) — new setODRLinkage helper centralizes weak/linkonce linkage and, on GOOS=windows, attaches a COMDAT with AnyComdatSelectionKind so lld-link can merge the identically-named ODR definitions that COFF would otherwise reject. All prior SetLinkage(WeakODR/LinkOnce…) call sites are routed through it. This is COMDAT deduplication of same-named symbols and does not conflict with the earlier /opt:noicf decision (which prevents folding of distinct functions to preserve Go's distinct-PC requirement). Covered by TestWindowsODRDefinitionsUseCOMDAT and TestUnixODRDefinitionsDoNotGainCOMDAT.
  • crosscompile refactor (internal/crosscompile/crosscompile.go) — extracted configureNativeTargetFlags and nativeSectionFlags(goos); native-host behavior is preserved (Unix --gc-sections/-latomic/-lpthread/-ldl reproduced faithfully), and Windows now uses the same explicit MSVC/COFF configuration for cross builds via nativeHost || goos == "windows". The host-only clang-lib/rpath/macOS-sysroot block stays correctly gated behind if nativeHost.
  • Windows debug linker policyDebugInfoPolicy gains PreserveLinkFlags (/debug:dwarf) vs OmitLinkFlags (/debug:none); debugInfoLinkerArgs now emits preserve-or-omit consistent with the compile-time policy, since lld-link discards DWARF unless preservation is requested. Sound.
  • GOARM soft-float triple (internal/xtool/llvm/llvm.go) — ARM triples now emit -gnueabi for softfloat and -gnueabihf otherwise; the Plan 9 test (801303c) is aligned to armv6-unknown-linux-gnueabi accordingly. Correct.
  • meta zero-copy cache bounds validation (internal/meta/meta.go) — a thorough validation pass added at the Open() trust boundary for mmap'd, file-controlled bytes: uint32 size bounds, per-section CSR header/data sizes and record alignment, monotonic offset arrays starting at 0 and covering exactly nrecords, and all name references bounded within the string table. The packageMetaView/validate split lets the builder skip validation for bytes it just wrote while Open validates untrusted input — the right boundary. Fully covered by new negative test cases (invalid symbols size, truncated CSR header, count mismatch, descending offsets, offset-past-data, and invalid name ranges in all four name-bearing sections).
  • ParseARM64 goto→continue cleanup — behavior-equivalent.

Prior findings: all remain resolved (documented resolver contracts, Spec() fallback comment, goArchitectureSetting helper, GOARM propagation).

Verification (this environment):

  • go build, go test, and go vet pass for internal/goarch and internal/meta (the pure-Go packages carrying the new parsing and hardening logic).
  • LLVM-cgo packages (ssa, crosscompile, xtool/llvm) can't be built here (LLVM C headers absent in this sandbox — environment limitation only). Per the PR, the full CI matrix is green on this head: LLVM 19 Windows host smoke, Go 1.26.5 / 1.24.2, Linux/macOS coverage, benchmarks, and release artifacts, with 99.69% patch coverage.

Conclusion: LGTM. No blocking or non-blocking findings. The COMDAT emission, crosscompile/debug refactor, GOARM soft-float triple handling, and the meta bounds-validation hardening are all correct and well-tested.

Note: I cannot approve PRs — this is a non-blocking review; a human maintainer's approval is still required to merge.

@cpunion
cpunion force-pushed the codex/windows-coff-linker-flags-20260815 branch from 801303c to 4711da5 Compare August 16, 2026 07:53
@cpunion
cpunion force-pushed the codex/windows-coff-linker-flags-20260815 branch from 4711da5 to a899147 Compare August 16, 2026 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant