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.
This commit is contained in:
Ayke van Laethem
2019-11-23 12:59:55 +01:00
committed by Ron Evans
parent bee5a67097
commit fc0ac9af8e
3 changed files with 43 additions and 50 deletions
+9 -11
View File
@@ -58,9 +58,8 @@ type compilerContext struct {
type Compiler struct { type Compiler struct {
compilerContext compilerContext
builder llvm.Builder builder llvm.Builder
initFuncs []llvm.Value initFuncs []llvm.Value
interfaceInvokeWrappers []interfaceInvokeWrapper
} }
type Frame struct { type Frame struct {
@@ -301,12 +300,6 @@ func (c *Compiler) Compile(mainPath string) []error {
c.parseFunc(frame) 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 // After all packages are imported, add a synthetic initializer function
// that calls the initializer of each package. // that calls the initializer of each package.
initFn := c.ir.GetFunction(c.ir.Program.ImportedPackage("runtime").Members["initAll"].(*ssa.Function)) 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 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()) pos := c.ir.Program.Fset.Position(f.Syntax().Pos())
return c.attachDebugInfoRaw(f, f.LLVMFn, "", pos.Filename, pos.Line) 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. // Debug info for this function.
diparams := make([]llvm.Metadata, 0, len(f.Params)) diparams := make([]llvm.Metadata, 0, len(f.Params))
for _, param := range f.Params { for _, param := range f.Params {
+27 -39
View File
@@ -422,20 +422,12 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
return fnCast, args return fnCast, args
} }
// interfaceInvokeWrapper keeps some state between getInterfaceInvokeWrapper and // getInterfaceInvokeWrapper returns a wrapper for the given method so it can be
// createInterfaceInvokeWrapper. The former is called during IR construction // invoked from an interface. The wrapper takes in a pointer to the underlying
// itself and the latter is called when finishing up the IR. // value, dereferences or unpacks it if necessary, and calls the real method.
type interfaceInvokeWrapper struct { // If the method to wrap has a pointer receiver, no wrapping is necessary and
fn *ir.Function // the function is returned directly.
wrapper llvm.Value func (c *compilerContext) getInterfaceInvokeWrapper(f *ir.Function) 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 {
wrapperName := f.LinkName() + "$invoke" wrapperName := f.LinkName() + "$invoke"
wrapper := c.mod.NamedFunction(wrapperName) wrapper := c.mod.NamedFunction(wrapperName)
if !wrapper.IsNil() { if !wrapper.IsNil() {
@@ -464,41 +456,37 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
if f.LLVMFn.LastParam().Name() == "parentHandle" { if f.LLVMFn.LastParam().Name() == "parentHandle" {
wrapper.LastParam().SetName("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.SetLinkage(llvm.InternalLinkage)
wrapper.SetUnnamedAddr(true) 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 // add debug info if needed
if c.Debug() { if c.Debug() {
pos := c.ir.Program.Fset.Position(fn.Pos()) pos := c.ir.Program.Fset.Position(f.Pos())
difunc := c.attachDebugInfoRaw(fn, wrapper, "$invoke", pos.Filename, pos.Line) difunc := c.attachDebugInfoRaw(f, wrapper, "$invoke", pos.Filename, pos.Line)
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{}) b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), difunc, llvm.Metadata{})
} }
// set up IR builder // set up IR builder
block := c.ctx.AddBasicBlock(wrapper, "entry") block := b.ctx.AddBasicBlock(wrapper, "entry")
c.builder.SetInsertPointAtEnd(block) b.SetInsertPointAtEnd(block)
receiverValue := c.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0] receiverValue := b.emitPointerUnpack(wrapper.Param(0), []llvm.Type{receiverType})[0]
params := append(c.expandFormalParam(receiverValue), wrapper.Params()[1:]...) params := append(b.expandFormalParam(receiverValue), wrapper.Params()[1:]...)
if fn.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind { if f.LLVMFn.Type().ElementType().ReturnType().TypeKind() == llvm.VoidTypeKind {
c.builder.CreateCall(fn.LLVMFn, params, "") b.CreateCall(f.LLVMFn, params, "")
c.builder.CreateRetVoid() b.CreateRetVoid()
} else { } else {
ret := c.builder.CreateCall(fn.LLVMFn, params, "ret") ret := b.CreateCall(f.LLVMFn, params, "ret")
c.builder.CreateRet(ret) b.CreateRet(ret)
} }
return wrapper
} }
+7
View File
@@ -64,6 +64,13 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
return llvmutil.EmitPointerPack(c.builder, c.mod, c.Config, values) 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. // emitPointerUnpack extracts a list of values packed using emitPointerPack.
func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value { func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []llvm.Value {
return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes) return llvmutil.EmitPointerUnpack(c.builder, c.mod, ptr, valueTypes)