Browse Source

all: add macOS support

pull/192/head
Ayke van Laethem 6 years ago
committed by Ron Evans
parent
commit
0b212cf2f6
  1. 17
      .travis.yml
  2. 6
      Gopkg.lock
  3. 2
      compiler/compiler.go
  4. 73
      compiler/syscall.go
  5. 6
      loader/libclang_config.go
  6. 6
      main.go
  7. 39
      main_test.go
  8. 2
      src/os/file_unix.go
  9. 5
      src/runtime/os_darwin.go
  10. 2
      src/runtime/runtime_unix.go
  11. 9
      target.go

17
.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

6
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"

2
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)
}

73
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)
}
}

6
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"

6
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")
}

39
main_test.go

@ -10,6 +10,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"sort"
"testing"
)
@ -53,31 +54,33 @@ func TestCompiler(t *testing.T) {
return
}
t.Log("running tests for linux/arm...")
t.Log("running tests for emulated cortex-m3...")
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)
runTest(path, tmpdir, "qemu", t)
})
}
t.Log("running tests for linux/arm64...")
for _, path := range matches {
if path == "testdata/cgo/" {
continue // TODO: improve CGo
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.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)
})
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)
})
}
}
}

2
src/os/file_unix.go

@ -1,4 +1,4 @@
// +build linux
// +build darwin linux
package os

5
src/runtime/os_darwin.go

@ -0,0 +1,5 @@
// +build darwin
package runtime
const GOOS = "darwin"

2
src/runtime/runtime_unix.go

@ -1,4 +1,4 @@
// +build linux
// +build darwin linux
package runtime

9
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" {

Loading…
Cancel
Save