Skip to content

Commit 93d5269

Browse files
aykevldeadprogram
authored andcommitted
compiler: add syscalls for 32-bit arm
1 parent 4b477fa commit 93d5269

File tree

4 files changed

+72
-21
lines changed

4 files changed

+72
-21
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ addons:
1515
- llvm-7-dev
1616
- clang-7
1717
- libclang-7-dev
18+
- gcc-arm-linux-gnueabi
1819
- binutils-arm-none-eabi
20+
- libc6-dev-armel-cross
1921
- qemu-system-arm
22+
- qemu-user
2023
- gcc-avr
2124
- avr-libc
2225

compiler/syscall.go

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package compiler
55

66
import (
77
"go/constant"
8+
"strconv"
89

910
"golang.org/x/tools/go/ssa"
1011
"tinygo.org/x/go-llvm"
@@ -14,6 +15,7 @@ import (
1415
// OS/arch.
1516
func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
1617
num, _ := constant.Uint64Val(call.Args[0].(*ssa.Const).Value)
18+
var syscallResult llvm.Value
1719
switch {
1820
case c.GOARCH == "amd64" && c.GOOS == "linux":
1921
// Sources:
@@ -43,25 +45,60 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
4345
constraints += ",~{rcx},~{r11}"
4446
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
4547
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
46-
syscallResult := c.builder.CreateCall(target, args, "")
47-
// Return values: r1, r1, err uintptr
48-
// Pseudocode:
49-
// var err uintptr
50-
// if syscallResult < 0 && syscallResult > -4096 {
51-
// err = -syscallResult
52-
// }
53-
// return syscallResult, 0, err
54-
zero := llvm.ConstInt(c.uintptrType, 0, false)
55-
inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
56-
inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096
57-
hasError := c.builder.CreateAnd(inrange1, inrange2, "")
58-
errResult := c.builder.CreateSelect(hasError, c.builder.CreateNot(syscallResult, ""), zero, "syscallError")
59-
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
60-
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
61-
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
62-
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
63-
return retval, nil
48+
syscallResult = c.builder.CreateCall(target, args, "")
49+
case c.GOARCH == "arm" && c.GOOS == "linux":
50+
// Implement the EABI system call convention for Linux.
51+
// Source: syscall(2) man page.
52+
args := []llvm.Value{}
53+
argTypes := []llvm.Type{}
54+
// Constraints will look something like:
55+
// ={r0},0,{r1},{r2},{r7},~{r3}
56+
constraints := "={r0}"
57+
for i, arg := range call.Args[1:] {
58+
constraints += "," + [...]string{
59+
"0", // tie to output
60+
"{r1}",
61+
"{r2}",
62+
"{r3}",
63+
"{r4}",
64+
"{r5}",
65+
"{r6}",
66+
}[i]
67+
llvmValue, err := c.parseExpr(frame, arg)
68+
if err != nil {
69+
return llvm.Value{}, err
70+
}
71+
args = append(args, llvmValue)
72+
argTypes = append(argTypes, llvmValue.Type())
73+
}
74+
args = append(args, llvm.ConstInt(c.uintptrType, num, false))
75+
argTypes = append(argTypes, c.uintptrType)
76+
constraints += ",{r7}" // syscall number
77+
for i := len(call.Args) - 1; i < 4; i++ {
78+
// r0-r3 get clobbered after the syscall returns
79+
constraints += ",~{r" + strconv.Itoa(i) + "}"
80+
}
81+
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
82+
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
83+
syscallResult = c.builder.CreateCall(target, args, "")
6484
default:
6585
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS+"/"+c.GOARCH)
6686
}
87+
// Return values: r0, r1, err uintptr
88+
// Pseudocode:
89+
// var err uintptr
90+
// if syscallResult < 0 && syscallResult > -4096 {
91+
// err = -syscallResult
92+
// }
93+
// return syscallResult, 0, err
94+
zero := llvm.ConstInt(c.uintptrType, 0, false)
95+
inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
96+
inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096
97+
hasError := c.builder.CreateAnd(inrange1, inrange2, "")
98+
errResult := c.builder.CreateSelect(hasError, c.builder.CreateNot(syscallResult, ""), zero, "syscallError")
99+
retval := llvm.Undef(llvm.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
100+
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
101+
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
102+
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
103+
return retval, nil
67104
}

main_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func TestCompiler(t *testing.T) {
4242
}
4343
defer os.RemoveAll(tmpdir)
4444

45-
t.Log("running tests on the host...")
45+
t.Log("running tests on host...")
4646
for _, path := range matches {
4747
t.Run(path, func(t *testing.T) {
4848
runTest(path, tmpdir, "", t)
@@ -53,7 +53,17 @@ func TestCompiler(t *testing.T) {
5353
return
5454
}
5555

56-
t.Log("running tests on the qemu target...")
56+
t.Log("running tests for linux/arm...")
57+
for _, path := range matches {
58+
if path == "testdata/cgo/" {
59+
continue // TODO: improve CGo
60+
}
61+
t.Run(path, func(t *testing.T) {
62+
runTest(path, tmpdir, "arm--linux-gnueabi", t)
63+
})
64+
}
65+
66+
t.Log("running tests for emulated cortex-m3...")
5767
for _, path := range matches {
5868
t.Run(path, func(t *testing.T) {
5969
runTest(path, tmpdir, "qemu", t)

target.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,11 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
218218
}
219219
if goarch != runtime.GOARCH {
220220
// Some educated guesses as to how to invoke helper programs.
221-
if goarch == "arm" {
221+
if goarch == "arm" && goos == "linux" {
222222
spec.Linker = "arm-linux-gnueabi-gcc"
223223
spec.Objcopy = "arm-linux-gnueabi-objcopy"
224224
spec.GDB = "arm-linux-gnueabi-gdb"
225+
spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabi"}
225226
}
226227
if goarch == "arm64" {
227228
spec.Linker = "aarch64-linux-gnu-gcc"

0 commit comments

Comments
 (0)