From 0b212cf2f6b070531392e783896a065f814a88a0 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 9 Feb 2019 23:18:17 +0100 Subject: [PATCH] all: add macOS support --- .travis.yml | 17 +++++++-- Gopkg.lock | 6 +-- compiler/compiler.go | 2 +- compiler/syscall.go | 73 ++++++++++++++++++++++++++++--------- loader/libclang_config.go | 6 ++- main.go | 6 ++- main_test.go | 43 ++++++++++++---------- src/os/file_unix.go | 2 +- src/runtime/os_darwin.go | 5 +++ src/runtime/runtime_unix.go | 2 +- target.go | 9 ++++- 11 files changed, 119 insertions(+), 52 deletions(-) create mode 100644 src/runtime/os_darwin.go diff --git a/.travis.yml b/.travis.yml index deedcdcb..2734e824 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,9 @@ matrix: include: - dist: xenial go: "1.11" + - os: osx + go: "1.11" + env: PATH="/usr/local/opt/llvm/bin:$PATH" addons: apt: @@ -24,8 +27,16 @@ addons: - qemu-user - gcc-avr - avr-libc + homebrew: + update: true + taps: ArmMbed/homebrew-formulae + packages: + - llvm@7 + - qemu + - arm-none-eabi-gcc install: + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then mkdir -p /Users/travis/gopath/bin; fi - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure --vendor-only @@ -35,14 +46,14 @@ script: - make gen-device - tinygo build -size short -o blinky1.nrf.elf -target=pca10040 examples/blinky1 - tinygo build -size short -o blinky2.nrf.elf -target=pca10040 examples/blinky2 - - tinygo build -size short -o blinky2 examples/blinky2 + - tinygo build -o blinky2 examples/blinky2 # TODO: re-enable -size flag with MachO support - tinygo build -size short -o test.nrf.elf -target=pca10040 examples/test - tinygo build -size short -o blinky1.nrf51.elf -target=microbit examples/echo - tinygo build -size short -o test.nrf.elf -target=nrf52840-mdk examples/blinky1 - tinygo build -size short -o blinky1.nrf51d.elf -target=pca10031 examples/blinky1 - tinygo build -size short -o blinky1.stm32.elf -target=bluepill examples/blinky1 - - tinygo build -size short -o blinky1.avr.elf -target=arduino examples/blinky1 - - tinygo build -size short -o blinky1.avr.elf -target=digispark examples/blinky1 + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then tinygo build -size short -o blinky1.avr.elf -target=arduino examples/blinky1; fi + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then tinygo build -size short -o blinky1.avr.elf -target=digispark examples/blinky1; fi - tinygo build -size short -o blinky1.reel.elf -target=reelboard examples/blinky1 - tinygo build -size short -o blinky2.reel.elf -target=reelboard examples/blinky2 - tinygo build -size short -o blinky1.pca10056.elf -target=pca10056 examples/blinky1 diff --git a/Gopkg.lock b/Gopkg.lock index aae7e7a5..0e0c2773 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -11,15 +11,15 @@ "go/types/typeutil", ] pruneopts = "UT" - revision = "40960b6deb8ecdb8bcde6a8f44722731939b8ddc" + revision = "3744606dbb67b99c60d3f11cb10bd3f9e6dad472" [[projects]] branch = "master" - digest = "1:3611159788efdd4e0cfae18b6ebcccbad25a2815968b0e4323b42647d201031a" + digest = "1:a6a25fd8906c74978f1ed811bc9fd3422da8093be863b458874b02a782b6ae3e" name = "tinygo.org/x/go-llvm" packages = ["."] pruneopts = "UT" - revision = "f420620d1a0f54417a5712260153fe861780d030" + revision = "d5f730401f5069618b275a5241c6417eb0c38a65" [solve-meta] analyzer-name = "dep" diff --git a/compiler/compiler.go b/compiler/compiler.go index d54219c6..cf5a61c6 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1353,7 +1353,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e } switch fn.RelString(nil) { - case "syscall.Syscall", "syscall.Syscall6": + case "syscall.Syscall", "syscall.Syscall6", "syscall.Syscall9": return c.emitSyscall(frame, instr) } diff --git a/compiler/syscall.go b/compiler/syscall.go index fc9235d3..f9e3e465 100644 --- a/compiler/syscall.go +++ b/compiler/syscall.go @@ -17,7 +17,20 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value) var syscallResult llvm.Value switch { - case c.GOARCH == "amd64" && c.GOOS == "linux": + case c.GOARCH == "amd64": + if c.GOOS == "darwin" { + // Darwin adds this magic number to system call numbers: + // + // > Syscall classes for 64-bit system call entry. + // > For 64-bit users, the 32-bit syscall number is partitioned + // > with the high-order bits representing the class and low-order + // > bits being the syscall number within that class. + // > The high-order 32-bits of the 64-bit syscall number are unused. + // > All system classes enter the kernel via the syscall instruction. + // + // Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h + num += 0x2000000 + } // Sources: // https://stackoverflow.com/a/2538212 // https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall @@ -34,6 +47,9 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, "{r10}", "{r8}", "{r9}", + "{r11}", + "{r12}", + "{r13}", }[i] llvmValue, err := c.parseExpr(frame, arg) if err != nil { @@ -119,21 +135,42 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, default: return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH) } - // Return values: r0, r1, err uintptr - // Pseudocode: - // var err uintptr - // if syscallResult < 0 && syscallResult > -4096 { - // err = -syscallResult - // } - // return syscallResult, 0, err - zero := llvm.ConstInt(c.uintptrType, 0, false) - inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") - inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096 - hasError := c.builder.CreateAnd(inrange1, inrange2, "") - errResult := c.builder.CreateSelect(hasError, c.builder.CreateNot(syscallResult, ""), zero, "syscallError") - retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) - retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") - retval = c.builder.CreateInsertValue(retval, zero, 1, "") - retval = c.builder.CreateInsertValue(retval, errResult, 2, "") - return retval, nil + switch c.GOOS { + case "linux": + // Return values: r0, r1 uintptr, err Errno + // Pseudocode: + // var err uintptr + // if syscallResult < 0 && syscallResult > -4096 { + // err = -syscallResult + // } + // return syscallResult, 0, err + zero := llvm.ConstInt(c.uintptrType, 0, false) + inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") + inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096 + hasError := c.builder.CreateAnd(inrange1, inrange2, "") + errResult := c.builder.CreateSelect(hasError, c.builder.CreateSub(zero, syscallResult, ""), zero, "syscallError") + retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) + retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") + retval = c.builder.CreateInsertValue(retval, zero, 1, "") + retval = c.builder.CreateInsertValue(retval, errResult, 2, "") + return retval, nil + case "darwin": + // Return values: r0, r1 uintptr, err Errno + // Pseudocode: + // var err uintptr + // if syscallResult != 0 { + // err = syscallResult + // } + // return syscallResult, 0, err + zero := llvm.ConstInt(c.uintptrType, 0, false) + hasError := c.builder.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") + errResult := c.builder.CreateSelect(hasError, syscallResult, zero, "syscallError") + retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) + retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") + retval = c.builder.CreateInsertValue(retval, zero, 1, "") + retval = c.builder.CreateInsertValue(retval, errResult, 2, "") + return retval, nil + default: + return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH) + } } diff --git a/loader/libclang_config.go b/loader/libclang_config.go index e8494b91..5a8e2b2a 100644 --- a/loader/libclang_config.go +++ b/loader/libclang_config.go @@ -3,7 +3,9 @@ package loader /* -#cgo CFLAGS: -I/usr/lib/llvm-7/include -#cgo LDFLAGS: -L/usr/lib/llvm-7/lib -lclang +#cgo linux CFLAGS: -I/usr/lib/llvm-7/include +#cgo darwin CFLAGS: -I/usr/local/opt/llvm/include +#cgo linux LDFLAGS: -L/usr/lib/llvm-7/lib -lclang +#cgo darwin LDFLAGS: -L/usr/local/opt/llvm/lib -lclang -lffi */ import "C" diff --git a/main.go b/main.go index 9e8f1d9a..48ca5c61 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,7 @@ import ( ) var commands = map[string]string{ - "ar": "ar", + "ar": "llvm-ar", "clang": "clang-7", "ld.lld": "ld.lld-7", "wasm-ld": "wasm-ld-7", @@ -102,7 +102,9 @@ func Compile(pkgName, outpath string, spec *TargetSpec, config *BuildConfig, act return errors.New("verification error after interpreting runtime.initAll") } - c.ApplyFunctionSections() // -ffunction-sections + if spec.GOOS != "darwin" { + c.ApplyFunctionSections() // -ffunction-sections + } if err := c.Verify(); err != nil { return errors.New("verification error after applying function sections") } diff --git a/main_test.go b/main_test.go index 6530f060..2e395d4c 100644 --- a/main_test.go +++ b/main_test.go @@ -10,6 +10,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "sort" "testing" ) @@ -53,32 +54,34 @@ func TestCompiler(t *testing.T) { return } - t.Log("running tests for linux/arm...") - for _, path := range matches { - if path == "testdata/cgo/" { - continue // TODO: improve CGo - } - t.Run(path, func(t *testing.T) { - runTest(path, tmpdir, "arm--linux-gnueabi", t) - }) - } - - t.Log("running tests for linux/arm64...") - for _, path := range matches { - if path == "testdata/cgo/" { - continue // TODO: improve CGo - } - t.Run(path, func(t *testing.T) { - runTest(path, tmpdir, "aarch64--linux-gnueabi", t) - }) - } - t.Log("running tests for emulated cortex-m3...") for _, path := range matches { t.Run(path, func(t *testing.T) { runTest(path, tmpdir, "qemu", t) }) } + + if runtime.GOOS == "linux" { + t.Log("running tests for linux/arm...") + for _, path := range matches { + if path == "testdata/cgo/" { + continue // TODO: improve CGo + } + t.Run(path, func(t *testing.T) { + runTest(path, tmpdir, "arm--linux-gnueabi", t) + }) + } + + t.Log("running tests for linux/arm64...") + for _, path := range matches { + if path == "testdata/cgo/" { + continue // TODO: improve CGo + } + t.Run(path, func(t *testing.T) { + runTest(path, tmpdir, "aarch64--linux-gnueabi", t) + }) + } + } } func runTest(path, tmpdir string, target string, t *testing.T) { diff --git a/src/os/file_unix.go b/src/os/file_unix.go index 1f1cb71c..03635130 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -1,4 +1,4 @@ -// +build linux +// +build darwin linux package os diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go new file mode 100644 index 00000000..60110161 --- /dev/null +++ b/src/runtime/os_darwin.go @@ -0,0 +1,5 @@ +// +build darwin + +package runtime + +const GOOS = "darwin" diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index 1749e70c..9e638ff6 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -1,4 +1,4 @@ -// +build linux +// +build darwin linux package runtime diff --git a/target.go b/target.go index a85e6561..f50c91d7 100644 --- a/target.go +++ b/target.go @@ -189,6 +189,9 @@ func LoadTarget(target string) (*TargetSpec, error) { return nil, errors.New("expected a full LLVM target or a custom target in -target flag") } goos := tripleSplit[2] + if strings.HasPrefix(goos, "darwin") { + goos = "darwin" + } goarch := map[string]string{ // map from LLVM arch to Go arch "i386": "386", "x86_64": "amd64", @@ -211,11 +214,15 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) { BuildTags: []string{goos, goarch}, Compiler: commands["clang"], Linker: "cc", - LDFlags: []string{"-no-pie", "-Wl,--gc-sections"}, // WARNING: clang < 5.0 requires -nopie Objcopy: "objcopy", GDB: "gdb", GDBCmds: []string{"run"}, } + if goos == "darwin" { + spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip") + } else { + spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie + } if goarch != runtime.GOARCH { // Some educated guesses as to how to invoke helper programs. if goarch == "arm" && goos == "linux" {