
This commit adds getValue which gets a const, global, or result of a local SSA expression and replaces (almost) all uses of parseExpr with getValue. The only remaining use is in parseInstr, which makes sure an instruction is only evaluated once.
155 строки
5,4 КиБ
Go
155 строки
5,4 КиБ
Go
package compiler
|
|
|
|
// This file implements inline asm support by calling special functions.
|
|
|
|
import (
|
|
"fmt"
|
|
"go/constant"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/tools/go/ssa"
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
// This is a compiler builtin, which reads the given register by name:
|
|
//
|
|
// func ReadRegister(name string) uintptr
|
|
//
|
|
// The register name must be a constant, for example "sp".
|
|
func (c *Compiler) emitReadRegister(args []ssa.Value) (llvm.Value, error) {
|
|
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false)
|
|
regname := constant.StringVal(args[0].(*ssa.Const).Value)
|
|
target := llvm.InlineAsm(fnType, "mov $0, "+regname, "=r", false, false, 0)
|
|
return c.builder.CreateCall(target, nil, ""), nil
|
|
}
|
|
|
|
// This is a compiler builtin, which emits a piece of inline assembly with no
|
|
// operands or return values. It is useful for trivial instructions, like wfi in
|
|
// ARM or sleep in AVR.
|
|
//
|
|
// func Asm(asm string)
|
|
//
|
|
// The provided assembly must be a constant.
|
|
func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) {
|
|
// Magic function: insert inline assembly instead of calling it.
|
|
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{}, false)
|
|
asm := constant.StringVal(args[0].(*ssa.Const).Value)
|
|
target := llvm.InlineAsm(fnType, asm, "", true, false, 0)
|
|
return c.builder.CreateCall(target, nil, ""), nil
|
|
}
|
|
|
|
// This is a compiler builtin, which allows assembly to be called in a flexible
|
|
// way.
|
|
//
|
|
// func AsmFull(asm string, regs map[string]interface{})
|
|
//
|
|
// The asm parameter must be a constant string. The regs parameter must be
|
|
// provided immediately. For example:
|
|
//
|
|
// arm.AsmFull(
|
|
// "str {value}, {result}",
|
|
// map[string]interface{}{
|
|
// "value": 1
|
|
// "result": &dest,
|
|
// })
|
|
func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
|
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
|
registers := map[string]llvm.Value{}
|
|
registerMap := instr.Args[1].(*ssa.MakeMap)
|
|
for _, r := range *registerMap.Referrers() {
|
|
switch r := r.(type) {
|
|
case *ssa.DebugRef:
|
|
// ignore
|
|
case *ssa.MapUpdate:
|
|
if r.Block() != registerMap.Block() {
|
|
return llvm.Value{}, c.makeError(instr.Pos(), "register value map must be created in the same basic block")
|
|
}
|
|
key := constant.StringVal(r.Key.(*ssa.Const).Value)
|
|
//println("value:", r.Value.(*ssa.MakeInterface).X.String())
|
|
registers[key] = c.getValue(frame, r.Value.(*ssa.MakeInterface).X)
|
|
case *ssa.Call:
|
|
if r.Common() == instr {
|
|
break
|
|
}
|
|
default:
|
|
return llvm.Value{}, c.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
|
|
}
|
|
}
|
|
// TODO: handle dollar signs in asm string
|
|
registerNumbers := map[string]int{}
|
|
var err error
|
|
argTypes := []llvm.Type{}
|
|
args := []llvm.Value{}
|
|
constraints := []string{}
|
|
asmString = regexp.MustCompile("\\{[a-zA-Z]+\\}").ReplaceAllStringFunc(asmString, func(s string) string {
|
|
// TODO: skip strings like {r4} etc. that look like ARM push/pop
|
|
// instructions.
|
|
name := s[1 : len(s)-1]
|
|
if _, ok := registers[name]; !ok {
|
|
if err == nil {
|
|
err = c.makeError(instr.Pos(), "unknown register name: "+name)
|
|
}
|
|
return s
|
|
}
|
|
if _, ok := registerNumbers[name]; !ok {
|
|
registerNumbers[name] = len(registerNumbers)
|
|
argTypes = append(argTypes, registers[name].Type())
|
|
args = append(args, registers[name])
|
|
switch registers[name].Type().TypeKind() {
|
|
case llvm.IntegerTypeKind:
|
|
constraints = append(constraints, "r")
|
|
case llvm.PointerTypeKind:
|
|
constraints = append(constraints, "*m")
|
|
default:
|
|
err = c.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
|
|
return s
|
|
}
|
|
}
|
|
return fmt.Sprintf("${%v}", registerNumbers[name])
|
|
})
|
|
if err != nil {
|
|
return llvm.Value{}, err
|
|
}
|
|
fnType := llvm.FunctionType(c.ctx.VoidType(), argTypes, false)
|
|
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0)
|
|
return c.builder.CreateCall(target, args, ""), 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.
|
|
func (c *Compiler) emitSVCall(frame *Frame, 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 := "={r0}"
|
|
for i, arg := range args[1:] {
|
|
arg = arg.(*ssa.MakeInterface).X
|
|
if i == 0 {
|
|
constraints += ",0"
|
|
} else {
|
|
constraints += ",{r" + strconv.Itoa(i) + "}"
|
|
}
|
|
llvmValue := c.getValue(frame, arg)
|
|
llvmArgs = append(llvmArgs, llvmValue)
|
|
argTypes = append(argTypes, llvmValue.Type())
|
|
}
|
|
// Implement the ARM calling convention by marking r1-r3 as
|
|
// clobbered. r0 is used as an output register so doesn't have to be
|
|
// marked as clobbered.
|
|
constraints += ",~{r1},~{r2},~{r3}"
|
|
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
|
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
|
|
return c.builder.CreateCall(target, llvmArgs, ""), nil
|
|
}
|