From 2ddb6f788a35b38645a9dd14d4f0f38aeb6e27e0 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 6 Nov 2018 12:19:04 +0100 Subject: [PATCH] compiler: move interface-related stuff to a separate file This makes it easier to get an overview of everything interface related, because interfaces are quite complicated and were scattered through the (huge!) compiler.go file. --- compiler/compiler.go | 419 +-------------------------------------- compiler/interface.go | 445 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 451 insertions(+), 413 deletions(-) create mode 100644 compiler/interface.go diff --git a/compiler/compiler.go b/compiler/compiler.go index b97f92ce..656d29e2 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -522,124 +522,12 @@ func (c *Compiler) Compile(mainPath string) error { } c.builder.CreateRetVoid() - // Initialize runtime type information, for interfaces. - // See src/runtime/interface.go for more details. - dynamicTypes := c.ir.AllDynamicTypes() - numDynamicTypes := 0 - for _, meta := range dynamicTypes { - numDynamicTypes += len(meta.Methods) + // Add runtime type information for interfaces: interface calls and type + // asserts. + err = c.createInterfaceRTTI() + if err != nil { + return err } - ranges := make([]llvm.Value, 0, len(dynamicTypes)) - funcPointers := make([]llvm.Value, 0, numDynamicTypes) - signatures := make([]llvm.Value, 0, numDynamicTypes) - startIndex := 0 - rangeType := c.mod.GetTypeByName("runtime.methodSetRange") - for _, meta := range dynamicTypes { - rangeValues := []llvm.Value{ - llvm.ConstInt(c.ctx.Int16Type(), uint64(startIndex), false), - llvm.ConstInt(c.ctx.Int16Type(), uint64(len(meta.Methods)), false), - } - rangeValue := llvm.ConstNamedStruct(rangeType, rangeValues) - ranges = append(ranges, rangeValue) - methods := make([]*types.Selection, 0, len(meta.Methods)) - for _, method := range meta.Methods { - methods = append(methods, method) - } - c.ir.SortMethods(methods) - for _, method := range methods { - f := c.ir.GetFunction(c.ir.Program.MethodValue(method)) - if f.LLVMFn.IsNil() { - return errors.New("cannot find function: " + f.LinkName()) - } - fn, err := c.wrapInterfaceInvoke(f) - if err != nil { - return err - } - fnPtr := llvm.ConstBitCast(fn, c.i8ptrType) - funcPointers = append(funcPointers, fnPtr) - signatureNum := c.ir.MethodNum(method.Obj().(*types.Func)) - signature := llvm.ConstInt(c.ctx.Int16Type(), uint64(signatureNum), false) - signatures = append(signatures, signature) - } - startIndex += len(meta.Methods) - } - - interfaceTypes := c.ir.AllInterfaces() - interfaceIndex := make([]llvm.Value, len(interfaceTypes)) - interfaceLengths := make([]llvm.Value, len(interfaceTypes)) - interfaceMethods := make([]llvm.Value, 0) - for i, itfType := range interfaceTypes { - if itfType.Type.NumMethods() > 0xff { - return errors.New("too many methods for interface " + itfType.Type.String()) - } - interfaceIndex[i] = llvm.ConstInt(c.ctx.Int16Type(), uint64(i), false) - interfaceLengths[i] = llvm.ConstInt(c.ctx.Int8Type(), uint64(itfType.Type.NumMethods()), false) - funcs := make([]*types.Func, itfType.Type.NumMethods()) - for i := range funcs { - funcs[i] = itfType.Type.Method(i) - } - c.ir.SortFuncs(funcs) - for _, f := range funcs { - id := llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.MethodNum(f)), false) - interfaceMethods = append(interfaceMethods, id) - } - } - - if len(ranges) >= 1<<16 { - return errors.New("method call numbers do not fit in a 16-bit integer") - } - - // Replace the pre-created arrays with the generated arrays. - rangeArray := llvm.ConstArray(rangeType, ranges) - rangeArrayNewGlobal := llvm.AddGlobal(c.mod, rangeArray.Type(), "runtime.methodSetRanges.tmp") - rangeArrayNewGlobal.SetInitializer(rangeArray) - rangeArrayNewGlobal.SetLinkage(llvm.InternalLinkage) - rangeArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetRanges") - rangeArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(rangeArrayNewGlobal, rangeArrayOldGlobal.Type())) - rangeArrayOldGlobal.EraseFromParentAsGlobal() - rangeArrayNewGlobal.SetName("runtime.methodSetRanges") - funcArray := llvm.ConstArray(c.i8ptrType, funcPointers) - funcArrayNewGlobal := llvm.AddGlobal(c.mod, funcArray.Type(), "runtime.methodSetFunctions.tmp") - funcArrayNewGlobal.SetInitializer(funcArray) - funcArrayNewGlobal.SetLinkage(llvm.InternalLinkage) - funcArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetFunctions") - funcArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(funcArrayNewGlobal, funcArrayOldGlobal.Type())) - funcArrayOldGlobal.EraseFromParentAsGlobal() - funcArrayNewGlobal.SetName("runtime.methodSetFunctions") - signatureArray := llvm.ConstArray(c.ctx.Int16Type(), signatures) - signatureArrayNewGlobal := llvm.AddGlobal(c.mod, signatureArray.Type(), "runtime.methodSetSignatures.tmp") - signatureArrayNewGlobal.SetInitializer(signatureArray) - signatureArrayNewGlobal.SetLinkage(llvm.InternalLinkage) - signatureArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetSignatures") - signatureArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(signatureArrayNewGlobal, signatureArrayOldGlobal.Type())) - signatureArrayOldGlobal.EraseFromParentAsGlobal() - signatureArrayNewGlobal.SetName("runtime.methodSetSignatures") - interfaceIndexArray := llvm.ConstArray(c.ctx.Int16Type(), interfaceIndex) - interfaceIndexArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceIndexArray.Type(), "runtime.interfaceIndex.tmp") - interfaceIndexArrayNewGlobal.SetInitializer(interfaceIndexArray) - interfaceIndexArrayNewGlobal.SetLinkage(llvm.InternalLinkage) - interfaceIndexArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceIndex") - interfaceIndexArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceIndexArrayNewGlobal, interfaceIndexArrayOldGlobal.Type())) - interfaceIndexArrayOldGlobal.EraseFromParentAsGlobal() - interfaceIndexArrayNewGlobal.SetName("runtime.interfaceIndex") - interfaceLengthsArray := llvm.ConstArray(c.ctx.Int8Type(), interfaceLengths) - interfaceLengthsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceLengthsArray.Type(), "runtime.interfaceLengths.tmp") - interfaceLengthsArrayNewGlobal.SetInitializer(interfaceLengthsArray) - interfaceLengthsArrayNewGlobal.SetLinkage(llvm.InternalLinkage) - interfaceLengthsArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceLengths") - interfaceLengthsArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceLengthsArrayNewGlobal, interfaceLengthsArrayOldGlobal.Type())) - interfaceLengthsArrayOldGlobal.EraseFromParentAsGlobal() - interfaceLengthsArrayNewGlobal.SetName("runtime.interfaceLengths") - interfaceMethodsArray := llvm.ConstArray(c.ctx.Int16Type(), interfaceMethods) - interfaceMethodsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceMethodsArray.Type(), "runtime.interfaceMethods.tmp") - interfaceMethodsArrayNewGlobal.SetInitializer(interfaceMethodsArray) - interfaceMethodsArrayNewGlobal.SetLinkage(llvm.InternalLinkage) - interfaceMethodsArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceMethods") - interfaceMethodsArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceMethodsArrayNewGlobal, interfaceMethodsArrayOldGlobal.Type())) - interfaceMethodsArrayOldGlobal.EraseFromParentAsGlobal() - interfaceMethodsArrayNewGlobal.SetName("runtime.interfaceMethods") - - c.mod.NamedGlobal("runtime.firstTypeWithMethods").SetInitializer(llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.FirstDynamicType()), false)) // see: https://reviews.llvm.org/D18355 c.mod.AddNamedMetadataOperand("llvm.module.flags", @@ -902,80 +790,6 @@ func (c *Compiler) getDIType(typ types.Type) (llvm.Metadata, error) { } } -// Wrap an interface method function pointer. The wrapper takes in a pointer to -// the underlying value, dereferences it, and calls the real method. This -// wrapper is only needed when the interface value actually doesn't fit in a -// pointer and a pointer to the value must be created. -func (c *Compiler) wrapInterfaceInvoke(f *ir.Function) (llvm.Value, error) { - receiverType, err := c.getLLVMType(f.Params[0].Type()) - if err != nil { - return llvm.Value{}, err - } - expandedReceiverType := c.expandFormalParamType(receiverType) - - if c.targetData.TypeAllocSize(receiverType) <= c.targetData.TypeAllocSize(c.i8ptrType) && len(expandedReceiverType) == 1 { - // nothing to wrap - return f.LLVMFn, nil - } - - // create wrapper function - fnType := f.LLVMFn.Type().ElementType() - paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...) - wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false) - wrapper := llvm.AddFunction(c.mod, f.LinkName()+"$invoke", wrapFnType) - wrapper.SetLinkage(llvm.InternalLinkage) - wrapper.SetUnnamedAddr(true) - - // add debug info - if c.Debug { - pos := c.ir.Program.Fset.Position(f.Pos()) - difunc, err := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line) - if err != nil { - return llvm.Value{}, err - } - c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) - } - - // set up IR builder - block := c.ctx.AddBasicBlock(wrapper, "entry") - c.builder.SetInsertPointAtEnd(block) - - var receiverPtr llvm.Value - if c.targetData.TypeAllocSize(receiverType) > c.targetData.TypeAllocSize(c.i8ptrType) { - // The receiver is passed in using a pointer. We have to load it here - // and pass it by value to the real function. - - // Load the underlying value. - receiverPtrType := llvm.PointerType(receiverType, 0) - receiverPtr = c.builder.CreateBitCast(wrapper.Param(0), receiverPtrType, "receiver.ptr") - } else if len(expandedReceiverType) != 1 { - // The value is stored in the interface, but it is of type struct which - // is expanded to multiple parameters (e.g. {i8, i8}). So we have to - // receive the struct as parameter, expand it, and pass it on to the - // real function. - - // Cast the passed-in i8* to the struct value (using an alloca) and - // extract its values. - alloca := c.builder.CreateAlloca(c.i8ptrType, "receiver.alloca") - c.builder.CreateStore(wrapper.Param(0), alloca) - receiverPtr = c.builder.CreateBitCast(alloca, llvm.PointerType(receiverType, 0), "receiver.ptr") - } else { - panic("unreachable") - } - - receiverValue := c.builder.CreateLoad(receiverPtr, "receiver") - params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...) - if fnType.ReturnType().TypeKind() == llvm.VoidTypeKind { - c.builder.CreateCall(f.LLVMFn, params, "") - c.builder.CreateRetVoid() - } else { - ret := c.builder.CreateCall(f.LLVMFn, params, "ret") - c.builder.CreateRet(ret) - } - - return wrapper, nil -} - func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) { frame := &Frame{ fn: f, @@ -2248,57 +2062,6 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l } } -// getInvokeCall creates and returns the function pointer and parameters of an -// interface call. It can be used in a call or defer instruction. -func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, []llvm.Value, error) { - // Call an interface method with dynamic dispatch. - itf, err := c.parseExpr(frame, instr.Value) // interface - if err != nil { - return llvm.Value{}, nil, err - } - - llvmFnType, err := c.getLLVMType(instr.Method.Type()) - if err != nil { - return llvm.Value{}, nil, err - } - if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) { - // This is somewhat of a hack. - // getLLVMType() has created a closure type for us, but we don't - // actually want a closure type as an interface call can never be a - // closure call. So extract the function pointer type from the - // closure. - // This happens because somewhere the same function signature is - // used in a closure or bound method. - llvmFnType = llvmFnType.Subtypes()[1] - } - - typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode") - values := []llvm.Value{ - typecode, - llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false), - } - fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func") - fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast") - receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver") - - args := []llvm.Value{receiverValue} - for _, arg := range instr.Args { - val, err := c.parseExpr(frame, arg) - if err != nil { - return llvm.Value{}, nil, err - } - args = append(args, val) - } - if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) { - // This function takes an extra context parameter. An interface call - // cannot also be a closure but we have to supply the nil pointer - // anyway. - args = append(args, llvm.ConstPointerNull(c.i8ptrType)) - } - - return fnCast, args, nil -} - func (c *Compiler) emitBoundsCheck(frame *Frame, arrayLen, index llvm.Value, indexType types.Type) { if frame.fn.IsNoBounds() { // The //go:nobounds pragma was added to the function to avoid bounds @@ -2831,127 +2594,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { return llvm.Value{}, errors.New("unknown slice type: " + typ.String()) } case *ssa.TypeAssert: - itf, err := c.parseExpr(frame, expr.X) - if err != nil { - return llvm.Value{}, err - } - - assertedType, err := c.getLLVMType(expr.AssertedType) - if err != nil { - return llvm.Value{}, err - } - valueNil, err := c.getZeroValue(assertedType) - if err != nil { - return llvm.Value{}, err - } - - actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") - commaOk := llvm.Value{} - if itf, ok := expr.AssertedType.Underlying().(*types.Interface); ok { - // Type assert on interface type. - // This is slightly non-trivial: at runtime the list of methods - // needs to be checked to see whether it implements the interface. - // At the same time, the interface value itself is unchanged. - itfTypeNum := c.ir.InterfaceNum(itf) - itfTypeNumValue := llvm.ConstInt(c.ctx.Int16Type(), uint64(itfTypeNum), false) - commaOk = c.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, itfTypeNumValue}, "") - - } else { - // Type assert on concrete type. - // This is easy: just compare the type number. - assertedTypeNum, typeExists := c.ir.TypeNum(expr.AssertedType) - if !typeExists { - // Static analysis has determined this type assert will never apply. - // Using undef here so that LLVM knows we'll never get here and - // can optimize accordingly. - undef := llvm.Undef(assertedType) - commaOk := llvm.ConstInt(c.ctx.Int1Type(), 0, false) - if expr.CommaOk { - return c.ctx.ConstStruct([]llvm.Value{undef, commaOk}, false), nil - } else { - c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "") - return undef, nil - } - } - if assertedTypeNum >= 1<<16 { - return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") - } - - assertedTypeNumValue := llvm.ConstInt(c.ctx.Int16Type(), uint64(assertedTypeNum), false) - commaOk = c.builder.CreateICmp(llvm.IntEQ, assertedTypeNumValue, actualTypeNum, "") - } - - // Add 2 new basic blocks (that should get optimized away): one for the - // 'ok' case and one for all instructions following this type assert. - // This is necessary because we need to insert the casted value or the - // nil value based on whether the assert was successful. Casting before - // this check tells LLVM that it can use this value and may - // speculatively dereference pointers before the check. This can lead to - // a miscompilation resulting in a segfault at runtime. - // Additionally, this is even required by the Go spec: a failed - // typeassert should return a zero value, not an incorrectly casted - // value. - - prevBlock := c.builder.GetInsertBlock() - okBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.ok") - nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.next") - frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes - c.builder.CreateCondBr(commaOk, okBlock, nextBlock) - - // Retrieve the value from the interface if the type assert was - // successful. - c.builder.SetInsertPointAtEnd(okBlock) - var valueOk llvm.Value - if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok { - // Type assert on interface type. Easy: just return the same - // interface value. - valueOk = itf - } else { - // Type assert on concrete type. Extract the underlying type from - // the interface (but only after checking it matches). - valuePtr := c.builder.CreateExtractValue(itf, 1, "typeassert.value.ptr") - if c.targetData.TypeAllocSize(assertedType) > c.targetData.TypeAllocSize(c.i8ptrType) { - // Value was stored in an allocated buffer, load it from there. - valuePtrCast := c.builder.CreateBitCast(valuePtr, llvm.PointerType(assertedType, 0), "") - valueOk = c.builder.CreateLoad(valuePtrCast, "typeassert.value.ok") - } else { - // Value was stored directly in the interface. - switch assertedType.TypeKind() { - case llvm.IntegerTypeKind: - valueOk = c.builder.CreatePtrToInt(valuePtr, assertedType, "typeassert.value.ok") - case llvm.PointerTypeKind: - valueOk = c.builder.CreateBitCast(valuePtr, assertedType, "typeassert.value.ok") - case llvm.StructTypeKind: - // A bitcast would be useful here, but bitcast doesn't allow - // aggregate types. So we'll bitcast it using an alloca. - // Hopefully this will get optimized away. - mem := c.builder.CreateAlloca(c.i8ptrType, "") - c.builder.CreateStore(valuePtr, mem) - memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(assertedType, 0), "") - valueOk = c.builder.CreateLoad(memStructPtr, "typeassert.value.ok") - default: - return llvm.Value{}, errors.New("todo: typeassert: bitcast small types") - } - } - } - c.builder.CreateBr(nextBlock) - - // Continue after the if statement. - c.builder.SetInsertPointAtEnd(nextBlock) - phi := c.builder.CreatePHI(assertedType, "typeassert.value") - phi.AddIncoming([]llvm.Value{valueNil, valueOk}, []llvm.BasicBlock{prevBlock, okBlock}) - - if expr.CommaOk { - tuple := c.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(c.ctx.Int1Type())}, false) // create empty tuple - tuple = c.builder.CreateInsertValue(tuple, phi, 0, "") // insert value - tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean - return tuple, nil - } else { - // This is kind of dirty as the branch above becomes mostly useless, - // but hopefully this gets optimized away. - c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "") - return phi, nil - } + return c.parseTypeAssert(frame, expr) case *ssa.UnOp: return c.parseUnOp(frame, expr) default: @@ -3550,56 +3193,6 @@ func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.V return closure, nil } -func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global string) (llvm.Value, error) { - var itfValue llvm.Value - size := c.targetData.TypeAllocSize(val.Type()) - if size > c.targetData.TypeAllocSize(c.i8ptrType) { - if global != "" { - // Allocate in a global variable. - global := llvm.AddGlobal(c.mod, val.Type(), global+"$itfvalue") - global.SetInitializer(val) - global.SetLinkage(llvm.InternalLinkage) - global.SetGlobalConstant(true) - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) - itfValueRaw := llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero}) - itfValue = llvm.ConstBitCast(itfValueRaw, c.i8ptrType) - } else { - // Allocate on the heap and put a pointer in the interface. - // TODO: escape analysis. - sizeValue := llvm.ConstInt(c.uintptrType, size, false) - alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "") - itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "") - c.builder.CreateStore(val, itfValueCast) - itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "") - } - } else { - // Directly place the value in the interface. - switch val.Type().TypeKind() { - case llvm.IntegerTypeKind: - itfValue = c.builder.CreateIntToPtr(val, c.i8ptrType, "") - case llvm.PointerTypeKind: - itfValue = c.builder.CreateBitCast(val, c.i8ptrType, "") - case llvm.StructTypeKind: - // A bitcast would be useful here, but bitcast doesn't allow - // aggregate types. So we'll bitcast it using an alloca. - // Hopefully this will get optimized away. - mem := c.builder.CreateAlloca(c.i8ptrType, "") - memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(val.Type(), 0), "") - c.builder.CreateStore(val, memStructPtr) - itfValue = c.builder.CreateLoad(mem, "") - default: - return llvm.Value{}, errors.New("todo: makeinterface: cast small type to i8*") - } - } - itfTypeNum, _ := c.ir.TypeNum(typ) - if itfTypeNum >= 1<<16 { - return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") - } - itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), []llvm.Value{llvm.ConstInt(c.ctx.Int16Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType)}) - itf = c.builder.CreateInsertValue(itf, itfValue, 1, "") - return itf, nil -} - func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) { x, err := c.parseExpr(frame, unop.X) if err != nil { diff --git a/compiler/interface.go b/compiler/interface.go new file mode 100644 index 00000000..36603e05 --- /dev/null +++ b/compiler/interface.go @@ -0,0 +1,445 @@ +package compiler + +import ( + "errors" + "go/types" + + "github.com/aykevl/go-llvm" + "github.com/aykevl/tinygo/ir" + "golang.org/x/tools/go/ssa" +) + +// parseMakeInterface emits the LLVM IR for the *ssa.MakeInterface instruction. +// It tries to put the type in the interface value, but if that's not possible, +// it will do an allocation of the right size and put that in the interface +// value field. +// +// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact. +func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, global string) (llvm.Value, error) { + var itfValue llvm.Value + size := c.targetData.TypeAllocSize(val.Type()) + if size > c.targetData.TypeAllocSize(c.i8ptrType) { + if global != "" { + // Allocate in a global variable. + global := llvm.AddGlobal(c.mod, val.Type(), global+"$itfvalue") + global.SetInitializer(val) + global.SetLinkage(llvm.InternalLinkage) + global.SetGlobalConstant(true) + zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) + itfValueRaw := llvm.ConstInBoundsGEP(global, []llvm.Value{zero, zero}) + itfValue = llvm.ConstBitCast(itfValueRaw, c.i8ptrType) + } else { + // Allocate on the heap and put a pointer in the interface. + // TODO: escape analysis. + sizeValue := llvm.ConstInt(c.uintptrType, size, false) + alloc := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "") + itfValueCast := c.builder.CreateBitCast(alloc, llvm.PointerType(val.Type(), 0), "") + c.builder.CreateStore(val, itfValueCast) + itfValue = c.builder.CreateBitCast(itfValueCast, c.i8ptrType, "") + } + } else { + // Directly place the value in the interface. + switch val.Type().TypeKind() { + case llvm.IntegerTypeKind: + itfValue = c.builder.CreateIntToPtr(val, c.i8ptrType, "") + case llvm.PointerTypeKind: + itfValue = c.builder.CreateBitCast(val, c.i8ptrType, "") + case llvm.StructTypeKind: + // A bitcast would be useful here, but bitcast doesn't allow + // aggregate types. So we'll bitcast it using an alloca. + // Hopefully this will get optimized away. + mem := c.builder.CreateAlloca(c.i8ptrType, "") + memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(val.Type(), 0), "") + c.builder.CreateStore(val, memStructPtr) + itfValue = c.builder.CreateLoad(mem, "") + default: + return llvm.Value{}, errors.New("todo: makeinterface: cast small type to i8*") + } + } + itfTypeNum, _ := c.ir.TypeNum(typ) + if itfTypeNum >= 1<<16 { + return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") + } + itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), []llvm.Value{llvm.ConstInt(c.ctx.Int16Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType)}) + itf = c.builder.CreateInsertValue(itf, itfValue, 1, "") + return itf, nil +} + +// parseTypeAssert will emit the code for a typeassert, used in if statements +// and in switch statements (Go SSA does not have type switches, only if/else +// chains). Note that even though the Go SSA does not contain type switches, +// LLVM will recognize the pattern and make it a real switch in many cases. +// +// Type asserts on concrete types are trivial: just compare type numbers. Type +// asserts on interfaces are more difficult to implement and so are delegated to +// a runtime library function. +func (c *Compiler) parseTypeAssert(frame *Frame, expr *ssa.TypeAssert) (llvm.Value, error) { + itf, err := c.parseExpr(frame, expr.X) + if err != nil { + return llvm.Value{}, err + } + assertedType, err := c.getLLVMType(expr.AssertedType) + if err != nil { + return llvm.Value{}, err + } + valueNil, err := c.getZeroValue(assertedType) + if err != nil { + return llvm.Value{}, err + } + + actualTypeNum := c.builder.CreateExtractValue(itf, 0, "interface.type") + commaOk := llvm.Value{} + if itf, ok := expr.AssertedType.Underlying().(*types.Interface); ok { + // Type assert on interface type. + // This is slightly non-trivial: at runtime the list of methods + // needs to be checked to see whether it implements the interface. + // At the same time, the interface value itself is unchanged. + itfTypeNum := c.ir.InterfaceNum(itf) + itfTypeNumValue := llvm.ConstInt(c.ctx.Int16Type(), uint64(itfTypeNum), false) + commaOk = c.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, itfTypeNumValue}, "") + + } else { + // Type assert on concrete type. + // This is easy: just compare the type number. + assertedTypeNum, typeExists := c.ir.TypeNum(expr.AssertedType) + if !typeExists { + // Static analysis has determined this type assert will never apply. + // Using undef here so that LLVM knows we'll never get here and + // can optimize accordingly. + undef := llvm.Undef(assertedType) + commaOk := llvm.ConstInt(c.ctx.Int1Type(), 0, false) + if expr.CommaOk { + return c.ctx.ConstStruct([]llvm.Value{undef, commaOk}, false), nil + } else { + c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "") + return undef, nil + } + } + if assertedTypeNum >= 1<<16 { + return llvm.Value{}, errors.New("interface typecodes do not fit in a 16-bit integer") + } + + assertedTypeNumValue := llvm.ConstInt(c.ctx.Int16Type(), uint64(assertedTypeNum), false) + commaOk = c.builder.CreateICmp(llvm.IntEQ, assertedTypeNumValue, actualTypeNum, "") + } + + // Add 2 new basic blocks (that should get optimized away): one for the + // 'ok' case and one for all instructions following this type assert. + // This is necessary because we need to insert the casted value or the + // nil value based on whether the assert was successful. Casting before + // this check tells LLVM that it can use this value and may + // speculatively dereference pointers before the check. This can lead to + // a miscompilation resulting in a segfault at runtime. + // Additionally, this is even required by the Go spec: a failed + // typeassert should return a zero value, not an incorrectly casted + // value. + + prevBlock := c.builder.GetInsertBlock() + okBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.ok") + nextBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "typeassert.next") + frame.blockExits[frame.currentBlock] = nextBlock // adjust outgoing block for phi nodes + c.builder.CreateCondBr(commaOk, okBlock, nextBlock) + + // Retrieve the value from the interface if the type assert was + // successful. + c.builder.SetInsertPointAtEnd(okBlock) + var valueOk llvm.Value + if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok { + // Type assert on interface type. Easy: just return the same + // interface value. + valueOk = itf + } else { + // Type assert on concrete type. Extract the underlying type from + // the interface (but only after checking it matches). + valuePtr := c.builder.CreateExtractValue(itf, 1, "typeassert.value.ptr") + if c.targetData.TypeAllocSize(assertedType) > c.targetData.TypeAllocSize(c.i8ptrType) { + // Value was stored in an allocated buffer, load it from there. + valuePtrCast := c.builder.CreateBitCast(valuePtr, llvm.PointerType(assertedType, 0), "") + valueOk = c.builder.CreateLoad(valuePtrCast, "typeassert.value.ok") + } else { + // Value was stored directly in the interface. + switch assertedType.TypeKind() { + case llvm.IntegerTypeKind: + valueOk = c.builder.CreatePtrToInt(valuePtr, assertedType, "typeassert.value.ok") + case llvm.PointerTypeKind: + valueOk = c.builder.CreateBitCast(valuePtr, assertedType, "typeassert.value.ok") + case llvm.StructTypeKind: + // A bitcast would be useful here, but bitcast doesn't allow + // aggregate types. So we'll bitcast it using an alloca. + // Hopefully this will get optimized away. + mem := c.builder.CreateAlloca(c.i8ptrType, "") + c.builder.CreateStore(valuePtr, mem) + memStructPtr := c.builder.CreateBitCast(mem, llvm.PointerType(assertedType, 0), "") + valueOk = c.builder.CreateLoad(memStructPtr, "typeassert.value.ok") + default: + return llvm.Value{}, errors.New("todo: typeassert: bitcast small types") + } + } + } + c.builder.CreateBr(nextBlock) + + // Continue after the if statement. + c.builder.SetInsertPointAtEnd(nextBlock) + phi := c.builder.CreatePHI(assertedType, "typeassert.value") + phi.AddIncoming([]llvm.Value{valueNil, valueOk}, []llvm.BasicBlock{prevBlock, okBlock}) + + if expr.CommaOk { + tuple := c.ctx.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(c.ctx.Int1Type())}, false) // create empty tuple + tuple = c.builder.CreateInsertValue(tuple, phi, 0, "") // insert value + tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean + return tuple, nil + } else { + // This is kind of dirty as the branch above becomes mostly useless, + // but hopefully this gets optimized away. + c.createRuntimeCall("interfaceTypeAssert", []llvm.Value{commaOk}, "") + return phi, nil + } +} + +// getInvokeCall creates and returns the function pointer and parameters of an +// interface call. It can be used in a call or defer instruction. +func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, []llvm.Value, error) { + // Call an interface method with dynamic dispatch. + itf, err := c.parseExpr(frame, instr.Value) // interface + if err != nil { + return llvm.Value{}, nil, err + } + + llvmFnType, err := c.getLLVMType(instr.Method.Type()) + if err != nil { + return llvm.Value{}, nil, err + } + if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) { + // This is somewhat of a hack. + // getLLVMType() has created a closure type for us, but we don't + // actually want a closure type as an interface call can never be a + // closure call. So extract the function pointer type from the + // closure. + // This happens because somewhere the same function signature is + // used in a closure or bound method. + llvmFnType = llvmFnType.Subtypes()[1] + } + + typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode") + values := []llvm.Value{ + typecode, + llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.MethodNum(instr.Method)), false), + } + fn := c.createRuntimeCall("interfaceMethod", values, "invoke.func") + fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast") + receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver") + + args := []llvm.Value{receiverValue} + for _, arg := range instr.Args { + val, err := c.parseExpr(frame, arg) + if err != nil { + return llvm.Value{}, nil, err + } + args = append(args, val) + } + if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) { + // This function takes an extra context parameter. An interface call + // cannot also be a closure but we have to supply the nil pointer + // anyway. + args = append(args, llvm.ConstPointerNull(c.i8ptrType)) + } + + return fnCast, args, nil +} + +// Initialize runtime type information, for interfaces. +// See src/runtime/interface.go for more details. +func (c *Compiler) createInterfaceRTTI() error { + dynamicTypes := c.ir.AllDynamicTypes() + numDynamicTypes := 0 + for _, meta := range dynamicTypes { + numDynamicTypes += len(meta.Methods) + } + ranges := make([]llvm.Value, 0, len(dynamicTypes)) + funcPointers := make([]llvm.Value, 0, numDynamicTypes) + signatures := make([]llvm.Value, 0, numDynamicTypes) + startIndex := 0 + rangeType := c.mod.GetTypeByName("runtime.methodSetRange") + for _, meta := range dynamicTypes { + rangeValues := []llvm.Value{ + llvm.ConstInt(c.ctx.Int16Type(), uint64(startIndex), false), + llvm.ConstInt(c.ctx.Int16Type(), uint64(len(meta.Methods)), false), + } + rangeValue := llvm.ConstNamedStruct(rangeType, rangeValues) + ranges = append(ranges, rangeValue) + methods := make([]*types.Selection, 0, len(meta.Methods)) + for _, method := range meta.Methods { + methods = append(methods, method) + } + c.ir.SortMethods(methods) + for _, method := range methods { + f := c.ir.GetFunction(c.ir.Program.MethodValue(method)) + if f.LLVMFn.IsNil() { + return errors.New("cannot find function: " + f.LinkName()) + } + fn, err := c.wrapInterfaceInvoke(f) + if err != nil { + return err + } + fnPtr := llvm.ConstBitCast(fn, c.i8ptrType) + funcPointers = append(funcPointers, fnPtr) + signatureNum := c.ir.MethodNum(method.Obj().(*types.Func)) + signature := llvm.ConstInt(c.ctx.Int16Type(), uint64(signatureNum), false) + signatures = append(signatures, signature) + } + startIndex += len(meta.Methods) + } + + interfaceTypes := c.ir.AllInterfaces() + interfaceIndex := make([]llvm.Value, len(interfaceTypes)) + interfaceLengths := make([]llvm.Value, len(interfaceTypes)) + interfaceMethods := make([]llvm.Value, 0) + for i, itfType := range interfaceTypes { + if itfType.Type.NumMethods() > 0xff { + return errors.New("too many methods for interface " + itfType.Type.String()) + } + interfaceIndex[i] = llvm.ConstInt(c.ctx.Int16Type(), uint64(i), false) + interfaceLengths[i] = llvm.ConstInt(c.ctx.Int8Type(), uint64(itfType.Type.NumMethods()), false) + funcs := make([]*types.Func, itfType.Type.NumMethods()) + for i := range funcs { + funcs[i] = itfType.Type.Method(i) + } + c.ir.SortFuncs(funcs) + for _, f := range funcs { + id := llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.MethodNum(f)), false) + interfaceMethods = append(interfaceMethods, id) + } + } + + if len(ranges) >= 1<<16 { + return errors.New("method call numbers do not fit in a 16-bit integer") + } + + // Replace the pre-created arrays with the generated arrays. + rangeArray := llvm.ConstArray(rangeType, ranges) + rangeArrayNewGlobal := llvm.AddGlobal(c.mod, rangeArray.Type(), "runtime.methodSetRanges.tmp") + rangeArrayNewGlobal.SetInitializer(rangeArray) + rangeArrayNewGlobal.SetLinkage(llvm.InternalLinkage) + rangeArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetRanges") + rangeArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(rangeArrayNewGlobal, rangeArrayOldGlobal.Type())) + rangeArrayOldGlobal.EraseFromParentAsGlobal() + rangeArrayNewGlobal.SetName("runtime.methodSetRanges") + funcArray := llvm.ConstArray(c.i8ptrType, funcPointers) + funcArrayNewGlobal := llvm.AddGlobal(c.mod, funcArray.Type(), "runtime.methodSetFunctions.tmp") + funcArrayNewGlobal.SetInitializer(funcArray) + funcArrayNewGlobal.SetLinkage(llvm.InternalLinkage) + funcArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetFunctions") + funcArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(funcArrayNewGlobal, funcArrayOldGlobal.Type())) + funcArrayOldGlobal.EraseFromParentAsGlobal() + funcArrayNewGlobal.SetName("runtime.methodSetFunctions") + signatureArray := llvm.ConstArray(c.ctx.Int16Type(), signatures) + signatureArrayNewGlobal := llvm.AddGlobal(c.mod, signatureArray.Type(), "runtime.methodSetSignatures.tmp") + signatureArrayNewGlobal.SetInitializer(signatureArray) + signatureArrayNewGlobal.SetLinkage(llvm.InternalLinkage) + signatureArrayOldGlobal := c.mod.NamedGlobal("runtime.methodSetSignatures") + signatureArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(signatureArrayNewGlobal, signatureArrayOldGlobal.Type())) + signatureArrayOldGlobal.EraseFromParentAsGlobal() + signatureArrayNewGlobal.SetName("runtime.methodSetSignatures") + interfaceIndexArray := llvm.ConstArray(c.ctx.Int16Type(), interfaceIndex) + interfaceIndexArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceIndexArray.Type(), "runtime.interfaceIndex.tmp") + interfaceIndexArrayNewGlobal.SetInitializer(interfaceIndexArray) + interfaceIndexArrayNewGlobal.SetLinkage(llvm.InternalLinkage) + interfaceIndexArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceIndex") + interfaceIndexArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceIndexArrayNewGlobal, interfaceIndexArrayOldGlobal.Type())) + interfaceIndexArrayOldGlobal.EraseFromParentAsGlobal() + interfaceIndexArrayNewGlobal.SetName("runtime.interfaceIndex") + interfaceLengthsArray := llvm.ConstArray(c.ctx.Int8Type(), interfaceLengths) + interfaceLengthsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceLengthsArray.Type(), "runtime.interfaceLengths.tmp") + interfaceLengthsArrayNewGlobal.SetInitializer(interfaceLengthsArray) + interfaceLengthsArrayNewGlobal.SetLinkage(llvm.InternalLinkage) + interfaceLengthsArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceLengths") + interfaceLengthsArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceLengthsArrayNewGlobal, interfaceLengthsArrayOldGlobal.Type())) + interfaceLengthsArrayOldGlobal.EraseFromParentAsGlobal() + interfaceLengthsArrayNewGlobal.SetName("runtime.interfaceLengths") + interfaceMethodsArray := llvm.ConstArray(c.ctx.Int16Type(), interfaceMethods) + interfaceMethodsArrayNewGlobal := llvm.AddGlobal(c.mod, interfaceMethodsArray.Type(), "runtime.interfaceMethods.tmp") + interfaceMethodsArrayNewGlobal.SetInitializer(interfaceMethodsArray) + interfaceMethodsArrayNewGlobal.SetLinkage(llvm.InternalLinkage) + interfaceMethodsArrayOldGlobal := c.mod.NamedGlobal("runtime.interfaceMethods") + interfaceMethodsArrayOldGlobal.ReplaceAllUsesWith(llvm.ConstBitCast(interfaceMethodsArrayNewGlobal, interfaceMethodsArrayOldGlobal.Type())) + interfaceMethodsArrayOldGlobal.EraseFromParentAsGlobal() + interfaceMethodsArrayNewGlobal.SetName("runtime.interfaceMethods") + + c.mod.NamedGlobal("runtime.firstTypeWithMethods").SetInitializer(llvm.ConstInt(c.ctx.Int16Type(), uint64(c.ir.FirstDynamicType()), false)) + + return nil +} + +// Wrap an interface method function pointer. The wrapper takes in a pointer to +// the underlying value, dereferences it, and calls the real method. This +// wrapper is only needed when the interface value actually doesn't fit in a +// pointer and a pointer to the value must be created. +func (c *Compiler) wrapInterfaceInvoke(f *ir.Function) (llvm.Value, error) { + receiverType, err := c.getLLVMType(f.Params[0].Type()) + if err != nil { + return llvm.Value{}, err + } + expandedReceiverType := c.expandFormalParamType(receiverType) + + if c.targetData.TypeAllocSize(receiverType) <= c.targetData.TypeAllocSize(c.i8ptrType) && len(expandedReceiverType) == 1 { + // nothing to wrap + return f.LLVMFn, nil + } + + // create wrapper function + fnType := f.LLVMFn.Type().ElementType() + paramTypes := append([]llvm.Type{c.i8ptrType}, fnType.ParamTypes()[len(expandedReceiverType):]...) + wrapFnType := llvm.FunctionType(fnType.ReturnType(), paramTypes, false) + wrapper := llvm.AddFunction(c.mod, f.LinkName()+"$invoke", wrapFnType) + wrapper.SetLinkage(llvm.InternalLinkage) + wrapper.SetUnnamedAddr(true) + + // add debug info + if c.Debug { + pos := c.ir.Program.Fset.Position(f.Pos()) + difunc, err := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line) + if err != nil { + return llvm.Value{}, err + } + c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) + } + + // set up IR builder + block := c.ctx.AddBasicBlock(wrapper, "entry") + c.builder.SetInsertPointAtEnd(block) + + var receiverPtr llvm.Value + if c.targetData.TypeAllocSize(receiverType) > c.targetData.TypeAllocSize(c.i8ptrType) { + // The receiver is passed in using a pointer. We have to load it here + // and pass it by value to the real function. + + // Load the underlying value. + receiverPtrType := llvm.PointerType(receiverType, 0) + receiverPtr = c.builder.CreateBitCast(wrapper.Param(0), receiverPtrType, "receiver.ptr") + } else if len(expandedReceiverType) != 1 { + // The value is stored in the interface, but it is of type struct which + // is expanded to multiple parameters (e.g. {i8, i8}). So we have to + // receive the struct as parameter, expand it, and pass it on to the + // real function. + + // Cast the passed-in i8* to the struct value (using an alloca) and + // extract its values. + alloca := c.builder.CreateAlloca(c.i8ptrType, "receiver.alloca") + c.builder.CreateStore(wrapper.Param(0), alloca) + receiverPtr = c.builder.CreateBitCast(alloca, llvm.PointerType(receiverType, 0), "receiver.ptr") + } else { + panic("unreachable") + } + + receiverValue := c.builder.CreateLoad(receiverPtr, "receiver") + params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...) + if fnType.ReturnType().TypeKind() == llvm.VoidTypeKind { + c.builder.CreateCall(f.LLVMFn, params, "") + c.builder.CreateRetVoid() + } else { + ret := c.builder.CreateCall(f.LLVMFn, params, "ret") + c.builder.CreateRet(ret) + } + + return wrapper, nil +}