Skip to content

Proposal: MSVC-first Windows support for amd64, arm64, and 386 #2325

Description

@cpunion

Summary

Add native Windows support to LLGo for all Windows architectures supported by the Go toolchain: windows/amd64, windows/arm64, and windows/386. The Microsoft ABI is the primary target, with separate MSYS2/MinGW compatibility lanes where the surrounding ecosystem still provides a maintained toolchain. Native LLGo must also be usable from MSYS2 and Cygwin shells without turning either shell into a target ABI.

Delivery should be staged: amd64 first, ARM64 second, and 386 third. The first usable milestone is a native windows/amd64 toolchain that uses the Windows SDK and UCRT, consumes and produces MSVC-compatible PE/COFF objects and libraries, interoperates with libraries built by Visual Studio, and does not depend on the MSYS2, Cygwin, or winpthreads runtimes. The proposal is not complete, however, until the same supported runtime and interoperability surface is validated on ARM64 and 386.

MSYS2 and Cygwin must be modeled separately from the target ABI. They can be host shells; they must not silently determine the ABI or leak msys-2.0.dll or cygwin1.dll into released programs.

Motivation

Native Windows support is important for LLGo's goals:

  • official CPython distributions and most native Python extensions use the MSVC ABI;
  • the Windows C and C++ ecosystem commonly ships Visual Studio .lib and .dll artifacts;
  • users should be able to build and run LLGo programs from PowerShell, cmd, Visual Studio developer shells, MSYS2, and Cygwin;
  • a native MSVC-compatible path avoids requiring a POSIX compatibility runtime in deployed programs;
  • Windows coverage is needed before LLGo can claim broad native platform and standard-library compatibility.

A MinGW-only implementation would provide useful bootstrap coverage, but it would leave the main Windows ABI, official Python interoperability, Visual Studio libraries, and native deployment experience insufficiently validated.

Current state

Current main contains pieces that are useful for Windows, including Windows executable/shared-library suffixes and LLVM configuration support, but it is not an end-to-end Windows toolchain:

  • the LLVM target helper always appends the GNU environment for Windows;
  • the existing 386, amd64, and ARM64 C ABI lowering tests use Linux configurations; in particular, the amd64 rules must not be mistaken for the Microsoft x64 ABI;
  • GO386, GOAMD64, and GOARM64 are not currently represented in LLGo's typed build configuration or code-generation/cache decisions;
  • the native linker setup assumes the Clang GNU driver and contains a Windows linker TODO;
  • dependency declarations commonly invoke pkg-config and use Unix -l flags;
  • host-side code still uses Unix-only APIs such as syscall.Mmap and syscall.Flock;
  • runtime threading, synchronization, and TLS are deeply based on pthreads;
  • the standard-library replacement/source-patch selection is not yet fully GOOS-aware;
  • Windows SyscallN, callbacks, DLL imports, exception handling, stack unwinding, IOCP polling, and PE/COFF runtime metadata are incomplete;
  • the current libffi hidden-environment bridge does not provide every required Windows fallback when libffi lacks Go-closure support, and its AArch64 final-hop bridge explicitly excludes Windows;
  • external PCLN sidecar support currently covers only Linux/macOS on amd64/ARM64, so PE/COFF metadata and 386 stack walking need explicit implementations.

Issue #1020 records an older MinGW installation failure. Its original compiler errors appear stale, but a current end-to-end Windows build has not replaced the need for a platform design and validation plan.

Terminology and support tiers

These are related environments, but they are not interchangeable targets:

Host environment Architecture Compiler/linker Output ABI Proposed support
PowerShell, cmd, or VS Developer Shell 386, amd64, ARM64 clang-cl, lld-link or link.exe, Windows SDK MSVC Primary, required
MSYS2 shell using the VS/LLVM environment Host-supported architectures Same native toolchain, with path conversion MSVC Required host compatibility
MSYS2 CLANG64 or UCRT64 amd64 Clang/GCC and MinGW-compatible linker GNU/UCRT Secondary target
MSYS2 CLANGARM64 ARM64 Clang and MinGW-compatible linker GNU/UCRT Secondary target after native ARM64
Standalone llvm-mingw 386, amd64, ARM64 Clang and MinGW-compatible linker GNU/UCRT Cross-toolchain validation; required for 386 GNU coverage
Cygwin shell invoking native llgo.exe amd64 host Native MSVC or MinGW toolchain MSVC or GNU Scheduled shell smoke coverage only
MSYS or Cygwin ABI output N/A POSIX-emulation runtime MSYS/Cygwin Not a supported target

LLGo should continue to use the official GOOS=windows and GOARCH values. The architecture scope is exactly the current Go toolchain set: 386, amd64, and arm64. It must not invent GOOS=cygwin, infer the target ABI from the current shell, or claim support for an architecture based only on successful object emission.

Go command compatibility and -target separation

Windows support must preserve Go command syntax, precedence, diagnostics, output naming, and build semantics. Platform work must not introduce a second, Windows-specific interpretation of existing Go flags.

  • The standard native entry points are GOOS=windows GOARCH=amd64 llgo build, GOOS=windows GOARCH=arm64 llgo build, and GOOS=windows GOARCH=386 llgo build. MSVC is the default Windows ABI for each.
  • LLGo -target remains the existing named target-configuration mechanism for boards and platforms such as RP2040 and WASI. It must not accept a raw LLVM triple or be reinterpreted as a Windows ABI selector.
  • Clang --target=x86_64-pc-windows-msvc is an internal child-process argument generated by the selected toolchain. It is not a user-facing LLGo flag.
  • The secondary GNU/MinGW lane uses a separate toolchain profile, selected and validated through Go-compatible toolchain inputs such as CC, CXX, and supported -ldflags=-extld=... semantics. The ABI must not be guessed from the invoking PowerShell, MSYS2, or Cygwin shell.
  • If an LLGo-specific sysroot or dependency-bundle setting is required, it must remain a toolchain configuration input and must not change the meaning of GOOS, GOARCH, GOAMD64, -target, or Go build flags.
  • -buildmode, -o, -p, -n, -x, -work, -trimpath, -tags, -gcflags, and -ldflags must retain Go command parsing, repetition, package-pattern, precedence, and error behavior for the supported subset. Unsupported semantics must report a clear error rather than being silently ignored or forwarded to the wrong linker dialect.
  • Go-compatible flags continue to be parsed and normalized in internal/goflags into typed build configuration. The MSVC and GNU backends translate that typed meaning into /... or -... compiler/linker arguments; user-provided Go flags are not rewritten as if they were raw MSVC flags.
  • GO386, GOAMD64, and GOARM64 must follow the supported Go toolchain values and semantics, be represented in typed build configuration, and map to the correct LLVM CPU/features without changing the meaning of GOARCH.
  • Windows ABI, architecture tuning level, compiler/linker identity, SDK/CRT version, target profile, build mode, and semantically relevant Go flags must participate in cache keys so incompatible architecture, feature-level, MSVC, and MinGW artifacts cannot be reused.
  • Existing embedded and WebAssembly -target behavior must be covered by regression tests and remain unchanged by the Windows work.

The intended primary invocations are therefore:

GOOS=windows GOARCH=amd64 llgo build ./cmd/app
GOOS=windows GOARCH=arm64 llgo build ./cmd/app
GOOS=windows GOARCH=386 llgo build ./cmd/app

Goals

  • Support every Windows architecture exposed by the Go toolchain: windows/amd64, windows/arm64, and windows/386, with the MSVC ABI as the default for each.
  • Stage delivery in the order amd64, ARM64, then 386 without treating an amd64-only milestone as completion of the proposal.
  • Implement and test the actual Windows ABI for each architecture rather than reusing arch-only Linux ABI rules.
  • Use the Windows SDK and UCRT and support standard PE/COFF files: .obj, .lib, .dll, and .exe.
  • Honor GO386, GOAMD64, and GOARM64 consistently in code generation, cache keys, diagnostics, and tests.
  • Link libraries compiled by cl.exe, including static libraries and DLL import libraries.
  • Make LLGo-produced C archives/shared libraries consumable by a Visual Studio or clang-cl program.
  • Support both lld-link and, where practical, link.exe without mixing their flag dialects.
  • Provide a native Win32 runtime backend with no required libwinpthread, MSYS2, or Cygwin runtime dependency.
  • Support MSYS2 CLANG64/UCRT64 for amd64 and CLANGARM64 for ARM64 as separate Windows GNU compatibility targets; use a maintained standalone LLVM/MinGW toolchain rather than obsolete MSYS2 32-bit environments for 386.
  • Allow native LLGo binaries to be invoked from MSYS2 and Cygwin shells with correct path, quoting, and environment behavior.
  • Exercise relevant Go standard-library and GOROOT tests on real Windows runners.
  • Require each architecture to pass the complete C ABI, FFI, closure-environment, goroutine/thread, GC, defer, panic/recover, fault, unwind, and debugging support gate defined below.
  • Provide reproducible native and cross-compiling toolchains without conflating host tools, target tools, or LLGo's existing -target mechanism.

Architecture scope and delivery order

The shared Win32 runtime should be pointer-width-safe and reused across architectures, but ABI lowering, syscall/callback thunks, unwind data, fault contexts, closure environment registers, and golden tests must be selected by both OS and architecture.

Target Required ABI work Existing gap Native CI
windows/amd64 Microsoft x64 register assignment, four-slot shadow space, aggregate rules, variadics, unwind tables, syscall argument mirroring, and GOAMD64 Current amd64 C ABI golden tests are Linux/SysV-oriented; successful amd64 lowering is not evidence of Win64 compatibility windows-2025 x64
windows/arm64 Windows AArch64 use of x18 as the TEB register, aggregate/HFA rules, variadics, 16-byte stack alignment, __chkstk, PE unwind data, and GOARM64 The generic ARM64 path needs Windows-specific validation, and the current libffi final-hop bridge rejects Windows AArch64 Native Windows ARM64 runner, with cross-build coverage until that lane is stable
windows/386 x86 cdecl/stdcall distinctions, caller/callee stack cleanup, symbol decoration, structure returns, callback thunks, x86 SEH, x87/SSE2 behavior, and GO386 The existing 386 lowering is a Linux cdecl baseline; modern MSYS2 no longer provides a maintained 32-bit environment x64 Windows runner under WoW64, plus explicit x86 compiler/linker selection

The delivery order is intentional:

  1. amd64 first: broadest user and dependency coverage, and the best bootstrap environment.
  2. ARM64 second: an official Go target with native Windows hardware/runners and a useful cross-development path from Apple Silicon.
  3. 386 third: still an official Go target and therefore in scope, but it carries the largest calling-convention and legacy-toolchain compatibility tail.

windows/arm (32-bit ARM) and ARM64EC are not current Go GOARCH targets. They must not be silently folded into ARM64 support.

Required language and runtime support surface

An architecture is not supported merely because it can link and run a small executable. Each of windows/386, windows/amd64, and windows/arm64 must pass the following coupled support surface:

Area Required behavior
C ABI Dispatch internal/cabi by OS, architecture, and ABI; cover scalar, floating-point, vector where applicable, complex, small/large aggregate, hidden structure return, variadic, stack alignment, import/export, and architecture-specific calling-convention cases. Golden tests must validate declarations, calls, callbacks, and returned values, not only LLVM type shapes.
FFI and callbacks Select the correct libffi ABI per architecture; cover direct calls, closures, callbacks, reflect calls, finalizers, aggregate/mixed arguments, DLL/static-library boundaries, foreign-thread entry, and both LTO and non-LTO. The fallback final-hop thunk must work when libffi Go closures are unavailable.
Closure environment Preserve LLGo's hidden closure environment through direct/indirect calls, method values, interface wrappers, reflect, C callbacks, and FFI thunks without consuming a register reserved by Windows or corrupting user arguments/shadow space. The environment must remain visible to GC for the full call and unwind lifetime. Tests must cover amd64 shadow space/register pressure, ARM64 x18/TEB reservation, and 386 stack cleanup.
Goroutines and OS threads Implement Win32 thread creation, TLS/FLS, scheduler wakeups, synchronization, stack-bound discovery, GOMAXPROCS, LockOSThread, thread teardown, and callbacks entering from foreign threads. Test goroutine migration and pinned goroutines under load, not only single-thread startup.
GC Register and unregister LLGo-created and foreign threads with the collector; scan stack, TLS/FLS, callee-saved registers, closure environments, callback frames, and finalizer roots correctly. Cover concurrent allocation/collection, finalizers, weakly timed roots, thread exit, and pointers live only across C/FFI calls on 32- and 64-bit targets.
defer Preserve LIFO order, named-result updates, open-coded/runtime defer paths, nested defers, loop/rangefunc defers, runtime.Goexit behavior, and defers during panic on every ABI and optimization mode.
panic/recover and faults Match pinned Go semantics for nested panic, repanic, direct recover, nil and hardware faults converted through VEH/SEH, source PC/line retention, and foreign callback boundaries. Go state must not accidentally escape as a C++ exception; unsupported cross-language unwinding must fail deterministically and be documented.
Debugging and unwind Preserve function/PCLN metadata, runtime.Callers, source file/line, panic traceback, and mixed Go/C/callback/fault stack walking. Emit valid PE/COFF unwind data and a usable CodeView/PDB link path for basic Visual Studio/WinDbg/LLVM debugger breakpoints and backtraces; verify -trimpath and cross-host path mapping.

These areas must also be tested together. At minimum, each architecture needs an integration case in which a foreign OS thread calls an LLGo closure, the closure environment is the only live reference to allocated data, a deferred function runs, and normal return, panic/recover, and fault paths all produce a valid traceback without losing GC roots.

MSVC support boundary

“MSVC support” has multiple layers. They should be delivered and tested explicitly:

  1. MSVC ABI, CRT, and SDK compatibility — required. LLGo-generated code uses the Microsoft x86, x64, or ARM64 ABI selected by GOARCH, together with Windows SDK/UCRT contracts.
  2. MSVC ecosystem interoperability — required. LLGo can consume cl.exe-built .lib/DLLs, and Visual Studio can consume supported LLGo library outputs.
  3. Microsoft linker compatibility — targeted. lld-link may be the bootstrap default because LLGo already depends on LLVM, but link.exe should be supported and tested.
  4. Pure cl.exe compilation of all LLGo internal wrappers — best effort and not an initial blocker. Some current bridges use Clang attributes or inline assembly. Keeping those small architecture-specific files on clang-cl does not change the ABI of the resulting program.

Each MSVC lane should use one consistent CRT model. /MD with the vcpkg x64-windows, arm64-windows, or x86-windows triplet is the preferred starting point because it matches the usual Visual Studio and official CPython ecosystem. Static CRT support can be added later as an explicit, non-mixing configuration.

The initial C++ promise is binary compatibility with MSVC-built libraries and extern "C" entry points. Propagating C++ exceptions or relying on arbitrary C++ object layouts across Go boundaries is not part of the first milestone.

Proposed design

1. Make the Windows ABI and linker flavor explicit

Introduce target/toolchain data that separates:

  • target OS and architecture, including GO386, GOAMD64, or GOARM64 feature level;
  • Windows ABI: MSVC or GNU;
  • compiler flag dialect: Clang GNU, clang-cl, or MSVC;
  • linker flavor: GNU/MinGW, lld-link, or link.exe;
  • archive and import-library formats and suffixes;
  • CRT linkage selection.

Do not infer these from runtime.GOOS or the invoking shell. Each supported GOOS=windows GOARCH=... pair should default to the corresponding MSVC ABI on a Windows host; the GNU target should be selected through a distinct toolchain profile that is validated against Go-compatible compiler and linker inputs.

Keep ICF disabled where required by Go function-PC identity semantics, using the correct spelling for each linker.

2. Bootstrap through LLVM with the MSVC ABI

Use clang-cl or Clang with the corresponding MSVC triple—i686-pc-windows-msvc, x86_64-pc-windows-msvc, or aarch64-pc-windows-msvc—for LLGo's internal C/assembly bridges and LLVM-generated objects. Initially use lld-link, then validate the same object set with the architecture-matched link.exe.

This preserves LLGo's LLVM architecture while producing native MSVC-compatible output. Dependencies and interoperability fixtures should still be built with cl.exe so CI verifies the actual ecosystem boundary rather than only Clang-to-Clang linking. Every ABI golden test must specify both GOOS=windows and GOARCH; arch-only golden coverage is insufficient.

3. Define a reproducible cross-compilation toolchain

Cross compilation must keep four roles separate:

  1. the host llgo process and host helper programs;
  2. target code generation and target C/assembly compilation;
  3. the target linker, librarian, resource/manifest tools, and compiler-rt builtins;
  4. the architecture-matched Windows SDK/UCRT sysroot and third-party libraries.

No target executable or target-side configure probe may run during compilation. Package resolution must return target metadata rather than executing a target binary. Host and target paths, executable suffixes, cache entries, generated wrappers, and dependency artifacts must never be mixed.

For the primary MSVC ABI:

  • On Windows x64, use Visual Studio Build Tools/Windows SDK cross environments for x86, x64, and ARM64, with clang-cl/Clang, lld-link or link.exe, and llvm-lib or lib.exe. CI must select the matching Host/Target developer environment explicitly.
  • On Windows ARM64, support a native ARM64 host lane and keep the target architecture explicit rather than deriving it from the host.
  • On macOS and Linux, use LLVM Clang with the MSVC triples, lld-link, llvm-lib, and LLVM resource tooling as required. Headers and import libraries come from an explicitly configured Windows SDK/UCRT sysroot. A pinned xwin-style acquisition step may be offered for development/CI subject to Microsoft licensing, but LLGo must also accept a user-provisioned sysroot and must not redistribute the SDK.
  • Native-Windows vcpkg manifests remain the canonical dependency build. Non-Windows cross builds require architecture/ABI-matched prebuilt libraries or an explicit CMake/vcpkg cross toolchain; the native x64-windows triplet must not be assumed to work unchanged on macOS/Linux.

For the GNU/UCRT ABI, use a pinned llvm-mingw bundle and the i686-w64-mingw32, x86_64-w64-mingw32, and aarch64-w64-mingw32 toolchains on macOS, Linux, or Windows. MSYS2 CLANG64/UCRT64 and CLANGARM64 remain native compatibility lanes, not the cross-compilation abstraction; 386 must not depend on a removed MSYS2 32-bit environment.

User-facing selection remains Go-compatible: GOOS, GOARCH, the matching GO386/GOAMD64/GOARM64 variable, CC/CXX where applicable, and supported external-linker settings. LLGo may need an explicit sysroot/toolchain configuration input, but it must not overload -target or expose raw Clang --target as a Go command flag.

Cross-build validation has two halves:

  • on macOS and Linux, build every architecture/ABI fixture and inspect COFF machine type, imports, relocations, calling-convention attributes, unwind sections, debug sections, and dependency architecture;
  • upload the same artifacts to native Windows CI, then execute amd64 and 386 under the x64 runner and ARM64 on a native ARM64 runner. Compile/link success alone is not acceptance.

Toolchain manifests and cache keys must pin/record LLVM, linker, SDK, UCRT, compiler-rt, dependency, ABI, architecture-feature, and debug-format versions. -n/-x command-line tests should make host/target tool selection auditable.

4. Replace pkg-config as the platform abstraction

Represent runtime dependencies logically rather than embedding one shell command in package metadata. A resolver should provide include directories, library files, link options, and runtime DLLs for the selected toolchain.

  • MSVC lanes: use a checked vcpkg manifest and the matching x86-windows, x64-windows, or arm64-windows triplet.
  • MSYS2 GNU lanes: retain pkg-config for amd64 CLANG64/UCRT64 and ARM64 CLANGARM64 packages.
  • 386 GNU lane: use a maintained standalone llvm-mingw/UCRT toolchain; do not depend on removed or deprecated MSYS2 32-bit environments.
  • Release packaging: either bundle required redistributable DLLs or document/install the matching runtime; never copy DLLs from the wrong ABI environment.

The first dependency spike must cover BDWGC, libffi, libuv, zlib, and OpenSSL because they exercise threads, callbacks/assembly, event polling, and common C package metadata.

5. Add a native Win32 runtime backend

Abstract the current pthread dependency behind a small internal OS-thread interface, retaining the pthread backend on Unix and adding a Windows backend based on native primitives:

  • _beginthreadex or CreateThread for thread creation;
  • FLS where destructor behavior is required, otherwise TLS;
  • SRWLOCK, CONDITION_VARIABLE, and INIT_ONCE for synchronization;
  • Win32 thread IDs, stack bounds, clocks, and sleep/wakeup operations;
  • explicit BDWGC registration and unregistration for LLGo-created and foreign threads;
  • preservation of closure-environment and other GC roots across scheduler switches, FFI calls, callbacks, thread exit, and fault/unwind paths.

A winpthreads implementation may help the MinGW lane, but it is not sufficient for marking the MSVC lane supported.

6. Implement Windows runtime and standard-library contracts

Make alternate-package and source-patch selection GOOS-aware and reuse the pinned GOROOT Windows implementation where its ABI is compatible. Add or adapt the remaining compiler/runtime boundary for:

  • DLL lookup and imports;
  • Windows SyscallN calling convention and GetLastError behavior;
  • callback trampolines;
  • Unicode command-line arguments, environment variables, and paths;
  • filesystem and process behavior, including Windows quoting and handle inheritance;
  • IOCP-backed internal polling and networking;
  • time, signals/events, console handling, and system information.

The raw SyscallN path needs an architecture-specific thunk matching the pinned Go runtime: Microsoft x64 register and floating-point mirroring rules on amd64, stack calling-convention and cleanup rules on 386, and Windows ARM64 register/stack and TEB conventions on ARM64. LLGo should follow Go's exposed behavior, including its architecture-specific limitations, rather than inventing a different API.

7. Complete Windows FFI and callback support

Validate the vcpkg libffi configuration for x86, x64, and ARM64 and implement an architecture-specific Windows final-hop bridge that does not assume FFI_GO_CLOSURES is enabled. Cover:

  • direct and closure calls with integer, floating-point, aggregate, and mixed arguments;
  • hidden LLGo closure environments, including GC liveness and architecture-specific reserved-register/stack rules;
  • callbacks from MSVC-built code;
  • reflect calls and finalizers;
  • foreign-thread entry and GC visibility;
  • LTO and non-LTO builds.

8. Add Windows fault, unwind, and debugging support

Implement and test:

  • VEH/SEH conversion for nil dereference and other Go-compatible faults;
  • x86 SEH and x64/ARM64 table-based unwind metadata for generated functions and runtime bridges;
  • stack walking through Go, C, callback, and fault frames;
  • PE/COFF placement and retention of PCLN/function metadata;
  • CodeView/PDB production, source mapping, and basic Visual Studio/WinDbg/LLVM debugger validation after runtime traceback correctness but before an architecture is marked fully supported.

9. Make the host toolchain Windows-safe

Replace Unix-only host operations with portable implementations, including metadata mapping, file locking, cache publication, executable replacement, temporary files, and path handling.

MSYS2 and Cygwin compatibility must normalize paths only at process boundaries. Internal cache keys and target selection must not depend on whether paths arrived in Unix or Windows spelling.

10. CI and release validation

Start with a resource-conscious matrix:

  • required PR job: windows-2025, amd64, current supported Go version, MSVC ABI, PowerShell/VS environment;
  • required PR cross-build jobs once their lowering lands: ARM64 and 386 compile/link plus ABI golden tests;
  • scheduled macOS and Linux cross-toolchain jobs: build all three MSVC targets with LLVM plus an SDK/UCRT sysroot, and all three GNU targets with llvm-mingw; upload representative artifacts for execution in Windows jobs;
  • native ARM64 job: run on a GitHub-hosted Windows ARM64 runner when available and stable; keep it nightly while the public runner remains preview;
  • native 386 job: run on the x64 Windows runner under WoW64 with an explicitly selected x86 compiler/linker environment;
  • required interop fixtures per architecture: cl.exe library consumed by LLGo and an LLGo library consumed by cl.exe;
  • nightly: fuller Windows MSVC GOROOT and standard-library execution on all three architectures, with sharding adjusted to actual runtime;
  • nightly or conditional GNU lanes: MSYS2 CLANG64/UCRT64 for amd64, CLANGARM64 for ARM64, and standalone llvm-mingw for 386;
  • scheduled smoke: invoke native amd64 LLGo from Cygwin and applicable LLGo binaries from MSYS2 shells;
  • release job: run each packaged artifact in its native/WoW64 environment from clean PowerShell with MSYS2 and Cygwin absent from PATH.

Use dumpbin /dependents or LLVM PE inspection to reject unexpected cygwin1.dll, msys-2.0.dll, and libwinpthread dependencies in MSVC release artifacts.

Phased work

Phase 0: multi-architecture toolchain and dependency spike

  • Add explicit MSVC/GNU Windows target, architecture, linker, sysroot, and host/target tool descriptors without changing LLGo -target.
  • Make the Windows x64 Visual Studio cross tools build x86, x64, and ARM64, and make pinned LLVM toolchains on macOS/Linux cross-build the same targets.
  • Cross-compile hello world and C-call fixtures for 386, amd64, and ARM64.
  • Build and run the amd64 fixtures in a VS Developer PowerShell environment.
  • Link architecture-matched cl.exe static libraries and DLLs.
  • Establish the vcpkg manifest for x86-windows, x64-windows, and arm64-windows and fix host-only portability blockers.
  • Add Windows-specific C ABI golden tests before treating any existing arch lowering as reusable.

Phase 1: shared Win32 thread, TLS, synchronization, and GC backend

  • Introduce the OS-thread abstraction and a pointer-width-safe Win32 backend.
  • Validate goroutine scheduling, foreign threads, TLS/FLS destructors, closure-environment roots, callbacks, concurrent GC, finalizers, and shutdown first on amd64, then rerun the same gate on ARM64 and 386.
  • Remove any winpthreads dependency from MSVC artifacts.
  • Keep architecture-specific stack discovery and context handling behind explicit implementations.

Phase 2: complete windows/amd64

  • Implement the Microsoft x64 C ABI, GOAMD64, SyscallN, callbacks, closure/FFI bridges, files, processes, networking, faults, unwind tables, and PE/COFF runtime/debug metadata.
  • Pass the full goroutine/thread, GC, closure-environment, defer, panic/recover, fault, traceback, and debugger support surface.
  • Run relevant amd64 standard-library and GOROOT Windows tests.
  • Qualify amd64 C interoperability and release packaging.

Phase 3: complete windows/arm64

  • Implement and validate Windows ARM64 aggregate/variadic rules, x18/TEB handling, GOARM64, __chkstk, syscall/callback/FFI bridges, closure-environment GC liveness, fault contexts, PE unwind data, and debug metadata.
  • Pass the same goroutine/thread, GC, defer, panic/recover, traceback, and debugger gate as amd64.
  • Run the same runtime, C interoperability, standard-library, and GOROOT surface natively on Windows ARM64.
  • Verify cross-development from macOS ARM64 without using host properties as target properties.

Phase 4: complete windows/386

  • Implement and validate Microsoft x86 cdecl/stdcall behavior, symbol decoration, structure returns, callback stack cleanup, closure-environment GC liveness, x86 SEH, GO386, x87/SSE2 behavior, unwind, and debug metadata.
  • Pass the same goroutine/thread, GC, defer, panic/recover, traceback, and debugger gate as amd64.
  • Run under WoW64 with architecture-matched MSVC libraries and tools.
  • Add pointer-width, large-file, handle, callback, and GC tests that would be hidden by amd64-only coverage.

Phase 5: GNU/MinGW compatibility by architecture

  • Run the supported language/runtime surface through MSYS2 CLANG64/UCRT64 on amd64 and CLANGARM64 on ARM64.
  • Validate 386 with standalone llvm-mingw instead of relying on obsolete MSYS2 32-bit environments.
  • Keep ABI-specific dependency, linker, archive, callback, and import-library handling separate from MSVC.

Phase 6: shell compatibility and release qualification

  • Validate native LLGo from PowerShell, cmd, VS Developer Shell, and applicable MSYS2 shells.
  • Add Cygwin invocation/path/quoting smoke tests on amd64 without creating a Cygwin target.
  • Produce native Windows releases for 386, amd64, and ARM64 and audit their dependencies.

Validation

The Windows suite should include:

  • compiler and host-tool unit tests on Windows, plus command-generation and artifact-inspection tests on macOS and Linux cross hosts;
  • separate Windows C ABI golden and execution tests for 386, amd64, and ARM64, including scalar/aggregate passing and return, variadics, stack layout, calling conventions, callbacks, imports/exports, and unwind metadata;
  • GO386, GOAMD64, and GOARM64 feature-level, diagnostics, and cache-separation tests;
  • executable, C archive, and C shared-library modes;
  • architecture-matched MSVC static library and DLL consumption, and Visual Studio consumption of LLGo-produced libraries;
  • direct, closure, reflect, finalizer, and callback FFI with integer, floating-point, aggregate, mixed, and supported variadic signatures;
  • closure-environment propagation and GC liveness through direct/indirect Go calls, interfaces, method values, FFI, callbacks, foreign threads, LTO/non-LTO, panic, and fault unwinding;
  • goroutines, channels, maps, synchronization, TLS/FLS, GOMAXPROCS, LockOSThread, stack bounds, thread exit, and scheduler stress;
  • concurrent GC, finalizers, foreign-thread registration, roots held only in stack/register/TLS/closure/callback frames, and 32-bit pointer-width stress;
  • defer order/named results/Goexit, nested panic/repanic/direct recover, nil and hardware faults, source locations, and mixed Go/C/callback stack traces;
  • runtime.Callers/PCLN correctness, PE/COFF unwind inspection, CodeView/PDB creation, basic debugger breakpoint/backtrace smoke, -trimpath, and cross-host source mapping;
  • Unicode and long paths, spaces in installation paths, process quoting, environment handling, files, processes, time, console, DNS, TCP, UDP, and IOCP;
  • native and cross builds, MSVC and GNU ABI, LTO and non-LTO, and release dependency inspection;
  • relevant GOROOT run, compile, and errorcheck coverage on Windows, with unsupported cases explicitly justified rather than hidden by architecture-wide skips.

Acceptance criteria

An architecture may be marked supported only when:

  • its official GOOS=windows GOARCH=... invocation produces a working native MSVC-ABI program and honors the corresponding GO386, GOAMD64, or GOARM64 semantics;
  • LLGo runs from PowerShell/cmd without MSYS2 or Cygwin installed;
  • its MSVC artifact has no MSYS2, Cygwin, or winpthreads runtime dependency;
  • LLGo consumes an architecture-matched cl.exe library and Visual Studio consumes a supported LLGo library output;
  • the complete C ABI, FFI, closure-environment, goroutine/thread, GC, defer, panic/recover/fault, traceback, PE unwind, and debugging gate passes natively;
  • the relevant Windows standard-library and GOROOT tests have tracked results and explicit unsupported cases;
  • architecture/ABI/toolchain cache separation and PE/COFF metadata are verified.

The first supported milestone applies those criteria to amd64. The proposal is complete only after ARM64 and 386 meet the same criteria. GNU compatibility is accepted separately per architecture: CLANG64/UCRT64 for amd64, CLANGARM64 for ARM64, and a maintained llvm-mingw lane for 386. Shell invocation must not change the selected ABI.

Non-goals

  • Windows 32-bit ARM (windows/arm), ARM64EC, UWP, or Windows kernel targets; these are not the three current Go Windows architecture targets;
  • a Cygwin or MSYS target ABI;
  • treating Cygwin as architecture coverage or requiring a Cygwin ARM64/386 environment;
  • relying on deprecated MSYS2 MINGW32/MINGW64 environments as the support baseline;
  • requiring every LLGo internal wrapper to compile with pure cl.exe;
  • arbitrary C++ exceptions or ownership-sensitive C++ objects crossing Go boundaries;
  • duplicating the full Windows CI matrix across every Go version or build mode before each architecture's primary lane is stable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions