aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-01-01 20:22:39 +0100
committerRon Evans <[email protected]>2019-03-05 19:54:55 +0100
commitc7b91da8c41f5b6c268a3c434c414925ee7d02ff (patch)
treedcb288eb68cf7b59b5c91ab1e251c99dce9fa29e
parentc7fdb6741fba93403ea7dedc3460172c190af121 (diff)
downloadtinygo-c7b91da8c41f5b6c268a3c434c414925ee7d02ff.tar.gz
tinygo-c7b91da8c41f5b6c268a3c434c414925ee7d02ff.zip
compiler: support function pointers outside of addrspace 0
In LLVM 8, the AVR backend has moved all function pointers to address space 1 by default. Much of the code still assumes function pointers live in address space 0, leading to assertion failures. This commit fixes this problem by autodetecting function pointers and avoiding them in interface pseudo-calls.
-rw-r--r--compiler/compiler.go8
-rw-r--r--compiler/interface-lowering.go34
-rw-r--r--compiler/interface.go4
-rw-r--r--src/runtime/interface.go6
4 files changed, 29 insertions, 23 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go
index a60341af1..26f145b5d 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -57,6 +57,7 @@ type Compiler struct {
targetData llvm.TargetData
intType llvm.Type
i8ptrType llvm.Type // for convenience
+ funcPtrAddrSpace int
uintptrType llvm.Type
initFuncs []llvm.Value
interfaceInvokeWrappers []interfaceInvokeWrapper
@@ -125,6 +126,11 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
}
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
+ dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
+ dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
+ c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
+ dummyFunc.EraseFromParentAsFunction()
+
return c, nil
}
@@ -462,7 +468,7 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
// {context, funcptr}
paramTypes = append(paramTypes, c.i8ptrType) // context
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
- ptr := llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0)
+ ptr := llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
ptr = c.ctx.StructType([]llvm.Type{c.i8ptrType, ptr}, false)
return ptr, nil
case *types.Slice:
diff --git a/compiler/interface-lowering.go b/compiler/interface-lowering.go
index 520932334..736237093 100644
--- a/compiler/interface-lowering.go
+++ b/compiler/interface-lowering.go
@@ -304,7 +304,7 @@ func (p *lowerInterfacesPass) run() {
// interface value should already have returned false.
// Replace the function pointer with undef (which will then be
// called), indicating to the optimizer this code is unreachable.
- use.ReplaceAllUsesWith(llvm.Undef(p.i8ptrType))
+ use.ReplaceAllUsesWith(llvm.Undef(p.uintptrType))
use.EraseFromParentAsInstruction()
} else if len(itf.types) == 1 {
// There is only one implementation of the given type.
@@ -314,12 +314,12 @@ func (p *lowerInterfacesPass) run() {
// There are multiple types implementing this interface, thus there
// are multiple possible functions to call. Delegate calling the
// right function to a special wrapper function.
- bitcasts := getUses(use)
- if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() {
- panic("expected exactly one bitcast use of runtime.interfaceMethod")
+ inttoptrs := getUses(use)
+ if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
+ panic("expected exactly one inttoptr use of runtime.interfaceMethod")
}
- bitcast := bitcasts[0]
- calls := getUses(bitcast)
+ inttoptr := inttoptrs[0]
+ calls := getUses(inttoptr)
if len(calls) != 1 || calls[0].IsACallInst().IsNil() {
panic("expected exactly one call use of runtime.interfaceMethod")
}
@@ -340,14 +340,14 @@ func (p *lowerInterfacesPass) run() {
// call, after selecting the right concrete type.
redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
- // Replace the old lookup/bitcast/call with the new call.
+ // Replace the old lookup/inttoptr/call with the new call.
p.builder.SetInsertPointBefore(call)
retval := p.builder.CreateCall(redirector, params, "")
if retval.Type().TypeKind() != llvm.VoidTypeKind {
call.ReplaceAllUsesWith(retval)
}
call.EraseFromParentAsInstruction()
- bitcast.EraseFromParentAsInstruction()
+ inttoptr.EraseFromParentAsInstruction()
use.EraseFromParentAsInstruction()
}
}
@@ -542,22 +542,22 @@ func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo {
return p.signatures[name]
}
-// replaceInvokeWithCall replaces a runtime.interfaceMethod + bitcast with a
+// replaceInvokeWithCall replaces a runtime.interfaceMethod + inttoptr with a
// concrete method. This can be done when only one type implements the
// interface.
func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) {
- bitcasts := getUses(use)
- if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() {
- panic("expected exactly one bitcast use of runtime.interfaceMethod")
+ inttoptrs := getUses(use)
+ if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
+ panic("expected exactly one inttoptr use of runtime.interfaceMethod")
}
- bitcast := bitcasts[0]
+ inttoptr := inttoptrs[0]
function := typ.getMethod(signature).function
- if bitcast.Type() != function.Type() {
+ if inttoptr.Type() != function.Type() {
p.builder.SetInsertPointBefore(use)
- function = p.builder.CreateBitCast(function, bitcast.Type(), "")
+ function = p.builder.CreateBitCast(function, inttoptr.Type(), "")
}
- bitcast.ReplaceAllUsesWith(function)
- bitcast.EraseFromParentAsInstruction()
+ inttoptr.ReplaceAllUsesWith(function)
+ inttoptr.EraseFromParentAsInstruction()
use.EraseFromParentAsInstruction()
}
diff --git a/compiler/interface.go b/compiler/interface.go
index 3a2f64bfe..f39bc1059 100644
--- a/compiler/interface.go
+++ b/compiler/interface.go
@@ -203,7 +203,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
}
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
signatureGlobal,
- llvm.ConstBitCast(fn, c.i8ptrType),
+ llvm.ConstPtrToInt(fn, c.uintptrType),
})
methods[i] = methodInfo
}
@@ -400,7 +400,7 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
c.getMethodSignature(instr.Method),
}
fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func")
- fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
+ fnCast := c.builder.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast")
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
args := []llvm.Value{receiverValue}
diff --git a/src/runtime/interface.go b/src/runtime/interface.go
index 3daf56d23..61d3114da 100644
--- a/src/runtime/interface.go
+++ b/src/runtime/interface.go
@@ -39,8 +39,8 @@ func interfaceTypeAssert(ok bool) {
// See compiler/interface-lowering.go for details.
type interfaceMethodInfo struct {
- signature *uint8 // external *i8 with a name identifying the Go function signature
- funcptr *uint8 // bitcast from the actual function pointer
+ signature *uint8 // external *i8 with a name identifying the Go function signature
+ funcptr uintptr // bitcast from the actual function pointer
}
// Pseudo function call used while putting a concrete value in an interface,
@@ -59,4 +59,4 @@ func interfaceImplements(typecode uintptr, interfaceMethodSet **uint8) bool
// Pseudo function that returns a function pointer to the method to call.
// See the interface lowering pass for how this is lowered to a real call.
-func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) *uint8
+func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) uintptr