From deaf532d613b266f427a39e379a2e57daa1d4cab Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Tue, 14 Jul 2026 06:22:20 -0700 Subject: [PATCH] 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. --- compiler/compiler.go | 24 ++++++++++++++---------- compiler/goroutine.go | 24 +++++++++++++----------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index 90776b892..46a6e1f8f 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -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. var callee, context llvm.Value var calleeType llvm.Type + var invokeTypecode, invokeReceiver llvm.Value exported := false if fn := instr.StaticCallee(); fn != nil { calleeType, callee = b.getFunction(fn) @@ -2127,19 +2123,18 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) exported = info.exported } else if call, ok := instr.Value.(*ssa.Builtin); ok { // Builtin function (append, close, delete, etc.).) + var params []llvm.Value var argTypes []types.Type for _, arg := range instr.Args { argTypes = append(argTypes, arg.Type()) + params = append(params, b.getValue(arg, getPos(instr))) } return b.createBuiltin(argTypes, params, call.Name(), instr.Pos()) } else if instr.IsInvoke() { // Interface method call (aka invoke call). itf := b.getValue(instr.Value, getPos(instr)) // interface value (runtime._interface) - typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode") - value := b.CreateExtractValue(itf, 1, "invoke.func.value") // receiver - // Prefix the params with receiver value and suffix with typecode. - params = append([]llvm.Value{value}, params...) - params = append(params, typecode) + invokeTypecode = b.CreateExtractValue(itf, 0, "invoke.func.typecode") + invokeReceiver = b.CreateExtractValue(itf, 1, "invoke.func.value") callee = b.getInvokeFunction(instr) calleeType = callee.GlobalValueType() 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") } + 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 { // This function takes a context parameter. // Add it to the end of the parameter list. diff --git a/compiler/goroutine.go b/compiler/goroutine.go index 3d2dac49b..741d79afb 100644 --- a/compiler/goroutine.go +++ b/compiler/goroutine.go @@ -47,20 +47,15 @@ func (b *builder) createGo(instr *ssa.Go) { return } - // Get all function parameters to pass to the goroutine. var params []llvm.Value - for _, param := range instr.Call.Args { - params = append(params, b.getGoroutineCallArgument(param)...) - } - var prefix string var funcPtr llvm.Value var funcType llvm.Type + var context llvm.Value hasContext := false if callee := instr.Call.StaticCallee(); callee != nil { // Static callee is known. This makes it easier to start a new // goroutine. - var context llvm.Value switch value := instr.Call.Value.(type) { case *ssa.Function: // 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") } if !context.IsNil() { - params = append(params, context) // context parameter hasContext = true } funcType, funcPtr = b.getFunction(callee) @@ -84,22 +78,30 @@ func (b *builder) createGo(instr *ssa.Go) { itfValue := b.CreateExtractValue(itf, 1, "") funcPtr = b.getInvokeFunction(&instr.Call) funcType = funcPtr.GlobalValueType() - params = append([]llvm.Value{itfValue}, params...) // start with receiver - params = append(params, itfTypeCode) // end with typecode + params = append(params, itfValue) + context = itfTypeCode } else { // This is a function pointer. // At the moment, two extra params are passed to the newly started // goroutine: // * The function context, for closures. // * The function pointer (for tasks). - var context llvm.Value funcPtr, context = b.decodeFuncValue(b.getValue(instr.Call.Value, getPos(instr))) funcType = b.getLLVMFunctionType(instr.Call.Value.Type().Underlying().(*types.Signature)) - params = append(params, context, funcPtr) hasContext = true 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()) var stackSize llvm.Value callee := b.createGoroutineStartWrapper(funcType, funcPtr, prefix, hasContext, false, instr.Pos())