Introduction
This proposal describes the new op to be added to the std (and later moved memref)
dialect called alloca_scope.
Motivation
Alloca operations are easy to misuse, especially if one relies on it while doing
rewriting/conversion passes. For example let's consider a simple example of two
independent dialects, one defines an op that wants to allocate on-stack and
another defines a construct that corresponds to some form of looping:
dialect1.looping_op { %x = dialect2.stack_allocating_op }
Since the dialects might not know about each other they are going to define a
lowering to std/scf/etc independently:
scf.for … { %x_temp = std.alloca … … // do some domain-specific work using %x_temp buffer … // and store the result into %result %x = %result }
Later on the scf and std.alloca is going to be lowered to llvm using a
combination of llvm.alloca and unstructured control flow.
At this point the use of %x_temp is bound to either be either optimized by
llvm (for example using mem2reg) or in the worst case: perform an independent
stack allocation on each iteration of the loop. While the llvm optimizations are
likely to succeed they are not guaranteed to do so, and they provide
opportunities for surprising issues with unexpected use of stack size.
Proposal
We propose a new operation that defines a finer-grain allocation scope for the
alloca-allocated memory called alloca_scope:
alloca_scope { %x_temp = alloca … ... }
Here the lifetime of %x_temp is going to be bound to the narrow annotated
region within alloca_scope. Moreover, one can also return values out of the
alloca_scope with an accompanying alloca_scope.return op (that behaves
similarly to scf.yield):
%result = alloca_scope { %x_temp = alloca … … alloca_scope.return %myvalue }
Under the hood the alloca_scope is going to lowered to a combination of
llvm.intr.stacksave and llvm.intr.strackrestore that are going to be invoked
automatically as control-flow enters and leaves the body of the alloca_scope.
The key value of the new op is to allow deterministic guaranteed stack use
through an explicit annotation in the code which is finer-grain than the
function-level scope of AutomaticAllocationScope interface. alloca_scope
can be inserted at arbitrary locations and doesn’t require non-trivial
transformations such as outlining.
Which dialect
Before memref dialect is split, alloca_scope can temporarily reside in std
dialect, and later on be moved to memref together with the rest of
memory-related operations.
Implementation
An implementation of the op is available here.
Original commits:
- Add initial scaffolding for alloca_scope op
- Add alloca_scope.return op
- Add no region arguments and variadic results
- Add op descriptions
- Add failing test case
- Add another failing test
- Initial implementation of lowering for std.alloca_scope
- Fix backticks
- Fix getSuccessorRegions implementation
Here and below std prefix needs to be updated to memref.