compiler: add support for parameters to inline assembly
Этот коммит содержится в:
родитель
52199f4a14
коммит
392bba8394
4 изменённых файлов: 123 добавлений и 17 удалений
|
@ -9,6 +9,7 @@ import (
|
|||
"go/types"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -1892,15 +1893,81 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
|
|||
|
||||
// Try to call the function directly for trivially static calls.
|
||||
if fn := instr.StaticCallee(); fn != nil {
|
||||
if fn.Name() == "Asm" && len(instr.Args) == 1 {
|
||||
if fn.RelString(nil) == "device/arm.Asm" || fn.RelString(nil) == "device/avr.Asm" {
|
||||
// Magic function: insert inline assembly instead of calling it.
|
||||
if named, ok := instr.Args[0].Type().(*types.Named); ok && named.Obj().Name() == "__asm" {
|
||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{}, false)
|
||||
asm := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||
target := llvm.InlineAsm(fnType, asm, "", true, false, 0)
|
||||
return c.builder.CreateCall(target, nil, ""), nil
|
||||
}
|
||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{}, false)
|
||||
asm := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||
target := llvm.InlineAsm(fnType, asm, "", true, false, 0)
|
||||
return c.builder.CreateCall(target, nil, ""), nil
|
||||
}
|
||||
|
||||
if fn.RelString(nil) == "device/arm.AsmFull" || fn.RelString(nil) == "device/avr.AsmFull" {
|
||||
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{}, errors.New("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())
|
||||
value, err := c.parseExpr(frame, r.Value.(*ssa.MakeInterface).X)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
registers[key] = value
|
||||
case *ssa.Call:
|
||||
if r.Common() == instr {
|
||||
break
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, errors.New("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 = errors.New("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 = errors.New("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
|
||||
}
|
||||
|
||||
targetFunc := c.ir.GetFunction(fn)
|
||||
if targetFunc.LLVMFn.IsNil() {
|
||||
return llvm.Value{}, errors.New("undefined function: " + targetFunc.LinkName())
|
||||
|
|
|
@ -113,8 +113,25 @@ The device-specific packages like ``device/avr`` and ``device/arm`` provide
|
|||
|
||||
arm.Asm("wfi")
|
||||
|
||||
There is no support yet for inline assembly that takes (register) parameters or
|
||||
returns a value.
|
||||
You can also pass parameters to the inline assembly::
|
||||
|
||||
var result int32
|
||||
arm.AsmFull(`
|
||||
lsls {value}, #1
|
||||
str {value}, {result}
|
||||
`, map[string]interface{}{
|
||||
"value": 42,
|
||||
"result": &result,
|
||||
})
|
||||
println("result:", result)
|
||||
|
||||
In general, types are autodetected. That is, integer types are passed as raw
|
||||
registers and pointer types are passed as memory locations. This means you can't
|
||||
easily do pointer arithmetic. To do that, convert a pointer value to a
|
||||
``uintptr``.
|
||||
|
||||
Inline assembly support is expected to change in the future and may change in a
|
||||
backwards-incompatible manner.
|
||||
|
||||
|
||||
Harvard architectures (AVR)
|
||||
|
|
|
@ -33,13 +33,26 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
// 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,
|
||||
// })
|
||||
func AsmFull(asm string, regs map[string]interface{})
|
||||
|
||||
//go:volatile
|
||||
type RegValue uint32
|
||||
|
||||
type __asm string
|
||||
|
||||
func Asm(s __asm)
|
||||
|
||||
const (
|
||||
SCS_BASE = 0xE000E000
|
||||
NVIC_BASE = SCS_BASE + 0x0100
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
package avr
|
||||
|
||||
// Magic type recognozed by the compiler to mark this string as inline assembly.
|
||||
type __asm string
|
||||
|
||||
// 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 __asm)
|
||||
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:
|
||||
//
|
||||
// avr.AsmFull(
|
||||
// "str {value}, {result}",
|
||||
// map[string]interface{}{
|
||||
// "value": 1
|
||||
// "result": &dest,
|
||||
// })
|
||||
func AsmFull(asm string, regs map[string]interface{})
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче