Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
)

require (
github.com/goplus/plan9asm v0.0.0-20260307134905-822503d6bf44
github.com/goplus/plan9asm v0.1.1-0.20260314085947-7f506d4eb357
github.com/marcinbor85/gohex v0.0.0-20210308104911-55fb1c624d84
github.com/mattn/go-tty v0.0.7
github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1
Expand Down
10 changes: 2 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,8 @@ github.com/goplus/llvm v0.8.7 h1:FUpjuZ4Y+F9wNw2ztN5dCGnZnAQgH1YHaYEiX2QpfkQ=
github.com/goplus/llvm v0.8.7/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
github.com/goplus/mod v0.19.5 h1:36sbRxKie2Or9aSfWT06X6WV37sBwARXYfKfGfHzEQs=
github.com/goplus/mod v0.19.5/go.mod h1:T6Ta3xPXx8NQaRb8h632P5iBgIB77zXfh9vLNRFqMqk=
github.com/goplus/plan9asm v0.0.0-20260212064924-71ea5584065b h1:DFZ0n92lGC3ogziCvtAHK8TKJf7oqiT4U576TIENiyk=
github.com/goplus/plan9asm v0.0.0-20260212064924-71ea5584065b/go.mod h1:nnr49+IlbnOI5c0yb1fkbilBRcr50RPX0JAreDdYTUI=
github.com/goplus/plan9asm v0.0.0-20260306151730-9203c54dfd4c h1:1S6F8dDxAnRiZAhqKjzLhAdx71jOaB5YdlHzfwCkdH8=
github.com/goplus/plan9asm v0.0.0-20260306151730-9203c54dfd4c/go.mod h1:nnr49+IlbnOI5c0yb1fkbilBRcr50RPX0JAreDdYTUI=
github.com/goplus/plan9asm v0.0.0-20260307044640-a5f1e3b27fc1 h1:hlVAtc3x34x6CQoOPnkOtz9p4juJ+cEZzgDyJpWnit4=
github.com/goplus/plan9asm v0.0.0-20260307044640-a5f1e3b27fc1/go.mod h1:nnr49+IlbnOI5c0yb1fkbilBRcr50RPX0JAreDdYTUI=
github.com/goplus/plan9asm v0.0.0-20260307134905-822503d6bf44 h1:zZXTz5CBO1uZIJGZSGWov5gE1CbulJ1QH6ruTMv3UdY=
github.com/goplus/plan9asm v0.0.0-20260307134905-822503d6bf44/go.mod h1:gpS4VVNoRykYTtc8kPFBowraN5SrBj8lIjW8/lNjaG4=
github.com/goplus/plan9asm v0.1.1-0.20260314085947-7f506d4eb357 h1:4typFkJN/D0YbNeNi5UIMoQdlWC+E4dn8goD4ddjMFs=
github.com/goplus/plan9asm v0.1.1-0.20260314085947-7f506d4eb357/go.mod h1:gpS4VVNoRykYTtc8kPFBowraN5SrBj8lIjW8/lNjaG4=
github.com/marcinbor85/gohex v0.0.0-20210308104911-55fb1c624d84 h1:hyAgCuG5nqTMDeUD8KZs7HSPs6KprPgPP8QmGV8nyvk=
github.com/marcinbor85/gohex v0.0.0-20210308104911-55fb1c624d84/go.mod h1:Pb6XcsXyropB9LNHhnqaknG/vEwYztLkQzVCHv8sQ3M=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
Expand Down
10 changes: 6 additions & 4 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -1253,10 +1253,12 @@ func buildPkg(ctx *context, aPkg *aPackage, verbose bool) error {
}
aPkg.ObjFiles = append(aPkg.ObjFiles, cgoLLFiles...)
aPkg.ObjFiles = append(aPkg.ObjFiles, concatPkgLinkFiles(ctx, pkg, printCmds)...)
if asmObjFiles, err := compilePkgSFiles(ctx, aPkg, pkg, printCmds); err != nil {
return err
} else {
aPkg.ObjFiles = append(aPkg.ObjFiles, asmObjFiles...)
if aPkg.AltPkg == nil || llruntime.HasAdditiveAltPkg(pkgPath) {
if asmObjFiles, err := compilePkgSFiles(ctx, aPkg, pkg, printCmds); err != nil {
return err
} else {
aPkg.ObjFiles = append(aPkg.ObjFiles, asmObjFiles...)
}
}
if aliasObjs, err := buildGoCgoAliasObjects(ctx, pkgPath, aPkg.Package.Syntax, printCmds); err != nil {
return err
Expand Down
41 changes: 32 additions & 9 deletions internal/build/plan9asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ func plan9asmSigsForPkg(ctx *context, pkgPath string) (map[string]struct{}, erro
plan9AsmSigCache.Store(key, sigs)
return sigs, nil
}
if hasAltPkgForTarget(ctx.buildConf, pkgPath) && !llruntime.HasAdditiveAltPkgForGOARCH(pkgPath, ctx.buildConf.Goarch) {
plan9AsmSigCache.Store(key, sigs)
return sigs, nil
}

var pkg *packages.Package
for p := range ctx.pkgs {
Expand Down Expand Up @@ -297,10 +301,10 @@ func (ctx *context) plan9asmEnabled(pkgPath string) bool {
}

func hasAltPkgForTarget(conf *Config, pkgPath string) bool {
if !llruntime.HasAltPkg(pkgPath) {
if conf == nil || !llruntime.HasAltPkgForGOARCH(pkgPath, conf.Goarch) {
return false
}
if llruntime.HasAdditiveAltPkg(pkgPath) {
if llruntime.HasAdditiveAltPkgForGOARCH(pkgPath, conf.Goarch) {
return true
}
// When Plan9 asm translation is enabled, avoid also pulling in alt packages
Expand Down Expand Up @@ -334,7 +338,7 @@ func plan9asmEnabledByDefault(conf *Config, pkgPath string) bool {
if !archSupportsPlan9AsmDefaults(conf.Goarch) {
return false
}
return !llruntime.HasAltPkg(pkgPath) || llruntime.HasAdditiveAltPkg(pkgPath)
return !llruntime.HasAltPkgForGOARCH(pkgPath, conf.Goarch) || llruntime.HasAdditiveAltPkgForGOARCH(pkgPath, conf.Goarch)
}

func pkgSFiles(ctx *context, pkg *packages.Package) ([]string, error) {
Expand Down Expand Up @@ -403,14 +407,33 @@ func pkgSFiles(ctx *context, pkg *packages.Package) ([]string, error) {
return paths, nil
}
}
// Embedded ARM targets currently reuse GOOS=linux metadata, but they do not
// have a Linux syscall surface. Skip syscall asm in that mode so embedded
// builds do not inherit Linux/ARM-specific frame layouts.
if shouldSkipPlan9AsmSFilesForTarget(ctx.buildConf, pkg.PkgPath) {
ctx.sfilesCache[pkg.ID] = nil
return nil, nil
}

paths := make([]string, 0, len(lp.SFiles))
for _, f := range lp.SFiles {
if lp.Dir == "" {
paths := selectedSFiles(lp.Dir, lp.SFiles)
ctx.sfilesCache[pkg.ID] = paths
return paths, nil
}

func selectedSFiles(dir string, files []string) []string {
if dir == "" || len(files) == 0 {
return nil
}
paths := make([]string, 0, len(files))
for _, f := range files {
if strings.HasSuffix(f, "_test.s") || strings.HasSuffix(f, "_test.S") {
continue
}
paths = append(paths, filepath.Join(lp.Dir, f))
paths = append(paths, filepath.Join(dir, f))
}
ctx.sfilesCache[pkg.ID] = paths
return paths, nil
return paths
}

func shouldSkipPlan9AsmSFilesForTarget(conf *Config, pkgPath string) bool {
return conf != nil && conf.Target != "" && conf.Goarch == "arm" && pkgPath == "syscall"
}
12 changes: 12 additions & 0 deletions internal/build/plan9asm_altpkg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,15 @@ func TestHasAltPkgForTarget_AllowsAdditivePatchWithPlan9Asm(t *testing.T) {
t.Fatal("internal/runtime/sys should keep its additive alt package even when plan9asm is enabled")
}
}

func TestHasAltPkgForTarget_UsesAtomicFallbackOnArm(t *testing.T) {
conf := &Config{Goarch: "arm", AbiMode: cabi.ModeAllFunc}
if !hasAltPkgForTarget(conf, "internal/runtime/atomic") {
t.Fatal("internal/runtime/atomic should use alt package on arm")
}

conf = &Config{Goarch: "arm64", AbiMode: cabi.ModeAllFunc}
if hasAltPkgForTarget(conf, "internal/runtime/atomic") {
t.Fatal("internal/runtime/atomic should keep plan9asm/std paths on arm64")
}
}
51 changes: 51 additions & 0 deletions internal/build/plan9asm_sfiles_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//go:build !llgo
// +build !llgo

package build

import (
"path/filepath"
"reflect"
"testing"
)

func TestSelectedSFilesSkipsTestAsm(t *testing.T) {
dir := "/tmp/pkg"
got := selectedSFiles(dir, []string{
"abi_test.s",
"stub.s",
"helper.S",
"compare_test.S",
})
want := []string{
filepath.Join(dir, "stub.s"),
filepath.Join(dir, "helper.S"),
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("selectedSFiles() = %#v, want %#v", got, want)
}
}

func TestSelectedSFilesHandlesEmptyInput(t *testing.T) {
if got := selectedSFiles("", []string{"stub.s"}); got != nil {
t.Fatalf("selectedSFiles(empty dir) = %#v, want nil", got)
}
if got := selectedSFiles("/tmp/pkg", nil); got != nil {
t.Fatalf("selectedSFiles(nil files) = %#v, want nil", got)
}
}

func TestShouldSkipPlan9AsmSFilesForTarget(t *testing.T) {
if !shouldSkipPlan9AsmSFilesForTarget(&Config{Target: "cortex-m-qemu", Goarch: "arm"}, "syscall") {
t.Fatal("embedded arm syscall asm should be skipped")
}
if shouldSkipPlan9AsmSFilesForTarget(&Config{Target: "", Goarch: "arm"}, "syscall") {
t.Fatal("host arm syscall asm should not be skipped")
}
if shouldSkipPlan9AsmSFilesForTarget(&Config{Target: "cortex-m-qemu", Goarch: "arm64"}, "syscall") {
t.Fatal("arm64 syscall asm should not be skipped by arm-only rule")
}
if shouldSkipPlan9AsmSFilesForTarget(&Config{Target: "cortex-m-qemu", Goarch: "arm"}, "internal/bytealg") {
t.Fatal("only syscall asm should be skipped by embedded arm rule")
}
}
85 changes: 85 additions & 0 deletions internal/plan9asm/bytealg_arm_sigs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//go:build !llgo
// +build !llgo

package plan9asm

import (
"os"
"path/filepath"
"runtime"
"testing"

"github.com/goplus/llgo/internal/packages"
extplan9asm "github.com/goplus/plan9asm"
)

func loadStdlibInternalBytealgForTarget(t *testing.T, goos, goarch string) *packages.Package {
t.Helper()
cfg := &packages.Config{
Mode: packages.NeedName | packages.NeedFiles | packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedTypesInfo | packages.NeedImports,
Env: append(os.Environ(), "GOOS="+goos, "GOARCH="+goarch),
}
pkgs, err := packages.LoadEx(nil, nil, cfg, "internal/bytealg")
if err != nil {
t.Fatal(err)
}
if len(pkgs) != 1 || pkgs[0].Types == nil {
t.Fatalf("load internal/bytealg: got %d pkgs, types=%v", len(pkgs), pkgs[0].Types)
}
return pkgs[0]
}

func TestSigsForStdlibInternalBytealgArmHelpers(t *testing.T) {
goroot := runtime.GOROOT()
if goroot == "" {
t.Skip("GOROOT not available")
}
pkg := loadStdlibInternalBytealgForTarget(t, "linux", "arm")

tests := map[string]map[string]extplan9asm.FuncSig{
filepath.Join(goroot, "src", "internal", "bytealg", "compare_arm.s"): {
"internal/bytealg.cmpbody": {
Args: []extplan9asm.LLVMType{extplan9asm.Ptr, "i32", extplan9asm.Ptr, "i32", extplan9asm.Ptr},
Ret: extplan9asm.Void,
ArgRegs: []extplan9asm.Reg{"R2", "R0", "R3", "R1", "R7"},
},
},
filepath.Join(goroot, "src", "internal", "bytealg", "count_arm.s"): {
"internal/bytealg.countbytebody": {
Args: []extplan9asm.LLVMType{extplan9asm.Ptr, "i32", "i8", extplan9asm.Ptr},
Ret: extplan9asm.Void,
ArgRegs: []extplan9asm.Reg{"R0", "R1", "R2", "R7"},
},
},
filepath.Join(goroot, "src", "internal", "bytealg", "equal_arm.s"): {
"internal/bytealg.memeqbody": {
Args: []extplan9asm.LLVMType{extplan9asm.Ptr, extplan9asm.Ptr, "i32", extplan9asm.Ptr},
Ret: extplan9asm.Void,
ArgRegs: []extplan9asm.Reg{"R0", "R2", "R1", "R7"},
},
},
filepath.Join(goroot, "src", "internal", "bytealg", "indexbyte_arm.s"): {
"internal/bytealg.indexbytebody": {
Args: []extplan9asm.LLVMType{extplan9asm.Ptr, "i32", "i8", extplan9asm.Ptr},
Ret: extplan9asm.Void,
ArgRegs: []extplan9asm.Reg{"R0", "R1", "R2", "R5"},
},
},
}

for path, wantSigs := range tests {
tr, err := TranslateFileForPkg(pkg, path, "linux", "arm", nil)
if err != nil {
t.Fatalf("translate %s: %v", path, err)
}
for name, want := range wantSigs {
got, ok := tr.Signatures[name]
if !ok {
t.Fatalf("missing symbol %s in %s", name, path)
}
if err := checkSig(got, want); err != nil {
t.Fatalf("%s (%s): %v", name, path, err)
}
}
}
}
21 changes: 21 additions & 0 deletions internal/plan9asm/translate.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,27 @@ func extraAsmSigsAndDeclMap(pkgPath string, goarch string) map[string]extplan9as
manual := map[string]extplan9asm.FuncSig{}
if pkgPath == "internal/bytealg" {
switch goarch {
case "arm":
manual["internal/bytealg.cmpbody"] = extplan9asm.FuncSig{
Args: []extplan9asm.LLVMType{extplan9asm.Ptr, "i32", extplan9asm.Ptr, "i32", extplan9asm.Ptr},
Ret: extplan9asm.Void,
ArgRegs: []extplan9asm.Reg{"R2", "R0", "R3", "R1", "R7"},
}
manual["internal/bytealg.memeqbody"] = extplan9asm.FuncSig{
Args: []extplan9asm.LLVMType{extplan9asm.Ptr, extplan9asm.Ptr, "i32", extplan9asm.Ptr},
Ret: extplan9asm.Void,
ArgRegs: []extplan9asm.Reg{"R0", "R2", "R1", "R7"},
}
manual["internal/bytealg.countbytebody"] = extplan9asm.FuncSig{
Args: []extplan9asm.LLVMType{extplan9asm.Ptr, "i32", "i8", extplan9asm.Ptr},
Ret: extplan9asm.Void,
ArgRegs: []extplan9asm.Reg{"R0", "R1", "R2", "R7"},
}
manual["internal/bytealg.indexbytebody"] = extplan9asm.FuncSig{
Args: []extplan9asm.LLVMType{extplan9asm.Ptr, "i32", "i8", extplan9asm.Ptr},
Ret: extplan9asm.Void,
ArgRegs: []extplan9asm.Reg{"R0", "R1", "R2", "R5"},
}
case "arm64":
manual["internal/bytealg.cmpbody"] = extplan9asm.FuncSig{Args: []extplan9asm.LLVMType{extplan9asm.Ptr, extplan9asm.I64, extplan9asm.Ptr, extplan9asm.I64}, Ret: extplan9asm.I64}
manual["internal/bytealg.memeqbody"] = extplan9asm.FuncSig{Args: []extplan9asm.LLVMType{extplan9asm.Ptr, extplan9asm.Ptr, extplan9asm.I64}, Ret: extplan9asm.I1}
Expand Down
12 changes: 12 additions & 0 deletions internal/plan9asm/translate_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,18 @@ func TestExtraAsmSigsAndDeclMap(t *testing.T) {
t.Fatalf("unexpected manual sigs for other/pkg: %#v", got)
}

arm := extraAsmSigsAndDeclMap("internal/bytealg", "arm")
for _, name := range []string{
"internal/bytealg.cmpbody",
"internal/bytealg.memeqbody",
"internal/bytealg.countbytebody",
"internal/bytealg.indexbytebody",
} {
if _, ok := arm[name]; !ok {
t.Fatalf("missing arm manual sig %s", name)
}
}

arm64 := extraAsmSigsAndDeclMap("internal/bytealg", "arm64")
for _, name := range []string{
"internal/bytealg.cmpbody",
Expand Down
56 changes: 42 additions & 14 deletions runtime/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ const (
altPkgAdditive
)

type altPkgSpec struct {
mode altPkgMode
goarchs map[string]struct{}
}

func (s altPkgSpec) enabledFor(goarch string) bool {
return len(s.goarchs) == 0 || hasGoarch(s.goarchs, goarch)
}

func hasGoarch(goarchs map[string]struct{}, goarch string) bool {
if goarchs == nil {
return false
}
_, ok := goarchs[goarch]
return ok
}

func SkipToBuild(pkgPath string) bool {
if _, ok := altPkgs[pkgPath]; ok {
return false
Expand All @@ -19,19 +36,30 @@ func HasAltPkg(path string) (b bool) {
return
}

func HasAltPkgForGOARCH(path, goarch string) bool {
spec, ok := altPkgs[path]
return ok && spec.enabledFor(goarch)
}

func HasAdditiveAltPkg(path string) bool {
return altPkgs[path] == altPkgAdditive
}

var altPkgs = map[string]altPkgMode{
"internal/abi": altPkgReplace,
"internal/reflectlite": altPkgReplace,
"internal/runtime/maps": altPkgReplace,
"internal/runtime/sys": altPkgAdditive,
"iter": altPkgReplace,
"reflect": altPkgReplace,
"runtime": altPkgReplace,
"unique": altPkgReplace,
"syscall/js": altPkgReplace,
"sync/atomic": altPkgReplace,
return altPkgs[path].mode == altPkgAdditive
}

func HasAdditiveAltPkgForGOARCH(path, goarch string) bool {
spec, ok := altPkgs[path]
return ok && spec.mode == altPkgAdditive && spec.enabledFor(goarch)
}

var altPkgs = map[string]altPkgSpec{
"internal/abi": {mode: altPkgReplace},
"internal/runtime/atomic": {mode: altPkgReplace, goarchs: map[string]struct{}{"arm": {}}},
"internal/reflectlite": {mode: altPkgReplace},
"internal/runtime/maps": {mode: altPkgReplace},
"internal/runtime/sys": {mode: altPkgAdditive},
"iter": {mode: altPkgReplace},
"reflect": {mode: altPkgReplace},
"runtime": {mode: altPkgReplace},
"sync/atomic": {mode: altPkgReplace},
"unique": {mode: altPkgReplace},
"syscall/js": {mode: altPkgReplace},
}
Loading
Loading