compiler: resolve callees before lowering arguments

createFunctionCall and createGo currently lower arguments before they
determine whether the call is direct, an interface invoke, or through a
function value.

Resolve the callee, function type, and context first, then append the
arguments in the same order as before. This does not change the
generated LLVM IR.
This commit is contained in:
Jake Bailey
2026-07-14 06:22:20 -07:00
committed by Damian Gryski
parent 5ec2632461
commit deaf532d61
2 changed files with 27 additions and 21 deletions
+14 -10
View File
@@ -2090,14 +2090,10 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
} }
} }
var params []llvm.Value
for _, param := range instr.Args {
params = append(params, b.getValue(param, getPos(instr)))
}
// Try to call the function directly for trivially static calls. // Try to call the function directly for trivially static calls.
var callee, context llvm.Value var callee, context llvm.Value
var calleeType llvm.Type var calleeType llvm.Type
var invokeTypecode, invokeReceiver llvm.Value
exported := false exported := false
if fn := instr.StaticCallee(); fn != nil { if fn := instr.StaticCallee(); fn != nil {
calleeType, callee = b.getFunction(fn) calleeType, callee = b.getFunction(fn)
@@ -2127,19 +2123,18 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
exported = info.exported exported = info.exported
} else if call, ok := instr.Value.(*ssa.Builtin); ok { } else if call, ok := instr.Value.(*ssa.Builtin); ok {
// Builtin function (append, close, delete, etc.).) // Builtin function (append, close, delete, etc.).)
var params []llvm.Value
var argTypes []types.Type var argTypes []types.Type
for _, arg := range instr.Args { for _, arg := range instr.Args {
argTypes = append(argTypes, arg.Type()) argTypes = append(argTypes, arg.Type())
params = append(params, b.getValue(arg, getPos(instr)))
} }
return b.createBuiltin(argTypes, params, call.Name(), instr.Pos()) return b.createBuiltin(argTypes, params, call.Name(), instr.Pos())
} else if instr.IsInvoke() { } else if instr.IsInvoke() {
// Interface method call (aka invoke call). // Interface method call (aka invoke call).
itf := b.getValue(instr.Value, getPos(instr)) // interface value (runtime._interface) itf := b.getValue(instr.Value, getPos(instr)) // interface value (runtime._interface)
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode") invokeTypecode = b.CreateExtractValue(itf, 0, "invoke.func.typecode")
value := b.CreateExtractValue(itf, 1, "invoke.func.value") // receiver invokeReceiver = b.CreateExtractValue(itf, 1, "invoke.func.value")
// Prefix the params with receiver value and suffix with typecode.
params = append([]llvm.Value{value}, params...)
params = append(params, typecode)
callee = b.getInvokeFunction(instr) callee = b.getInvokeFunction(instr)
calleeType = callee.GlobalValueType() calleeType = callee.GlobalValueType()
context = llvm.Undef(b.dataPtrType) context = llvm.Undef(b.dataPtrType)
@@ -2153,6 +2148,15 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
b.createNilCheck(instr.Value, callee, "fpcall") b.createNilCheck(instr.Value, callee, "fpcall")
} }
var params []llvm.Value
for _, param := range instr.Args {
params = append(params, b.getValue(param, getPos(instr)))
}
if instr.IsInvoke() {
params = append([]llvm.Value{invokeReceiver}, params...)
params = append(params, invokeTypecode)
}
if !exported { if !exported {
// This function takes a context parameter. // This function takes a context parameter.
// Add it to the end of the parameter list. // Add it to the end of the parameter list.
+13 -11
View File
@@ -47,20 +47,15 @@ func (b *builder) createGo(instr *ssa.Go) {
return return
} }
// Get all function parameters to pass to the goroutine.
var params []llvm.Value var params []llvm.Value
for _, param := range instr.Call.Args {
params = append(params, b.getGoroutineCallArgument(param)...)
}
var prefix string var prefix string
var funcPtr llvm.Value var funcPtr llvm.Value
var funcType llvm.Type var funcType llvm.Type
var context llvm.Value
hasContext := false hasContext := false
if callee := instr.Call.StaticCallee(); callee != nil { if callee := instr.Call.StaticCallee(); callee != nil {
// Static callee is known. This makes it easier to start a new // Static callee is known. This makes it easier to start a new
// goroutine. // goroutine.
var context llvm.Value
switch value := instr.Call.Value.(type) { switch value := instr.Call.Value.(type) {
case *ssa.Function: case *ssa.Function:
// Goroutine call is regular function call. No context is necessary. // Goroutine call is regular function call. No context is necessary.
@@ -73,7 +68,6 @@ func (b *builder) createGo(instr *ssa.Go) {
panic("StaticCallee returned an unexpected value") panic("StaticCallee returned an unexpected value")
} }
if !context.IsNil() { if !context.IsNil() {
params = append(params, context) // context parameter
hasContext = true hasContext = true
} }
funcType, funcPtr = b.getFunction(callee) funcType, funcPtr = b.getFunction(callee)
@@ -84,22 +78,30 @@ func (b *builder) createGo(instr *ssa.Go) {
itfValue := b.CreateExtractValue(itf, 1, "") itfValue := b.CreateExtractValue(itf, 1, "")
funcPtr = b.getInvokeFunction(&instr.Call) funcPtr = b.getInvokeFunction(&instr.Call)
funcType = funcPtr.GlobalValueType() funcType = funcPtr.GlobalValueType()
params = append([]llvm.Value{itfValue}, params...) // start with receiver params = append(params, itfValue)
params = append(params, itfTypeCode) // end with typecode context = itfTypeCode
} else { } else {
// This is a function pointer. // This is a function pointer.
// At the moment, two extra params are passed to the newly started // At the moment, two extra params are passed to the newly started
// goroutine: // goroutine:
// * The function context, for closures. // * The function context, for closures.
// * The function pointer (for tasks). // * The function pointer (for tasks).
var context llvm.Value
funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr))) funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr)))
funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature)) funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature))
params = append(params, context, funcPtr)
hasContext = true hasContext = true
prefix = b.getFunctionInfo(b.fn).linkName prefix = b.getFunctionInfo(b.fn).linkName
} }
for _, param := range instr.Call.Args {
params = append(params, b.getGoroutineCallArgument(param)...)
}
if !context.IsNil() {
params = append(params, context)
}
if hasContext && instr.Call.StaticCallee() == nil {
params = append(params, funcPtr)
}
paramBundle := b.emitPointerPack(params, instr.Pos()) paramBundle := b.emitPointerPack(params, instr.Pos())
var stackSize llvm.Value var stackSize llvm.Value
callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, instr.Pos()) callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, instr.Pos())