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:
Ayke van Laethem
2018-12-09 16:14:47 +01:00
parent e42289ce61
commit 3fec22e819
3 changed files with 240 additions and 237 deletions
+3 -23
View File
@@ -1,29 +1,9 @@
package runtime
// Defer statements are implemented by transforming the function in the
// following way:
// * Creating an alloca in the entry block that contains a pointer (initially
// null) to the linked list of defer frames.
// * Every time a defer statement is executed, a new defer frame is created
// using alloca with a pointer to the previous defer frame, and the head
// pointer in the entry block is replaced with a pointer to this defer
// frame.
// * On return, runtime.rundefers is called which calls all deferred functions
// from the head of the linked list until it has gone through all defer
// frames.
import "unsafe"
type deferContext unsafe.Pointer
// Some helper types for the defer statement.
// See compiler/defer.go for details.
type _defer struct {
callback func(*_defer)
callback uintptr // callback number
next *_defer
}
func rundefers(stack *_defer) {
for stack != nil {
stack.callback(stack)
stack = stack.next
}
}