| # Design and Usage of the InAlloca Attribute |
| |
| ## Introduction |
| |
| The {ref}`inalloca <attr_inalloca>` attribute is designed to allow |
| taking the address of an aggregate argument that is being passed by |
| value through memory. Primarily, this feature is required for |
| compatibility with the Microsoft C++ ABI. Under that ABI, class |
| instances that are passed by value are constructed directly into |
| argument stack memory. Prior to the addition of inalloca, calls in LLVM |
| were indivisible instructions. There was no way to perform intermediate |
| work, such as object construction, between the first stack adjustment |
| and the final control transfer. With inalloca, all arguments passed in |
| memory are modelled as a single alloca, which can be stored to prior to |
| the call. Unfortunately, this complicated feature comes with a large |
| set of restrictions designed to bound the lifetime of the argument |
| memory around the call. |
| |
| For now, it is recommended that frontends and optimizers avoid producing |
| this construct, primarily because it forces the use of a base pointer. |
| This feature may grow in the future to allow general mid-level |
| optimization, but for now, it should be regarded as less efficient than |
| passing by value with a copy. |
| |
| ## Intended Usage |
| |
| The example below is the intended LLVM IR lowering for some C++ code |
| that passes two default-constructed `Foo` objects to `g` in the |
| 32-bit Microsoft C++ ABI. |
| |
| ```c++ |
| // Foo is non-trivial. |
| struct Foo { int a, b; Foo(); ~Foo(); Foo(const Foo &); }; |
| void g(Foo a, Foo b); |
| void f() { |
| g(Foo(), Foo()); |
| } |
| ``` |
| |
| ```text |
| %struct.Foo = type { i32, i32 } |
| declare void @Foo_ctor(%struct.Foo* %this) |
| declare void @Foo_dtor(%struct.Foo* %this) |
| declare void @g(<{ %struct.Foo, %struct.Foo }>* inalloca %memargs) |
| |
| define void @f() { |
| entry: |
| %base = call i8* @llvm.stacksave() |
| %memargs = alloca <{ %struct.Foo, %struct.Foo }> |
| %b = getelementptr <{ %struct.Foo, %struct.Foo }>* %memargs, i32 1 |
| call void @Foo_ctor(%struct.Foo* %b) |
| |
| ; If a's ctor throws, we must destruct b. |
| %a = getelementptr <{ %struct.Foo, %struct.Foo }>* %memargs, i32 0 |
| invoke void @Foo_ctor(%struct.Foo* %a) |
| to label %invoke.cont unwind %invoke.unwind |
| |
| invoke.cont: |
| call void @g(<{ %struct.Foo, %struct.Foo }>* inalloca %memargs) |
| call void @llvm.stackrestore(i8* %base) |
| ... |
| |
| invoke.unwind: |
| call void @Foo_dtor(%struct.Foo* %b) |
| call void @llvm.stackrestore(i8* %base) |
| ... |
| } |
| ``` |
| |
| To avoid stack leaks, the frontend saves the current stack pointer with |
| a call to {ref}`llvm.stacksave <int_stacksave>`. Then, it allocates the |
| argument stack space with alloca and calls the default constructor. The |
| default constructor could throw an exception, so the frontend has to |
| create a landing pad. The frontend has to destroy the already |
| constructed argument `b` before restoring the stack pointer. If the |
| constructor does not unwind, `g` is called. In the Microsoft C++ ABI, |
| `g` will destroy its arguments, and then the stack is restored in |
| `f`. |
| |
| ## Design Considerations |
| |
| ### Lifetime |
| |
| The biggest design consideration for this feature is object lifetime. |
| We cannot model the arguments as static allocas in the entry block, |
| because all calls need to use the memory at the top of the stack to pass |
| arguments. We cannot vend pointers to that memory at function entry |
| because after code generation they will alias. |
| |
| The rule against allocas between argument allocations and the call site |
| avoids this problem, but it creates a cleanup problem. Cleanup and |
| lifetime is handled explicitly with stack save and restore calls. In |
| the future, we may want to introduce a new construct such as `freea` |
| or `afree` to make it clear that this stack adjusting cleanup is less |
| powerful than a full stack save and restore. |
| |
| ### Nested Calls and Copy Elision |
| |
| We also want to be able to support copy elision into these argument |
| slots. This means we have to support multiple live argument |
| allocations. |
| |
| Consider the evaluation of: |
| |
| ```c++ |
| // Foo is non-trivial. |
| struct Foo { int a; Foo(); Foo(const &Foo); ~Foo(); }; |
| Foo bar(Foo b); |
| int main() { |
| bar(bar(Foo())); |
| } |
| ``` |
| |
| In this case, we want to be able to elide copies into `bar`'s argument |
| slots. That means we need to have more than one set of argument frames |
| active at the same time. First, we need to allocate the frame for the |
| outer call so we can pass it in as the hidden struct return pointer to |
| the middle call. Then we do the same for the middle call, allocating a |
| frame and passing its address to `Foo`'s default constructor. By |
| wrapping the evaluation of the inner `bar` with stack save and |
| restore, we can have multiple overlapping active call frames. |
| |
| ### Callee-cleanup Calling Conventions |
| |
| Another wrinkle is the existence of callee-cleanup conventions. On |
| Windows, all methods and many other functions adjust the stack to clear |
| the memory used to pass their arguments. In some sense, this means that |
| the allocas are automatically cleared by the call. However, LLVM |
| instead models this as a write of undef to all of the inalloca values |
| passed to the call instead of a stack adjustment. Frontends should |
| still restore the stack pointer to avoid a stack leak. |
| |
| ### Exceptions |
| |
| There is also the possibility of an exception. If argument evaluation |
| or copy construction throws an exception, the landing pad must do |
| cleanup, which includes adjusting the stack pointer to avoid a stack |
| leak. This means the cleanup of the stack memory cannot be tied to the |
| call itself. There needs to be a separate IR-level instruction that can |
| perform independent cleanup of arguments. |
| |
| ### Efficiency |
| |
| Eventually, it should be possible to generate efficient code for this |
| construct. In particular, using inalloca should not require a base |
| pointer. If the backend can prove that all points in the CFG only have |
| one possible stack level, then it can address the stack directly from |
| the stack pointer. While this is not yet implemented, the plan is that |
| the inalloca attribute should not change much, but the frontend IR |
| generation recommendations may change. |
| |