Skip to content

Commit c7b91da

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent c7fdb67 commit c7b91da

File tree

4 files changed

+29
-23
lines changed

4 files changed

+29
-23
lines changed

compiler/compiler.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type Compiler struct {
5757
targetData llvm.TargetData
5858
intType llvm.Type
5959
i8ptrType llvm.Type // for convenience
60+
funcPtrAddrSpace int
6061
uintptrType llvm.Type
6162
initFuncs []llvm.Value
6263
interfaceInvokeWrappers []interfaceInvokeWrapper
@@ -125,6 +126,11 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
125126
}
126127
c.i8ptrType = llvm.PointerType(c.ctx.Int8Type(), 0)
127128

129+
dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
130+
dummyFunc := llvm.AddFunction(c.mod, "tinygo.dummy", dummyFuncType)
131+
c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
132+
dummyFunc.EraseFromParentAsFunction()
133+
128134
return c, nil
129135
}
130136

@@ -462,7 +468,7 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
462468
// {context, funcptr}
463469
paramTypes = append(paramTypes, c.i8ptrType) // context
464470
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
465-
ptr := llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0)
471+
ptr := llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), c.funcPtrAddrSpace)
466472
ptr = c.ctx.StructType([]llvm.Type{c.i8ptrType, ptr}, false)
467473
return ptr, nil
468474
case *types.Slice:

compiler/interface-lowering.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ func (p *lowerInterfacesPass) run() {
304304
// interface value should already have returned false.
305305
// Replace the function pointer with undef (which will then be
306306
// called), indicating to the optimizer this code is unreachable.
307-
use.ReplaceAllUsesWith(llvm.Undef(p.i8ptrType))
307+
use.ReplaceAllUsesWith(llvm.Undef(p.uintptrType))
308308
use.EraseFromParentAsInstruction()
309309
} else if len(itf.types) == 1 {
310310
// There is only one implementation of the given type.
@@ -314,12 +314,12 @@ func (p *lowerInterfacesPass) run() {
314314
// There are multiple types implementing this interface, thus there
315315
// are multiple possible functions to call. Delegate calling the
316316
// right function to a special wrapper function.
317-
bitcasts := getUses(use)
318-
if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() {
319-
panic("expected exactly one bitcast use of runtime.interfaceMethod")
317+
inttoptrs := getUses(use)
318+
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
319+
panic("expected exactly one inttoptr use of runtime.interfaceMethod")
320320
}
321-
bitcast := bitcasts[0]
322-
calls := getUses(bitcast)
321+
inttoptr := inttoptrs[0]
322+
calls := getUses(inttoptr)
323323
if len(calls) != 1 || calls[0].IsACallInst().IsNil() {
324324
panic("expected exactly one call use of runtime.interfaceMethod")
325325
}
@@ -340,14 +340,14 @@ func (p *lowerInterfacesPass) run() {
340340
// call, after selecting the right concrete type.
341341
redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
342342

343-
// Replace the old lookup/bitcast/call with the new call.
343+
// Replace the old lookup/inttoptr/call with the new call.
344344
p.builder.SetInsertPointBefore(call)
345345
retval := p.builder.CreateCall(redirector, params, "")
346346
if retval.Type().TypeKind() != llvm.VoidTypeKind {
347347
call.ReplaceAllUsesWith(retval)
348348
}
349349
call.EraseFromParentAsInstruction()
350-
bitcast.EraseFromParentAsInstruction()
350+
inttoptr.EraseFromParentAsInstruction()
351351
use.EraseFromParentAsInstruction()
352352
}
353353
}
@@ -542,22 +542,22 @@ func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo {
542542
return p.signatures[name]
543543
}
544544

545-
// replaceInvokeWithCall replaces a runtime.interfaceMethod + bitcast with a
545+
// replaceInvokeWithCall replaces a runtime.interfaceMethod + inttoptr with a
546546
// concrete method. This can be done when only one type implements the
547547
// interface.
548548
func (p *lowerInterfacesPass) replaceInvokeWithCall(use llvm.Value, typ *typeInfo, signature *signatureInfo) {
549-
bitcasts := getUses(use)
550-
if len(bitcasts) != 1 || bitcasts[0].IsABitCastInst().IsNil() {
551-
panic("expected exactly one bitcast use of runtime.interfaceMethod")
549+
inttoptrs := getUses(use)
550+
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
551+
panic("expected exactly one inttoptr use of runtime.interfaceMethod")
552552
}
553-
bitcast := bitcasts[0]
553+
inttoptr := inttoptrs[0]
554554
function := typ.getMethod(signature).function
555-
if bitcast.Type() != function.Type() {
555+
if inttoptr.Type() != function.Type() {
556556
p.builder.SetInsertPointBefore(use)
557-
function = p.builder.CreateBitCast(function, bitcast.Type(), "")
557+
function = p.builder.CreateBitCast(function, inttoptr.Type(), "")
558558
}
559-
bitcast.ReplaceAllUsesWith(function)
560-
bitcast.EraseFromParentAsInstruction()
559+
inttoptr.ReplaceAllUsesWith(function)
560+
inttoptr.EraseFromParentAsInstruction()
561561
use.EraseFromParentAsInstruction()
562562
}
563563

compiler/interface.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
203203
}
204204
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
205205
signatureGlobal,
206-
llvm.ConstBitCast(fn, c.i8ptrType),
206+
llvm.ConstPtrToInt(fn, c.uintptrType),
207207
})
208208
methods[i] = methodInfo
209209
}
@@ -400,7 +400,7 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
400400
c.getMethodSignature(instr.Method),
401401
}
402402
fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func")
403-
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
403+
fnCast := c.builder.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast")
404404
receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver")
405405

406406
args := []llvm.Value{receiverValue}

src/runtime/interface.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func interfaceTypeAssert(ok bool) {
3939
// See compiler/interface-lowering.go for details.
4040

4141
type interfaceMethodInfo struct {
42-
signature *uint8 // external *i8 with a name identifying the Go function signature
43-
funcptr *uint8 // bitcast from the actual function pointer
42+
signature *uint8 // external *i8 with a name identifying the Go function signature
43+
funcptr uintptr // bitcast from the actual function pointer
4444
}
4545

4646
// Pseudo function call used while putting a concrete value in an interface,
@@ -59,4 +59,4 @@ func interfaceImplements(typecode uintptr, interfaceMethodSet **uint8) bool
5959

6060
// Pseudo function that returns a function pointer to the method to call.
6161
// See the interface lowering pass for how this is lowered to a real call.
62-
func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) *uint8
62+
func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) uintptr

0 commit comments

Comments
 (0)