From 107fccb288f8ba6258d417c5e14921d4d97f3e64 Mon Sep 17 00:00:00 2001 From: Ayke Date: Sat, 5 Jan 2019 11:46:25 +0100 Subject: [PATCH] all: add support for more architectures and GOOS/GOARCH (#118) This commit does two things: * It adds support for the GOOS and GOARCH environment variables. They fall back to runtime.GO* only when not available. * It adds support for 3 new architectures: 386, arm, and arm64. For now, this is Linux-only. --- compiler/compiler.go | 12 ++--- main.go | 2 + src/runtime/arch_386.go | 11 +++++ src/runtime/arch_amd64.go | 2 - src/runtime/arch_arm.go | 11 +++++ src/runtime/arch_arm64.go | 11 +++++ target.go | 95 ++++++++++++++++++++++++++++++++------- targets/avr.json | 2 + targets/cortex-m.json | 2 + 9 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 src/runtime/arch_386.go create mode 100644 src/runtime/arch_arm.go create mode 100644 src/runtime/arch_arm64.go diff --git a/compiler/compiler.go b/compiler/compiler.go index 9be4f527..c37ed34d 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -32,6 +32,8 @@ func init() { type Config struct { Triple string // LLVM target triple, e.g. x86_64-unknown-linux-gnu (empty string means default) CPU string // LLVM CPU name, e.g. atmega328p (empty string means default) + GOOS string // + GOARCH string // GC string // garbage collection strategy CFlags []string // cflags to pass to cgo LDFlags []string // ldflags to pass to cgo @@ -39,7 +41,7 @@ type Config struct { Debug bool // add debug symbols for gdb RootDir string // GOROOT for TinyGo GOPATH string // GOPATH, like `go env GOPATH` - BuildTags []string // build tags for TinyGo (empty means {runtime.GOOS/runtime.GOARCH}) + BuildTags []string // build tags for TinyGo (empty means {Config.GOOS/Config.GOARCH}) InitInterp bool // use new init interpretation, meaning the old one is disabled } @@ -97,7 +99,7 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) { config.Triple = llvm.DefaultTargetTriple() } if len(config.BuildTags) == 0 { - config.BuildTags = []string{runtime.GOOS, runtime.GOARCH} + config.BuildTags = []string{config.GOOS, config.GOARCH} } c := &Compiler{ Config: config, @@ -178,8 +180,6 @@ func (c *Compiler) selectGC() string { // Compile the given package path or .go file path. Return an error when this // fails (in any stage). func (c *Compiler) Compile(mainPath string) error { - tripleSplit := strings.Split(c.Triple, "-") - // Prefix the GOPATH with the system GOROOT, as GOROOT is already set to // the TinyGo root. gopath := c.GOPATH @@ -195,8 +195,8 @@ func (c *Compiler) Compile(mainPath string) error { } lprogram := &loader.Program{ Build: &build.Context{ - GOARCH: tripleSplit[0], - GOOS: tripleSplit[2], + GOARCH: c.GOARCH, + GOOS: c.GOOS, GOROOT: c.RootDir, GOPATH: gopath, CgoEnabled: true, diff --git a/main.go b/main.go index c66ec838..51cc6220 100644 --- a/main.go +++ b/main.go @@ -52,6 +52,8 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act compilerConfig := compiler.Config{ Triple: spec.Triple, CPU: spec.CPU, + GOOS: spec.GOOS, + GOARCH: spec.GOARCH, GC: config.gc, CFlags: spec.CFlags, LDFlags: spec.LDFlags, diff --git a/src/runtime/arch_386.go b/src/runtime/arch_386.go new file mode 100644 index 00000000..76b0c7e3 --- /dev/null +++ b/src/runtime/arch_386.go @@ -0,0 +1,11 @@ +package runtime + +const GOARCH = "386" + +// The bitness of the CPU (e.g. 8, 32, 64). +const TargetBits = 32 + +// Align on word boundary. +func align(ptr uintptr) uintptr { + return (ptr + 3) &^ 3 +} diff --git a/src/runtime/arch_amd64.go b/src/runtime/arch_amd64.go index b9d32f91..db784327 100644 --- a/src/runtime/arch_amd64.go +++ b/src/runtime/arch_amd64.go @@ -1,5 +1,3 @@ -// +build amd64 - package runtime const GOARCH = "amd64" diff --git a/src/runtime/arch_arm.go b/src/runtime/arch_arm.go new file mode 100644 index 00000000..9c7858e5 --- /dev/null +++ b/src/runtime/arch_arm.go @@ -0,0 +1,11 @@ +package runtime + +const GOARCH = "arm" + +// The bitness of the CPU (e.g. 8, 32, 64). +const TargetBits = 32 + +// Align on word boundary. +func align(ptr uintptr) uintptr { + return (ptr + 3) &^ 3 +} diff --git a/src/runtime/arch_arm64.go b/src/runtime/arch_arm64.go new file mode 100644 index 00000000..09b7b314 --- /dev/null +++ b/src/runtime/arch_arm64.go @@ -0,0 +1,11 @@ +package runtime + +const GOARCH = "arm64" + +// The bitness of the CPU (e.g. 8, 32, 64). +const TargetBits = 64 + +// Align on word boundary. +func align(ptr uintptr) uintptr { + return (ptr + 7) &^ 7 +} diff --git a/target.go b/target.go index 898646c4..23ba83d1 100644 --- a/target.go +++ b/target.go @@ -2,14 +2,13 @@ package main import ( "encoding/json" + "errors" "io" "os" "os/user" "path/filepath" "runtime" "strings" - - "github.com/aykevl/go-llvm" ) // Target specification for a given target. Used for bare metal targets. @@ -21,6 +20,8 @@ type TargetSpec struct { Inherits []string `json:"inherits"` Triple string `json:"llvm-target"` CPU string `json:"cpu"` + GOOS string `json:"goos"` + GOARCH string `json:"goarch"` BuildTags []string `json:"build-tags"` GC string `json:"gc"` Compiler string `json:"compiler"` @@ -48,6 +49,12 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) { if spec2.CPU != "" { spec.CPU = spec2.CPU } + if spec2.GOOS != "" { + spec.GOOS = spec2.GOOS + } + if spec2.GOARCH != "" { + spec.GOARCH = spec2.GOARCH + } spec.BuildTags = append(spec.BuildTags, spec2.BuildTags...) if spec2.GC != "" { spec.GC = spec2.GC @@ -134,7 +141,27 @@ func (spec *TargetSpec) resolveInherits() error { // Load a target specification. func LoadTarget(target string) (*TargetSpec, error) { if target == "" { - target = llvm.DefaultTargetTriple() + // Configure based on GOOS/GOARCH environment variables (falling back to + // runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it. + goos := os.Getenv("GOOS") + if goos == "" { + goos = runtime.GOOS + } + goarch := os.Getenv("GOARCH") + if goarch == "" { + goarch = runtime.GOARCH + } + llvmos := goos + llvmarch := map[string]string{ + "386": "i386", + "amd64": "x86_64", + "arm64": "aarch64", + }[goarch] + if llvmarch == "" { + llvmarch = goarch + } + target = llvmarch + "--" + llvmos + return defaultTarget(goos, goarch, target) } // See whether there is a target specification for this target (e.g. @@ -154,22 +181,60 @@ func LoadTarget(target string) (*TargetSpec, error) { // an error. return nil, err } else { - // No target spec available. Use the default one, useful on most systems - // with a regular OS. - *spec = TargetSpec{ - Triple: target, - BuildTags: []string{runtime.GOOS, runtime.GOARCH}, - Compiler: commands["clang"], - Linker: "cc", - LDFlags: []string{"-no-pie"}, // WARNING: clang < 5.0 requires -nopie - Objcopy: "objcopy", - GDB: "gdb", - GDBCmds: []string{"run"}, + // Load target from given triple, ignore GOOS/GOARCH environment + // variables. + tripleSplit := strings.Split(target, "-") + if len(tripleSplit) == 1 { + return nil, errors.New("expected a full LLVM target or a custom target in -target flag") } - return spec, nil + goos := tripleSplit[2] + goarch := map[string]string{ // map from LLVM arch to Go arch + "i386": "386", + "x86_64": "amd64", + "aarch64": "arm64", + }[tripleSplit[0]] + if goarch == "" { + goarch = tripleSplit[0] + } + return defaultTarget(goos, goarch, target) } } +func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) { + // No target spec available. Use the default one, useful on most systems + // with a regular OS. + spec := TargetSpec{ + Triple: triple, + GOOS: goos, + GOARCH: goarch, + BuildTags: []string{goos, goarch}, + Compiler: commands["clang"], + Linker: "cc", + LDFlags: []string{"-no-pie"}, // WARNING: clang < 5.0 requires -nopie + Objcopy: "objcopy", + GDB: "gdb", + GDBCmds: []string{"run"}, + } + if goarch != runtime.GOARCH { + // Some educated guesses as to how to invoke helper programs. + if goarch == "arm" { + spec.Linker = "arm-linux-gnueabi-gcc" + spec.Objcopy = "arm-linux-gnueabi-objcopy" + spec.GDB = "arm-linux-gnueabi-gdb" + } + if goarch == "arm64" { + spec.Linker = "aarch64-linux-gnu-gcc" + spec.Objcopy = "aarch64-linux-gnu-objcopy" + spec.GDB = "aarch64-linux-gnu-gdb" + } + if goarch == "386" { + spec.CFlags = []string{"-m32"} + spec.LDFlags = []string{"-m32"} + } + } + return &spec, nil +} + // Return the source directory of this package, or "." when it cannot be // recovered. func sourceDir() string { diff --git a/targets/avr.json b/targets/avr.json index abe486ec..e74ed05b 100644 --- a/targets/avr.json +++ b/targets/avr.json @@ -1,5 +1,7 @@ { "build-tags": ["avr", "js", "wasm"], + "goos": "js", + "goarch": "wasm", "compiler": "avr-gcc", "linker": "avr-gcc", "objcopy": "avr-objcopy", diff --git a/targets/cortex-m.json b/targets/cortex-m.json index c24c083f..f4f50783 100644 --- a/targets/cortex-m.json +++ b/targets/cortex-m.json @@ -1,5 +1,7 @@ { "build-tags": ["tinygo.arm", "js", "wasm"], + "goos": "js", + "goarch": "wasm", "compiler": "clang-7", "gc": "marksweep", "linker": "arm-none-eabi-ld",