diff --git a/compiler/compiler.go b/compiler/compiler.go index 6889e55b..4a42bc69 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1336,12 +1336,14 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) return b.createMemoryCopyCall(fn, instr.Args) case name == "runtime.memzero": return b.createMemoryZeroCall(instr.Args) - case name == "device.Asm" || name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm": + case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm": return b.createInlineAsm(instr.Args) - case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull": + case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull": return b.createInlineAsmFull(instr) case strings.HasPrefix(name, "device/arm.SVCall"): return b.emitSVCall(instr.Args) + case strings.HasPrefix(name, "device/arm64.SVCall"): + return b.emitSV64Call(instr.Args) case strings.HasPrefix(name, "(device/riscv.CSR)."): return b.emitCSROperation(instr) case strings.HasPrefix(name, "syscall.Syscall"): diff --git a/compiler/inlineasm.go b/compiler/inlineasm.go index 6089f096..31312a92 100644 --- a/compiler/inlineasm.go +++ b/compiler/inlineasm.go @@ -163,6 +163,44 @@ func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) { return b.CreateCall(target, llvmArgs, ""), nil } +// This is a compiler builtin which emits an inline SVCall instruction. It can +// be one of: +// +// func SVCall0(num uintptr) uintptr +// func SVCall1(num uintptr, a1 interface{}) uintptr +// func SVCall2(num uintptr, a1, a2 interface{}) uintptr +// func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr +// func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr +// +// The num parameter must be a constant. All other parameters may be any scalar +// value supported by LLVM inline assembly. +// Same as emitSVCall but for AArch64 +func (b *builder) emitSV64Call(args []ssa.Value) (llvm.Value, error) { + num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value) + llvmArgs := []llvm.Value{} + argTypes := []llvm.Type{} + asm := "svc #" + strconv.FormatUint(num, 10) + constraints := "={x0}" + for i, arg := range args[1:] { + arg = arg.(*ssa.MakeInterface).X + if i == 0 { + constraints += ",0" + } else { + constraints += ",{x" + strconv.Itoa(i) + "}" + } + llvmValue := b.getValue(arg) + llvmArgs = append(llvmArgs, llvmValue) + argTypes = append(argTypes, llvmValue.Type()) + } + // Implement the ARM64 calling convention by marking x1-x7 as + // clobbered. x0 is used as an output register so doesn't have to be + // marked as clobbered. + constraints += ",~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7}" + fnType := llvm.FunctionType(b.uintptrType, argTypes, false) + target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0) + return b.CreateCall(target, llvmArgs, ""), nil +} + // This is a compiler builtin which emits CSR instructions. It can be one of: // // func (csr CSR) Get() uintptr diff --git a/src/device/arm64/arm64.go b/src/device/arm64/arm64.go new file mode 100644 index 00000000..bfd18ab2 --- /dev/null +++ b/src/device/arm64/arm64.go @@ -0,0 +1,36 @@ +package arm64 + +// Run the given assembly code. The code will be marked as having side effects, +// as it doesn't produce output and thus would normally be eliminated by the +// optimizer. +func Asm(asm string) + +// Run the given inline assembly. The code will be marked as having side +// effects, as it would otherwise be optimized away. The inline assembly string +// recognizes template values in the form {name}, like so: +// +// arm.AsmFull( +// "str {value}, {result}", +// map[string]interface{}{ +// "value": 1 +// "result": &dest, +// }) +// +// You can use {} in the asm string (which expands to a register) to set the +// return value. +func AsmFull(asm string, regs map[string]interface{}) uintptr + +// Run the following system call (SVCall) with 0 arguments. +func SVCall0(num uintptr) uintptr + +// Run the following system call (SVCall) with 1 argument. +func SVCall1(num uintptr, a1 interface{}) uintptr + +// Run the following system call (SVCall) with 2 arguments. +func SVCall2(num uintptr, a1, a2 interface{}) uintptr + +// Run the following system call (SVCall) with 3 arguments. +func SVCall3(num uintptr, a1, a2, a3 interface{}) uintptr + +// Run the following system call (SVCall) with 4 arguments. +func SVCall4(num uintptr, a1, a2, a3, a4 interface{}) uintptr