mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 07:23:39 +00:00
compiler: avoid function pointers in defer calls
Implement defer in a different way, which results in smaller binaries. The binary produced from testdata/calls.go (the only test case with defer) is reduced a bit in size, but the savings in bytes greatly vary by architecture: Cortex-M0: -96 .text / flash WebAssembly: -215 entire file Linux x64: -32 .text Deferred functions in TinyGo were implemented by creating a linked list of struct objects that contain a function pointer to a thunk, a pointer to the next object, and a list of parameters. When it was time to run deferred functions, a helper runtime function called each function pointer (the thunk) with the struct pointer as a parameter. This thunk would then in turn extract the saved function parameter from the struct and call the real function. What this commit changes, is that the loop to call deferred functions is moved into the end of the function (practically inlining it) and replacing the thunks with direct calls inside this loop. This makes it much easier for LLVM to perform all kinds of optimizations like inlining and dead argument elimination.
This commit is contained in:
+16
-21
@@ -62,26 +62,27 @@ type Compiler struct {
|
||||
coroEndFunc llvm.Value
|
||||
coroFreeFunc llvm.Value
|
||||
initFuncs []llvm.Value
|
||||
deferFuncs []*ir.Function
|
||||
deferInvokeFuncs []InvokeDeferFunction
|
||||
ctxDeferFuncs []ContextDeferFunction
|
||||
interfaceInvokeWrappers []interfaceInvokeWrapper
|
||||
ir *ir.Program
|
||||
}
|
||||
|
||||
type Frame struct {
|
||||
fn *ir.Function
|
||||
locals map[ssa.Value]llvm.Value // local variables
|
||||
blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
|
||||
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
|
||||
currentBlock *ssa.BasicBlock
|
||||
phis []Phi
|
||||
blocking bool
|
||||
taskHandle llvm.Value
|
||||
cleanupBlock llvm.BasicBlock
|
||||
suspendBlock llvm.BasicBlock
|
||||
deferPtr llvm.Value
|
||||
difunc llvm.Metadata
|
||||
fn *ir.Function
|
||||
locals map[ssa.Value]llvm.Value // local variables
|
||||
blockEntries map[*ssa.BasicBlock]llvm.BasicBlock // a *ssa.BasicBlock may be split up
|
||||
blockExits map[*ssa.BasicBlock]llvm.BasicBlock // these are the exit blocks
|
||||
currentBlock *ssa.BasicBlock
|
||||
phis []Phi
|
||||
blocking bool
|
||||
taskHandle llvm.Value
|
||||
cleanupBlock llvm.BasicBlock
|
||||
suspendBlock llvm.BasicBlock
|
||||
deferPtr llvm.Value
|
||||
difunc llvm.Metadata
|
||||
allDeferFuncs []interface{}
|
||||
deferFuncs map[*ir.Function]int
|
||||
deferInvokeFuncs map[string]int
|
||||
deferClosureFuncs map[*ir.Function]int
|
||||
}
|
||||
|
||||
type Phi struct {
|
||||
@@ -342,12 +343,6 @@ func (c *Compiler) Compile(mainPath string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Create thunks for deferred functions.
|
||||
err = c.finalizeDefers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Define the already declared functions that wrap methods for use in
|
||||
// interfaces.
|
||||
for _, state := range c.interfaceInvokeWrappers {
|
||||
|
||||
Reference in New Issue
Block a user