|
|
@ -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) |
|
|
|
} |
|
|
|