From 5fabc203dbf5d6e57c1adef9552f1eb23f2c7e1f Mon Sep 17 00:00:00 2001 From: Konstantin Sharlaimov Date: Sun, 10 May 2026 21:21:59 +0200 Subject: [PATCH] builder: increase stack size margin for automatic stack allocation Adjust the stack size margin to account for the tinygo_swapTask overhead. --- builder/build.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/builder/build.go b/builder/build.go index 4987fdddc..5fe1429b8 100644 --- a/builder/build.go +++ b/builder/build.go @@ -1352,6 +1352,14 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri } baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize() + // Account for the bytes that tinygo_swapTask pushes onto the goroutine stack + // on every context switch. The static analysis correctly traces Go calls, + // but it cannot see into the assembly-level register push. + var contextSwitchOverhead uint64 + if swapFuncs, ok := functions["tinygo_swapTask"]; ok && len(swapFuncs) == 1 { + contextSwitchOverhead = swapFuncs[0].FrameSize + } + sizes := make(map[string]functionStackSize) // Add the reset handler function, for convenience. The reset handler runs @@ -1400,6 +1408,12 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri // overflow will occur even before the goroutine is started. stackSize = baseStackSize } + if stackSizeType == stacksize.Bounded { + // Add the overhead of context switching. This is needed because the + // context switch (tinygo_swapTask) pushes callee-saved registers + // onto the current stack, which is not seen by the static analysis. + stackSize += contextSwitchOverhead + } sizes[name] = functionStackSize{ stackSize: stackSize, stackSizeType: stackSizeType,