From 405ec2a5633efb755a45af3e2e2a4f70cc6c0572 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 24 Nov 2019 12:43:43 +0100 Subject: [PATCH] compiler: refactor defer operations --- compiler/compiler.go | 6 +- compiler/defer.go | 216 +++++++++++++++++++++---------------------- compiler/gc.go | 9 ++ 3 files changed, 120 insertions(+), 111 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index 4a1d1ee9..dbf59ad8 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -958,7 +958,7 @@ func (c *Compiler) parseFunc(frame *Frame) { if frame.fn.Recover != nil { // This function has deferred function calls. Set some things up for // them. - c.deferInitFunc(frame) + frame.deferInitFunc() } // Fill blocks with instructions. @@ -1044,7 +1044,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { case *ssa.DebugRef: // ignore case *ssa.Defer: - c.emitDefer(frame, instr) + frame.createDefer(instr) case *ssa.Go: // Get all function parameters to pass to the goroutine. var params []llvm.Value @@ -1127,7 +1127,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) { c.builder.CreateRet(retVal) } case *ssa.RunDefers: - c.emitRunDefers(frame) + frame.createRunDefers() case *ssa.Send: frame.createChanSend(instr) case *ssa.Store: diff --git a/compiler/defer.go b/compiler/defer.go index 03d57618..fa7d7623 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -23,16 +23,16 @@ import ( // deferInitFunc sets up this function for future deferred calls. It must be // called from within the entry block when this function contains deferred // calls. -func (c *Compiler) deferInitFunc(frame *Frame) { +func (b *builder) deferInitFunc() { // Some setup. - frame.deferFuncs = make(map[*ir.Function]int) - frame.deferInvokeFuncs = make(map[string]int) - frame.deferClosureFuncs = make(map[*ir.Function]int) + b.deferFuncs = make(map[*ir.Function]int) + b.deferInvokeFuncs = make(map[string]int) + b.deferClosureFuncs = make(map[*ir.Function]int) // Create defer list pointer. - deferType := llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0) - frame.deferPtr = c.builder.CreateAlloca(deferType, "deferPtr") - c.builder.CreateStore(llvm.ConstPointerNull(deferType), frame.deferPtr) + deferType := llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0) + b.deferPtr = b.CreateAlloca(deferType, "deferPtr") + b.CreateStore(llvm.ConstPointerNull(deferType), b.deferPtr) } // isInLoop checks if there is a path from a basic block to itself. @@ -69,53 +69,53 @@ func isInLoop(start *ssa.BasicBlock) bool { return false } -// emitDefer emits a single defer instruction, to be run when this function +// createDefer emits a single defer instruction, to be run when this function // returns. -func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) { +func (b *builder) createDefer(instr *ssa.Defer) { // The pointer to the previous defer struct, which we will replace to // make a linked list. - next := c.builder.CreateLoad(frame.deferPtr, "defer.next") + next := b.CreateLoad(b.deferPtr, "defer.next") var values []llvm.Value - valueTypes := []llvm.Type{c.uintptrType, next.Type()} + valueTypes := []llvm.Type{b.uintptrType, next.Type()} if instr.Call.IsInvoke() { // Method call on an interface. // Get callback type number. methodName := instr.Call.Method.FullName() - if _, ok := frame.deferInvokeFuncs[methodName]; !ok { - frame.deferInvokeFuncs[methodName] = len(frame.allDeferFuncs) - frame.allDeferFuncs = append(frame.allDeferFuncs, &instr.Call) + if _, ok := b.deferInvokeFuncs[methodName]; !ok { + b.deferInvokeFuncs[methodName] = len(b.allDeferFuncs) + b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call) } - callback := llvm.ConstInt(c.uintptrType, uint64(frame.deferInvokeFuncs[methodName]), false) + callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[methodName]), false) // Collect all values to be put in the struct (starting with // runtime._defer fields, followed by the call parameters). - itf := frame.getValue(instr.Call.Value) // interface - receiverValue := c.builder.CreateExtractValue(itf, 1, "invoke.func.receiver") + itf := b.getValue(instr.Call.Value) // interface + receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver") values = []llvm.Value{callback, next, receiverValue} - valueTypes = append(valueTypes, c.i8ptrType) + valueTypes = append(valueTypes, b.i8ptrType) for _, arg := range instr.Call.Args { - val := frame.getValue(arg) + val := b.getValue(arg) values = append(values, val) valueTypes = append(valueTypes, val.Type()) } } else if callee, ok := instr.Call.Value.(*ssa.Function); ok { // Regular function call. - fn := c.ir.GetFunction(callee) + fn := b.ir.GetFunction(callee) - if _, ok := frame.deferFuncs[fn]; !ok { - frame.deferFuncs[fn] = len(frame.allDeferFuncs) - frame.allDeferFuncs = append(frame.allDeferFuncs, fn) + if _, ok := b.deferFuncs[fn]; !ok { + b.deferFuncs[fn] = len(b.allDeferFuncs) + b.allDeferFuncs = append(b.allDeferFuncs, fn) } - callback := llvm.ConstInt(c.uintptrType, uint64(frame.deferFuncs[fn]), false) + callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[fn]), false) // Collect all values to be put in the struct (starting with // runtime._defer fields). values = []llvm.Value{callback, next} for _, param := range instr.Call.Args { - llvmParam := frame.getValue(param) + llvmParam := b.getValue(param) values = append(values, llvmParam) valueTypes = append(valueTypes, llvmParam.Type()) } @@ -127,23 +127,23 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) { // pointer. // TODO: ignore this closure entirely and put pointers to the free // variables directly in the defer struct, avoiding a memory allocation. - closure := frame.getValue(instr.Call.Value) - context := c.builder.CreateExtractValue(closure, 0, "") + closure := b.getValue(instr.Call.Value) + context := b.CreateExtractValue(closure, 0, "") // Get the callback number. - fn := c.ir.GetFunction(makeClosure.Fn.(*ssa.Function)) - if _, ok := frame.deferClosureFuncs[fn]; !ok { - frame.deferClosureFuncs[fn] = len(frame.allDeferFuncs) - frame.allDeferFuncs = append(frame.allDeferFuncs, makeClosure) + fn := b.ir.GetFunction(makeClosure.Fn.(*ssa.Function)) + if _, ok := b.deferClosureFuncs[fn]; !ok { + b.deferClosureFuncs[fn] = len(b.allDeferFuncs) + b.allDeferFuncs = append(b.allDeferFuncs, makeClosure) } - callback := llvm.ConstInt(c.uintptrType, uint64(frame.deferClosureFuncs[fn]), false) + callback := llvm.ConstInt(b.uintptrType, uint64(b.deferClosureFuncs[fn]), false) // Collect all values to be put in the struct (starting with // runtime._defer fields, followed by all parameters including the // context pointer). values = []llvm.Value{callback, next} for _, param := range instr.Call.Args { - llvmParam := frame.getValue(param) + llvmParam := b.getValue(param) values = append(values, llvmParam) valueTypes = append(valueTypes, llvmParam.Type()) } @@ -151,41 +151,41 @@ func (c *Compiler) emitDefer(frame *Frame, instr *ssa.Defer) { valueTypes = append(valueTypes, context.Type()) } else { - c.addError(instr.Pos(), "todo: defer on uncommon function call type") + b.addError(instr.Pos(), "todo: defer on uncommon function call type") return } // Make a struct out of the collected values to put in the defer frame. - deferFrameType := c.ctx.StructType(valueTypes, false) + deferFrameType := b.ctx.StructType(valueTypes, false) deferFrame := llvm.ConstNull(deferFrameType) for i, value := range values { - deferFrame = c.builder.CreateInsertValue(deferFrame, value, i, "") + deferFrame = b.CreateInsertValue(deferFrame, value, i, "") } // Put this struct in an allocation. var alloca llvm.Value if !isInLoop(instr.Block()) { // This can safely use a stack allocation. - alloca = llvmutil.CreateEntryBlockAlloca(c.builder, deferFrameType, "defer.alloca") + alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferFrameType, "defer.alloca") } else { // This may be hit a variable number of times, so use a heap allocation. - size := c.targetData.TypeAllocSize(deferFrameType) - sizeValue := llvm.ConstInt(c.uintptrType, size, false) - allocCall := c.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call") - alloca = c.builder.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc") + size := b.targetData.TypeAllocSize(deferFrameType) + sizeValue := llvm.ConstInt(b.uintptrType, size, false) + allocCall := b.createRuntimeCall("alloc", []llvm.Value{sizeValue}, "defer.alloc.call") + alloca = b.CreateBitCast(allocCall, llvm.PointerType(deferFrameType, 0), "defer.alloc") } - if c.NeedsStackObjects() { - c.trackPointer(alloca) + if b.NeedsStackObjects() { + b.trackPointer(alloca) } - c.builder.CreateStore(deferFrame, alloca) + b.CreateStore(deferFrame, alloca) // Push it on top of the linked list by replacing deferPtr. - allocaCast := c.builder.CreateBitCast(alloca, next.Type(), "defer.alloca.cast") - c.builder.CreateStore(allocaCast, frame.deferPtr) + allocaCast := b.CreateBitCast(alloca, next.Type(), "defer.alloca.cast") + b.CreateStore(allocaCast, b.deferPtr) } -// emitRunDefers emits code to run all deferred functions. -func (c *Compiler) emitRunDefers(frame *Frame) { +// createRunDefers emits code to run all deferred functions. +func (b *builder) createRunDefers() { // Add a loop like the following: // for stack != nil { // _stack := stack @@ -202,44 +202,44 @@ func (c *Compiler) emitRunDefers(frame *Frame) { // } // Create loop. - loophead := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.loophead") - loop := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.loop") - unreachable := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.default") - end := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.end") - c.builder.CreateBr(loophead) + loophead := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loophead") + loop := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.loop") + unreachable := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.default") + end := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.end") + b.CreateBr(loophead) // Create loop head: // for stack != nil { - c.builder.SetInsertPointAtEnd(loophead) - deferData := c.builder.CreateLoad(frame.deferPtr, "") - stackIsNil := c.builder.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil") - c.builder.CreateCondBr(stackIsNil, end, loop) + b.SetInsertPointAtEnd(loophead) + deferData := b.CreateLoad(b.deferPtr, "") + stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil") + b.CreateCondBr(stackIsNil, end, loop) // Create loop body: // _stack := stack // stack = stack.next // switch stack.callback { - c.builder.SetInsertPointAtEnd(loop) - nextStackGEP := c.builder.CreateInBoundsGEP(deferData, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 1, false), // .next field + b.SetInsertPointAtEnd(loop) + nextStackGEP := b.CreateInBoundsGEP(deferData, []llvm.Value{ + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field }, "stack.next.gep") - nextStack := c.builder.CreateLoad(nextStackGEP, "stack.next") - c.builder.CreateStore(nextStack, frame.deferPtr) - gep := c.builder.CreateInBoundsGEP(deferData, []llvm.Value{ - llvm.ConstInt(c.ctx.Int32Type(), 0, false), - llvm.ConstInt(c.ctx.Int32Type(), 0, false), // .callback field + nextStack := b.CreateLoad(nextStackGEP, "stack.next") + b.CreateStore(nextStack, b.deferPtr) + gep := b.CreateInBoundsGEP(deferData, []llvm.Value{ + llvm.ConstInt(b.ctx.Int32Type(), 0, false), + llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field }, "callback.gep") - callback := c.builder.CreateLoad(gep, "callback") - sw := c.builder.CreateSwitch(callback, unreachable, len(frame.allDeferFuncs)) + callback := b.CreateLoad(gep, "callback") + sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs)) - for i, callback := range frame.allDeferFuncs { + for i, callback := range b.allDeferFuncs { // Create switch case, for example: // case 0: // // run first deferred call - block := c.ctx.AddBasicBlock(frame.fn.LLVMFn, "rundefers.callback") - sw.AddCase(llvm.ConstInt(c.uintptrType, uint64(i), false), block) - c.builder.SetInsertPointAtEnd(block) + block := b.ctx.AddBasicBlock(b.fn.LLVMFn, "rundefers.callback") + sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block) + b.SetInsertPointAtEnd(block) switch callback := callback.(type) { case *ssa.CallCommon: // Call on an interface value. @@ -248,50 +248,50 @@ func (c *Compiler) emitRunDefers(frame *Frame) { } // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0), c.i8ptrType} + valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0), b.i8ptrType} for _, arg := range callback.Args { - valueTypes = append(valueTypes, c.getLLVMType(arg.Type())) + valueTypes = append(valueTypes, b.getLLVMType(arg.Type())) } - deferFrameType := c.ctx.StructType(valueTypes, false) - deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") + deferFrameType := b.ctx.StructType(valueTypes, false) + deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") // Extract the params from the struct (including receiver). forwardParams := []llvm.Value{} - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := 2; i < len(valueTypes); i++ { - gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "gep") - forwardParam := c.builder.CreateLoad(gep, "param") + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep") + forwardParam := b.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } // Add the context parameter. An interface call cannot also be a // closure but we have to supply the parameter anyway for platforms // with a strict calling convention. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) // Parent coroutine handle. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) - fnPtr, _ := frame.getInvokeCall(callback) - c.createCall(fnPtr, forwardParams, "") + fnPtr, _ := b.getInvokeCall(callback) + b.createCall(fnPtr, forwardParams, "") case *ir.Function: // Direct call. // Get the real defer struct type and cast to it. - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)} + valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} for _, param := range callback.Params { - valueTypes = append(valueTypes, c.getLLVMType(param.Type())) + valueTypes = append(valueTypes, b.getLLVMType(param.Type())) } - deferFrameType := c.ctx.StructType(valueTypes, false) - deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") + deferFrameType := b.ctx.StructType(valueTypes, false) + deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") // Extract the params from the struct. forwardParams := []llvm.Value{} - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := range callback.Params { - gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i+2), false)}, "gep") - forwardParam := c.builder.CreateLoad(gep, "param") + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") + forwardParam := b.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } @@ -300,57 +300,57 @@ func (c *Compiler) emitRunDefers(frame *Frame) { if !callback.IsExported() { // Add the context parameter. We know it is ignored by the receiving // function, but we have to pass one anyway. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) // Parent coroutine handle. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) } // Call real function. - c.createCall(callback.LLVMFn, forwardParams, "") + b.createCall(callback.LLVMFn, forwardParams, "") case *ssa.MakeClosure: // Get the real defer struct type and cast to it. - fn := c.ir.GetFunction(callback.Fn.(*ssa.Function)) - valueTypes := []llvm.Type{c.uintptrType, llvm.PointerType(c.getLLVMRuntimeType("_defer"), 0)} + fn := b.ir.GetFunction(callback.Fn.(*ssa.Function)) + valueTypes := []llvm.Type{b.uintptrType, llvm.PointerType(b.getLLVMRuntimeType("_defer"), 0)} params := fn.Signature.Params() for i := 0; i < params.Len(); i++ { - valueTypes = append(valueTypes, c.getLLVMType(params.At(i).Type())) + valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type())) } - valueTypes = append(valueTypes, c.i8ptrType) // closure - deferFrameType := c.ctx.StructType(valueTypes, false) - deferFramePtr := c.builder.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") + valueTypes = append(valueTypes, b.i8ptrType) // closure + deferFrameType := b.ctx.StructType(valueTypes, false) + deferFramePtr := b.CreateBitCast(deferData, llvm.PointerType(deferFrameType, 0), "deferFrame") // Extract the params from the struct. forwardParams := []llvm.Value{} - zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := 2; i < len(valueTypes); i++ { - gep := c.builder.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false)}, "") - forwardParam := c.builder.CreateLoad(gep, "param") + gep := b.CreateInBoundsGEP(deferFramePtr, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") + forwardParam := b.CreateLoad(gep, "param") forwardParams = append(forwardParams, forwardParam) } // Parent coroutine handle. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + forwardParams = append(forwardParams, llvm.Undef(b.i8ptrType)) // Call deferred function. - c.createCall(fn.LLVMFn, forwardParams, "") + b.createCall(fn.LLVMFn, forwardParams, "") default: panic("unknown deferred function type") } // Branch back to the start of the loop. - c.builder.CreateBr(loophead) + b.CreateBr(loophead) } // Create default unreachable block: // default: // unreachable // } - c.builder.SetInsertPointAtEnd(unreachable) - c.builder.CreateUnreachable() + b.SetInsertPointAtEnd(unreachable) + b.CreateUnreachable() // End of loop. - c.builder.SetInsertPointAtEnd(end) + b.SetInsertPointAtEnd(end) } diff --git a/compiler/gc.go b/compiler/gc.go index 0992c01d..bd8b964f 100644 --- a/compiler/gc.go +++ b/compiler/gc.go @@ -81,6 +81,15 @@ func (c *Compiler) trackPointer(value llvm.Value) { c.createRuntimeCall("trackPointer", []llvm.Value{value}, "") } +// trackPointer creates a call to runtime.trackPointer, bitcasting the poitner +// first if needed. The input value must be of LLVM pointer type. +func (b *builder) trackPointer(value llvm.Value) { + if value.Type() != b.i8ptrType { + value = b.CreateBitCast(value, b.i8ptrType, "") + } + b.createRuntimeCall("trackPointer", []llvm.Value{value}, "") +} + // typeHasPointers returns whether this type is a pointer or contains pointers. // If the type is an aggregate type, it will check whether there is a pointer // inside.