From aa914bea5cbc4c7f5e5b1f4a3c50b4b39fae98a2 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 14 Jul 2026 06:22:02 -0700 Subject: [PATCH] compiler: centralize deferred call record types createDefer builds an LLVM struct containing the deferred function and its arguments. createRunDefers separately reconstructs the same struct type before loading the fields. Keep each LLVM value together with its type while building a deferred call record, and load all argument fields through one helper. This removes the duplicate lists of field types and field loads. --- compiler/defer.go | 124 ++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 65 deletions(-) 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 {