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
}
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 {