This environment variable can be set to 5, 6, or 7 and controls which
ARM version (ARMv5, ARMv6, ARMv7) is used when compiling for GOARCH=arm.

I have picked the default value ARMv6, which I believe is supported on
most common single board computers including all Raspberry Pis. The
difference in code size is pretty big.

We could even go further and support ARMv4 if anybody is interested. It
should be pretty simple to add this if needed.
Этот коммит содержится в:
Ayke van Laethem 2021-11-14 01:39:34 +01:00 коммит произвёл Ron Evans
родитель 73ad825b67
коммит 7cb44fb373
7 изменённых файлов: 80 добавлений и 16 удалений

Просмотреть файл

@ -52,7 +52,9 @@ func TestClangAttributes(t *testing.T) {
for _, options := range []*compileopts.Options{ for _, options := range []*compileopts.Options{
{GOOS: "linux", GOARCH: "386"}, {GOOS: "linux", GOARCH: "386"},
{GOOS: "linux", GOARCH: "amd64"}, {GOOS: "linux", GOARCH: "amd64"},
{GOOS: "linux", GOARCH: "arm"}, {GOOS: "linux", GOARCH: "arm", GOARM: "5"},
{GOOS: "linux", GOARCH: "arm", GOARM: "6"},
{GOOS: "linux", GOARCH: "arm", GOARM: "7"},
{GOOS: "linux", GOARCH: "arm64"}, {GOOS: "linux", GOARCH: "arm64"},
{GOOS: "darwin", GOARCH: "amd64"}, {GOOS: "darwin", GOARCH: "amd64"},
{GOOS: "darwin", GOARCH: "arm64"}, {GOOS: "darwin", GOARCH: "arm64"},

Просмотреть файл

@ -60,6 +60,12 @@ func (c *Config) GOARCH() string {
return c.Target.GOARCH return c.Target.GOARCH
} }
// GOARM will return the GOARM environment variable given to the compiler when
// building a program.
func (c *Config) GOARM() string {
return c.Options.GOARM
}
// BuildTags returns the complete list of build tags used during this build. // BuildTags returns the complete list of build tags used during this build.
func (c *Config) BuildTags() []string { func (c *Config) BuildTags() []string {
tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...) tags := append(c.Target.BuildTags, []string{"tinygo", "math_big_pure_go", "gc." + c.GC(), "scheduler." + c.Scheduler(), "serial." + c.Serial()}...)

Просмотреть файл

@ -21,6 +21,7 @@ var (
type Options struct { type Options struct {
GOOS string // environment variable GOOS string // environment variable
GOARCH string // environment variable GOARCH string // environment variable
GOARM string // environment variable (only used with GOARCH=arm)
Target string Target string
Opt string Opt string
GC string GC string

Просмотреть файл

@ -5,6 +5,7 @@ package compileopts
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"io" "io"
"os" "os"
"os/exec" "os/exec"
@ -165,13 +166,26 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
if options.Target == "" { if options.Target == "" {
// Configure based on GOOS/GOARCH environment variables (falling back to // Configure based on GOOS/GOARCH environment variables (falling back to
// runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it. // runtime.GOOS/runtime.GOARCH), and generate a LLVM target based on it.
llvmarch := map[string]string{ var llvmarch string
"386": "i386", switch options.GOARCH {
"amd64": "x86_64", case "386":
"arm64": "aarch64", llvmarch = "i386"
"arm": "armv7", case "amd64":
}[options.GOARCH] llvmarch = "x86_64"
if llvmarch == "" { case "arm64":
llvmarch = "aarch64"
case "arm":
switch options.GOARM {
case "5":
llvmarch = "armv5"
case "6":
llvmarch = "armv6"
case "7":
llvmarch = "armv7"
default:
return nil, fmt.Errorf("invalid GOARM=%s, must be 5, 6, or 7", options.GOARM)
}
default:
llvmarch = options.GOARCH llvmarch = options.GOARCH
} }
llvmos := options.GOOS llvmos := options.GOOS
@ -245,7 +259,14 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87" spec.Features = "+cx8,+fxsr,+mmx,+sse,+sse2,+x87"
case "arm": case "arm":
spec.CPU = "generic" spec.CPU = "generic"
switch strings.Split(triple, "-")[0] {
case "armv5":
spec.Features = "+armv5t,+strict-align,-thumb-mode"
case "armv6":
spec.Features = "+armv6,+dsp,+fp64,+strict-align,+vfp2,+vfp2sp,-thumb-mode"
case "armv7":
spec.Features = "+armv7-a,+dsp,+fp64,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp,-thumb-mode" spec.Features = "+armv7-a,+dsp,+fp64,+vfp2,+vfp2sp,+vfp3d16,+vfp3d16sp,-thumb-mode"
}
case "arm64": case "arm64":
spec.CPU = "generic" spec.CPU = "generic"
spec.Features = "+neon" spec.Features = "+neon"

Просмотреть файл

@ -25,6 +25,12 @@ var Keys = []string{
"TINYGOROOT", "TINYGOROOT",
} }
func init() {
if Get("GOARCH") == "arm" {
Keys = append(Keys, "GOARM")
}
}
// TINYGOROOT is the path to the final location for checking tinygo files. If // TINYGOROOT is the path to the final location for checking tinygo files. If
// unset (by a -X ldflag), then sourceDir() will fallback to the original build // unset (by a -X ldflag), then sourceDir() will fallback to the original build
// directory. // directory.
@ -44,6 +50,20 @@ func Get(name string) string {
return dir return dir
} }
return runtime.GOARCH return runtime.GOARCH
case "GOARM":
if goarm := os.Getenv("GOARM"); goarm != "" {
return goarm
}
if goos := Get("GOOS"); goos == "windows" || goos == "android" {
// Assume Windows and Android are running on modern CPU cores.
// This matches upstream Go.
return "7"
}
// Default to ARMv6 on other devices.
// The difference between ARMv5 and ARMv6 is big, much bigger than the
// difference between ARMv6 and ARMv7. ARMv6 binaries are much smaller,
// especially when floating point instructions are involved.
return "6"
case "GOROOT": case "GOROOT":
return getGoroot() return getGoroot()
case "GOPATH": case "GOPATH":

Просмотреть файл

@ -1192,6 +1192,7 @@ func main() {
options := &compileopts.Options{ options := &compileopts.Options{
GOOS: goenv.Get("GOOS"), GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"), GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
Target: *target, Target: *target,
Opt: *opt, Opt: *opt,
GC: *gc, GC: *gc,
@ -1401,6 +1402,7 @@ func main() {
fmt.Printf("LLVM triple: %s\n", config.Triple()) fmt.Printf("LLVM triple: %s\n", config.Triple())
fmt.Printf("GOOS: %s\n", config.GOOS()) fmt.Printf("GOOS: %s\n", config.GOOS())
fmt.Printf("GOARCH: %s\n", config.GOARCH()) fmt.Printf("GOARCH: %s\n", config.GOARCH())
fmt.Printf("GOARM: %s\n", config.GOARM())
fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " ")) fmt.Printf("build tags: %s\n", strings.Join(config.BuildTags(), " "))
fmt.Printf("garbage collector: %s\n", config.GC()) fmt.Printf("garbage collector: %s\n", config.GC())
fmt.Printf("scheduler: %s\n", config.Scheduler()) fmt.Printf("scheduler: %s\n", config.Scheduler())

Просмотреть файл

@ -95,13 +95,13 @@ func TestCompiler(t *testing.T) {
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
t.Run("X86Linux", func(t *testing.T) { t.Run("X86Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux", "386"), tests, t) runPlatTests(optionsFromOSARCH("linux/386"), tests, t)
}) })
t.Run("ARMLinux", func(t *testing.T) { t.Run("ARMLinux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux", "arm"), tests, t) runPlatTests(optionsFromOSARCH("linux/arm/6"), tests, t)
}) })
t.Run("ARM64Linux", func(t *testing.T) { t.Run("ARM64Linux", func(t *testing.T) {
runPlatTests(optionsFromOSARCH("linux", "arm64"), tests, t) runPlatTests(optionsFromOSARCH("linux/arm64"), tests, t)
}) })
t.Run("WebAssembly", func(t *testing.T) { t.Run("WebAssembly", func(t *testing.T) {
runPlatTests(optionsFromTarget("wasm"), tests, t) runPlatTests(optionsFromTarget("wasm"), tests, t)
@ -125,6 +125,7 @@ func TestCompiler(t *testing.T) {
runTestWithConfig("stdlib.go", t, compileopts.Options{ runTestWithConfig("stdlib.go", t, compileopts.Options{
GOOS: goenv.Get("GOOS"), GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"), GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
Opt: "1", Opt: "1",
}, nil, nil) }, nil, nil)
}) })
@ -136,6 +137,7 @@ func TestCompiler(t *testing.T) {
runTestWithConfig("print.go", t, compileopts.Options{ runTestWithConfig("print.go", t, compileopts.Options{
GOOS: goenv.Get("GOOS"), GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"), GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
Opt: "0", Opt: "0",
}, nil, nil) }, nil, nil)
}) })
@ -145,6 +147,7 @@ func TestCompiler(t *testing.T) {
runTestWithConfig("ldflags.go", t, compileopts.Options{ runTestWithConfig("ldflags.go", t, compileopts.Options{
GOOS: goenv.Get("GOOS"), GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"), GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
GlobalValues: map[string]map[string]string{ GlobalValues: map[string]map[string]string{
"main": { "main": {
"someGlobal": "foobar", "someGlobal": "foobar",
@ -200,15 +203,24 @@ func optionsFromTarget(target string) compileopts.Options {
// GOOS/GOARCH are only used if target == "" // GOOS/GOARCH are only used if target == ""
GOOS: goenv.Get("GOOS"), GOOS: goenv.Get("GOOS"),
GOARCH: goenv.Get("GOARCH"), GOARCH: goenv.Get("GOARCH"),
GOARM: goenv.Get("GOARM"),
Target: target, Target: target,
} }
} }
func optionsFromOSARCH(goos, goarch string) compileopts.Options { // optionsFromOSARCH returns a set of options based on the "osarch" string. This
return compileopts.Options{ // string is in the form of "os/arch/subarch", with the subarch only sometimes
GOOS: goos, // being necessary. Examples are "darwin/amd64" or "linux/arm/7".
GOARCH: goarch, func optionsFromOSARCH(osarch string) compileopts.Options {
parts := strings.Split(osarch, "/")
options := compileopts.Options{
GOOS: parts[0],
GOARCH: parts[1],
} }
if options.GOARCH == "arm" {
options.GOARM = parts[2]
}
return options
} }
func runTest(name string, options compileopts.Options, t *testing.T, cmdArgs, environmentVars []string) { func runTest(name string, options compileopts.Options, t *testing.T, cmdArgs, environmentVars []string) {