diff --git a/cmd/internal/build/build.go b/cmd/internal/build/build.go index a093b609..3bbde7c7 100644 --- a/cmd/internal/build/build.go +++ b/cmd/internal/build/build.go @@ -37,10 +37,7 @@ func init() { } func runCmd(cmd *base.Command, args []string) { - conf := &build.Config{ - Mode: build.ModeBuild, - AppExt: build.DefaultAppExt(), - } + conf := build.NewDefaultConf(build.ModeBuild) if len(args) >= 2 && args[0] == "-o" { conf.OutFile = args[1] args = args[2:] diff --git a/internal/build/build.go b/internal/build/build.go index c36786d5..b2bf418a 100644 --- a/internal/build/build.go +++ b/internal/build/build.go @@ -21,7 +21,6 @@ import ( "debug/macho" "fmt" "go/ast" - "go/build" "go/constant" "go/token" "go/types" @@ -31,6 +30,7 @@ import ( "path" "path/filepath" "runtime" + "slices" "strings" "unsafe" @@ -41,13 +41,13 @@ import ( "github.com/goplus/llgo/internal/mockable" "github.com/goplus/llgo/internal/packages" "github.com/goplus/llgo/internal/typepatch" + llvmTarget "github.com/goplus/llgo/internal/xtool/llvm" "github.com/goplus/llgo/ssa/abi" xenv "github.com/goplus/llgo/xtool/env" "github.com/goplus/llgo/xtool/env/llvm" llruntime "github.com/goplus/llgo/runtime" llssa "github.com/goplus/llgo/ssa" - clangCheck "github.com/goplus/llgo/xtool/clang/check" ) type Mode int @@ -66,6 +66,8 @@ const ( ) type Config struct { + Goos string + Goarch string BinPath string AppExt string // ".exe" on Windows, empty on Unix OutFile string // only valid for ModeBuild when len(pkgs) == 1 @@ -86,10 +88,19 @@ func NewDefaultConf(mode Mode) *Config { if err := os.MkdirAll(bin, 0755); err != nil { panic(fmt.Errorf("cannot create bin directory: %v", err)) } + goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH") + if goos == "" { + goos = runtime.GOOS + } + if goarch == "" { + goarch = runtime.GOARCH + } conf := &Config{ + Goos: goos, + Goarch: goarch, BinPath: bin, Mode: mode, - AppExt: DefaultAppExt(), + AppExt: DefaultAppExt(goos), } return conf } @@ -105,9 +116,12 @@ func envGOPATH() (string, error) { return filepath.Join(home, "go"), nil } -func DefaultAppExt() string { - if runtime.GOOS == "windows" { +func DefaultAppExt(goos string) string { + switch goos { + case "windows": return ".exe" + case "wasi", "wasip1", "js": + return ".wasm" } return "" } @@ -121,9 +135,65 @@ const ( loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo ) +func mergeFlags(flags, extraFlags []string) (newFlags []string, tags []string) { + // Combine all flags + allFlags := append([]string{}, flags...) + allFlags = append(allFlags, extraFlags...) + + // Find all -tags flags and extract their values + tagValues := []string{} + + for i := 0; i < len(allFlags); i++ { + flag := allFlags[i] + // Handle -tags=value format + if strings.HasPrefix(flag, "-tags=") { + value := strings.TrimPrefix(flag, "-tags=") + if value != "" { + tagValues = append(tagValues, strings.Split(value, ",")...) + } + continue + } + // Handle -tags value format + if flag == "-tags" && i+1 < len(allFlags) { + i++ + value := allFlags[i] + if value != "" { + tagValues = append(tagValues, strings.Split(value, ",")...) + } + continue + } + // Keep other flags + newFlags = append(newFlags, flag) + } + // Add combined -tags flag if we found any tag values + if len(tagValues) > 0 { + // Remove duplicates + uniqueTags := make([]string, 0, len(tagValues)) + seen := make(map[string]bool) + for _, tag := range tagValues { + tag = strings.TrimSpace(tag) + if tag != "" && !seen[tag] { + seen[tag] = true + uniqueTags = append(uniqueTags, tag) + } + } + if len(uniqueTags) > 0 { + newFlags = append(newFlags, "-tags", strings.Join(uniqueTags, ",")) + tags = []string{"-tags", strings.Join(uniqueTags, ",")} + } + } + return newFlags, tags +} + func Do(args []string, conf *Config) ([]Package, error) { + if conf.Goos == "" { + conf.Goos = runtime.GOOS + } + if conf.Goarch == "" { + conf.Goarch = runtime.GOARCH + } flags, patterns, verbose := ParseArgs(args, buildFlags) - flags = append(flags, "-tags", "llgo") + flags, _ = mergeFlags(flags, []string{"-tags", "llgo"}) cfg := &packages.Config{ Mode: loadSyntax | packages.NeedDeps | packages.NeedModule | packages.NeedExportFile, BuildFlags: flags, @@ -148,8 +218,8 @@ func Do(args []string, conf *Config) ([]Package, error) { llssa.Initialize(llssa.InitAll) target := &llssa.Target{ - GOOS: build.Default.GOOS, - GOARCH: build.Default.GOARCH, + GOOS: conf.Goos, + GOARCH: conf.Goarch, } prog := llssa.NewProgram(target) @@ -221,7 +291,7 @@ func Do(args []string, conf *Config) ([]Package, error) { os.Setenv("PATH", env.BinDir()+":"+os.Getenv("PATH")) // TODO(xsw): check windows output := conf.OutFile != "" - ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0, output, make(map[*packages.Package]bool), make(map[*packages.Package]bool)} + ctx := &context{env, cfg, progSSA, prog, dedup, patches, make(map[string]none), initial, mode, 0, output, make(map[*packages.Package]bool), make(map[*packages.Package]bool), conf} pkgs, err := buildAllPkgs(ctx, initial, verbose) check(err) if mode == ModeGen { @@ -286,6 +356,8 @@ type context struct { needRt map[*packages.Package]bool needPyInit map[*packages.Package]bool + + buildConf *Config } func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs []*aPackage, err error) { @@ -364,7 +436,7 @@ func buildAllPkgs(ctx *context, initial []*packages.Package, verbose bool) (pkgs ctx.nLibdir++ } } - if err := clangCheck.CheckLinkArgs(pkgLinkArgs); err != nil { + if err := ctx.env.Clang().CheckLinkArgs(pkgLinkArgs); err != nil { panic(fmt.Sprintf("test link args '%s' failed\n\texpanded to: %v\n\tresolved to: %v\n\terror: %v", param, expdArgs, pkgLinkArgs, err)) } aPkg.LinkArgs = append(aPkg.LinkArgs, pkgLinkArgs...) @@ -401,39 +473,18 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs } else { app = filepath.Join(conf.BinPath, name+conf.AppExt) } + } else if !strings.HasSuffix(app, conf.AppExt) { + app += conf.AppExt } + + // Start with output file argument args := make([]string, 0, len(pkg.Imports)+len(linkArgs)+16) - args = append( - args, - "-o", app, - "-Wl,--error-limit=0", - "-fuse-ld=lld", - "-Wno-override-module", - // "-O2", // FIXME: This will cause TestFinalizer in _test/bdwgc.go to fail on macOS. - ) - switch runtime.GOOS { - case "darwin": // ld64.lld (macOS) - args = append( - args, - "-rpath", "@loader_path", - "-rpath", "@loader_path/../lib", - "-Xlinker", "-dead_strip", - ) - case "windows": // lld-link (Windows) - // TODO: Add options for Windows. - default: // ld.lld (Unix), wasm-ld (WebAssembly) - args = append( - args, - "-rpath", "$ORIGIN", - "-rpath", "$ORIGIN/../lib", - "-fdata-sections", - "-ffunction-sections", - "-Xlinker", "--gc-sections", - "-lm", - "-latomic", - "-lpthread", // libpthread is built-in since glibc 2.34 (2021-08-01); we need to support earlier versions. - ) - } + args = append(args, "-o", app) + + // Add common linker arguments based on target OS and architecture + targetTriple := llvmTarget.GetTargetTriple(conf.Goos, conf.Goarch) + args = append(args, buildLdflags(conf.Goos, conf.Goarch, targetTriple)...) + needRuntime := false needPyInit := false pkgsMap := make(map[*packages.Package]*aPackage, len(pkgs)) @@ -453,7 +504,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs } } }) - entryLLFile, err := genMainModuleFile(llssa.PkgRuntime, pkg.PkgPath, needRuntime, needPyInit) + entryLLFile, err := genMainModuleFile(conf, llssa.PkgRuntime, pkg.PkgPath, needRuntime, needPyInit) if err != nil { panic(err) } @@ -478,11 +529,13 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs // add rpath and find libs exargs := make([]string, 0, ctx.nLibdir<<1) libs := make([]string, 0, ctx.nLibdir*3) - for _, arg := range args { - if strings.HasPrefix(arg, "-L") { - exargs = append(exargs, "-rpath", arg[2:]) - } else if strings.HasPrefix(arg, "-l") { - libs = append(libs, arg[2:]) + if IsRpathChangeEnabled() { + for _, arg := range args { + if strings.HasPrefix(arg, "-L") { + exargs = append(exargs, "-rpath", arg[2:]) + } else if strings.HasPrefix(arg, "-l") { + libs = append(libs, arg[2:]) + } } } args = append(args, exargs...) @@ -490,14 +543,12 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs args = append(args, "-gdwarf-4") } - // TODO(xsw): show work - if verbose { - fmt.Fprintln(os.Stderr, "clang", args) - } - err = ctx.env.Clang().Link(args...) + cmd := ctx.env.Clang() + cmd.Verbose = verbose + err = cmd.Link(args...) check(err) - if IsRpathChangeEnabled() && runtime.GOOS == "darwin" { + if IsRpathChangeEnabled() && conf.Goos == "darwin" { dylibDeps := make([]string, 0, len(libs)) for _, lib := range libs { dylibDep := findDylibDep(app, lib) @@ -519,7 +570,16 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs fmt.Fprintf(os.Stderr, "%s: exit code %d\n", app, s.ExitCode()) } case ModeRun: - cmd := exec.Command(app, conf.RunArgs...) + args := make([]string, 0, len(conf.RunArgs)+1) + copy(args, conf.RunArgs) + if isWasmTarget(conf.Goos) { + args = append(args, app, "--wasm", "multi-memory=true") + args = append(args, conf.RunArgs...) + app = "wasmtime" + } else { + args = conf.RunArgs + } + cmd := exec.Command(app, args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -532,7 +592,84 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, linkArgs } } -func genMainModuleFile(rtPkgPath, mainPkgPath string, needRuntime, needPyInit bool) (path string, err error) { +func buildCflags(goos, goarch, targetTriple string) []string { + args := []string{} + if goarch == "wasm" { + args = append(args, "-target", targetTriple) + } + return args +} + +// buildLdflags builds the common linker arguments based on target OS and architecture +func buildLdflags(goos, goarch, targetTriple string) []string { + args := []string{ + "-target", targetTriple, + "-Wno-override-module", + } + if goos == runtime.GOOS { + // Non-cross-compile + args = append(args, + "-Wl,--error-limit=0", + "-fuse-ld=lld", + "-Wno-override-module", + ) + } + + switch goos { + case "darwin": // ld64.lld (macOS) + if IsRpathChangeEnabled() { + args = append( + args, + "-rpath", "@loader_path", + "-rpath", "@loader_path/../lib", + ) + } + args = append( + args, + "-Xlinker", "-dead_strip", + ) + case "windows": // lld-link (Windows) + // TODO(xsw): Add options for Windows. + case "wasi", "wasip1", "js": // wasm-ld (WebAssembly) + args = append( + args, + "-fdata-sections", + "-ffunction-sections", + // "-nostdlib", + // "-Wl,--no-entry", + "-Wl,--export-all", + "-Wl,--allow-undefined", + // "-Wl,--import-memory,", + "-Wl,--export-memory", + "-Wl,--initial-memory=16777216", // 16MB + // "-pthread", + // "-matomics", + // "-mbulk-memory", + // "-mmultimemory", + ) + default: // ld.lld (Unix) + args = append( + args, + // "-rpath", "$ORIGIN", + // "-rpath", "$ORIGIN/../lib", + "-fdata-sections", + "-ffunction-sections", + "-Xlinker", + "--gc-sections", + "-lm", + "-latomic", + "-lpthread", // libpthread is built-in since glibc 2.34 (2021-08-01); we need to support earlier versions. + ) + } + + return args +} + +func isWasmTarget(goos string) bool { + return slices.Contains([]string{"wasi", "js", "wasip1"}, goos) +} + +func genMainModuleFile(conf *Config, rtPkgPath, mainPkgPath string, needRuntime, needPyInit bool) (path string, err error) { var ( pyInitDecl string pyInit string @@ -547,6 +684,10 @@ func genMainModuleFile(rtPkgPath, mainPkgPath string, needRuntime, needPyInit bo pyInit = "call void @Py_Initialize()" pyInitDecl = "declare void @Py_Initialize()" } + mainDefine := "define i32 @main(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr" + if isWasmTarget(conf.Goos) { + mainDefine = "define hidden noundef i32 @__main_argc_argv(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr" + } mainCode := fmt.Sprintf(`; ModuleID = 'main' source_filename = "main" @@ -566,7 +707,7 @@ define weak void @"syscall.init"() { ret void } -define i32 @main(i32 %%0, ptr %%1) { +%s { _llgo_0: %s store i32 %%0, ptr @__llgo_argc, align 4 @@ -577,7 +718,7 @@ _llgo_0: call void @"%s.main"() ret i32 0 } -`, pyInitDecl, rtInitDecl, mainPkgPath, mainPkgPath, +`, pyInitDecl, rtInitDecl, mainPkgPath, mainPkgPath, mainDefine, pyInit, rtInit, mainPkgPath, mainPkgPath) f, err := os.CreateTemp("", "main*.ll") @@ -877,11 +1018,15 @@ func clFiles(ctx *context, files string, pkg *packages.Package, procFile func(li func clFile(ctx *context, args []string, cFile, expFile string, procFile func(linkFile string), verbose bool) { llFile := expFile + filepath.Base(cFile) + ".ll" + targetTriple := llvmTarget.GetTargetTriple(ctx.buildConf.Goos, ctx.buildConf.Goarch) + cflags := buildCflags(ctx.buildConf.Goos, ctx.buildConf.Goarch, targetTriple) + args = append(cflags, args...) args = append(args, "-emit-llvm", "-S", "-o", llFile, "-c", cFile) if verbose { fmt.Fprintln(os.Stderr, "clang", args) } - err := ctx.env.Clang().Compile(args...) + cmd := ctx.env.Clang() + err := cmd.Compile(args...) check(err) procFile(llFile) } diff --git a/internal/llgen/llgenf.go b/internal/llgen/llgenf.go index 2e33ee67..55e5d5af 100644 --- a/internal/llgen/llgenf.go +++ b/internal/llgen/llgenf.go @@ -20,6 +20,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "github.com/goplus/llgo/internal/build" @@ -46,7 +47,7 @@ func genFrom(pkgPath string) (build.Package, error) { conf := &build.Config{ Mode: build.ModeGen, - AppExt: build.DefaultAppExt(), + AppExt: build.DefaultAppExt(runtime.GOOS), } pkgs, err := build.Do([]string{pkgPath}, conf) if err != nil { diff --git a/internal/xtool/llvm/llvm.go b/internal/xtool/llvm/llvm.go new file mode 100644 index 00000000..05fe805b --- /dev/null +++ b/internal/xtool/llvm/llvm.go @@ -0,0 +1,55 @@ +package llvm + +import "runtime" + +func GetTargetTriple(goos, goarch string) string { + var llvmarch string + if goarch == "" { + goarch = runtime.GOARCH + } + if goos == "" { + goos = runtime.GOOS + } + switch goarch { + case "386": + llvmarch = "i386" + case "amd64": + llvmarch = "x86_64" + case "arm64": + llvmarch = "aarch64" + case "arm": + switch goarch { + case "5": + llvmarch = "armv5" + case "6": + llvmarch = "armv6" + default: + llvmarch = "armv7" + } + case "wasm": + llvmarch = "wasm32" + default: + llvmarch = goarch + } + llvmvendor := "unknown" + llvmos := goos + switch goos { + case "darwin": + // Use macosx* instead of darwin, otherwise darwin/arm64 will refer + // to iOS! + llvmos = "macosx" + if llvmarch == "aarch64" { + // Looks like Apple prefers to call this architecture ARM64 + // instead of AArch64. + llvmarch = "arm64" + llvmos = "macosx" + } + llvmvendor = "apple" + case "wasip1": + llvmos = "wasip1" + } + // Target triples (which actually have four components, but are called + // triples for historical reasons) have the form: + // arch-vendor-os-environment + return llvmarch + "-" + llvmvendor + "-" + llvmos +} diff --git a/internal/xtool/llvm/llvm_test.go b/internal/xtool/llvm/llvm_test.go new file mode 100644 index 00000000..f56f2f65 --- /dev/null +++ b/internal/xtool/llvm/llvm_test.go @@ -0,0 +1,150 @@ +//go:build !llgo +// +build !llgo + +package llvm + +import ( + "os/exec" + "strings" + "testing" +) + +func TestGetTargetTriple(t *testing.T) { + // Get the list of supported architectures from clang + cmd := exec.Command("clang", "--print-targets") + output, err := cmd.Output() + if err != nil { + t.Fatalf("Failed to run clang --print-targets: %v", err) + } + + // Parse the output to get the list of supported architectures + supportedArchs := make(map[string]bool) + lines := strings.Split(string(output), "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if line != "" && !strings.HasPrefix(line, "Registered Targets:") { + // Extract the architecture from the line + parts := strings.SplitN(line, " - ", 2) + if len(parts) > 0 { + arch := strings.TrimSpace(parts[0]) + supportedArchs[arch] = true + } + } + } + + if len(supportedArchs) == 0 { + t.Fatal("No supported architectures found from clang --print-targets") + } + + t.Logf("Found %d supported architectures from clang", len(supportedArchs)) + + // Map our architecture names to clang's architecture names + clangArchMap := map[string][]string{ + "x86_64": {"x86-64", "x86_64"}, + "i386": {"x86", "i386"}, + "aarch64": {"aarch64", "arm64"}, + "arm64": {"arm64", "aarch64"}, + "armv7": {"arm", "thumb"}, + "wasm32": {"wasm32"}, + } + + // Define a function to check if the architecture is supported by clang + isArchSupported := func(archPart string) (bool, string) { + if mappedArchs, ok := clangArchMap[archPart]; ok { + for _, mappedArch := range mappedArchs { + if supportedArchs[mappedArch] { + return true, mappedArch + } + } + } else if supportedArchs[archPart] { + // Direct match + return true, archPart + } + return false, "" + } + + // Define a function to verify OS name + isOSValid := func(os, goos string) bool { + validOSMap := map[string][]string{ + "linux": {"linux", "linux-gnu"}, + "darwin": {"macosx", "darwin"}, + "windows": {"windows", "win32"}, + "wasip1": {"wasip1", "wasi"}, + "js": {"js", "javascript"}, + } + + if validVariants, ok := validOSMap[goos]; ok { + for _, validVariant := range validVariants { + if strings.HasPrefix(os, validVariant) { + return true + } + } + } + return false + } + + // Define a function to check if vendor is valid + isVendorValid := func(vendor string) bool { + validVendors := map[string]bool{ + "unknown": true, + "apple": true, + "pc": true, + "ibm": true, + } + return validVendors[vendor] + } + + // Define the test function + checkTriple := func(t *testing.T, testName, goos, goarch, expected string) { + t.Helper() + got := GetTargetTriple(goos, goarch) + + // Check if the generated triple matches the expected value + if got != expected { + t.Errorf("getTargetTriple(%q, %q) = %q, want %q", + goos, goarch, got, expected) + } + + // Extract the architecture part from the triple (first component) + parts := strings.Split(got, "-") + if len(parts) < 3 { + t.Errorf("Invalid target triple format: %s, should have at least 3 components", got) + return + } + + archPart := parts[0] + vendor := parts[1] + os := parts[2] + + // Check if the architecture is supported by clang + supported, mappedArch := isArchSupported(archPart) + if supported { + t.Logf("Architecture %s (mapped to %s) is supported by clang", archPart, mappedArch) + } else { + t.Logf("WARNING: Architecture %s from triple %q for %s/%s not found in clang's supported architectures", + archPart, got, goos, goarch) + } + + // Verify vendor + if !isVendorValid(vendor) { + t.Errorf("Invalid vendor in triple: %s", vendor) + } + + // Verify OS + if !isOSValid(os, goos) { + t.Errorf("OS in triple %q doesn't match expected OS %q", os, goos) + } + } + + // Run tests for different OS/arch combinations + checkTriple(t, "wasip1/wasm", "wasip1", "wasm", "wasm32-unknown-wasip1") + checkTriple(t, "linux/amd64", "linux", "amd64", "x86_64-unknown-linux") + checkTriple(t, "linux/386", "linux", "386", "i386-unknown-linux") + checkTriple(t, "linux/arm64", "linux", "arm64", "aarch64-unknown-linux") + checkTriple(t, "linux/arm", "linux", "arm", "armv7-unknown-linux") + checkTriple(t, "darwin/amd64", "darwin", "amd64", "x86_64-apple-macosx") + checkTriple(t, "darwin/arm64", "darwin", "arm64", "arm64-apple-macosx") + checkTriple(t, "windows/amd64", "windows", "amd64", "x86_64-unknown-windows") + checkTriple(t, "windows/386", "windows", "386", "i386-unknown-windows") + checkTriple(t, "js/wasm", "js", "wasm", "wasm32-unknown-js") +} diff --git a/llgo_wasm b/llgo_wasm new file mode 100755 index 00000000..5232313f --- /dev/null +++ b/llgo_wasm @@ -0,0 +1,11 @@ +#!/bin/bash +set -e +WORKDIR='' +WORKDIR=$(pwd) +LLGO_ROOT='' +LLGO_ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd ) +export LLGO_ROOT +cd $LLGO_ROOT +go install ./cmd/llgo +cd $WORKDIR +GOOS=wasip1 GOARCH=wasm llgo "$@" diff --git a/ssa/cl_test.go b/ssa/cl_test.go index 7294cc34..c49aad3e 100644 --- a/ssa/cl_test.go +++ b/ssa/cl_test.go @@ -24,6 +24,8 @@ import ( "go/types" "io" "log" + "os" + "runtime" "testing" "github.com/goplus/llgo/cl/cltest" @@ -65,7 +67,13 @@ func TestFromTestdata(t *testing.T) { } func TestMakeInterface(t *testing.T) { - prog := ssatest.NewProgram(t, &ssa.Target{GOARCH: "x86"}) + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + os.Chdir("../runtime") + defer os.Chdir(wd) + prog := ssatest.NewProgram(t, &ssa.Target{GOARCH: runtime.GOARCH}) pkg := prog.NewPackage("foo", "foo") fn := pkg.NewFunc("main", types.NewSignatureType(nil, nil, nil, nil, nil, false), ssa.InC) b := fn.MakeBody(1) diff --git a/ssa/datastruct.go b/ssa/datastruct.go index dedc081c..32cbaa6f 100644 --- a/ssa/datastruct.go +++ b/ssa/datastruct.go @@ -245,7 +245,7 @@ func (b Builder) checkIndex(idx Expr, max Expr) Expr { } else { typ = prog.Uint() } - if prog.SizeOf(idx.Type) < prog.SizeOf(typ) { + if prog.SizeOf(idx.Type) != prog.SizeOf(typ) { idx.Type = typ idx.impl = castUintptr(b, idx.impl, typ) } diff --git a/ssa/package.go b/ssa/package.go index 8c23515e..cee0bbd3 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -210,6 +210,22 @@ type aProgram struct { // A Program presents a program. type Program = *aProgram +var arch32 = map[string]bool{ + "386": true, + "arm": true, + "mips": true, + "mipsle": true, + "s390x": true, + "wasm": true, +} + +func is32Bits(arch string) bool { + if v, ok := arch32[arch]; ok { + return v + } + return false +} + // NewProgram creates a new program. func NewProgram(target *Target) Program { if target == nil { @@ -231,7 +247,7 @@ func NewProgram(target *Target) Program { // TODO(xsw): Finalize may cause panic, so comment it. ctx.Finalize() */ - is32Bits := (td.PointerSize() == 4 || target.GOARCH == "x86") // TODO(xsw): remove temp code + is32Bits := (td.PointerSize() == 4 || is32Bits(target.GOARCH)) return &aProgram{ ctx: ctx, gocvt: newGoTypes(), fnsCompiled: fnsCompiled, target: target, td: td, is32Bits: is32Bits, @@ -379,6 +395,12 @@ func (p Program) tyComplex128() llvm.Type { // NewPackage creates a new package. func (p Program) NewPackage(name, pkgPath string) Package { mod := p.ctx.NewModule(pkgPath) + // TODO(lijie): enable target output will check module override, but can't + // pass the snapshot test, so disable it for now + // if p.target.GOARCH != runtime.GOARCH && p.target.GOOS != runtime.GOOS { + // mod.SetTarget(p.target.Spec().Triple) + // } + // TODO(xsw): Finalize may cause panic, so comment it. // mod.Finalize() gbls := make(map[string]Global) diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 2f88a7fa..b61fb624 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -23,6 +23,7 @@ import ( "go/constant" "go/token" "go/types" + "os" "testing" "unsafe" @@ -40,6 +41,12 @@ func TestEndDefer(t *testing.T) { } func TestUnsafeString(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + os.Chdir("../../runtime") + defer os.Chdir(wd) prog := NewProgram(nil) prog.SetRuntime(func() *types.Package { fset := token.NewFileSet() diff --git a/ssa/target.go b/ssa/target.go index 77e27b9b..9b998024 100644 --- a/ssa/target.go +++ b/ssa/target.go @@ -17,6 +17,8 @@ package ssa import ( + "runtime" + "github.com/goplus/llvm" ) @@ -29,15 +31,15 @@ type Target struct { } func (p *Target) targetData() llvm.TargetData { - spec := p.toSpec() - if spec.triple == "" { - spec.triple = llvm.DefaultTargetTriple() + spec := p.Spec() + if spec.Triple == "" { + spec.Triple = llvm.DefaultTargetTriple() } - t, err := llvm.GetTargetFromTriple(spec.triple) + t, err := llvm.GetTargetFromTriple(spec.Triple) if err != nil { panic(err) } - machine := t.CreateTargetMachine(spec.triple, spec.cpu, spec.features, llvm.CodeGenLevelDefault, llvm.RelocDefault, llvm.CodeModelDefault) + machine := t.CreateTargetMachine(spec.Triple, spec.CPU, spec.Features, llvm.CodeGenLevelDefault, llvm.RelocDefault, llvm.CodeModelDefault) return machine.CreateTargetData() } @@ -62,19 +64,13 @@ func (p *Program) targetMachine() llvm.TargetMachine { } */ -type targetSpec struct { - triple string - cpu string - features string +type TargetSpec struct { + Triple string + CPU string + Features string } -// TODO config -func (p *Target) toSpec() (spec targetSpec) { - return -} - -/* -func (p *Target) toSpec() (spec targetSpec) { +func (p *Target) Spec() (spec TargetSpec) { // Configure based on GOOS/GOARCH environment variables (falling back to // runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it. var llvmarch string @@ -113,56 +109,55 @@ func (p *Target) toSpec() (spec targetSpec) { case "darwin": // Use macosx* instead of darwin, otherwise darwin/arm64 will refer // to iOS! - llvmos = "macosx10.12.0" + llvmos = "macosx" if llvmarch == "aarch64" { // Looks like Apple prefers to call this architecture ARM64 // instead of AArch64. llvmarch = "arm64" - llvmos = "macosx11.0.0" + llvmos = "macosx" } llvmvendor = "apple" case "wasip1": - llvmos = "wasi" + llvmos = "wasip1" } // Target triples (which actually have four components, but are called // triples for historical reasons) have the form: // arch-vendor-os-environment - spec.triple = llvmarch + "-" + llvmvendor + "-" + llvmos + spec.Triple = llvmarch + "-" + llvmvendor + "-" + llvmos if llvmos == "windows" { - spec.triple += "-gnu" + spec.Triple += "-gnu" } else if goarch == "arm" { - spec.triple += "-gnueabihf" + spec.Triple += "-gnueabihf" } switch goarch { case "386": - spec.cpu = "pentium4" - spec.features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87" + spec.CPU = "pentium4" + spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87" case "amd64": - spec.cpu = "x86-64" - spec.features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87" + spec.CPU = "x86-64" + spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87" case "arm": - spec.cpu = "generic" + spec.CPU = "generic" switch llvmarch { case "armv5": - spec.features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" + spec.Features = "+armv5t,+strict-align,-aes,-bf16,-d32,-dotprod,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fp64,-fpregs,-fullfp16,-mve.fp,-neon,-sha2,-thumb-mode,-vfp2,-vfp2sp,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" case "armv6": - spec.features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-aes,-d32,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-neon,-sha2,-thumb-mode,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" + spec.Features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-aes,-d32,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-neon,-sha2,-thumb-mode,-vfp3,-vfp3d16,-vfp3d16sp,-vfp3sp,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" case "armv7": - spec.features = "+armv7-a,+d32,+dsp,+fp64,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-thumb-mode,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" + spec.Features = "+armv7-a,+d32,+dsp,+fp64,+neon,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-thumb-mode,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" } case "arm64": - spec.cpu = "generic" + spec.CPU = "generic" if goos == "darwin" { - spec.features = "+neon" + spec.Features = "+neon" } else { // windows, linux - spec.features = "+neon,-fmv" + spec.Features = "+neon,-fmv" } case "wasm": - spec.cpu = "generic" - spec.features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" + spec.CPU = "generic" + spec.Features = "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } return } -*/ // ----------------------------------------------------------------------------- diff --git a/xtool/safesplit/safesplit_test.go b/xtool/safesplit/safesplit_test.go index 93eb953d..cbb8c9cc 100644 --- a/xtool/safesplit/safesplit_test.go +++ b/xtool/safesplit/safesplit_test.go @@ -1,3 +1,6 @@ +//go:build !llgo +// +build !llgo + /* * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. *