From fc0ac9af8ec59c27810b3a0c1e783cf667dd0518 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 23 Nov 2019 12:59:55 +0100 Subject: [PATCH] compiler: refactor interface invoke wrapper creation Now that most of the utility compiler methods are ported over to the builder or compilerContext, it is possible to avoid having to do the wrapper creation in two steps. A new builder is created just to create the wrapper. This is a small reduction in line count (and a significant reduction in complexity!), even though more documentation was added. --- compiler/compiler.go | 20 ++++++------- compiler/interface.go | 66 ++++++++++++++++++------------------------- compiler/llvm.go | 7 +++++ 3 files changed, 43 insertions(+), 50 deletions(-) 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)