From 26a78962746d41b6b2b571b13a1569f2d827568c Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Sun, 2 Aug 2026 14:17:59 +0800 Subject: [PATCH 1/2] cmd: qualify the LLVM backend on linux/amd64 --- src/cmd/compile/internal/ssa/ssa2llvm.go | 9 +++++++-- src/cmd/dist/build.go | 1 + src/cmd/go.mod | 2 +- src/cmd/go.sum | 2 ++ src/cmd/internal/testdir/llvm_abi_test.go | 8 ++++---- src/cmd/internal/testdir/llvm_alloca_test.go | 18 ++++++++++++------ .../internal/testdir/llvm_writebarrier_test.go | 11 ++++++----- src/cmd/llvmplugin/CMakeLists.txt | 2 +- .../alloca-pointer-vector-unsupported.ll | 2 +- .../goallc/go-llvm/llvm_link_static.go | 2 +- src/cmd/vendor/modules.txt | 2 +- test/codegen/llvm_memops.go | 4 ++-- 12 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/cmd/compile/internal/ssa/ssa2llvm.go b/src/cmd/compile/internal/ssa/ssa2llvm.go index fc8ac3ed11b71d..a987a79c1d58e9 100644 --- a/src/cmd/compile/internal/ssa/ssa2llvm.go +++ b/src/cmd/compile/internal/ssa/ssa2llvm.go @@ -600,7 +600,12 @@ func (lfc *LLVMFuncContext) staticCall(v *Value) llvm.Value { sig := llvmStaticCallSignature(v, aux, llvmSignature(aux)) cc := llvmCallConv(aux.ABI().Which()) fn := getOrInsertLLVMFunction(aux.Fn.Name, sig, cc) - rawWriteBarrier := aux.Fn == ir.Syms.WBZero || aux.Fn == ir.Syms.WBMove + // AMD64 rewrites some Move and Eq operations to static runtime calls before + // LLVM emission. Keep the same leaf contract as the dedicated LLVM lowering + // paths so RewriteStatepointsForGC does not turn these raw helpers into + // statepoints. + llvmGCLeaf := aux.Fn == ir.Syms.WBZero || aux.Fn == ir.Syms.WBMove || + aux.Fn == ir.Syms.Memmove || aux.Fn == ir.Syms.Memequal args := make([]llvm.Value, 0, aux.NArgs()) for i := int64(0); i < aux.NArgs(); i++ { arg := lfc.GenLV(v.Args[i]) @@ -615,7 +620,7 @@ func (lfc *LLVMFuncContext) staticCall(v *Value) llvm.Value { } call := lfc.b.CreateCall(sig.Type, fn, args, name) call.SetInstructionCallConv(cc) - if rawWriteBarrier { + if llvmGCLeaf { markLLVMGCLeaf(fn, call) } return call diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index b384f77958fa12..d033311b661a93 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -1924,6 +1924,7 @@ func isGoallcLLVMSystemLibrary(library string) bool { allowed := map[string]bool{ "-lm": true, "-lz": true, + "-lzstd": true, "-lxml2": true, "-ldl": true, "-lpthread": true, diff --git a/src/cmd/go.mod b/src/cmd/go.mod index b7950049445d37..216d2c60cbcae5 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -3,7 +3,7 @@ module cmd go 1.27 require ( - github.com/goallc/go-llvm v0.0.0-20260802014221-e0a85f0a18bc + github.com/goallc/go-llvm v0.0.0-20260802061343-79fd6b1ee93f github.com/google/pprof v0.0.0-20260507013755-92041b743c96 golang.org/x/arch v0.27.1-0.20260521044007-9c1a596a2c97 golang.org/x/build v0.0.0-20260522210304-d55d0041b921 diff --git a/src/cmd/go.sum b/src/cmd/go.sum index eb02792871ea3e..dae471622c4e4e 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -1,5 +1,7 @@ github.com/goallc/go-llvm v0.0.0-20260802014221-e0a85f0a18bc h1:d8GeynggX+JI+ntwrs/9PyA3QYqYtNar6XqPSDEdoAE= github.com/goallc/go-llvm v0.0.0-20260802014221-e0a85f0a18bc/go.mod h1:s70tU8u5zDo1LwIPqBxh6Qgol/tNt1kmn9caPfsTz8A= +github.com/goallc/go-llvm v0.0.0-20260802061343-79fd6b1ee93f h1:B5ddEWuCodOV+J7LfMGJLY/IcLWOA378WXhln6IfhSA= +github.com/goallc/go-llvm v0.0.0-20260802061343-79fd6b1ee93f/go.mod h1:s70tU8u5zDo1LwIPqBxh6Qgol/tNt1kmn9caPfsTz8A= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20260507013755-92041b743c96 h1:YDDnaZ9afWajDboPMt9Vikqca/yWAX7KAxVzb4lJU1M= diff --git a/src/cmd/internal/testdir/llvm_abi_test.go b/src/cmd/internal/testdir/llvm_abi_test.go index a1f8c7b274129d..165f539422f552 100644 --- a/src/cmd/internal/testdir/llvm_abi_test.go +++ b/src/cmd/internal/testdir/llvm_abi_test.go @@ -424,7 +424,7 @@ func runLLVMAMD64ArgsPointerMapDifferentialTest(t *testing.T, gorootTestDir stri machinePatterns := map[string][]string{ "p.initializedPointerResult": { `STATEPOINT 5147424658422983495,[^\n]*\$rsp, 72,`, - `STATEPOINT -[0-9]+,[^\n]*\$rsp, 8,`, + `STATEPOINT -[0-9]+,[^\n]*\$rsp, 0,`, }, "p.partiallyInitializedAggregateResult": { `STATEPOINT 5147424658422983495,[^\n]*\$rsp, 80,[^\n]*\$rsp, 88,`, @@ -483,11 +483,11 @@ func runLLVMAMD64ArgsPointerMapDifferentialTest(t *testing.T, gorootTestDir stri cases := []amd64Case{ { name: "initializedPointerResult", args: 72, entryBits: []int{8}, - goallcLocals: 24, goallcArgs: [][]int{{8}, nil}, - goallcMaps: [][]int{nil, {2}}, + goallcLocals: 16, goallcArgs: [][]int{{8}, nil}, + goallcMaps: [][]int{nil, {1}}, nativePCData: []int32{-1, 0, -1}, goallcPCData: []int32{-1, 1, 0}, nativeQueries: []int32{0, -1}, goallcQueries: []int32{1, 0}, - nativePCSP: []int32{0, 8, 0}, goallcPCSP: []int32{0, 8, 24, 8, 0}, + nativePCSP: []int32{0, 8, 0}, goallcPCSP: []int32{0, 8, 16, 8, 0}, }, { name: "partiallyInitializedAggregateResult", args: 88, diff --git a/src/cmd/internal/testdir/llvm_alloca_test.go b/src/cmd/internal/testdir/llvm_alloca_test.go index e5566501bbed0b..017ad50dcbcf59 100644 --- a/src/cmd/internal/testdir/llvm_alloca_test.go +++ b/src/cmd/internal/testdir/llvm_alloca_test.go @@ -16,8 +16,9 @@ import ( func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) { t.Helper() - if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { - t.Skip("exact alloca statepoint frame and stack-map expectations are qualified on darwin/arm64") + if runtime.GOOS+"/"+runtime.GOARCH != "darwin/arm64" && + runtime.GOOS+"/"+runtime.GOARCH != "linux/amd64" { + t.Skip("exact alloca statepoint frame and stack-map expectations are qualified on darwin/arm64 and linux/amd64") } dir := t.TempDir() @@ -98,7 +99,7 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) { "-load-pass-plugin="+plugin, "-stop-after=finalize-isel", "-o", "-", goallcIR) machineFunction := llvmABIMachineFunction(t, machineIR, "p.localAcrossSafepoints") - if !regexp.MustCompile(`(?s)stack:.*?size:\s+40,\s+alignment:\s+16`).Match(machineFunction) { + if !regexp.MustCompile(`(?s)stack:.*?size:\s+40,\s+alignment:\s+8`).Match(machineFunction) { t.Fatalf("MIR has no 40-byte pointer alloca\n%s", machineFunction) } ordinaryStatepoints := regexp.MustCompile(`(?m)^.*STATEPOINT.*%stack\.[0-9]+.*$`). @@ -132,12 +133,17 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) { goallcAssembly := runLLVMABICommand(t, nil, llc, "-load-pass-plugin="+plugin, "-verify-machineinstrs", "-filetype=asm", "-o", "-", goallcIR) - betweenCalls := regexp.MustCompile(`(?s)\bbl\s+p\.mutateLocal\n(.*?)\bbl\s+p\.safepoint`). - FindSubmatch(goallcAssembly) + betweenCallsPattern := `(?s)\bbl\s+p\.mutateLocal\n(.*?)\bbl\s+p\.safepoint` + storePattern := `(?m)^\s*(?:str|stp)\b` + if runtime.GOARCH == "amd64" { + betweenCallsPattern = `(?s)\bcallq\s+p\.mutateLocal\n(.*?)\bcallq\s+p\.safepoint` + storePattern = `(?m)^\s*mov[a-z]*\s+[^,\n]+,\s*-[0-9]+\(%rbp\)` + } + betweenCalls := regexp.MustCompile(betweenCallsPattern).FindSubmatch(goallcAssembly) if len(betweenCalls) != 2 { t.Fatalf("GoALLC assembly has no adjacent mutateLocal/safepoint calls\n%s", goallcAssembly) } - if regexp.MustCompile(`(?m)^\s*(?:str|stp)\b`).Match(betweenCalls[1]) { + if regexp.MustCompile(storePattern).Match(betweenCalls[1]) { t.Fatalf("GoALLC restored an alloca field after mutateLocal:\n%s", betweenCalls[1]) } diff --git a/src/cmd/internal/testdir/llvm_writebarrier_test.go b/src/cmd/internal/testdir/llvm_writebarrier_test.go index 4b52f5f8aa132e..c21320e8113653 100644 --- a/src/cmd/internal/testdir/llvm_writebarrier_test.go +++ b/src/cmd/internal/testdir/llvm_writebarrier_test.go @@ -14,8 +14,9 @@ import ( func runLLVMWriteBarrierHelperTest(t *testing.T) { t.Helper() - if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { - t.Skip("write-barrier helper IR and RS4GC expectations are qualified on darwin/arm64") + if runtime.GOOS+"/"+runtime.GOARCH != "darwin/arm64" && + runtime.GOOS+"/"+runtime.GOARCH != "linux/amd64" { + t.Skip("write-barrier helper IR and RS4GC expectations are qualified on darwin/arm64 and linux/amd64") } dir := t.TempDir() @@ -23,7 +24,7 @@ func runLLVMWriteBarrierHelperTest(t *testing.T) { const program = `package p type Big [32]*int -type Move8 [8]*int +type Move2 [2]*int func ordinary(*int) @@ -37,7 +38,7 @@ func heapZero(dst *Big, live *int) { } //go:noinline -func heapMove(dst, src *Move8) { +func heapMove(dst, src *Move2) { *dst = *src } ` @@ -70,7 +71,7 @@ func heapMove(dst, src *Move8) { name: "wbMove", body: llvmAllocaIRFunction(t, ir, "p.heapMove"), want: [][]byte{ - []byte("call goabiinternal void @runtime.wbMove(ptr @\"type:p.Move8\", ptr %dst, ptr %src)"), + []byte("call goabiinternal void @runtime.wbMove(ptr @\"type:p.Move2\", ptr %dst, ptr %src)"), }, }, } { diff --git a/src/cmd/llvmplugin/CMakeLists.txt b/src/cmd/llvmplugin/CMakeLists.txt index c0901aff884ff7..85de2a7b9c7949 100644 --- a/src/cmd/llvmplugin/CMakeLists.txt +++ b/src/cmd/llvmplugin/CMakeLists.txt @@ -168,7 +168,7 @@ if(BUILD_TESTING) foreach(GOALLC_CALLIND_TARGET IN ITEMS X86 AArch64) if(GOALLC_CALLIND_TARGET STREQUAL "X86") set(GOALLC_CALLIND_TRIPLE "x86_64-unknown-linux-goobj") - set(GOALLC_CALLIND_OFFSET 20) + set(GOALLC_CALLIND_OFFSET 17) set(GOALLC_DIRECT_CALL_RELOC 7) else() set(GOALLC_CALLIND_TRIPLE "aarch64-unknown-linux-goobj") diff --git a/src/cmd/llvmplugin/testdata/alloca-pointer-vector-unsupported.ll b/src/cmd/llvmplugin/testdata/alloca-pointer-vector-unsupported.ll index 97580c33975290..c8168da89f5824 100644 --- a/src/cmd/llvmplugin/testdata/alloca-pointer-vector-unsupported.ll +++ b/src/cmd/llvmplugin/testdata/alloca-pointer-vector-unsupported.ll @@ -4,7 +4,7 @@ declare goabiinternal void @safepoint() define goabiinternal void @pointer_vector_alloca() "go-stack-growth-statepoint" gc "goallc" { entry: - %slot = alloca <2 x ptr>, align 16 + %slot = alloca <2 x ptr>, align 8 call goabiinternal void @safepoint() ret void } diff --git a/src/cmd/vendor/github.com/goallc/go-llvm/llvm_link_static.go b/src/cmd/vendor/github.com/goallc/go-llvm/llvm_link_static.go index c9667f64c71ff0..b8dacfb5a49fa3 100644 --- a/src/cmd/vendor/github.com/goallc/go-llvm/llvm_link_static.go +++ b/src/cmd/vendor/github.com/goallc/go-llvm/llvm_link_static.go @@ -4,7 +4,7 @@ package llvm /* #cgo darwin LDFLAGS: -L${SRCDIR} -lLLVMGoALLC -lm -lz -lxml2 -#cgo linux LDFLAGS: -L${SRCDIR} -lLLVMGoALLC -lm -lz -lxml2 -ldl -lpthread -lrt +#cgo linux LDFLAGS: -L${SRCDIR} -lLLVMGoALLC -lm -lz -lzstd -lxml2 -ldl -lpthread -lrt */ import "C" diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 86e95c395fb8ac..3786d654afb0ed 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/goallc/go-llvm v0.0.0-20260802014221-e0a85f0a18bc +# github.com/goallc/go-llvm v0.0.0-20260802061343-79fd6b1ee93f ## explicit; go 1.24 github.com/goallc/go-llvm # github.com/google/pprof v0.0.0-20260507013755-92041b743c96 diff --git a/test/codegen/llvm_memops.go b/test/codegen/llvm_memops.go index 72a983e10ae318..09dc0b585f40d4 100644 --- a/test/codegen/llvm_memops.go +++ b/test/codegen/llvm_memops.go @@ -75,13 +75,13 @@ func llvmMovePointerToStack(src *llvmPointerStackZero) *int { } // LLVM-DAG: define goabiinternal void @codegen.llvmMoveOverlapSized( -// LLVM-DAG: call void @llvm.memmove.p0.p0.i64(ptr align 1 %dst, ptr align 1 %src, i64 32, i1 false) +// LLVM-DAG: call {{(void @llvm\.memmove\.p0\.p0\.i64\(ptr align 1 %dst, ptr align 1 %src, i64 32, i1 false\)|goabiinternal void @runtime\.memmove\(ptr %dst, ptr %src, i64 32\) #[0-9]+)}} func llvmMoveOverlapSized(dst, src *[32]byte) { *dst = *src } // LLVM-DAG: define goabiinternal void @codegen.llvmMoveAligned( -// LLVM-DAG: call void @llvm.memmove.p0.p0.i64(ptr align 8 %dst, ptr align 8 %src, i64 24, i1 false) +// LLVM-DAG: call {{(void @llvm\.memmove\.p0\.p0\.i64\(ptr align 8 %dst, ptr align 8 %src, i64 24, i1 false\)|goabiinternal void @runtime\.memmove\(ptr %dst, ptr %src, i64 24\) #[0-9]+)}} func llvmMoveAligned(dst, src *[3]uint64) { *dst = *src } From d504650e5375399c2e891818d6ccf0d1721a4924 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Sun, 2 Aug 2026 14:40:26 +0800 Subject: [PATCH 2/2] cmd: refresh go-llvm and split LLVM architecture checks --- src/cmd/go.mod | 2 +- src/cmd/go.sum | 6 ++-- src/cmd/internal/testdir/llvm_abi_test.go | 14 +++++--- src/cmd/internal/testdir/llvm_alloca_test.go | 35 +++++++++++++------ src/cmd/internal/testdir/llvm_test.go | 21 +++++++++-- .../testdir/llvm_writebarrier_test.go | 25 +++++++++---- src/cmd/vendor/modules.txt | 2 +- test/codegen/llvm_memops.go | 6 ++-- 8 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/cmd/go.mod b/src/cmd/go.mod index 216d2c60cbcae5..76414c2f822299 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -3,7 +3,7 @@ module cmd go 1.27 require ( - github.com/goallc/go-llvm v0.0.0-20260802061343-79fd6b1ee93f + github.com/goallc/go-llvm v0.0.0-20260802062312-1f9c9bc944a6 github.com/google/pprof v0.0.0-20260507013755-92041b743c96 golang.org/x/arch v0.27.1-0.20260521044007-9c1a596a2c97 golang.org/x/build v0.0.0-20260522210304-d55d0041b921 diff --git a/src/cmd/go.sum b/src/cmd/go.sum index dae471622c4e4e..6a983c36d539ee 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -1,7 +1,5 @@ -github.com/goallc/go-llvm v0.0.0-20260802014221-e0a85f0a18bc h1:d8GeynggX+JI+ntwrs/9PyA3QYqYtNar6XqPSDEdoAE= -github.com/goallc/go-llvm v0.0.0-20260802014221-e0a85f0a18bc/go.mod h1:s70tU8u5zDo1LwIPqBxh6Qgol/tNt1kmn9caPfsTz8A= -github.com/goallc/go-llvm v0.0.0-20260802061343-79fd6b1ee93f h1:B5ddEWuCodOV+J7LfMGJLY/IcLWOA378WXhln6IfhSA= -github.com/goallc/go-llvm v0.0.0-20260802061343-79fd6b1ee93f/go.mod h1:s70tU8u5zDo1LwIPqBxh6Qgol/tNt1kmn9caPfsTz8A= +github.com/goallc/go-llvm v0.0.0-20260802062312-1f9c9bc944a6 h1:5Ipo1Z/H/Aet8t3N1+a0o+vh7NVRfXpWeCYngaKHOOM= +github.com/goallc/go-llvm v0.0.0-20260802062312-1f9c9bc944a6/go.mod h1:s70tU8u5zDo1LwIPqBxh6Qgol/tNt1kmn9caPfsTz8A= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20260507013755-92041b743c96 h1:YDDnaZ9afWajDboPMt9Vikqca/yWAX7KAxVzb4lJU1M= diff --git a/src/cmd/internal/testdir/llvm_abi_test.go b/src/cmd/internal/testdir/llvm_abi_test.go index 165f539422f552..b808dd12b35342 100644 --- a/src/cmd/internal/testdir/llvm_abi_test.go +++ b/src/cmd/internal/testdir/llvm_abi_test.go @@ -84,14 +84,18 @@ type llvmABICase struct { func runLLVMABIDifferentialTest(t *testing.T, gorootTestDir string) { t.Helper() - if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" { + switch runtime.GOOS + "/" + runtime.GOARCH { + case "darwin/arm64": + runLLVMAArch64ABIDifferentialTest(t, gorootTestDir) + case "linux/amd64": runLLVMAMD64ArgsPointerMapDifferentialTest(t, gorootTestDir) - return - } - if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { - t.Skip("exact ABI differential expectations are currently qualified on darwin/arm64") + default: + t.Skip("exact ABI differential expectations are currently qualified on darwin/arm64 and linux/amd64") } +} +func runLLVMAArch64ABIDifferentialTest(t *testing.T, gorootTestDir string) { + t.Helper() dir := t.TempDir() source := filepath.Join(gorootTestDir, "abi", "llvm_args_results.go") nativeObject := filepath.Join(dir, "native.o") diff --git a/src/cmd/internal/testdir/llvm_alloca_test.go b/src/cmd/internal/testdir/llvm_alloca_test.go index 017ad50dcbcf59..c580f7405902f5 100644 --- a/src/cmd/internal/testdir/llvm_alloca_test.go +++ b/src/cmd/internal/testdir/llvm_alloca_test.go @@ -14,10 +14,29 @@ import ( "testing" ) +type llvmAllocaArchitectureChecks struct { + machineAllocaPattern string + betweenCallsPattern string + restoredStorePattern string +} + +var llvmAllocaChecks = map[string]llvmAllocaArchitectureChecks{ + "darwin/arm64": { + machineAllocaPattern: `(?s)stack:.*?size:\s+40,\s+alignment:\s+8`, + betweenCallsPattern: `(?s)\bbl\s+p\.mutateLocal\n(.*?)\bbl\s+p\.safepoint`, + restoredStorePattern: `(?m)^\s*(?:str|stp)\b`, + }, + "linux/amd64": { + machineAllocaPattern: `(?s)stack:.*?size:\s+40,\s+alignment:\s+8`, + betweenCallsPattern: `(?s)\bcallq\s+p\.mutateLocal\n(.*?)\bcallq\s+p\.safepoint`, + restoredStorePattern: `(?m)^\s*mov[a-z]*\s+[^,\n]+,\s*-[0-9]+\(%rbp\)`, + }, +} + func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) { t.Helper() - if runtime.GOOS+"/"+runtime.GOARCH != "darwin/arm64" && - runtime.GOOS+"/"+runtime.GOARCH != "linux/amd64" { + checks, ok := llvmAllocaChecks[runtime.GOOS+"/"+runtime.GOARCH] + if !ok { t.Skip("exact alloca statepoint frame and stack-map expectations are qualified on darwin/arm64 and linux/amd64") } @@ -99,7 +118,7 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) { "-load-pass-plugin="+plugin, "-stop-after=finalize-isel", "-o", "-", goallcIR) machineFunction := llvmABIMachineFunction(t, machineIR, "p.localAcrossSafepoints") - if !regexp.MustCompile(`(?s)stack:.*?size:\s+40,\s+alignment:\s+8`).Match(machineFunction) { + if !regexp.MustCompile(checks.machineAllocaPattern).Match(machineFunction) { t.Fatalf("MIR has no 40-byte pointer alloca\n%s", machineFunction) } ordinaryStatepoints := regexp.MustCompile(`(?m)^.*STATEPOINT.*%stack\.[0-9]+.*$`). @@ -133,17 +152,11 @@ func runLLVMAllocaStatepointTest(t *testing.T, gorootTestDir string) { goallcAssembly := runLLVMABICommand(t, nil, llc, "-load-pass-plugin="+plugin, "-verify-machineinstrs", "-filetype=asm", "-o", "-", goallcIR) - betweenCallsPattern := `(?s)\bbl\s+p\.mutateLocal\n(.*?)\bbl\s+p\.safepoint` - storePattern := `(?m)^\s*(?:str|stp)\b` - if runtime.GOARCH == "amd64" { - betweenCallsPattern = `(?s)\bcallq\s+p\.mutateLocal\n(.*?)\bcallq\s+p\.safepoint` - storePattern = `(?m)^\s*mov[a-z]*\s+[^,\n]+,\s*-[0-9]+\(%rbp\)` - } - betweenCalls := regexp.MustCompile(betweenCallsPattern).FindSubmatch(goallcAssembly) + betweenCalls := regexp.MustCompile(checks.betweenCallsPattern).FindSubmatch(goallcAssembly) if len(betweenCalls) != 2 { t.Fatalf("GoALLC assembly has no adjacent mutateLocal/safepoint calls\n%s", goallcAssembly) } - if regexp.MustCompile(storePattern).Match(betweenCalls[1]) { + if regexp.MustCompile(checks.restoredStorePattern).Match(betweenCalls[1]) { t.Fatalf("GoALLC restored an alloca field after mutateLocal:\n%s", betweenCalls[1]) } diff --git a/src/cmd/internal/testdir/llvm_test.go b/src/cmd/internal/testdir/llvm_test.go index 1a23caa70f7c77..21c141e5a7da9a 100644 --- a/src/cmd/internal/testdir/llvm_test.go +++ b/src/cmd/internal/testdir/llvm_test.go @@ -273,7 +273,7 @@ func runLLVMCodegenTest(t *testing.T, gorootTestDir, name string) { t.Fatalf("LLVM verifier failed: %v\n%s", err, out) } fileCheck := llvmToolPath(t, "FileCheck", "GOALLC_FILECHECK") - cmd = exec.Command(fileCheck, "--check-prefix=LLVM", source) + cmd = exec.Command(fileCheck, "--check-prefixes="+llvmFileCheckPrefixes("LLVM", src), source) cmd.Stdin = bytes.NewReader(irBytes) cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=") if out, err := cmd.CombinedOutput(); err != nil { @@ -292,7 +292,7 @@ func runLLVMCodegenTest(t *testing.T, gorootTestDir, name string) { if err != nil { t.Fatalf("LLVM optimization failed: %v\n%s", err, stderr.Bytes()) } - cmd = exec.Command(fileCheck, "--check-prefix=LLVM-OPT", source) + cmd = exec.Command(fileCheck, "--check-prefixes="+llvmFileCheckPrefixes("LLVM-OPT", src), source) cmd.Stdin = bytes.NewReader(optimizedIR) cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=") if out, err := cmd.CombinedOutput(); err != nil { @@ -300,6 +300,23 @@ func runLLVMCodegenTest(t *testing.T, gorootTestDir, name string) { } } +func llvmFileCheckPrefixes(base string, source []byte) string { + prefixes := []string{base} + var architecturePrefix string + switch runtime.GOARCH { + case "amd64": + architecturePrefix = base + "-AMD64" + case "arm64": + architecturePrefix = base + "-ARM64" + } + if architecturePrefix != "" && + (bytes.Contains(source, []byte(architecturePrefix+":")) || + bytes.Contains(source, []byte(architecturePrefix+"-"))) { + prefixes = append(prefixes, architecturePrefix) + } + return strings.Join(prefixes, ",") +} + func runLLVMWriteBarrierIRTests(t *testing.T) { tests := []struct { name string diff --git a/src/cmd/internal/testdir/llvm_writebarrier_test.go b/src/cmd/internal/testdir/llvm_writebarrier_test.go index c21320e8113653..e0af56ccc9a0a2 100644 --- a/src/cmd/internal/testdir/llvm_writebarrier_test.go +++ b/src/cmd/internal/testdir/llvm_writebarrier_test.go @@ -6,25 +6,36 @@ package testdir_test import ( "bytes" + "fmt" "os" "path/filepath" "runtime" "testing" ) +type llvmWriteBarrierArchitectureChecks struct { + moveType string + moveElements int +} + +var llvmWriteBarrierChecks = map[string]llvmWriteBarrierArchitectureChecks{ + "darwin/arm64": {moveType: "Move8", moveElements: 8}, + "linux/amd64": {moveType: "Move2", moveElements: 2}, +} + func runLLVMWriteBarrierHelperTest(t *testing.T) { t.Helper() - if runtime.GOOS+"/"+runtime.GOARCH != "darwin/arm64" && - runtime.GOOS+"/"+runtime.GOARCH != "linux/amd64" { + checks, ok := llvmWriteBarrierChecks[runtime.GOOS+"/"+runtime.GOARCH] + if !ok { t.Skip("write-barrier helper IR and RS4GC expectations are qualified on darwin/arm64 and linux/amd64") } dir := t.TempDir() source := filepath.Join(dir, "writebarrier.go") - const program = `package p + program := fmt.Sprintf(`package p type Big [32]*int -type Move2 [2]*int +type %[1]s [%[2]d]*int func ordinary(*int) @@ -38,10 +49,10 @@ func heapZero(dst *Big, live *int) { } //go:noinline -func heapMove(dst, src *Move2) { +func heapMove(dst, src *%[1]s) { *dst = *src } -` +`, checks.moveType, checks.moveElements) if err := os.WriteFile(source, []byte(program), 0o666); err != nil { t.Fatal(err) } @@ -71,7 +82,7 @@ func heapMove(dst, src *Move2) { name: "wbMove", body: llvmAllocaIRFunction(t, ir, "p.heapMove"), want: [][]byte{ - []byte("call goabiinternal void @runtime.wbMove(ptr @\"type:p.Move2\", ptr %dst, ptr %src)"), + []byte(fmt.Sprintf("call goabiinternal void @runtime.wbMove(ptr @\"type:p.%s\", ptr %%dst, ptr %%src)", checks.moveType)), }, }, } { diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 3786d654afb0ed..4b1b0d2daa3a18 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/goallc/go-llvm v0.0.0-20260802061343-79fd6b1ee93f +# github.com/goallc/go-llvm v0.0.0-20260802062312-1f9c9bc944a6 ## explicit; go 1.24 github.com/goallc/go-llvm # github.com/google/pprof v0.0.0-20260507013755-92041b743c96 diff --git a/test/codegen/llvm_memops.go b/test/codegen/llvm_memops.go index 09dc0b585f40d4..ddb23d04da8adf 100644 --- a/test/codegen/llvm_memops.go +++ b/test/codegen/llvm_memops.go @@ -75,13 +75,15 @@ func llvmMovePointerToStack(src *llvmPointerStackZero) *int { } // LLVM-DAG: define goabiinternal void @codegen.llvmMoveOverlapSized( -// LLVM-DAG: call {{(void @llvm\.memmove\.p0\.p0\.i64\(ptr align 1 %dst, ptr align 1 %src, i64 32, i1 false\)|goabiinternal void @runtime\.memmove\(ptr %dst, ptr %src, i64 32\) #[0-9]+)}} +// LLVM-ARM64-DAG: call void @llvm.memmove.p0.p0.i64(ptr align 1 %dst, ptr align 1 %src, i64 32, i1 false) +// LLVM-AMD64-DAG: call goabiinternal void @runtime.memmove(ptr %dst, ptr %src, i64 32) #{{[0-9]+}} func llvmMoveOverlapSized(dst, src *[32]byte) { *dst = *src } // LLVM-DAG: define goabiinternal void @codegen.llvmMoveAligned( -// LLVM-DAG: call {{(void @llvm\.memmove\.p0\.p0\.i64\(ptr align 8 %dst, ptr align 8 %src, i64 24, i1 false\)|goabiinternal void @runtime\.memmove\(ptr %dst, ptr %src, i64 24\) #[0-9]+)}} +// LLVM-ARM64-DAG: call void @llvm.memmove.p0.p0.i64(ptr align 8 %dst, ptr align 8 %src, i64 24, i1 false) +// LLVM-AMD64-DAG: call goabiinternal void @runtime.memmove(ptr %dst, ptr %src, i64 24) #{{[0-9]+}} func llvmMoveAligned(dst, src *[3]uint64) { *dst = *src }