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.
This commit is contained in:
Jake Bailey
2026-07-14 06:22:02 -07:00
committed by Damian Gryski
parent 3071e339cd
commit aa914bea5c
+59 -65
View File
@@ -359,6 +359,44 @@ type tarjanNode struct {
cyclic bool 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 // createDefer emits a single defer instruction, to be run when this function
// returns. // returns.
func (b *builder) createDefer(instr *ssa.Defer) { func (b *builder) createDefer(instr *ssa.Defer) {
@@ -366,8 +404,10 @@ func (b *builder) createDefer(instr *ssa.Defer) {
// make a linked list. // make a linked list.
next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next") next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
var values []llvm.Value var values llvmValueList
valueTypes := []llvm.Type{b.uintptrType, next.Type()} lowerArgument := func(value ssa.Value) llvm.Value {
return b.getValue(value, getPos(instr))
}
if instr.Call.IsInvoke() { if instr.Call.IsInvoke() {
// Method call on an interface. // 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 itf := b.getValue(instr.Call.Value, getPos(instr)) // interface
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode") typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver") receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
values = []llvm.Value{callback, next, typecode, receiverValue} values = newLLVMValueList(callback, next, typecode, receiverValue)
valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType) values.appendSSAValues(instr.Call.Args, lowerArgument)
for _, arg := range instr.Call.Args {
val := b.getValue(arg, getPos(instr))
values = append(values, val)
valueTypes = append(valueTypes, val.Type())
}
} else if callee, ok := instr.Call.Value.(*ssa.Function); ok { } else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
// Regular function call. // 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 // Collect all values to be put in the struct (starting with
// runtime._defer fields). // runtime._defer fields).
values = []llvm.Value{callback, next} values = newLLVMValueList(callback, next)
for _, param := range instr.Call.Args { values.appendSSAValues(instr.Call.Args, lowerArgument)
llvmParam := b.getValue(param, getPos(instr))
values = append(values, llvmParam)
valueTypes = append(valueTypes, llvmParam.Type())
}
} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok { } else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
// Immediately applied function literal with free variables. // 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 // Collect all values to be put in the struct (starting with
// runtime._defer fields, followed by all parameters including the // runtime._defer fields, followed by all parameters including the
// context pointer). // context pointer).
values = []llvm.Value{callback, next} values = newLLVMValueList(callback, next)
for _, param := range instr.Call.Args { values.appendSSAValues(instr.Call.Args, lowerArgument)
llvmParam := b.getValue(param, getPos(instr)) values.append(context)
values = append(values, llvmParam)
valueTypes = append(valueTypes, llvmParam.Type())
}
values = append(values, context)
valueTypes = append(valueTypes, context.Type())
} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { } else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
var argTypes []types.Type 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 // Collect all values to be put in the struct (starting with
// runtime._defer fields). // runtime._defer fields).
values = []llvm.Value{callback, next} values = newLLVMValueList(callback, next)
for _, param := range argValues { values.append(argValues...)
values = append(values, param)
valueTypes = append(valueTypes, param.Type())
}
} else { } else {
funcValue := b.getValue(instr.Call.Value, getPos(instr)) 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 // Collect all values to be put in the struct (starting with
// runtime._defer fields, followed by all parameters including the // runtime._defer fields, followed by all parameters including the
// context pointer). // context pointer).
values = []llvm.Value{callback, next, funcValue} values = newLLVMValueList(callback, next, funcValue)
valueTypes = append(valueTypes, funcValue.Type()) values.appendSSAValues(instr.Call.Args, lowerArgument)
for _, param := range instr.Call.Args {
llvmParam := b.getValue(param, getPos(instr))
values = append(values, llvmParam)
valueTypes = append(valueTypes, llvmParam.Type())
}
} }
// Make a struct out of the collected values to put in the deferred call // Make a struct out of the collected values to put in the deferred call
// struct. // struct.
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(values.types, false)
deferredCall := llvm.ConstNull(deferredCallType) deferredCall := llvm.ConstNull(deferredCallType)
for i, value := range values { for i, value := range values.values {
deferredCall = b.CreateInsertValue(deferredCall, value, i, "") deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
} }
@@ -599,14 +617,8 @@ func (b *builder) createRunDefers() {
} }
// Extract the params from the struct (including receiver). // 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) deferredCallType := b.ctx.StructType(valueTypes, false)
for i := 2; i < len(valueTypes); i++ { forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
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)
}
var fnPtr llvm.Value var fnPtr llvm.Value
var fnType llvm.Type var fnType llvm.Type
@@ -649,13 +661,7 @@ func (b *builder) createRunDefers() {
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(valueTypes, false)
// Extract the params from the struct. // Extract the params from the struct.
forwardParams := []llvm.Value{} forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
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)
}
// Plain TinyGo functions add some extra parameters to implement async functionality and function receivers. // 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. // 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) deferredCallType := b.ctx.StructType(valueTypes, false)
// Extract the params from the struct. // Extract the params from the struct.
forwardParams := []llvm.Value{} forwardParams := b.loadDeferredCallParams(deferredCallType, deferData)
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)
}
// Call deferred function. // Call deferred function.
fnType, llvmFn := b.getFunction(fn) fnType, llvmFn := b.getFunction(fn)
@@ -707,13 +707,7 @@ func (b *builder) createRunDefers() {
deferredCallType := b.ctx.StructType(valueTypes, false) deferredCallType := b.ctx.StructType(valueTypes, false)
// Extract the params from the struct. // Extract the params from the struct.
var argValues []llvm.Value argValues := b.loadDeferredCallParams(deferredCallType, deferData)
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)
}
_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos) _, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
if err != nil { if err != nil {