diff --git a/compiler/compiler.go b/compiler/compiler.go index 07ce42140..cb63868fb 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -58,9 +58,8 @@ type compilerContext struct { type Compiler struct { compilerContext - builder llvm.Builder - initFuncs []llvm.Value - interfaceInvokeWrappers []interfaceInvokeWrapper + builder llvm.Builder + initFuncs []llvm.Value } type Frame struct { @@ -301,12 +300,6 @@ func (c *Compiler) Compile(mainPath string) []error { c.parseFunc(frame) } - // Define the already declared functions that wrap methods for use in - // interfaces. - for _, state := range c.interfaceInvokeWrappers { - c.createInterfaceInvokeWrapper(state) - } - // After all packages are imported, add a synthetic initializer function // that calls the initializer of each package. initFn := c.ir.GetFunction(c.ir.Program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)) @@ -801,12 +794,17 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) *Frame { return frame } -func (c *Compiler) attachDebugInfo(f *ir.Function) llvm.Metadata { +// attachDebugInfo adds debug info to a function declaration. It returns the +// DISubprogram metadata node. +func (c *compilerContext) attachDebugInfo(f *ir.Function) llvm.Metadata { pos := c.ir.Program.Fset.Position(f.Syntax().Pos()) return c.attachDebugInfoRaw(f, f.LLVMFn, "", pos.Filename, pos.Line) } -func (c *Compiler) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata { +// attachDebugInfo adds debug info to a function declaration. It returns the +// DISubprogram metadata node. This method allows some more control over how +// debug info is added to the function. +func (c *compilerContext) attachDebugInfoRaw(f *ir.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata { // Debug info for this function. diparams := make([]llvm.Metadata, 0, len(f.Params)) for _, param := range f.Params { diff --git a/compiler/interface.go b/compiler/interface.go index 66a3b9a4a..86f8d3338 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -422,20 +422,12 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu return fnCast, args } -// interfaceInvokeWrapper keeps some state between getInterfaceInvokeWrapper and -// createInterfaceInvokeWrapper. The former is called during IR construction -// itself and the latter is called when finishing up the IR. -type interfaceInvokeWrapper struct { - fn *ir.Function - wrapper llvm.Value - receiverType llvm.Type -} - -// 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) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value { +// getInterfaceInvokeWrapper returns a wrapper for the given method so it can be +// invoked from an interface. The wrapper takes in a pointer to the underlying +// value, dereferences or unpacks it if necessary, and calls the real method. +// If the method to wrap has a pointer receiver, no wrapping is necessary and +// the function is returned directly. +func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value { wrapperName := f.LinkName() + "$invoke" wrapper := c.mod.NamedFunction(wrapperName) if !wrapper.IsNil() { @@ -464,41 +456,37 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value { if f.LLVMFn.LastParam().Name() == "parentHandle" { wrapper.LastParam().SetName("parentHandle") } - c.interfaceInvokeWrappers = append(c.interfaceInvokeWrappers, interfaceInvokeWrapper{ - fn: f, - wrapper: wrapper, - receiverType: receiverType, - }) - return wrapper -} -// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper, -// see that function for details. -func (c *Compiler) createInterfaceInvokeWrapper(state interfaceInvokeWrapper) { - wrapper := state.wrapper - fn := state.fn - receiverType := state.receiverType wrapper.SetLinkage(llvm.InternalLinkage) wrapper.SetUnnamedAddr(true) + // Create a new builder just to create this wrapper. + b := builder{ + compilerContext: c, + Builder: c.ctx.NewBuilder(), + } + defer b.Builder.Dispose() + // add debug info if needed if c.Debug() { - pos := c.ir.Program.Fset.Position(fn.Pos()) - difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line) - c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) + pos := c.ir.Program.Fset.Position(f.Pos()) + difunc := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line) + b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) } // set up IR builder - block := c.ctx.AddBasicBlock(wrapper, "entry") - c.builder.SetInsertPointAtEnd(block) + block := b.ctx.AddBasicBlock(wrapper, "entry") + b.SetInsertPointAtEnd(block) - receiverValue := c.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0] - params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...) - if fn.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind { - c.builder.CreateCall(fn.LLVMFn, params, "") - c.builder.CreateRetVoid() + receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0] + params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...) + if f.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind { + b.CreateCall(f.LLVMFn, params, "") + b.CreateRetVoid() } else { - ret := c.builder.CreateCall(fn.LLVMFn, params, "ret") - c.builder.CreateRet(ret) + ret := b.CreateCall(f.LLVMFn, params, "ret") + b.CreateRet(ret) } + + return wrapper } diff --git a/compiler/llvm.go b/compiler/llvm.go index 2a9f6c9d9..ed5e00404 100644 --- a/compiler/llvm.go +++ b/compiler/llvm.go @@ -64,6 +64,13 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value { return llvmutil.EmitPointerPack(c.builder, c.mod, c.Config, values) } +// emitPointerPack packs the list of values into a single pointer value using +// bitcasts, or else allocates a value on the heap if it cannot be packed in the +// pointer value directly. It returns the pointer with the packed data. +func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value { + return llvmutil.EmitPointerPack(b.Builder, b.mod, b.Config, values) +} + // emitPointerUnpack extracts a list of values packed using emitPointerPack. func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value { return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes)