compiler: refactor function calling
Этот коммит содержится в:
родитель
d46934d1f1
коммит
d752e66be5
6 изменённых файлов: 198 добавлений и 192 удалений
|
@ -1066,7 +1066,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
||||||
// A goroutine call on a func value, but the callee is trivial to find. For
|
// A goroutine call on a func value, but the callee is trivial to find. For
|
||||||
// example: immediately applied functions.
|
// example: immediately applied functions.
|
||||||
funcValue := frame.getValue(value)
|
funcValue := frame.getValue(value)
|
||||||
context = c.extractFuncContext(funcValue)
|
context = frame.extractFuncContext(funcValue)
|
||||||
default:
|
default:
|
||||||
panic("StaticCallee returned an unexpected value")
|
panic("StaticCallee returned an unexpected value")
|
||||||
}
|
}
|
||||||
|
@ -1078,7 +1078,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
||||||
// goroutine:
|
// goroutine:
|
||||||
// * The function context, for closures.
|
// * The function context, for closures.
|
||||||
// * The function pointer (for tasks).
|
// * The function pointer (for tasks).
|
||||||
funcPtr, context := c.decodeFuncValue(frame.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature))
|
funcPtr, context := frame.decodeFuncValue(frame.getValue(instr.Call.Value), instr.Call.Value.Type().(*types.Signature))
|
||||||
params = append(params, context) // context parameter
|
params = append(params, context) // context parameter
|
||||||
switch c.Scheduler() {
|
switch c.Scheduler() {
|
||||||
case "none", "coroutines":
|
case "none", "coroutines":
|
||||||
|
@ -1315,10 +1315,78 @@ func (b *builder) createBuiltin(args []ssa.Value, callName string, pos token.Pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, context llvm.Value, exported bool) llvm.Value {
|
// createFunctionCall lowers a Go SSA call instruction (to a simple function,
|
||||||
|
// closure, function pointer, builtin, method, etc.) to LLVM IR, usually a call
|
||||||
|
// instruction.
|
||||||
|
//
|
||||||
|
// This is also where compiler intrinsics are implemented.
|
||||||
|
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
|
if instr.IsInvoke() {
|
||||||
|
fnCast, args := b.getInvokeCall(instr)
|
||||||
|
return b.createCall(fnCast, args, ""), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to call the function directly for trivially static calls.
|
||||||
|
var callee, context llvm.Value
|
||||||
|
exported := false
|
||||||
|
if fn := instr.StaticCallee(); fn != nil {
|
||||||
|
// Direct function call, either to a named or anonymous (directly
|
||||||
|
// applied) function call. If it is anonymous, it may be a closure.
|
||||||
|
name := fn.RelString(nil)
|
||||||
|
switch {
|
||||||
|
case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister":
|
||||||
|
return b.createReadRegister(name, instr.Args)
|
||||||
|
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
||||||
|
return b.createInlineAsm(instr.Args)
|
||||||
|
case name == "device/arm.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/riscv.CSR)."):
|
||||||
|
return b.emitCSROperation(instr)
|
||||||
|
case strings.HasPrefix(name, "syscall.Syscall"):
|
||||||
|
return b.createSyscall(instr)
|
||||||
|
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
||||||
|
return b.createVolatileLoad(instr)
|
||||||
|
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
||||||
|
return b.createVolatileStore(instr)
|
||||||
|
case name == "runtime/interrupt.New":
|
||||||
|
return b.createInterruptGlobal(instr)
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFunc := b.ir.GetFunction(fn)
|
||||||
|
if targetFunc.LLVMFn.IsNil() {
|
||||||
|
return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName())
|
||||||
|
}
|
||||||
|
switch value := instr.Value.(type) {
|
||||||
|
case *ssa.Function:
|
||||||
|
// Regular function call. No context is necessary.
|
||||||
|
context = llvm.Undef(b.i8ptrType)
|
||||||
|
case *ssa.MakeClosure:
|
||||||
|
// A call on a func value, but the callee is trivial to find. For
|
||||||
|
// example: immediately applied functions.
|
||||||
|
funcValue := b.getValue(value)
|
||||||
|
context = b.extractFuncContext(funcValue)
|
||||||
|
default:
|
||||||
|
panic("StaticCallee returned an unexpected value")
|
||||||
|
}
|
||||||
|
callee = targetFunc.LLVMFn
|
||||||
|
exported = targetFunc.IsExported()
|
||||||
|
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
||||||
|
// Builtin function (append, close, delete, etc.).)
|
||||||
|
return b.createBuiltin(instr.Args, call.Name(), instr.Pos())
|
||||||
|
} else {
|
||||||
|
// Function pointer.
|
||||||
|
value := b.getValue(instr.Value)
|
||||||
|
// This is a func value, which cannot be called directly. We have to
|
||||||
|
// extract the function pointer and context first from the func value.
|
||||||
|
callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
||||||
|
b.createNilCheck(callee, "fpcall")
|
||||||
|
}
|
||||||
|
|
||||||
var params []llvm.Value
|
var params []llvm.Value
|
||||||
for _, param := range args {
|
for _, param := range instr.Args {
|
||||||
params = append(params, frame.getValue(param))
|
params = append(params, b.getValue(param))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exported {
|
if !exported {
|
||||||
|
@ -1327,74 +1395,10 @@ func (c *Compiler) parseFunctionCall(frame *Frame, args []ssa.Value, llvmFn, con
|
||||||
params = append(params, context)
|
params = append(params, context)
|
||||||
|
|
||||||
// Parent coroutine handle.
|
// Parent coroutine handle.
|
||||||
params = append(params, llvm.Undef(c.i8ptrType))
|
params = append(params, llvm.Undef(b.i8ptrType))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.createCall(llvmFn, params, "")
|
return b.createCall(callee, params, ""), nil
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
|
||||||
if instr.IsInvoke() {
|
|
||||||
fnCast, args := frame.getInvokeCall(instr)
|
|
||||||
return c.createCall(fnCast, args, ""), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to call the function directly for trivially static calls.
|
|
||||||
if fn := instr.StaticCallee(); fn != nil {
|
|
||||||
name := fn.RelString(nil)
|
|
||||||
switch {
|
|
||||||
case name == "device/arm.ReadRegister" || name == "device/riscv.ReadRegister":
|
|
||||||
return c.emitReadRegister(name, instr.Args)
|
|
||||||
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
|
|
||||||
return c.emitAsm(instr.Args)
|
|
||||||
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
|
|
||||||
return c.emitAsmFull(frame, instr)
|
|
||||||
case strings.HasPrefix(name, "device/arm.SVCall"):
|
|
||||||
return c.emitSVCall(frame, instr.Args)
|
|
||||||
case strings.HasPrefix(name, "(device/riscv.CSR)."):
|
|
||||||
return c.emitCSROperation(frame, instr)
|
|
||||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
|
||||||
return c.emitSyscall(frame, instr)
|
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
|
||||||
return c.emitVolatileLoad(frame, instr)
|
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
|
||||||
return c.emitVolatileStore(frame, instr)
|
|
||||||
case name == "runtime/interrupt.New":
|
|
||||||
return c.emitInterruptGlobal(frame, instr)
|
|
||||||
}
|
|
||||||
|
|
||||||
targetFunc := c.ir.GetFunction(fn)
|
|
||||||
if targetFunc.LLVMFn.IsNil() {
|
|
||||||
return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName())
|
|
||||||
}
|
|
||||||
var context llvm.Value
|
|
||||||
switch value := instr.Value.(type) {
|
|
||||||
case *ssa.Function:
|
|
||||||
// Regular function call. No context is necessary.
|
|
||||||
context = llvm.Undef(c.i8ptrType)
|
|
||||||
case *ssa.MakeClosure:
|
|
||||||
// A call on a func value, but the callee is trivial to find. For
|
|
||||||
// example: immediately applied functions.
|
|
||||||
funcValue := frame.getValue(value)
|
|
||||||
context = c.extractFuncContext(funcValue)
|
|
||||||
default:
|
|
||||||
panic("StaticCallee returned an unexpected value")
|
|
||||||
}
|
|
||||||
return c.parseFunctionCall(frame, instr.Args, targetFunc.LLVMFn, context, targetFunc.IsExported()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builtin or function pointer.
|
|
||||||
switch call := instr.Value.(type) {
|
|
||||||
case *ssa.Builtin:
|
|
||||||
return frame.createBuiltin(instr.Args, call.Name(), instr.Pos())
|
|
||||||
default: // function pointer
|
|
||||||
value := frame.getValue(instr.Value)
|
|
||||||
// This is a func value, which cannot be called directly. We have to
|
|
||||||
// extract the function pointer and context first from the func value.
|
|
||||||
funcPtr, context := c.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
|
|
||||||
frame.createNilCheck(funcPtr, "fpcall")
|
|
||||||
return c.parseFunctionCall(frame, instr.Args, funcPtr, context, false), nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getValue returns the LLVM value of a constant, function value, global, or
|
// getValue returns the LLVM value of a constant, function value, global, or
|
||||||
|
@ -1462,9 +1466,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
y := frame.getValue(expr.Y)
|
y := frame.getValue(expr.Y)
|
||||||
return frame.createBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos())
|
return frame.createBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos())
|
||||||
case *ssa.Call:
|
case *ssa.Call:
|
||||||
// Passing the current task here to the subroutine. It is only used when
|
return frame.createFunctionCall(expr.Common())
|
||||||
// the subroutine is blocking.
|
|
||||||
return c.parseCall(frame, expr.Common())
|
|
||||||
case *ssa.ChangeInterface:
|
case *ssa.ChangeInterface:
|
||||||
// Do not change between interface types: always use the underlying
|
// Do not change between interface types: always use the underlying
|
||||||
// (concrete) type in the type number of the interface. Every method
|
// (concrete) type in the type number of the interface. Every method
|
||||||
|
|
|
@ -71,22 +71,22 @@ func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
|
||||||
|
|
||||||
// extractFuncContext extracts the context pointer from this function value. It
|
// extractFuncContext extracts the context pointer from this function value. It
|
||||||
// is a cheap operation.
|
// is a cheap operation.
|
||||||
func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
|
||||||
return c.builder.CreateExtractValue(funcValue, 0, "")
|
return b.CreateExtractValue(funcValue, 0, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeFuncValue extracts the context and the function pointer from this func
|
// decodeFuncValue extracts the context and the function pointer from this func
|
||||||
// value. This may be an expensive operation.
|
// value. This may be an expensive operation.
|
||||||
func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
|
||||||
context = c.builder.CreateExtractValue(funcValue, 0, "")
|
context = b.CreateExtractValue(funcValue, 0, "")
|
||||||
switch c.FuncImplementation() {
|
switch b.FuncImplementation() {
|
||||||
case compileopts.FuncValueDoubleword:
|
case compileopts.FuncValueDoubleword:
|
||||||
funcPtr = c.builder.CreateExtractValue(funcValue, 1, "")
|
funcPtr = b.CreateExtractValue(funcValue, 1, "")
|
||||||
case compileopts.FuncValueSwitch:
|
case compileopts.FuncValueSwitch:
|
||||||
llvmSig := c.getRawFuncType(sig)
|
llvmSig := b.getRawFuncType(sig)
|
||||||
sigGlobal := c.getTypeCode(sig)
|
sigGlobal := b.getTypeCode(sig)
|
||||||
funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
|
||||||
funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "")
|
funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
|
||||||
default:
|
default:
|
||||||
panic("unimplemented func value variant")
|
panic("unimplemented func value variant")
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ import (
|
||||||
// func ReadRegister(name string) uintptr
|
// func ReadRegister(name string) uintptr
|
||||||
//
|
//
|
||||||
// The register name must be a constant, for example "sp".
|
// The register name must be a constant, for example "sp".
|
||||||
func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
|
func (b *builder) createReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
|
||||||
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false)
|
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{}, false)
|
||||||
regname := constant.StringVal(args[0].(*ssa.Const).Value)
|
regname := constant.StringVal(args[0].(*ssa.Const).Value)
|
||||||
var asm string
|
var asm string
|
||||||
switch name {
|
switch name {
|
||||||
|
@ -31,7 +31,7 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value,
|
||||||
panic("unknown architecture")
|
panic("unknown architecture")
|
||||||
}
|
}
|
||||||
target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0)
|
target := llvm.InlineAsm(fnType, asm, "=r", false, false, 0)
|
||||||
return c.builder.CreateCall(target, nil, ""), nil
|
return b.CreateCall(target, nil, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a compiler builtin, which emits a piece of inline assembly with no
|
// This is a compiler builtin, which emits a piece of inline assembly with no
|
||||||
|
@ -41,12 +41,12 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value,
|
||||||
// func Asm(asm string)
|
// func Asm(asm string)
|
||||||
//
|
//
|
||||||
// The provided assembly must be a constant.
|
// The provided assembly must be a constant.
|
||||||
func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) {
|
func (b *builder) createInlineAsm(args []ssa.Value) (llvm.Value, error) {
|
||||||
// Magic function: insert inline assembly instead of calling it.
|
// Magic function: insert inline assembly instead of calling it.
|
||||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{}, false)
|
||||||
asm := constant.StringVal(args[0].(*ssa.Const).Value)
|
asm := constant.StringVal(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 b.CreateCall(target, nil, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a compiler builtin, which allows assembly to be called in a flexible
|
// This is a compiler builtin, which allows assembly to be called in a flexible
|
||||||
|
@ -63,7 +63,7 @@ func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) {
|
||||||
// "value": 1
|
// "value": 1
|
||||||
// "result": &dest,
|
// "result": &dest,
|
||||||
// })
|
// })
|
||||||
func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createInlineAsmFull(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
asmString := constant.StringVal(instr.Args[0].(*ssa.Const).Value)
|
||||||
registers := map[string]llvm.Value{}
|
registers := map[string]llvm.Value{}
|
||||||
registerMap := instr.Args[1].(*ssa.MakeMap)
|
registerMap := instr.Args[1].(*ssa.MakeMap)
|
||||||
|
@ -73,17 +73,17 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
|
||||||
// ignore
|
// ignore
|
||||||
case *ssa.MapUpdate:
|
case *ssa.MapUpdate:
|
||||||
if r.Block() != registerMap.Block() {
|
if r.Block() != registerMap.Block() {
|
||||||
return llvm.Value{}, c.makeError(instr.Pos(), "register value map must be created in the same basic block")
|
return llvm.Value{}, b.makeError(instr.Pos(), "register value map must be created in the same basic block")
|
||||||
}
|
}
|
||||||
key := constant.StringVal(r.Key.(*ssa.Const).Value)
|
key := constant.StringVal(r.Key.(*ssa.Const).Value)
|
||||||
//println("value:", r.Value.(*ssa.MakeInterface).X.String())
|
//println("value:", r.Value.(*ssa.MakeInterface).X.String())
|
||||||
registers[key] = frame.getValue(r.Value.(*ssa.MakeInterface).X)
|
registers[key] = b.getValue(r.Value.(*ssa.MakeInterface).X)
|
||||||
case *ssa.Call:
|
case *ssa.Call:
|
||||||
if r.Common() == instr {
|
if r.Common() == instr {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
|
return llvm.Value{}, b.makeError(instr.Pos(), "don't know how to handle argument to inline assembly: "+r.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: handle dollar signs in asm string
|
// TODO: handle dollar signs in asm string
|
||||||
|
@ -98,7 +98,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
|
||||||
name := s[1 : len(s)-1]
|
name := s[1 : len(s)-1]
|
||||||
if _, ok := registers[name]; !ok {
|
if _, ok := registers[name]; !ok {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = c.makeError(instr.Pos(), "unknown register name: "+name)
|
err = b.makeError(instr.Pos(), "unknown register name: "+name)
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
constraints = append(constraints, "*m")
|
constraints = append(constraints, "*m")
|
||||||
default:
|
default:
|
||||||
err = c.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
|
err = b.makeError(instr.Pos(), "unknown type in inline assembly for value: "+name)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,9 +121,9 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
fnType := llvm.FunctionType(c.ctx.VoidType(), argTypes, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0)
|
target := llvm.InlineAsm(fnType, asmString, strings.Join(constraints, ","), true, false, 0)
|
||||||
return c.builder.CreateCall(target, args, ""), nil
|
return b.CreateCall(target, args, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
// This is a compiler builtin which emits an inline SVCall instruction. It can
|
||||||
|
@ -137,7 +137,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
|
||||||
//
|
//
|
||||||
// The num parameter must be a constant. All other parameters may be any scalar
|
// The num parameter must be a constant. All other parameters may be any scalar
|
||||||
// value supported by LLVM inline assembly.
|
// value supported by LLVM inline assembly.
|
||||||
func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error) {
|
func (b *builder) emitSVCall(args []ssa.Value) (llvm.Value, error) {
|
||||||
num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value)
|
num, _ := constant.Uint64Val(args[0].(*ssa.Const).Value)
|
||||||
llvmArgs := []llvm.Value{}
|
llvmArgs := []llvm.Value{}
|
||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
|
@ -150,7 +150,7 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
|
||||||
} else {
|
} else {
|
||||||
constraints += ",{r" + strconv.Itoa(i) + "}"
|
constraints += ",{r" + strconv.Itoa(i) + "}"
|
||||||
}
|
}
|
||||||
llvmValue := frame.getValue(arg)
|
llvmValue := b.getValue(arg)
|
||||||
llvmArgs = append(llvmArgs, llvmValue)
|
llvmArgs = append(llvmArgs, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
|
@ -158,9 +158,9 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
|
||||||
// clobbered. r0 is used as an output register so doesn't have to be
|
// clobbered. r0 is used as an output register so doesn't have to be
|
||||||
// marked as clobbered.
|
// marked as clobbered.
|
||||||
constraints += ",~{r1},~{r2},~{r3}"
|
constraints += ",~{r1},~{r2},~{r3}"
|
||||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
|
target := llvm.InlineAsm(fnType, asm, constraints, true, false, 0)
|
||||||
return c.builder.CreateCall(target, llvmArgs, ""), nil
|
return b.CreateCall(target, llvmArgs, ""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a compiler builtin which emits CSR instructions. It can be one of:
|
// This is a compiler builtin which emits CSR instructions. It can be one of:
|
||||||
|
@ -172,38 +172,38 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
|
||||||
//
|
//
|
||||||
// The csr parameter (method receiver) must be a constant. Other parameter can
|
// The csr parameter (method receiver) must be a constant. Other parameter can
|
||||||
// be any value.
|
// be any value.
|
||||||
func (c *Compiler) emitCSROperation(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) emitCSROperation(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
csrConst, ok := call.Args[0].(*ssa.Const)
|
csrConst, ok := call.Args[0].(*ssa.Const)
|
||||||
if !ok {
|
if !ok {
|
||||||
return llvm.Value{}, c.makeError(call.Pos(), "CSR must be constant")
|
return llvm.Value{}, b.makeError(call.Pos(), "CSR must be constant")
|
||||||
}
|
}
|
||||||
csr := csrConst.Uint64()
|
csr := csrConst.Uint64()
|
||||||
switch name := call.StaticCallee().Name(); name {
|
switch name := call.StaticCallee().Name(); name {
|
||||||
case "Get":
|
case "Get":
|
||||||
// Note that this instruction may have side effects, and thus must be
|
// Note that this instruction may have side effects, and thus must be
|
||||||
// marked as such.
|
// marked as such.
|
||||||
fnType := llvm.FunctionType(c.uintptrType, nil, false)
|
fnType := llvm.FunctionType(b.uintptrType, nil, false)
|
||||||
asm := fmt.Sprintf("csrr $0, %d", csr)
|
asm := fmt.Sprintf("csrr $0, %d", csr)
|
||||||
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0)
|
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0)
|
||||||
return c.builder.CreateCall(target, nil, ""), nil
|
return b.CreateCall(target, nil, ""), nil
|
||||||
case "Set":
|
case "Set":
|
||||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.uintptrType}, false)
|
fnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.uintptrType}, false)
|
||||||
asm := fmt.Sprintf("csrw %d, $0", csr)
|
asm := fmt.Sprintf("csrw %d, $0", csr)
|
||||||
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0)
|
target := llvm.InlineAsm(fnType, asm, "r", true, false, 0)
|
||||||
return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil
|
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||||
case "SetBits":
|
case "SetBits":
|
||||||
// Note: it may be possible to optimize this to csrrsi in many cases.
|
// Note: it may be possible to optimize this to csrrsi in many cases.
|
||||||
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false)
|
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
||||||
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
|
asm := fmt.Sprintf("csrrs $0, %d, $1", csr)
|
||||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
|
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
|
||||||
return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil
|
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||||
case "ClearBits":
|
case "ClearBits":
|
||||||
// Note: it may be possible to optimize this to csrrci in many cases.
|
// Note: it may be possible to optimize this to csrrci in many cases.
|
||||||
fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{c.uintptrType}, false)
|
fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{b.uintptrType}, false)
|
||||||
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
|
asm := fmt.Sprintf("csrrc $0, %d, $1", csr)
|
||||||
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
|
target := llvm.InlineAsm(fnType, asm, "=r,r", true, false, 0)
|
||||||
return c.builder.CreateCall(target, []llvm.Value{frame.getValue(call.Args[1])}, ""), nil
|
return b.CreateCall(target, []llvm.Value{b.getValue(call.Args[1])}, ""), nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown CSR operation: "+name)
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,62 +8,62 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// emitInterruptGlobal creates a new runtime/interrupt.Interrupt struct that
|
// createInterruptGlobal creates a new runtime/interrupt.Interrupt struct that
|
||||||
// will be lowered to a real interrupt during interrupt lowering.
|
// will be lowered to a real interrupt during interrupt lowering.
|
||||||
//
|
//
|
||||||
// This two-stage approach allows unused interrupts to be optimized away if
|
// This two-stage approach allows unused interrupts to be optimized away if
|
||||||
// necessary.
|
// necessary.
|
||||||
func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
// Get the interrupt number, which must be a compile-time constant.
|
// Get the interrupt number, which must be a compile-time constant.
|
||||||
id, ok := instr.Args[0].(*ssa.Const)
|
id, ok := instr.Args[0].(*ssa.Const)
|
||||||
if !ok {
|
if !ok {
|
||||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt ID is not a constant")
|
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt ID is not a constant")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the func value, which also must be a compile time constant.
|
// Get the func value, which also must be a compile time constant.
|
||||||
// Note that bound functions are allowed if the function has a pointer
|
// Note that bound functions are allowed if the function has a pointer
|
||||||
// receiver and is a global. This is rather strict but still allows for
|
// receiver and is a global. This is rather strict but still allows for
|
||||||
// idiomatic Go code.
|
// idiomatic Go code.
|
||||||
funcValue := frame.getValue(instr.Args[1])
|
funcValue := b.getValue(instr.Args[1])
|
||||||
if funcValue.IsAConstant().IsNil() {
|
if funcValue.IsAConstant().IsNil() {
|
||||||
// Try to determine the cause of the non-constantness for a nice error
|
// Try to determine the cause of the non-constantness for a nice error
|
||||||
// message.
|
// message.
|
||||||
switch instr.Args[1].(type) {
|
switch instr.Args[1].(type) {
|
||||||
case *ssa.MakeClosure:
|
case *ssa.MakeClosure:
|
||||||
// This may also be a bound method.
|
// This may also be a bound method.
|
||||||
return llvm.Value{}, c.makeError(instr.Pos(), "closures are not supported in interrupt.New")
|
return llvm.Value{}, b.makeError(instr.Pos(), "closures are not supported in interrupt.New")
|
||||||
}
|
}
|
||||||
// Fall back to a generic error.
|
// Fall back to a generic error.
|
||||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt function must be constant")
|
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new global of type runtime/interrupt.handle. Globals of this
|
// Create a new global of type runtime/interrupt.handle. Globals of this
|
||||||
// type are lowered in the interrupt lowering pass.
|
// type are lowered in the interrupt lowering pass.
|
||||||
globalType := c.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
globalType := b.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
||||||
globalLLVMType := c.getLLVMType(globalType)
|
globalLLVMType := b.getLLVMType(globalType)
|
||||||
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
||||||
if global := c.mod.NamedGlobal(globalName); !global.IsNil() {
|
if global := b.mod.NamedGlobal(globalName); !global.IsNil() {
|
||||||
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt redeclared in this program")
|
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
|
||||||
}
|
}
|
||||||
global := llvm.AddGlobal(c.mod, globalLLVMType, globalName)
|
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.PrivateLinkage)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
initializer := llvm.ConstNull(globalLLVMType)
|
initializer := llvm.ConstNull(globalLLVMType)
|
||||||
initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0})
|
initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0})
|
||||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(c.intType, uint64(id.Int64()), true), []uint32{1, 0})
|
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{1, 0})
|
||||||
global.SetInitializer(initializer)
|
global.SetInitializer(initializer)
|
||||||
|
|
||||||
// Add debug info to the interrupt global.
|
// Add debug info to the interrupt global.
|
||||||
if c.Debug() {
|
if b.Debug() {
|
||||||
pos := c.ir.Program.Fset.Position(instr.Pos())
|
pos := b.ir.Program.Fset.Position(instr.Pos())
|
||||||
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
|
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
|
||||||
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
||||||
LinkageName: globalName,
|
LinkageName: globalName,
|
||||||
File: c.getDIFile(pos.Filename),
|
File: b.getDIFile(pos.Filename),
|
||||||
Line: pos.Line,
|
Line: pos.Line,
|
||||||
Type: c.getDIType(globalType),
|
Type: b.getDIType(globalType),
|
||||||
Expr: c.dibuilder.CreateExpression(nil),
|
Expr: b.dibuilder.CreateExpression(nil),
|
||||||
LocalToUnit: false,
|
LocalToUnit: false,
|
||||||
})
|
})
|
||||||
global.AddMetadata(0, diglobal)
|
global.AddMetadata(0, diglobal)
|
||||||
|
@ -71,21 +71,21 @@ func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llv
|
||||||
|
|
||||||
// Create the runtime/interrupt.Interrupt type. It is a struct with a single
|
// Create the runtime/interrupt.Interrupt type. It is a struct with a single
|
||||||
// member of type int.
|
// member of type int.
|
||||||
num := llvm.ConstPtrToInt(global, c.intType)
|
num := llvm.ConstPtrToInt(global, b.intType)
|
||||||
interrupt := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
|
interrupt := llvm.ConstNamedStruct(b.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
|
||||||
|
|
||||||
// Add dummy "use" call for AVR, because interrupts may be used even though
|
// Add dummy "use" call for AVR, because interrupts may be used even though
|
||||||
// they are never referenced again. This is unlike Cortex-M or the RISC-V
|
// they are never referenced again. This is unlike Cortex-M or the RISC-V
|
||||||
// PLIC where each interrupt must be enabled using the interrupt number, and
|
// PLIC where each interrupt must be enabled using the interrupt number, and
|
||||||
// thus keeps the Interrupt object alive.
|
// thus keeps the Interrupt object alive.
|
||||||
// This call is removed during interrupt lowering.
|
// This call is removed during interrupt lowering.
|
||||||
if strings.HasPrefix(c.Triple(), "avr") {
|
if strings.HasPrefix(b.Triple(), "avr") {
|
||||||
useFn := c.mod.NamedFunction("runtime/interrupt.use")
|
useFn := b.mod.NamedFunction("runtime/interrupt.use")
|
||||||
if useFn.IsNil() {
|
if useFn.IsNil() {
|
||||||
useFnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
||||||
useFn = llvm.AddFunction(c.mod, "runtime/interrupt.use", useFnType)
|
useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType)
|
||||||
}
|
}
|
||||||
c.builder.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
b.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
return interrupt, nil
|
return interrupt, nil
|
||||||
|
|
|
@ -10,14 +10,14 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// emitSyscall emits an inline system call instruction, depending on the target
|
// createSyscall emits an inline system call instruction, depending on the
|
||||||
// OS/arch.
|
// target OS/arch.
|
||||||
func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
num := frame.getValue(call.Args[0])
|
num := b.getValue(call.Args[0])
|
||||||
var syscallResult llvm.Value
|
var syscallResult llvm.Value
|
||||||
switch {
|
switch {
|
||||||
case c.GOARCH() == "amd64":
|
case b.GOARCH() == "amd64":
|
||||||
if c.GOOS() == "darwin" {
|
if b.GOOS() == "darwin" {
|
||||||
// Darwin adds this magic number to system call numbers:
|
// Darwin adds this magic number to system call numbers:
|
||||||
//
|
//
|
||||||
// > Syscall classes for 64-bit system call entry.
|
// > Syscall classes for 64-bit system call entry.
|
||||||
|
@ -28,13 +28,13 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
// > All system classes enter the kernel via the syscall instruction.
|
// > All system classes enter the kernel via the syscall instruction.
|
||||||
//
|
//
|
||||||
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
// Source: https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h
|
||||||
num = c.builder.CreateOr(num, llvm.ConstInt(c.uintptrType, 0x2000000, false), "")
|
num = b.CreateOr(num, llvm.ConstInt(b.uintptrType, 0x2000000, false), "")
|
||||||
}
|
}
|
||||||
// Sources:
|
// Sources:
|
||||||
// https://stackoverflow.com/a/2538212
|
// https://stackoverflow.com/a/2538212
|
||||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
|
||||||
args := []llvm.Value{num}
|
args := []llvm.Value{num}
|
||||||
argTypes := []llvm.Type{c.uintptrType}
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
|
||||||
constraints := "={rax},0"
|
constraints := "={rax},0"
|
||||||
|
@ -50,21 +50,21 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
"{r12}",
|
"{r12}",
|
||||||
"{r13}",
|
"{r13}",
|
||||||
}[i]
|
}[i]
|
||||||
llvmValue := frame.getValue(arg)
|
llvmValue := b.getValue(arg)
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
constraints += ",~{rcx},~{r11}"
|
constraints += ",~{rcx},~{r11}"
|
||||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
target := llvm.InlineAsm(fnType, "syscall", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||||
syscallResult = c.builder.CreateCall(target, args, "")
|
syscallResult = b.CreateCall(target, args, "")
|
||||||
case c.GOARCH() == "386" && c.GOOS() == "linux":
|
case b.GOARCH() == "386" && b.GOOS() == "linux":
|
||||||
// Sources:
|
// Sources:
|
||||||
// syscall(2) man page
|
// syscall(2) man page
|
||||||
// https://stackoverflow.com/a/2538212
|
// https://stackoverflow.com/a/2538212
|
||||||
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#int_0x80
|
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#int_0x80
|
||||||
args := []llvm.Value{num}
|
args := []llvm.Value{num}
|
||||||
argTypes := []llvm.Type{c.uintptrType}
|
argTypes := []llvm.Type{b.uintptrType}
|
||||||
// Constraints will look something like:
|
// Constraints will look something like:
|
||||||
// "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}"
|
// "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}"
|
||||||
constraints := "={eax},0"
|
constraints := "={eax},0"
|
||||||
|
@ -77,14 +77,14 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
"{edi}",
|
"{edi}",
|
||||||
"{ebp}",
|
"{ebp}",
|
||||||
}[i]
|
}[i]
|
||||||
llvmValue := frame.getValue(arg)
|
llvmValue := b.getValue(arg)
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
|
target := llvm.InlineAsm(fnType, "int 0x80", constraints, true, false, llvm.InlineAsmDialectIntel)
|
||||||
syscallResult = c.builder.CreateCall(target, args, "")
|
syscallResult = b.CreateCall(target, args, "")
|
||||||
case c.GOARCH() == "arm" && c.GOOS() == "linux":
|
case b.GOARCH() == "arm" && b.GOOS() == "linux":
|
||||||
// Implement the EABI system call convention for Linux.
|
// Implement the EABI system call convention for Linux.
|
||||||
// Source: syscall(2) man page.
|
// Source: syscall(2) man page.
|
||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
|
@ -102,21 +102,21 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
"{r5}",
|
"{r5}",
|
||||||
"{r6}",
|
"{r6}",
|
||||||
}[i]
|
}[i]
|
||||||
llvmValue := frame.getValue(arg)
|
llvmValue := b.getValue(arg)
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
args = append(args, num)
|
args = append(args, num)
|
||||||
argTypes = append(argTypes, c.uintptrType)
|
argTypes = append(argTypes, b.uintptrType)
|
||||||
constraints += ",{r7}" // syscall number
|
constraints += ",{r7}" // syscall number
|
||||||
for i := len(call.Args) - 1; i < 4; i++ {
|
for i := len(call.Args) - 1; i < 4; i++ {
|
||||||
// r0-r3 get clobbered after the syscall returns
|
// r0-r3 get clobbered after the syscall returns
|
||||||
constraints += ",~{r" + strconv.Itoa(i) + "}"
|
constraints += ",~{r" + strconv.Itoa(i) + "}"
|
||||||
}
|
}
|
||||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||||
syscallResult = c.builder.CreateCall(target, args, "")
|
syscallResult = b.CreateCall(target, args, "")
|
||||||
case c.GOARCH() == "arm64" && c.GOOS() == "linux":
|
case b.GOARCH() == "arm64" && b.GOOS() == "linux":
|
||||||
// Source: syscall(2) man page.
|
// Source: syscall(2) man page.
|
||||||
args := []llvm.Value{}
|
args := []llvm.Value{}
|
||||||
argTypes := []llvm.Type{}
|
argTypes := []llvm.Type{}
|
||||||
|
@ -132,12 +132,12 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
"{x4}",
|
"{x4}",
|
||||||
"{x5}",
|
"{x5}",
|
||||||
}[i]
|
}[i]
|
||||||
llvmValue := frame.getValue(arg)
|
llvmValue := b.getValue(arg)
|
||||||
args = append(args, llvmValue)
|
args = append(args, llvmValue)
|
||||||
argTypes = append(argTypes, llvmValue.Type())
|
argTypes = append(argTypes, llvmValue.Type())
|
||||||
}
|
}
|
||||||
args = append(args, num)
|
args = append(args, num)
|
||||||
argTypes = append(argTypes, c.uintptrType)
|
argTypes = append(argTypes, b.uintptrType)
|
||||||
constraints += ",{x8}" // syscall number
|
constraints += ",{x8}" // syscall number
|
||||||
for i := len(call.Args) - 1; i < 8; i++ {
|
for i := len(call.Args) - 1; i < 8; i++ {
|
||||||
// x0-x7 may get clobbered during the syscall following the aarch64
|
// x0-x7 may get clobbered during the syscall following the aarch64
|
||||||
|
@ -145,13 +145,13 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
constraints += ",~{x" + strconv.Itoa(i) + "}"
|
constraints += ",~{x" + strconv.Itoa(i) + "}"
|
||||||
}
|
}
|
||||||
constraints += ",~{x16},~{x17}" // scratch registers
|
constraints += ",~{x16},~{x17}" // scratch registers
|
||||||
fnType := llvm.FunctionType(c.uintptrType, argTypes, false)
|
fnType := llvm.FunctionType(b.uintptrType, argTypes, false)
|
||||||
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
target := llvm.InlineAsm(fnType, "svc #0", constraints, true, false, 0)
|
||||||
syscallResult = c.builder.CreateCall(target, args, "")
|
syscallResult = b.CreateCall(target, args, "")
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH())
|
||||||
}
|
}
|
||||||
switch c.GOOS() {
|
switch b.GOOS() {
|
||||||
case "linux", "freebsd":
|
case "linux", "freebsd":
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
|
@ -160,15 +160,15 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
// err = -syscallResult
|
// err = -syscallResult
|
||||||
// }
|
// }
|
||||||
// return syscallResult, 0, err
|
// return syscallResult, 0, err
|
||||||
zero := llvm.ConstInt(c.uintptrType, 0, false)
|
zero := llvm.ConstInt(b.uintptrType, 0, false)
|
||||||
inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
|
inrange1 := b.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "")
|
||||||
inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096
|
inrange2 := b.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(b.uintptrType, 0xfffffffffffff000, true), "") // -4096
|
||||||
hasError := c.builder.CreateAnd(inrange1, inrange2, "")
|
hasError := b.CreateAnd(inrange1, inrange2, "")
|
||||||
errResult := c.builder.CreateSelect(hasError, c.builder.CreateSub(zero, syscallResult, ""), zero, "syscallError")
|
errResult := b.CreateSelect(hasError, b.CreateSub(zero, syscallResult, ""), zero, "syscallError")
|
||||||
retval := llvm.Undef(c.ctx.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
|
retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
|
||||||
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
|
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
||||||
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
|
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||||
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
case "darwin":
|
case "darwin":
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
|
@ -178,15 +178,15 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
|
||||||
// err = syscallResult
|
// err = syscallResult
|
||||||
// }
|
// }
|
||||||
// return syscallResult, 0, err
|
// return syscallResult, 0, err
|
||||||
zero := llvm.ConstInt(c.uintptrType, 0, false)
|
zero := llvm.ConstInt(b.uintptrType, 0, false)
|
||||||
hasError := c.builder.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
|
hasError := b.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "")
|
||||||
errResult := c.builder.CreateSelect(hasError, syscallResult, zero, "syscallError")
|
errResult := b.CreateSelect(hasError, syscallResult, zero, "syscallError")
|
||||||
retval := llvm.Undef(c.ctx.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
|
retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
|
||||||
retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
|
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
||||||
retval = c.builder.CreateInsertValue(retval, zero, 1, "")
|
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||||
retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+c.GOOS()+"/"+c.GOARCH())
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS()+"/"+b.GOARCH())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,23 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
// createVolatileLoad is the implementation of the intrinsic function
|
||||||
addr := frame.getValue(instr.Args[0])
|
// runtime/volatile.LoadT().
|
||||||
frame.createNilCheck(addr, "deref")
|
func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
val := c.builder.CreateLoad(addr, "")
|
addr := b.getValue(instr.Args[0])
|
||||||
|
b.createNilCheck(addr, "deref")
|
||||||
|
val := b.CreateLoad(addr, "")
|
||||||
val.SetVolatile(true)
|
val.SetVolatile(true)
|
||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
// createVolatileStore is the implementation of the intrinsic function
|
||||||
addr := frame.getValue(instr.Args[0])
|
// runtime/volatile.StoreT().
|
||||||
val := frame.getValue(instr.Args[1])
|
func (b *builder) createVolatileStore(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
frame.createNilCheck(addr, "deref")
|
addr := b.getValue(instr.Args[0])
|
||||||
store := c.builder.CreateStore(val, addr)
|
val := b.getValue(instr.Args[1])
|
||||||
|
b.createNilCheck(addr, "deref")
|
||||||
|
store := b.CreateStore(val, addr)
|
||||||
store.SetVolatile(true)
|
store.SetVolatile(true)
|
||||||
return llvm.Value{}, nil
|
return llvm.Value{}, nil
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче