compiler: add syscalls for 32-bit arm
Этот коммит содержится в:
родитель
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
|
||||||
}
|
}
|
||||||
|
|
14
main_test.go
14
main_test.go
|
@ -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"
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче