aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-11-23 21:36:26 +0100
committerRon Evans <[email protected]>2020-03-25 20:17:46 +0100
commitd752e66be552f074c3e23ca2c22507f95db6eef0 (patch)
tree066fec54d0ccca6ee0e8bc6ae1e08a0d7363ce0f
parentd46934d1f1a0dc4f534a1f68012659acf50a5774 (diff)
downloadtinygo-d752e66be552f074c3e23ca2c22507f95db6eef0.tar.gz
tinygo-d752e66be552f074c3e23ca2c22507f95db6eef0.zip
compiler: refactor function calling
-rw-r--r--compiler/compiler.go108
-rw-r--r--compiler/func.go20
-rw-r--r--compiler/inlineasm.go58
-rw-r--r--compiler/interrupt.go50
-rw-r--r--compiler/syscall.go90
-rw-r--r--compiler/volatile.go22
6 files changed, 177 insertions, 171 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go
index 7222041e7..1807b8af5 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -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
// example: immediately applied functions.
funcValue := frame.getValue(value)
- context = c.extractFuncContext(funcValue)
+ context = frame.extractFuncContext(funcValue)
default:
panic("StaticCallee returned an unexpected value")
}
@@ -1078,7 +1078,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
// goroutine:
// * The function context, for closures.
// * 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
switch c.Scheduler() {
case "none", "coroutines":
@@ -1315,86 +1315,90 @@ 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 {
- var params []llvm.Value
- for _, param := range args {
- params = append(params, frame.getValue(param))
- }
-
- if !exported {
- // This function takes a context parameter.
- // Add it to the end of the parameter list.
- params = append(params, context)
-
- // Parent coroutine handle.
- params = append(params, llvm.Undef(c.i8ptrType))
- }
-
- return c.createCall(llvmFn, params, "")
-}
-
-func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
+// 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 := frame.getInvokeCall(instr)
- return c.createCall(fnCast, args, ""), nil
+ 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 c.emitReadRegister(name, instr.Args)
+ return b.createReadRegister(name, instr.Args)
case name == "device/arm.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
- return c.emitAsm(instr.Args)
+ return b.createInlineAsm(instr.Args)
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
- return c.emitAsmFull(frame, instr)
+ return b.createInlineAsmFull(instr)
case strings.HasPrefix(name, "device/arm.SVCall"):
- return c.emitSVCall(frame, instr.Args)
+ return b.emitSVCall(instr.Args)
case strings.HasPrefix(name, "(device/riscv.CSR)."):
- return c.emitCSROperation(frame, instr)
+ return b.emitCSROperation(instr)
case strings.HasPrefix(name, "syscall.Syscall"):
- return c.emitSyscall(frame, instr)
+ return b.createSyscall(instr)
case strings.HasPrefix(name, "runtime/volatile.Load"):
- return c.emitVolatileLoad(frame, instr)
+ return b.createVolatileLoad(instr)
case strings.HasPrefix(name, "runtime/volatile.Store"):
- return c.emitVolatileStore(frame, instr)
+ return b.createVolatileStore(instr)
case name == "runtime/interrupt.New":
- return c.emitInterruptGlobal(frame, instr)
+ return b.createInterruptGlobal(instr)
}
- targetFunc := c.ir.GetFunction(fn)
+ targetFunc := b.ir.GetFunction(fn)
if targetFunc.LLVMFn.IsNil() {
- return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName())
+ return llvm.Value{}, b.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)
+ 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 := frame.getValue(value)
- context = c.extractFuncContext(funcValue)
+ funcValue := b.getValue(value)
+ context = b.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)
+ 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.
- 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
+ callee, context = b.decodeFuncValue(value, instr.Value.Type().Underlying().(*types.Signature))
+ b.createNilCheck(callee, "fpcall")
+ }
+
+ var params []llvm.Value
+ for _, param := range instr.Args {
+ params = append(params, b.getValue(param))
+ }
+
+ if !exported {
+ // This function takes a context parameter.
+ // Add it to the end of the parameter list.
+ params = append(params, context)
+
+ // Parent coroutine handle.
+ params = append(params, llvm.Undef(b.i8ptrType))
}
+
+ return b.createCall(callee, params, ""), nil
}
// 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)
return frame.createBinOp(expr.Op, expr.X.Type(), x, y, expr.Pos())
case *ssa.Call:
- // Passing the current task here to the subroutine. It is only used when
- // the subroutine is blocking.
- return c.parseCall(frame, expr.Common())
+ return frame.createFunctionCall(expr.Common())
case *ssa.ChangeInterface:
// Do not change between interface types: always use the underlying
// (concrete) type in the type number of the interface. Every method
diff --git a/compiler/func.go b/compiler/func.go
index 69fb78bce..9632e7125 100644
--- a/compiler/func.go
+++ b/compiler/func.go
@@ -71,22 +71,22 @@ func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value {
// extractFuncContext extracts the context pointer from this function value. It
// is a cheap operation.
-func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value {
- return c.builder.CreateExtractValue(funcValue, 0, "")
+func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
+ return b.CreateExtractValue(funcValue, 0, "")
}
// decodeFuncValue extracts the context and the function pointer from this func
// value. This may be an expensive operation.
-func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
- context = c.builder.CreateExtractValue(funcValue, 0, "")
- switch c.FuncImplementation() {
+func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
+ context = b.CreateExtractValue(funcValue, 0, "")
+ switch b.FuncImplementation() {
case compileopts.FuncValueDoubleword:
- funcPtr = c.builder.CreateExtractValue(funcValue, 1, "")
+ funcPtr = b.CreateExtractValue(funcValue, 1, "")
case compileopts.FuncValueSwitch:
- llvmSig := c.getRawFuncType(sig)
- sigGlobal := c.getTypeCode(sig)
- funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
- funcPtr = c.builder.CreateIntToPtr(funcPtr, llvmSig, "")
+ llvmSig := b.getRawFuncType(sig)
+ sigGlobal := b.getTypeCode(sig)
+ funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
+ funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
default:
panic("unimplemented func value variant")
}
diff --git a/compiler/inlineasm.go b/compiler/inlineasm.go
index 77e1af96a..2727228a1 100644
--- a/compiler/inlineasm.go
+++ b/compiler/inlineasm.go
@@ -18,8 +18,8 @@ import (
// func ReadRegister(name string) uintptr
//
// The register name must be a constant, for example "sp".
-func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
- fnType := llvm.FunctionType(c.uintptrType, []llvm.Type{}, false)
+func (b *builder) createReadRegister(name string, args []ssa.Value) (llvm.Value, error) {
+ fnType := llvm.FunctionType(b.uintptrType, []llvm.Type{}, false)
regname := constant.StringVal(args[0].(*ssa.Const).Value)
var asm string
switch name {
@@ -31,7 +31,7 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value,
panic("unknown architecture")
}
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
@@ -41,12 +41,12 @@ func (c *Compiler) emitReadRegister(name string, args []ssa.Value) (llvm.Value,
// func Asm(asm string)
//
// 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.
- 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)
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
@@ -63,7 +63,7 @@ func (c *Compiler) emitAsm(args []ssa.Value) (llvm.Value, error) {
// "value": 1
// "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)
registers := map[string]llvm.Value{}
registerMap := instr.Args[1].(*ssa.MakeMap)
@@ -73,17 +73,17 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
// 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")
+ 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)
//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:
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())
+ 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
@@ -98,7 +98,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
name := s[1 : len(s)-1]
if _, ok := registers[name]; !ok {
if err == nil {
- err = c.makeError(instr.Pos(), "unknown register name: "+name)
+ err = b.makeError(instr.Pos(), "unknown register name: "+name)
}
return s
}
@@ -112,7 +112,7 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
case llvm.PointerTypeKind:
constraints = append(constraints, "*m")
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
}
}
@@ -121,9 +121,9 @@ func (c *Compiler) emitAsmFull(frame *Frame, instr *ssa.CallCommon) (llvm.Value,
if err != nil {
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)
- 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
@@ -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
// 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)
llvmArgs := []llvm.Value{}
argTypes := []llvm.Type{}
@@ -150,7 +150,7 @@ func (c *Compiler) emitSVCall(frame *Frame, args []ssa.Value) (llvm.Value, error
} else {
constraints += ",{r" + strconv.Itoa(i) + "}"
}
- llvmValue := frame.getValue(arg)
+ llvmValue := b.getValue(arg)
llvmArgs = append(llvmArgs, llvmValue)
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
// marked as clobbered.
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)
- 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:
@@ -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
// 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)
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()
switch name := call.StaticCallee().Name(); name {
case "Get":
// Note that this instruction may have side effects, and thus must be
// marked as such.
- fnType := llvm.FunctionType(c.uintptrType, nil, false)
+ fnType := llvm.FunctionType(b.uintptrType, nil, false)
asm := fmt.Sprintf("csrr $0, %d", csr)
target := llvm.InlineAsm(fnType, asm, "=r", true, false, 0)
- return c.builder.CreateCall(target, nil, ""), nil
+ return b.CreateCall(target, nil, ""), nil
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)
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":
// 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)
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":
// 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)
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:
- return llvm.Value{}, c.makeError(call.Pos(), "unknown CSR operation: "+name)
+ return llvm.Value{}, b.makeError(call.Pos(), "unknown CSR operation: "+name)
}
}
diff --git a/compiler/interrupt.go b/compiler/interrupt.go
index 7f4fd77ac..e4e31c080 100644
--- a/compiler/interrupt.go
+++ b/compiler/interrupt.go
@@ -8,62 +8,62 @@ import (
"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.
//
// This two-stage approach allows unused interrupts to be optimized away if
// 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.
id, ok := instr.Args[0].(*ssa.Const)
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.
// 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
// idiomatic Go code.
- funcValue := frame.getValue(instr.Args[1])
+ funcValue := b.getValue(instr.Args[1])
if funcValue.IsAConstant().IsNil() {
// Try to determine the cause of the non-constantness for a nice error
// message.
switch instr.Args[1].(type) {
case *ssa.MakeClosure:
// 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.
- 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
// type are lowered in the interrupt lowering pass.
- globalType := c.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
- globalLLVMType := c.getLLVMType(globalType)
+ globalType := b.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
+ globalLLVMType := b.getLLVMType(globalType)
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
- if global := c.mod.NamedGlobal(globalName); !global.IsNil() {
- return llvm.Value{}, c.makeError(instr.Pos(), "interrupt redeclared in this program")
+ if global := b.mod.NamedGlobal(globalName); !global.IsNil() {
+ 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.SetGlobalConstant(true)
global.SetUnnamedAddr(true)
initializer := llvm.ConstNull(globalLLVMType)
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)
// Add debug info to the interrupt global.
- if c.Debug() {
- pos := c.ir.Program.Fset.Position(instr.Pos())
- diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
+ if b.Debug() {
+ pos := b.ir.Program.Fset.Position(instr.Pos())
+ diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
LinkageName: globalName,
- File: c.getDIFile(pos.Filename),
+ File: b.getDIFile(pos.Filename),
Line: pos.Line,
- Type: c.getDIType(globalType),
- Expr: c.dibuilder.CreateExpression(nil),
+ Type: b.getDIType(globalType),
+ Expr: b.dibuilder.CreateExpression(nil),
LocalToUnit: false,
})
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
// member of type int.
- num := llvm.ConstPtrToInt(global, c.intType)
- interrupt := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
+ num := llvm.ConstPtrToInt(global, b.intType)
+ 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
// 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
// thus keeps the Interrupt object alive.
// This call is removed during interrupt lowering.
- if strings.HasPrefix(c.Triple(), "avr") {
- useFn := c.mod.NamedFunction("runtime/interrupt.use")
+ if strings.HasPrefix(b.Triple(), "avr") {
+ useFn := b.mod.NamedFunction("runtime/interrupt.use")
if useFn.IsNil() {
- useFnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
- useFn = llvm.AddFunction(c.mod, "runtime/interrupt.use", useFnType)
+ useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
+ 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
diff --git a/compiler/syscall.go b/compiler/syscall.go
index 074a88caf..8bb873fac 100644
--- a/compiler/syscall.go
+++ b/compiler/syscall.go
@@ -10,14 +10,14 @@ import (
"tinygo.org/x/go-llvm"
)
-// emitSyscall emits an inline system call instruction, depending on the target
-// OS/arch.
-func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value, error) {
- num := frame.getValue(call.Args[0])
+// createSyscall emits an inline system call instruction, depending on the
+// target OS/arch.
+func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
+ num := b.getValue(call.Args[0])
var syscallResult llvm.Value
switch {
- case c.GOARCH() == "amd64":
- if c.GOOS() == "darwin" {
+ case b.GOARCH() == "amd64":
+ if b.GOOS() == "darwin" {
// Darwin adds this magic number to system call numbers:
//
// > 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.
//
// 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:
// https://stackoverflow.com/a/2538212
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#syscall
args := []llvm.Value{num}
- argTypes := []llvm.Type{c.uintptrType}
+ argTypes := []llvm.Type{b.uintptrType}
// Constraints will look something like:
// "={rax},0,{rdi},{rsi},{rdx},{r10},{r8},{r9},~{rcx},~{r11}"
constraints := "={rax},0"
@@ -50,21 +50,21 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
"{r12}",
"{r13}",
}[i]
- llvmValue := frame.getValue(arg)
+ llvmValue := b.getValue(arg)
args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
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)
- syscallResult = c.builder.CreateCall(target, args, "")
- case c.GOARCH() == "386" && c.GOOS() == "linux":
+ syscallResult = b.CreateCall(target, args, "")
+ case b.GOARCH() == "386" && b.GOOS() == "linux":
// Sources:
// syscall(2) man page
// https://stackoverflow.com/a/2538212
// https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux#int_0x80
args := []llvm.Value{num}
- argTypes := []llvm.Type{c.uintptrType}
+ argTypes := []llvm.Type{b.uintptrType}
// Constraints will look something like:
// "={eax},0,{ebx},{ecx},{edx},{esi},{edi},{ebp}"
constraints := "={eax},0"
@@ -77,14 +77,14 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
"{edi}",
"{ebp}",
}[i]
- llvmValue := frame.getValue(arg)
+ llvmValue := b.getValue(arg)
args = append(args, llvmValue)
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)
- syscallResult = c.builder.CreateCall(target, args, "")
- case c.GOARCH() == "arm" && c.GOOS() == "linux":
+ syscallResult = b.CreateCall(target, args, "")
+ case b.GOARCH() == "arm" && b.GOOS() == "linux":
// Implement the EABI system call convention for Linux.
// Source: syscall(2) man page.
args := []llvm.Value{}
@@ -102,21 +102,21 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
"{r5}",
"{r6}",
}[i]
- llvmValue := frame.getValue(arg)
+ llvmValue := b.getValue(arg)
args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
args = append(args, num)
- argTypes = append(argTypes, c.uintptrType)
+ argTypes = append(argTypes, b.uintptrType)
constraints += ",{r7}" // syscall number
for i := len(call.Args) - 1; i < 4; i++ {
// r0-r3 get clobbered after the syscall returns
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)
- syscallResult = c.builder.CreateCall(target, args, "")
- case c.GOARCH() == "arm64" && c.GOOS() == "linux":
+ syscallResult = b.CreateCall(target, args, "")
+ case b.GOARCH() == "arm64" && b.GOOS() == "linux":
// Source: syscall(2) man page.
args := []llvm.Value{}
argTypes := []llvm.Type{}
@@ -132,12 +132,12 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
"{x4}",
"{x5}",
}[i]
- llvmValue := frame.getValue(arg)
+ llvmValue := b.getValue(arg)
args = append(args, llvmValue)
argTypes = append(argTypes, llvmValue.Type())
}
args = append(args, num)
- argTypes = append(argTypes, c.uintptrType)
+ argTypes = append(argTypes, b.uintptrType)
constraints += ",{x8}" // syscall number
for i := len(call.Args) - 1; i < 8; i++ {
// 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 += ",~{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)
- syscallResult = c.builder.CreateCall(target, args, "")
+ syscallResult = b.CreateCall(target, args, "")
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":
// Return values: r0, r1 uintptr, err Errno
// Pseudocode:
@@ -160,15 +160,15 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
// err = -syscallResult
// }
// return syscallResult, 0, err
- zero := llvm.ConstInt(c.uintptrType, 0, false)
- inrange1 := c.builder.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
- inrange2 := c.builder.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(c.uintptrType, 0xfffffffffffff000, true), "") // -4096
- hasError := c.builder.CreateAnd(inrange1, inrange2, "")
- errResult := c.builder.CreateSelect(hasError, c.builder.CreateSub(zero, syscallResult, ""), zero, "syscallError")
- retval := llvm.Undef(c.ctx.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
- retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
- retval = c.builder.CreateInsertValue(retval, zero, 1, "")
- retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
+ zero := llvm.ConstInt(b.uintptrType, 0, false)
+ inrange1 := b.CreateICmp(llvm.IntSLT, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "")
+ inrange2 := b.CreateICmp(llvm.IntSGT, syscallResult, llvm.ConstInt(b.uintptrType, 0xfffffffffffff000, true), "") // -4096
+ hasError := b.CreateAnd(inrange1, inrange2, "")
+ errResult := b.CreateSelect(hasError, b.CreateSub(zero, syscallResult, ""), zero, "syscallError")
+ retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
+ retval = b.CreateInsertValue(retval, syscallResult, 0, "")
+ retval = b.CreateInsertValue(retval, zero, 1, "")
+ retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
case "darwin":
// Return values: r0, r1 uintptr, err Errno
@@ -178,15 +178,15 @@ func (c *Compiler) emitSyscall(frame *Frame, call *ssa.CallCommon) (llvm.Value,
// err = syscallResult
// }
// return syscallResult, 0, err
- zero := llvm.ConstInt(c.uintptrType, 0, false)
- hasError := c.builder.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(c.uintptrType, 0, false), "")
- errResult := c.builder.CreateSelect(hasError, syscallResult, zero, "syscallError")
- retval := llvm.Undef(c.ctx.StructType([]llvm.Type{c.uintptrType, c.uintptrType, c.uintptrType}, false))
- retval = c.builder.CreateInsertValue(retval, syscallResult, 0, "")
- retval = c.builder.CreateInsertValue(retval, zero, 1, "")
- retval = c.builder.CreateInsertValue(retval, errResult, 2, "")
+ zero := llvm.ConstInt(b.uintptrType, 0, false)
+ hasError := b.CreateICmp(llvm.IntNE, syscallResult, llvm.ConstInt(b.uintptrType, 0, false), "")
+ errResult := b.CreateSelect(hasError, syscallResult, zero, "syscallError")
+ retval := llvm.Undef(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
+ retval = b.CreateInsertValue(retval, syscallResult, 0, "")
+ retval = b.CreateInsertValue(retval, zero, 1, "")
+ retval = b.CreateInsertValue(retval, errResult, 2, "")
return retval, nil
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())
}
}
diff --git a/compiler/volatile.go b/compiler/volatile.go
index 237d55461..d2e51c84d 100644
--- a/compiler/volatile.go
+++ b/compiler/volatile.go
@@ -8,19 +8,23 @@ import (
"tinygo.org/x/go-llvm"
)
-func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
- addr := frame.getValue(instr.Args[0])
- frame.createNilCheck(addr, "deref")
- val := c.builder.CreateLoad(addr, "")
+// createVolatileLoad is the implementation of the intrinsic function
+// runtime/volatile.LoadT().
+func (b *builder) createVolatileLoad(instr *ssa.CallCommon) (llvm.Value, error) {
+ addr := b.getValue(instr.Args[0])
+ b.createNilCheck(addr, "deref")
+ val := b.CreateLoad(addr, "")
val.SetVolatile(true)
return val, nil
}
-func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
- addr := frame.getValue(instr.Args[0])
- val := frame.getValue(instr.Args[1])
- frame.createNilCheck(addr, "deref")
- store := c.builder.CreateStore(val, addr)
+// createVolatileStore is the implementation of the intrinsic function
+// runtime/volatile.StoreT().
+func (b *builder) createVolatileStore(instr *ssa.CallCommon) (llvm.Value, error) {
+ addr := b.getValue(instr.Args[0])
+ val := b.getValue(instr.Args[1])
+ b.createNilCheck(addr, "deref")
+ store := b.CreateStore(val, addr)
store.SetVolatile(true)
return llvm.Value{}, nil
}