compiler: add support for parameters to inline assembly
Этот коммит содержится в:
родитель
52199f4a14
коммит
392bba8394
4 изменённых файлов: 123 добавлений и 17 удалений
|
@ -9,6 +9,7 @@ import (
|
||||||
"go/types"
|
"go/types"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"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.
|
// Try to call the function directly for trivially static calls.
|
||||||
if fn := instr.StaticCallee(); fn != nil {
|
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.
|
// 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)
|
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{}, false)
|
||||||
asm := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
asm := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||||
target := llvm.InlineAsm(fnType, asm, "", true, false, 0)
|
target := llvm.InlineAsm(fnType, asm, "", true, false, 0)
|
||||||
return c.builder.CreateCall(target, nil, ""), nil
|
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)
|
targetFunc := c.ir.GetFunction(fn)
|
||||||
if targetFunc.LLVMFn.IsNil() {
|
if targetFunc.LLVMFn.IsNil() {
|
||||||
return llvm.Value{}, errors.New("undefined function: " + targetFunc.LinkName())
|
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")
|
arm.Asm("wfi")
|
||||||
|
|
||||||
There is no support yet for inline assembly that takes (register) parameters or
|
You can also pass parameters to the inline assembly::
|
||||||
returns a value.
|
|
||||||
|
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)
|
Harvard architectures (AVR)
|
||||||
|
|
|
@ -33,13 +33,26 @@ import (
|
||||||
"unsafe"
|
"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
|
//go:volatile
|
||||||
type RegValue uint32
|
type RegValue uint32
|
||||||
|
|
||||||
type __asm string
|
|
||||||
|
|
||||||
func Asm(s __asm)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SCS_BASE = 0xE000E000
|
SCS_BASE = 0xE000E000
|
||||||
NVIC_BASE = SCS_BASE + 0x0100
|
NVIC_BASE = SCS_BASE + 0x0100
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
package avr
|
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,
|
// 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
|
// as it doesn't produce output and thus would normally be eliminated by the
|
||||||
// optimizer.
|
// 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{})
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче