[depends on #2335] cabi: lower aggregates for Windows MSVC targets - #2336
[depends on #2335] cabi: lower aggregates for Windows MSVC targets#2336cpunion wants to merge 40 commits into
Conversation
LLGo baseline benchmarks
Program measurements
Core language and compiler benchmarks
Compared with |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
@fennoai All GitHub checks pass, and the current head has also passed the focused and complete internal/cabi suites natively on Windows 11 ARM64. Could you please review this PR? |
There was a problem hiding this comment.
Review: Windows MSVC C ABI support
Overall this is a high-quality, well-tested PR. The MSVC C ABI lowering (internal/cabi/arch_msvc.go), the per-index attribute-slice refactor, the platform-split filelock_*/mmap_* files, and the internal/meta bounds-validation hardening are clean, idiomatic, and thoroughly covered by tests (VerifyModule-backed lowering tests, GOARCH resolution, LTO mapping). No correctness bugs were found in the changed logic.
The most important findings are in the archive-extraction path that this PR's cross-compilation feature relies on. That code is pre-existing (not in this diff), but Windows toolchain support makes the ZIP path a first-class, reachable download surface — so it is worth addressing here. Inline comments cover the two in-diff refinements.
Security (pre-existing code, no diff line — expanded surface)
- Zip-Slip / path traversal in
extractZip—internal/crosscompile/fetch.go:287.filepath.Join(dest, file.Name)is used with no containment check beforeos.Create(line 299). A malicious/tampered archive entry named..\..\evilescapesdestand writes arbitrary files. The siblingextractTarGz(line 248) already performs thestrings.HasPrefix(target, filepath.Clean(dest)+os.PathSeparator)guard — apply the same toextractZip, and skip/validate symlink entries. Since Windows toolchains ship as.zip, this path becomes reachable with this PR. - No integrity verification of downloaded toolchains —
internal/crosscompile/fetch.go:209(downloadFile), URLs atcrosscompile.go:69. Archives are fetched and extracted with no SHA-256 pinning or signature check; extracted contents are executed as part of the compiler/linker toolchain. A tampered release asset or a redirect/MITM on the download yields code execution at build time. Consider pinning per-URL checksums and rejecting non-HTTPS / cross-host redirects. - Untrusted file-mode bits on extraction —
internal/crosscompile/fetch.go:260.os.OpenFile(target, ..., os.FileMode(header.Mode))applies the archive's mode verbatim, allowing setuid/setgid/world-writable files. Mask withheader.Mode & 0o777and strip setuid/setgid.
Minor (non-blocking)
internal/meta/mmap_windows.go/mmap_unix.go:mapFiletrusts the caller-suppliedsizeforunsafe.Slice; the bounds check lives only inOpen. Fine today (single caller), but a short "caller validates size" contract comment onmapFilewould guard against future misuse.internal/crosscompile/fetch_test.go:148:active.Add(-1)runs beforereleaseLock, decrementing the critical-section counter while the lock is still held — weakens the overlap-detection invariant the test asserts. Test-only.
Positives worth calling out: the releaseLock fix (no longer deleting the lock file, with the explanatory comment on the lock-recycling hazard) is correct; the meta.go offset/order/alignment validation loop and idempotent Close() are real robustness wins; the map[int]llvm.Attribute → map[int][]llvm.Attribute generalization was applied consistently across all four consumers.
Review completeI reviewed the locked range Summary: The x64 and Windows ARM64 aggregate lowering look correct, the One finding (P1, Windows x86 / MSVC only): Note: LLVM headers aren't available in this sandbox, so I couldn't build or run the suite here — the finding is grounded in Clang's |
1719198 to
319c032
Compare
There was a problem hiding this comment.
FennoAI Review — MSVC aggregate C ABI lowering
Thanks for the thorough write-up and the native Windows 11 ARM64 validation. I reviewed the locked range 4a5bd76...1719198 (28 files), concentrating on the ABI core (internal/cabi/arch_msvc.go, internal/cabi/cabi.go, tests). The supporting infrastructure (crosscompile file locking, meta mmap, goarch, target-triple parsing, workflow) reads cleanly.
The map[int]llvm.Attribute → map[int][]llvm.Attribute refactor is correct and is applied consistently across transformFunc, transformCallInstr, and transformCallbackFunc. The ByValAlign handling and the replaceAllocaInstrs guard (ti.ByValAlign == 0 || ti.ByValAlign >= ti.Align) are sound — they correctly avoid redirecting naturally-aligned local accesses onto a weaker 4-byte incoming pointer.
Verification note: LLVM headers are not available in this sandbox, so I could not compile or run the suite here. The findings below are grounded in Clang's authoritative X86_32ABIInfo::classifyArgumentType (release/19.x), not in local test runs.
I found one ABI-correctness issue scoped to Windows x86 (MSVC) only. The x64 and arm64 paths look correct (x64: 1/2/4/8-byte aggregates in one integer register, everything else indirect via sret/pointer, no register splitting; arm64: explicit AAPCS64 reuse).
What I could not fully verify
- Runtime/link-level behavior (no LLVM toolchain here). CI green + your native runs cover this; I'm relying on those for the paths I couldn't exercise.
09368e0 to
2baf2d4
Compare
|
@fennoai Please re-review the current head now that the full CI matrix, native Windows host smoke, both coverage jobs, and Codecov have passed. The four prior review threads have been addressed or resolved with the Clang 19 x86 ABI evidence recorded in-thread. |
Re-review complete — LGTMI re-reviewed head Prior P1 finding — retracted; I was wrong. I re-read Clang 19's Incremental changes are a net improvement: No blocking or outstanding correctness concerns. (Caveat unchanged: no LLVM headers in this sandbox, so I verified against Clang source + your reproduced output rather than a local build; CI/native smoke/Codecov cover execution.) |
There was a problem hiding this comment.
FennoAI Re-review — MSVC aggregate C ABI lowering (head 2baf2d4)
I re-reviewed the locked range 4a5bd76...2baf2d4, focusing on the incremental changes since my last pass (1719198) and on whether the four prior threads were addressed.
Prior P1 finding — I was wrong; retracting it ✅
My earlier P1 claimed that on i686-pc-windows-msvc, over-aligned structs (containing i64/double) must be passed as a plain indirect pointer before the expand/byval paths, and that {i64,double}→AttrExtract and {i32,i64}→byval align 4 diverged from Clang.
That finding was incorrect, and your response is right. I re-read Clang 19's X86_32ABIInfo::classifyArgumentType (release/19.x) line by line. The Win32 over-alignment check is:
if (IsWin32StructABI && State.Required.isRequiredArg(ArgIndex)) {
// uses Layout.getRequiredAlignment() / TI.isAlignRequired()
if (AlignInBits > 32)
return getIndirectResult(Ty, /*ByVal=*/false, State);
}The alignment it tests is the record's required alignment (getRequiredAlignment() / isAlignRequired()) — i.e. explicit alignas / __declspec(align) — not the natural 8-byte alignment of a long long/double member. So a naturally-aligned struct never trips that branch; it falls through to:
- expand when unpadded and ≤128 bits (
canExpandIndirectArgument:Size == getTypeSize(Ty)) → matchesAttrExtract, and - byval align 4 fallback when padded → matches
AttrPointer+ByValAlign=4.
That reproduces exactly the Clang 19.1.7 output you recorded in-thread — f(i64, double, ptr byval(B) align 4, ptr byval(C) align 4) for A={long long,double}, B={int,long long}, C={long long,int}. My proposed info.Align > 4 gate would have introduced a divergence from Clang, because it conflated LLVM datalayout alignment (i64:64) with the C AST required alignment Clang actually uses in this check. Thanks for pushing back with reproducible evidence — the current classification is correct, and the strengthened TestMSVCAggregateClassification assertions (ByValAlign == 0 for the expanded case, == 4 for the two padded cases) now lock in the right behavior.
The two P2/P3 documentation threads (isMSVCTarget default-to-MSVC comment; x64 large-aggregate byval-omission comment) are addressed as described.
Incremental changes since 1719198
internal/meta/meta.go— the newvalidate()/validateCSRSection/ name-range bounds checks over mmap'd, file-controlled bytes are a solid hardening improvement (untrusted metadata now can't drive out-of-range reads), and theuint32-range size guard before mmap is correct. Good addition.internal/build/link_options.go—debugInfoLinkerArgsnow emitsPreserveLinkFlagswhen DWARF is kept, which is the right fix for linkers likelld-linkthat drop DWARF unless preservation is explicit.internal/goarch/config.go—ParseARM64goto→continue/breakrefactor is behavior-preserving and clearer.- Remaining changes (crosscompile, ssa COMDAT/decl, workflow) are consistent supporting work.
Conclusion
No blocking or outstanding correctness concerns from me. The MSVC x64 / x86 / ARM64 aggregate lowering matches Clang 19, the prior threads are resolved, and the incremental hardening is a net improvement. LGTM.
Caveat unchanged from last time: LLVM headers aren't available in this sandbox, so I verified ABI behavior against Clang source + your reproduced output rather than a local build/test run; CI, native Windows smoke, and Codecov cover the executable paths.
084c7eb to
910e595
Compare
910e595 to
e4c6ea7
Compare
|
@fennoai, please re-review the current head The three C ABI commits were rebased without conflict onto the fully reviewed #2335 head Please focus on MSVC target detection and amd64/arm64/386 aggregate classification, x86 |
Re-review complete — LGTMI re-reviewed rebased head Focus areas unchanged & still correct: New since last review — COFF COMDAT propagation (correct): Minor (non-blocking): two near-identical Remaining changes are the supporting infra reviewed before plus the mechanical Caveat unchanged: no LLVM headers in this sandbox, so I verified against the diff + Clang/LLVM COFF semantics + in-repo tests; the 42 passing checks, native Windows 11 ARM64 validation, and Codecov cover execution. |
There was a problem hiding this comment.
FennoAI Re-review — rebased head e4c6ea7
I re-reviewed the locked range 0f48025...e4c6ea7 (rebased onto the reviewed #2335 head a8fb0fc). To separate rebase churn from real change, I diffed the C ABI files against my last confirmed-good head 2baf2d4.
Focus areas — aggregate classification & byval align 4: unchanged, still correct ✅
internal/cabi/arch_msvc.go is byte-identical to the state I confirmed correct against Clang 19 last round. The MSVC target detection (targetArch / isMSVCTarget, windows && (msvc || !gnu)), the amd64 / arm64 / 386 classification, the x86 byval align 4 preservation (including the expand-vs-byval split for unpadded vs padded i64/double structs), and the multi-attribute (byval + align) propagation all carry over verbatim. No re-litigation needed — the prior Clang-19-verified conclusion stands.
New since last review — COFF COMDAT propagation ✅
The substantive addition in this range is COMDAT handling for the COFF/architecture foundation:
transformFuncnow doesnfn.SetComdat(fn.Comdat()), carrying the source function's COMDAT onto the lowered definition. The name-swap ordering is safe: the comdat table entry (keyed by the original name) survives thefn.SetName("")→AddFunction(fname)dance, andSetComdat(nil)is a no-op for non-COMDAT functions.transformCallbackFuncroutes thelinkoncewrapper through the newsetODRLinkage, which attachescomdat anyonly whenGOOS == windows. The wrapper is name-deduplicated (m.NamedFunction(wrapName)guard) before creation, and the name is set beforesetODRLinkage, sovalue.Name()keys the comdat correctly.ssa/decl.goadds a parallelPackage.setODRLinkagefor the module zerobase, instantiated functions, and ODR globals — same Windows-onlycomdat anyrule.
This is the right fix: COFF requires ODR/linkonce definitions to carry a COMDAT (IMAGE_COMDAT_SELECT_ANY) so lld-link can coalesce otherwise-identical definitions across objects; plain LLVM weak/linkonce alone degrades to per-object weak externals that the linker won't merge. AnyComdatSelectionKind matches the linkonce/weak_odr coalescing semantics. The new tests (TestCABILoweringPreservesWindowsCOMDAT, TestWindowsCallbackWrapperUsesCOMDAT, ssa/coff_comdat_test.go) assert both the Windows-present and Unix-absent behavior and VerifyModule the result.
The remaining changes in the range are the same supporting infra I reviewed before (crosscompile, meta hardening, goarch, link options, workflow) plus the mechanical goplus/llgo → xgo-dev/llgo import rename from the rebase — all consistent.
Minor (non-blocking)
There are now two near-identical setODRLinkage helpers — Package.setODRLinkage (ssa/decl.go) and Transformer.setODRLinkage (internal/cabi/cabi.go) — with the same doc comment and logic on different receiver types. Fine as-is; if the COFF COMDAT convention grows, a single shared helper would avoid drift between the two.
Conclusion
No blocking or outstanding correctness concerns. The rebase is clean, the ABI core is unchanged from the confirmed-correct state, and the new COMDAT propagation is correct and well-tested. LGTM.
Caveat unchanged: LLVM headers aren't available in this sandbox, so I verified against the diff, Clang/LLVM COFF semantics, and the in-repo tests rather than a local build/run; the 42 passing checks + native Windows 11 ARM64 validation + Codecov cover execution.
Part of #2325, the MSVC-first Windows support proposal.
Depends on #2335.
Summary
byvalalignment on definitions, calls, and callback wrappers without redirecting naturally aligned local accesses to a weaker incoming pointerValidation
go test -count=1 -coverprofile=/tmp/llgo-windows-cabi-cover.out ./internal/cabi(97.7% statement coverage)go test -count=1 -run "^(TestTargetArchAndNewTransformerArchSelection|TestMSVC.*)$" ./internal/cabigo vet ./internal/cabigit diff --checkand workflow YAML parsinginternal/cabi/_testdata/wrapC/Go signature against Clang 19 output forx86_64-pc-windows-msvc,aarch64-pc-windows-msvc, andi686-pc-windows-msvcScope
This provides the aggregate C ABI foundation for MSVC targets. Runtime threading, GC, panic/recover, debug information, import/export details, other calling conventions, and complete MinGW/Cygwin support remain tracked by #2325.
Native Windows ARM64 validation
Validated on Windows 11 ARM64 with Go 1.26.5 and native MSYS2 CLANGARM64 LLVM 19.1.7:
internal/cabipackage passes natively in 112.747 seconds on the rebased stack;\\Mac\Home\source\goplus\llgo-wt-windows-cabi-20260815; caches and output remained on Windows.GitHub CI covers Windows AMD64, Ubuntu, macOS, and Codecov on the pushed contribution head.
Final CI and coverage