diff --git a/compiler/calls.go b/compiler/calls.go index 05cba1c3..18dd74ae 100644 --- a/compiler/calls.go +++ b/compiler/calls.go @@ -5,32 +5,11 @@ import ( "golang.org/x/tools/go/ssa" ) -// This file implements the calling convention used by Go. -// The calling convention is like the C calling convention (or, whatever LLVM -// makes of it) with the following modifications: -// * Struct parameters are fully expanded to individual fields (recursively), -// when the number of fields (combined) is 3 or less. -// Examples: -// {i8*, i32} -> i8*, i32 -// {{i8*, i32}, i16} -> i8*, i32, i16 -// {{i64}} -> i64 -// {} -> -// {i8*, i32, i8, i8} -> {i8*, i32, i8, i8} -// Note that all native Go data types that don't exist in LLVM (string, -// slice, interface, fat function pointer) can be expanded this way, making -// the work of LLVM optimizers easier. -// * Closures have an extra context paramter appended at the end of the -// argument list. -// * Blocking functions have a coroutine pointer prepended to the argument -// list, see src/runtime/scheduler.go for details. +// For a description of the calling convention in prose, see docs/internals.rst +// or the online version of this document: +// https://tinygo.readthedocs.io/en/latest/internals.html#calling-convention // // Some further notes: -// * Function pointers are lowered to either a raw function pointer or a -// closure struct: { i8*, function pointer } -// The function pointer type depends on whether the exact same signature is -// used anywhere else in the program for a call that needs a context -// (closures, bound methods). If it isn't, it is lowered to a raw function -// pointer. // * Defer statements are implemented by transforming the function in the // following way: // * Creating an alloca in the entry block that contains a pointer diff --git a/docs/internals.rst b/docs/internals.rst index 836be2b2..e401465a 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -79,12 +79,12 @@ interface function pointer A function pointer has two representations: a literal function pointer and a - tuple of ``{context, function pointer}``. Which representation is chosen - depends on the AnalyseFunctionPointers pass in `ir/passes.go - `_: it tries to - use a raw function pointer but will use a function pointer with context if - there is a closure or bound method somewhere in the program with the exact - same signature. + fat function pointer in the form of ``{context, function pointer}``. Which + representation is chosen depends on the AnalyseFunctionPointers pass in + `ir/passes.go `_: + it tries to use a raw function pointer but will use a fat function pointer + if there is a closure or bound method somewhere in the program with the + exact same signature. goroutine A goroutine is a linked list of `LLVM coroutines @@ -101,6 +101,66 @@ goroutine consumption. +Calling convention +------------------ + +Go uses a stack-based calling convention and passes a pointer to the argument +list as the first argument in the function. There were/are `plans to switch to a +register-based calling convention `_ +but they're now on hold. + +.. highlight:: llvm + +TinyGo, however, uses a register based calling convention. In fact it is +somewhat compatible with the C calling convention but with a few quirks: + + * Struct parameters are split into separate arguments, if the number of fields + (after flattening recursively) is 3 or lower. This is similar to the `Swift + calling convention + `_. + In the case of TinyGo, the size of each field does not matter, a field can + even be an array. :: + + {i8*, i32} -> i8*, i32 + {{i8*, i32}, i16} -> i8*, i32, i16 + {{i64}} -> i64 + {} -> + {i8*, i32, i8, i8} -> {i8*, i32, i8, i8} + {{i8*, i32, i8}, i8} -> {i8*, i32, i8, i8} + + Note that all native Go data types that are lowered to aggregate types in + LLVM are expanded this way: ``string``, slices, interfaces, and fat function + pointers. This avoids some overhead in the C calling convention and makes + the work of the LLVM optimizers easier. + + * Some functions have an extra context parameter appended at the end of the + argument list. This only happens when both of these conditions hold: + + * The address of the function is taken, for example when passing the + function as function pointer to another function or storing it in a + global variable. + + * This function or another function somewhere in the compiled code has the + exact same signature and is used in a closure or bound method. Signature + matching is very strict: it is based on Go types including named types + and return types, although parameter names or the function name itself + are not included in the match. + + Whether a function needs this is determined by `FunctionNeedsContext + `_, + which bases itself on analysis done by AnalyseFunctionPointers. + + * Blocking functions have a coroutine pointer prepended to the argument list, + see `src/runtime/scheduler.go + `_ + for details. Whether a function is blocking is determined by the + AnalyseBlockingRecursive pass. + +This calling convention may change in the future. Changes will be documented +here. However, even though it may change, it is expected that function +signatures that only contain integers and pointers will remain stable. + + Pipeline --------