compiler: add syscalls for 32-bit arm

Этот коммит содержится в:
Ayke van Laethem 2019-02-05 19:30:52 +01:00 коммит произвёл Ron Evans
родитель 4b477fad55
коммит 93d5269fef
4 изменённых файлов: 72 добавлений и 21 удалений

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

@ -15,8 +15,11 @@ addons:
- llvm-7-dev - llvm-7-dev
- clang-7 - clang-7
- libclang-7-dev - libclang-7-dev
- gcc-arm-linux-gnueabi
- binutils-arm-none-eabi - binutils-arm-none-eabi
- libc6-dev-armel-cross
- qemu-system-arm - qemu-system-arm
- qemu-user
- gcc-avr - gcc-avr
- avr-libc - avr-libc

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

@ -5,6 +5,7 @@ package compiler
import ( import (
"go/constant" "go/constant"
"strconv"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
@ -14,6 +15,7 @@ import (
// OS/arch. // OS/arch.
func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) { func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value) num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value)
var syscallResult llvm.Value
switch { switch {
case c.GOARCH == "amd64" && c.GOOS == "linux": case c.GOARCH == "amd64" && c.GOOS == "linux":
// Sources: // Sources:
@ -43,25 +45,60 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
constraints += ",~{rcx},~{r11}" constraints += ",~{rcx},~{r11}"
fnType := llvm.FunctionType(c.uintptrType, argTypes, false) fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel) target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
syscallResult := c.builder.CreateCall(target, args, "") syscallResult = c.builder.CreateCall(target, args, "")
// Return values: r1, r1, err uintptr case c.GOARCH == "arm" && c.GOOS == "linux":
// Pseudocode: // Implement the EABI system call convention for Linux.
// var err uintptr // Source: syscall(2) man page.
// if syscallResult < 0 && syscallResult > -4096 { args := []llvm.Value{}
// err = -syscallResult argTypes := []llvm.Type{}
// } // Constraints will look something like:
// return syscallResult, 0, err // ={r0},0,{r1},{r2},{r7},~{r3}
zero := llvm.ConstInt(c.uintptrType, 0, false) constraints := "={r0}"
inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "") for i, arg := range call.Args[1:] {
inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096 constraints += "," + [...]string{
hasError := c.builder.CreateAnd(inrange1, inrange2, "") "0", // tie to output
errResult := c.builder.CreateSelect(hasError, c.builder.CreateNot(syscallResult, ""), zero, "syscallError") "{r1}",
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false)) "{r2}",
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "") "{r3}",
retval = c.builder.CreateInsertValue(retval, zero, 1, "") "{r4}",
retval = c.builder.CreateInsertValue(retval, errResult, 2, "") "{r5}",
return retval, nil "{r6}",
}[i]
llvmValue, err := c.parseExpr(frame, arg)
if err != nil {
return llvm.Value{}, err
}
args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
args = append(args, llvm.ConstInt(c.uintptrType, num, false))
argTypes = append(argTypes, c.uintptrType)
constraints += ",{r7}" // syscall number
for i := len(call.Args) - 1; i < 4; i++ {
// r0-r3 get clobbered after the syscall returns
constraints += ",~{r" + strconv.Itoa(i) + "}"
}
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
syscallResult = c.builder.CreateCall(target, args, "")
default: default:
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH) 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
} }

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

@ -42,7 +42,7 @@ func TestCompiler(t *testing.T) {
} }
defer os.RemoveAll(tmpdir) defer os.RemoveAll(tmpdir)
t.Log("running tests on the host...") t.Log("running tests on host...")
for _, path := range matches { for _, path := range matches {
t.Run(path, func(t *testing.T) { t.Run(path, func(t *testing.T) {
runTest(path, tmpdir, "", t) runTest(path, tmpdir, "", t)
@ -53,7 +53,17 @@ func TestCompiler(t *testing.T) {
return return
} }
t.Log("running tests on the qemu target...") 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 emulated cortex-m3...")
for _, path := range matches { for _, path := range matches {
t.Run(path, func(t *testing.T) { t.Run(path, func(t *testing.T) {
runTest(path, tmpdir, "qemu", t) runTest(path, tmpdir, "qemu", t)

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

@ -218,10 +218,11 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
} }
if goarch != runtime.GOARCH { if goarch != runtime.GOARCH {
// Some educated guesses as to how to invoke helper programs. // Some educated guesses as to how to invoke helper programs.
if goarch == "arm" { if goarch == "arm" && goos == "linux" {
spec.Linker = "arm-linux-gnueabi-gcc" spec.Linker = "arm-linux-gnueabi-gcc"
spec.Objcopy = "arm-linux-gnueabi-objcopy" spec.Objcopy = "arm-linux-gnueabi-objcopy"
spec.GDB = "arm-linux-gnueabi-gdb" spec.GDB = "arm-linux-gnueabi-gdb"
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabi"}
} }
if goarch == "arm64" { if goarch == "arm64" {
spec.Linker = "aarch64-linux-gnu-gcc" spec.Linker = "aarch64-linux-gnu-gcc"