Skip to content

test: separate behavior and command compatibility checks - #2450

Merged
xushiwei merged 3 commits into
xgo-dev:mainfrom
cpunion:codex/test-command-compat-refactor-20260830
Aug 30, 2026
Merged

test: separate behavior and command compatibility checks#2450
xushiwei merged 3 commits into
xgo-dev:mainfrom
cpunion:codex/test-command-compat-refactor-20260830

Conversation

@cpunion

@cpunion cpunion commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

This extracts the test-layout refactor from the Windows R10 work so it can be reviewed and merged independently. It does not change compiler or runtime production code.

Test ownership

The change keeps each kind of test at the layer that owns it:

  • ordinary Go/runtime semantics run directly as shared _test.go cases under test/go;
  • command-line compatibility cases share one body under test/cmd/llgo, with llgo and !llgo wrappers selecting the compiler;
  • LLVM IR checks live under cl/_test*;
  • DWARF and ABI build-mode checks live under internal/build;
  • C and LLGo-private behavior live under test/cgo and test/llgoext.

Benefits

  • Compiles ordinary behavior cases once in the shared test binary instead of repeatedly invoking the LLGo compiler and linker from individual tests. Command, ABI, DWARF, IR, C, and LLGo-specific checks are retained in their owning packages rather than merely deleted.
  • Uses one command-compatibility case body for both Go and LLGo wrappers, reducing duplicated fixtures and preventing the two paths from drifting apart.
  • Makes failures point at the owning layer: language behavior, command compatibility, LLVM IR, build-mode metadata, C integration, or LLGo-specific behavior.
  • Keeps process-global checks deterministic without restoring nested compilation. The finalizer and runtime.NumGoroutine cases re-execute the already-built test binary in an isolated child process, adding neither another package nor another LLGo build.

The isolated test/go timing is not used as the overall speedup because some work moved to better-owned packages. Instead, the comparison below measures the directly affected CI steps, which include test/go and every destination package:

Directly affected CI step Base Initial PR run Fixed PR run Fixed vs. base
Go Test with coverage, aggregate across macOS, Linux, MinGW, and MSVC 82m20s 70m15s 66m58s -15m22s (-18.7%)
Go 1.27 Run versioned LLGo tests, aggregate across five platform/shard jobs 112m14s 107m28s 106m12s -6m02s (-5.4%)

These values sum the duration of the affected step on each runner; they are not workflow wall time. Both PR runs improve the aggregate, while individual runners remain noisy (for example, the LLGo MSVC lane is about three minutes slower in the fixed run). The repository gains one test package (test/cmd/llgo), while the changed _test.go files have 26 added and 27 removed test functions overall, so the result reflects retained-but-consolidated coverage rather than hiding the work outside test/go.

Validation

  • go test ./test/go ./test/cmd/llgo
  • complete test/go with LLGo and Go 1.26.7
  • the finalizer and runtime.NumGoroutine regression cases repeated 100 times with Go 1.26.7 and 20 times with Go 1.27
  • llgo test ./test/cmd/llgo, using the same Go 1.27 modfile path as CI
  • targeted internal/build tests for ABI modes, DWARF panic modes, and SSA DebugRef ordering
  • targeted cl fixtures for deferred panic IR and cgo defer ordering
  • migrated test/cgo and test/llgoext cases under LLGo
  • the affected Ubuntu Go 1.27 full shard in CI

The complete internal/build package also passed in the original development worktree. The complete cl package exceeded its existing 10-minute aggregate timeout in an unrelated Python/DWARF fixture; all changed cl fixtures pass independently.

@cpunion cpunion added the go-test-compat Go standard-library and GOROOT test compatibility label Aug 30, 2026
@codecov

codecov Bot commented Aug 30, 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

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

67131f6071b8 | 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 19496 B 0 B / +0.0% 387 B 0 B / +0.0% 340.570 ms +2.954 ms / +0.9% (worse) 1.305 ms +22.11 us / +1.7% (worse)
Linux cprintf-lto 19328 B 0 B / +0.0% 368 B 0 B / +0.0% 343.619 ms +16.39 ms / +5.0% (worse) 1.320 ms +55.66 us / +4.4% (worse)
Linux fmtprintf 1607896 B +8 B / +0.0004975% (worse) 490042 B 0 B / +0.0% 2.466 s +18.99 ms / +0.8% (worse) 3.103 ms +34.85 us / +1.1% (worse)
Linux fmtprintf-lto 1485616 B 0 B / +0.0% 450888 B 0 B / +0.0% 8.178 s +40.95 ms / +0.5% (worse) 2.971 ms -6.335 us / -0.2% (better)
Linux println 62344 B 0 B / +0.0% 15286 B 0 B / +0.0% 338.933 ms -9.549 ms / -2.7% (better) 1.629 ms +66.51 us / +4.3% (worse)
Linux println-lto 54160 B 0 B / +0.0% 12914 B 0 B / +0.0% 511.976 ms -3.851 ms / -0.7% (better) 1.604 ms +45.6 us / +2.9% (worse)
macOS cprintf 84480 B 0 B / +0.0% 16733 B 0 B / +0.0% 390.444 ms +15.34 ms / +4.1% (worse) 2.729 ms -239.6 us / -8.1% (better)
macOS cprintf-lto 100704 B 0 B / +0.0% 16713 B 0 B / +0.0% 400.480 ms -38.13 ms / -8.7% (better) 2.583 ms -35.38 us / -1.4% (better)
macOS fmtprintf 1470304 B 0 B / +0.0% 867088 B 0 B / +0.0% 2.467 s +303.5 ms / +14.0% (worse) 6.918 ms +763.8 us / +12.4% (worse)
macOS fmtprintf-lto 1175536 B 0 B / +0.0% 862804 B 0 B / +0.0% 5.825 s -108.9 ms / -1.8% (better) 5.109 ms -575.5 us / -10.1% (better)
macOS println 114784 B 0 B / +0.0% 35181 B 0 B / +0.0% 374.746 ms +2.982 ms / +0.8% (worse) 3.659 ms +390.1 us / +11.9% (worse)
macOS println-lto 118656 B 0 B / +0.0% 32833 B 0 B / +0.0% 493.065 ms -15.39 ms / -3.0% (better) 3.167 ms -87.46 us / -2.7% (better)
Windows MinGW cprintf 20480 B 0 B / +0.0% 4662 B 0 B / +0.0% 877.364 ms +42.58 ms / +5.1% (worse) 3.506 ms +35.9 us / +1.0% (worse)
Windows MinGW cprintf-lto 18432 B 0 B / +0.0% 4582 B 0 B / +0.0% 884.704 ms -6.233 ms / -0.7% (better) 3.429 ms +38.4 us / +1.1% (worse)
Windows MinGW fmtprintf 1936384 B 0 B / +0.0% 597398 B 0 B / +0.0% 3.676 s +117.9 ms / +3.3% (worse) 8.549 ms +396.8 us / +4.9% (worse)
Windows MinGW fmtprintf-lto 1990656 B 0 B / +0.0% 590870 B 0 B / +0.0% 9.583 s +444.3 ms / +4.9% (worse) 9.309 ms +1.289 ms / +16.1% (worse)
Windows MinGW println 74240 B 0 B / +0.0% 25062 B 0 B / +0.0% 867.360 ms +30.81 ms / +3.7% (worse) 6.715 ms +58.7 us / +0.9% (worse)
Windows MinGW println-lto 67584 B 0 B / +0.0% 21734 B 0 B / +0.0% 1.063 s -1.76 ms / -0.2% (better) 6.584 ms -298.6 us / -4.3% (better)
Windows MSVC cprintf 12288 B 0 B / +0.0% 4438 B 0 B / +0.0% 580.161 ms -11.92 ms / -2.0% (better) 2.793 ms -116.9 us / -4.0% (better)
Windows MSVC cprintf-lto 11776 B 0 B / +0.0% 4278 B 0 B / +0.0% 590.176 ms +7.689 ms / +1.3% (worse) 2.783 ms -88.5 us / -3.1% (better)
Windows MSVC fmtprintf 1475584 B 0 B / +0.0% 596982 B 0 B / +0.0% 2.691 s +74.82 ms / +2.9% (worse) 6.646 ms -91.9 us / -1.4% (better)
Windows MSVC fmtprintf-lto 1522688 B 0 B / +0.0% 597782 B 0 B / +0.0% 8.593 s +460.5 ms / +5.7% (worse) 6.627 ms -299.9 us / -4.3% (better)
Windows MSVC println 47104 B 0 B / +0.0% 25062 B 0 B / +0.0% 569.033 ms +6.498 ms / +1.2% (worse) 5.323 ms +148.7 us / +2.9% (worse)
Windows MSVC println-lto 44032 B 0 B / +0.0% 22102 B 0 B / +0.0% 755.877 ms +539.5 us / +0.1% (worse) 5.585 ms +362.9 us / +6.9% (worse)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 13.410 ns/op +0.03 ns/op / +0.2% (worse)
Linux BenchmarkMergeCompilerFlags 140.500 ns/op +0.9 ns/op / +0.6% (worse)
Linux BenchmarkMergeLinkerFlags 92.240 ns/op +3.7 ns/op / +4.2% (worse)
Linux BenchmarkChannelBuffered 35.520 ns/op -0.02 ns/op / -0.1% (better)
Linux BenchmarkChannelHandoff 26369 ns/op -550 ns/op / -2.0% (better)
Linux BenchmarkDefer 48.640 ns/op +1.7 ns/op / +3.6% (worse)
Linux BenchmarkDirectCall 1.557 ns/op +0.001 ns/op / +0.1% (worse)
Linux BenchmarkGlobalRead 1.868 ns/op +0.312 ns/op / +20.1% (worse)
Linux BenchmarkGlobalWrite 2.486 ns/op -0.002 ns/op / -0.1% (better)
Linux BenchmarkGoroutine 32072 ns/op +144 ns/op / +0.5% (worse)
Linux BenchmarkInterfaceCall 7.784 ns/op -0.306 ns/op / -3.8% (better)
Linux BenchmarkRuntimeGetG 2.492 ns/op -0.003 ns/op / -0.1% (better)
macOS BenchmarkLookupPCRandom 11.560 ns/op -1.75 ns/op / -13.1% (better)
macOS BenchmarkMergeCompilerFlags 96.890 ns/op -23.91 ns/op / -19.8% (better)
macOS BenchmarkMergeLinkerFlags 60.650 ns/op -21.07 ns/op / -25.8% (better)
macOS BenchmarkChannelBuffered 23.110 ns/op -3.7 ns/op / -13.8% (better)
macOS BenchmarkChannelHandoff 7005 ns/op -2766 ns/op / -28.3% (better)
macOS BenchmarkDefer 36.040 ns/op +2.85 ns/op / +8.6% (worse)
macOS BenchmarkDirectCall 1.018 ns/op -0.046 ns/op / -4.3% (better)
macOS BenchmarkGlobalRead 1.021 ns/op -0.215 ns/op / -17.4% (better)
macOS BenchmarkGlobalWrite 1.017 ns/op -0.144 ns/op / -12.4% (better)
macOS BenchmarkGoroutine 32275 ns/op +1751 ns/op / +5.7% (worse)
macOS BenchmarkInterfaceCall 4.426 ns/op -0.199 ns/op / -4.3% (better)
macOS BenchmarkRuntimeGetG 2.445 ns/op +0.278 ns/op / +12.8% (worse)
Windows MinGW BenchmarkLookupPCRandom 13.110 ns/op -0.06 ns/op / -0.5% (better)
Windows MinGW BenchmarkMergeCompilerFlags 655.200 ns/op +5.7 ns/op / +0.9% (worse)
Windows MinGW BenchmarkMergeLinkerFlags 545.900 ns/op -26.1 ns/op / -4.6% (better)
Windows MinGW BenchmarkChannelBuffered 34.330 ns/op -0.19 ns/op / -0.6% (better)
Windows MinGW BenchmarkChannelHandoff 897.900 ns/op -6.8 ns/op / -0.8% (better)
Windows MinGW BenchmarkDefer 58.520 ns/op -0.83 ns/op / -1.4% (better)
Windows MinGW BenchmarkDirectCall 1.860 ns/op +0.311 ns/op / +20.1% (worse)
Windows MinGW BenchmarkGlobalRead 1.552 ns/op +0.003 ns/op / +0.2% (worse)
Windows MinGW BenchmarkGlobalWrite 2.474 ns/op -0.001 ns/op / -0.0404% (better)
Windows MinGW BenchmarkGoroutine 86262 ns/op -6575 ns/op / -7.1% (better)
Windows MinGW BenchmarkInterfaceCall 9 ns/op -0.308 ns/op / -3.3% (better)
Windows MinGW BenchmarkRuntimeGetG 2.481 ns/op +0.003 ns/op / +0.1% (worse)
Windows MSVC BenchmarkLookupPCRandom 9.635 ns/op +0.082 ns/op / +0.9% (worse)
Windows MSVC BenchmarkMergeCompilerFlags 370.300 ns/op -44 ns/op / -10.6% (better)
Windows MSVC BenchmarkMergeLinkerFlags 330.900 ns/op -8.8 ns/op / -2.6% (better)
Windows MSVC BenchmarkChannelBuffered 30.470 ns/op -0.34 ns/op / -1.1% (better)
Windows MSVC BenchmarkChannelHandoff 1111 ns/op +23 ns/op / +2.1% (worse)
Windows MSVC BenchmarkDefer 49.660 ns/op +5.47 ns/op / +12.4% (worse)
Windows MSVC BenchmarkDirectCall 1.629 ns/op +0.264 ns/op / +19.3% (worse)
Windows MSVC BenchmarkGlobalRead 1.356 ns/op -0.002 ns/op / -0.1% (better)
Windows MSVC BenchmarkGlobalWrite 2.165 ns/op -0.001 ns/op / -0.04617% (better)
Windows MSVC BenchmarkGoroutine 54438 ns/op -917 ns/op / -1.7% (better)
Windows MSVC BenchmarkInterfaceCall 8.152 ns/op +0.127 ns/op / +1.6% (worse)
Windows MSVC BenchmarkRuntimeGetG 2.173 ns/op +0.765 ns/op / +54.3% (worse)

Compared with c4941f9ae706 measured in the same runner job.

@cpunion
cpunion marked this pull request as ready for review August 30, 2026 08:57

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

Code Review: Test suite reorganization

This is a well-structured refactor of the test suite: consolidating test/go acceptance tests into command-integration tests under test/cmd/llgo/, splitting the compiler driver by build tag (//go:build !llgo vs //go:build llgo), adding testdata fixtures, and re-enabling go vet on ./test/go. The design is coherent and the sync.Once build-once pattern for the llgo binary is implemented correctly (build error stored, t.Fatalf called outside the Do closure).

Below are the noteworthy findings. Most are minor; the CI vet change is worth a closer look.

Non-inline / design notes

  • Duplicated helper body. runGoCompiler in test/cmd/llgo/command_helpers_test.go and runCompiler in test/cmd/llgo/command_go_test.go (//go:build !llgo) have byte-for-byte identical bodies. The intent is sound (runCompiler becomes llgo under the llgo tag), but under !llgo the duplication means a future fix to stream/error handling must land in two places. Consider having the !llgo runCompiler delegate to runGoCompiler.

  • runCompiler merges stderr into stdout only on error. On a successful run stderr is discarded; on failure it is appended. For the textual-diff tests (print-builtin, float conversion) this means a warning-only successful run yields different captured text than a failure. Fine for pass/fail, but worth a one-line comment on the convention since three definitions share it.

  • Redundant full-stdlib export. writeToolCompileStdlibImportCfg in test/cmd/llgo/tool_compile_compat_test.go shells out to go list -export ... std per subtest (and per build variant). The Go build cache absorbs this on warm caches, but on cold CI caches it pays the full stdlib export multiple times; memoizing via a package-level sync.Once would remove the repeat.

  • Timeout comment references a stale run. In .github/workflows/go.yml, the "known-good #172 Windows coverage run took about 56 minutes" figure predates this reorg; refresh it if the new workload changes wall-clock time materially. (Info only.)

Everything else — the deliberately-unsafe cgo fault injection (properly gated behind //go:build llgo, volatile used to defeat clang UB folding, MallocFree null-checks), the CHECK-comment fixtures, and the pull_request (not pull_request_target) workflow trigger keeping CODECOV_TOKEN away from fork PRs — checked out cleanly.

Findings without inline locations

  • .github/workflows/go.yml:138: Dropping -vet=off here re-enables go vet on ./test/go. Note that test/go/newexpr_go126_test.go (gated //go:build go1.26) uses llgo's new(expr) extension (new(0), new(123), new(i > 0)), which is not valid standard Go. When this step runs on a Go 1.26+ toolchain with the stock go compiler, that file would fail to build/vet. Please confirm this was validated on the CI Go version — the removed comment previously documented exactly why -vet=off was needed here. Consider adding a short note explaining vet is now safe because the vet-incompatible fixtures moved to test/cmd/llgo (as embedded source strings).

Comment thread test/cmd/llgo/command_helpers_test.go Outdated
Comment thread test/cmd/llgo/command_llgo_test.go
Comment thread test/cmd/llgo/command_llgo_test.go
@cpunion

cpunion commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the remaining non-inline review points in ee6bf8010:

  • the !llgo wrapper now delegates to runGoCompiler, and Go/LLGo share one documented command-output helper;
  • successful runs intentionally compare stdout only, while stderr is appended on failure for diagnostics;
  • the standard-library importcfg is generated once per test process with sync.Once and reused by both subtests;
  • the coverage timeout comment was already refreshed by this PR, and the workflow now explains why test/go can use go test's default vet checks. The current Go 1.27 toolchain officially accepts new(expr), and go test ./test/go passes with vet enabled.

Local validation after the changes:

  • go test -count=1 ./test/cmd/llgo
  • go test -count=1 -tags=llgo ./test/cmd/llgo
  • current LLGo: TestToolCompileFrontendDiagnosticNormalization
  • go test -count=1 ./test/go

@xushiwei
xushiwei merged commit c891707 into xgo-dev:main Aug 30, 2026
50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go-test-compat Go standard-library and GOROOT test compatibility

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants