diff --git a/compiler/defer.go b/compiler/defer.go index e58002798..f38e053bd 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -359,6 +359,44 @@ type tarjanNode struct { cyclic bool } +type llvmValueList struct { + values []llvm.Value + types []llvm.Type +} + +func newLLVMValueList(values ...llvm.Value) llvmValueList { + var list llvmValueList + list.append(values...) + return list +} + +func (l *llvmValueList) append(values ...llvm.Value) { + for _, value := range values { + l.values = append(l.values, value) + l.types = append(l.types, value.Type()) + } +} + +func (l *llvmValueList) appendSSAValues(values []ssa.Value, lower func(ssa.Value) llvm.Value) { + for _, value := range values { + l.append(lower(value)) + } +} + +func (b *builder) loadDeferredCallParams(structType llvm.Type, ptr llvm.Value) []llvm.Value { + fieldTypes := structType.StructElementTypes() + values := make([]llvm.Value, 0, len(fieldTypes)-2) + zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) + for i := 2; i < len(fieldTypes); i++ { + fieldPtr := b.CreateInBoundsGEP(structType, ptr, []llvm.Value{ + zero, + llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false), + }, "gep") + values = append(values, b.CreateLoad(fieldTypes[i], fieldPtr, "param")) + } + return values +} + // createDefer emits a single defer instruction, to be run when this function // returns. func (b *builder) createDefer(instr *ssa.Defer) { @@ -366,8 +404,10 @@ func (b *builder) createDefer(instr *ssa.Defer) { // make a linked list. next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next") - var values []llvm.Value - valueTypes := []llvm.Type{b.uintptrType, next.Type()} + var values llvmValueList + lowerArgument := func(value ssa.Value) llvm.Value { + return b.getValue(value, getPos(instr)) + } if instr.Call.IsInvoke() { // Method call on an interface. @@ -384,13 +424,8 @@ func (b *builder) createDefer(instr *ssa.Defer) { itf := b.getValue(instr.Call.Value, getPos(instr)) // interface typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode") receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver") - values = []llvm.Value{callback, next, typecode, receiverValue} - valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType) - for _, arg := range instr.Call.Args { - val := b.getValue(arg, getPos(instr)) - values = append(values, val) - valueTypes = append(valueTypes, val.Type()) - } + values = newLLVMValueList(callback, next, typecode, receiverValue) + values.appendSSAValues(instr.Call.Args, lowerArgument) } else if callee, ok := instr.Call.Value.(*ssa.Function); ok { // Regular function call. @@ -402,12 +437,8 @@ func (b *builder) createDefer(instr *ssa.Defer) { // 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 := b.getValue(param, getPos(instr)) - values = append(values, llvmParam) - valueTypes = append(valueTypes, llvmParam.Type()) - } + values = newLLVMValueList(callback, next) + values.appendSSAValues(instr.Call.Args, lowerArgument) } else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok { // Immediately applied function literal with free variables. @@ -430,14 +461,9 @@ func (b *builder) createDefer(instr *ssa.Defer) { // 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 := b.getValue(param, getPos(instr)) - values = append(values, llvmParam) - valueTypes = append(valueTypes, llvmParam.Type()) - } - values = append(values, context) - valueTypes = append(valueTypes, context.Type()) + values = newLLVMValueList(callback, next) + values.appendSSAValues(instr.Call.Args, lowerArgument) + values.append(context) } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { var argTypes []types.Type @@ -460,11 +486,8 @@ func (b *builder) createDefer(instr *ssa.Defer) { // Collect all values to be put in the struct (starting with // runtime._defer fields). - values = []llvm.Value{callback, next} - for _, param := range argValues { - values = append(values, param) - valueTypes = append(valueTypes, param.Type()) - } + values = newLLVMValueList(callback, next) + values.append(argValues...) } else { funcValue := b.getValue(instr.Call.Value, getPos(instr)) @@ -479,20 +502,15 @@ func (b *builder) createDefer(instr *ssa.Defer) { // 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, funcValue} - valueTypes = append(valueTypes, funcValue.Type()) - for _, param := range instr.Call.Args { - llvmParam := b.getValue(param, getPos(instr)) - values = append(values, llvmParam) - valueTypes = append(valueTypes, llvmParam.Type()) - } + values = newLLVMValueList(callback, next, funcValue) + values.appendSSAValues(instr.Call.Args, lowerArgument) } // Make a struct out of the collected values to put in the deferred call // struct. - deferredCallType := b.ctx.StructType(valueTypes, false) + deferredCallType := b.ctx.StructType(values.types, false) deferredCall := llvm.ConstNull(deferredCallType) - for i, value := range values { + for i, value := range values.values { deferredCall = b.CreateInsertValue(deferredCall, value, i, "") } @@ -599,14 +617,8 @@ func (b *builder) createRunDefers() { } // Extract the params from the struct (including receiver). - forwardParams := []llvm.Value{} - zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) deferredCallType := b.ctx.StructType(valueTypes, false) - for i := 2; i < len(valueTypes); i++ { - gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep") - forwardParam := b.CreateLoad(valueTypes[i], gep, "param") - forwardParams = append(forwardParams, forwardParam) - } + forwardParams := b.loadDeferredCallParams(deferredCallType, deferData) var fnPtr llvm.Value var fnType llvm.Type @@ -649,13 +661,7 @@ func (b *builder) createRunDefers() { deferredCallType := b.ctx.StructType(valueTypes, false) // Extract the params from the struct. - forwardParams := []llvm.Value{} - zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) - for i := range getParams(callback.Signature) { - gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") - forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param") - forwardParams = append(forwardParams, forwardParam) - } + forwardParams := b.loadDeferredCallParams(deferredCallType, deferData) // Plain TinyGo functions add some extra parameters to implement async functionality and function receivers. // These parameters should not be supplied when calling into an external C/ASM function. @@ -681,13 +687,7 @@ func (b *builder) createRunDefers() { deferredCallType := b.ctx.StructType(valueTypes, false) // Extract the params from the struct. - forwardParams := []llvm.Value{} - zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) - for i := 2; i < len(valueTypes); i++ { - gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") - forwardParam := b.CreateLoad(valueTypes[i], gep, "param") - forwardParams = append(forwardParams, forwardParam) - } + forwardParams := b.loadDeferredCallParams(deferredCallType, deferData) // Call deferred function. fnType, llvmFn := b.getFunction(fn) @@ -707,13 +707,7 @@ func (b *builder) createRunDefers() { deferredCallType := b.ctx.StructType(valueTypes, false) // Extract the params from the struct. - var argValues []llvm.Value - zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) - for i := 0; i < params.Len(); i++ { - gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") - forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param") - argValues = append(argValues, forwardParam) - } + argValues := b.loadDeferredCallParams(deferredCallType, deferData) _, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos) if err != nil {