In order to extend shrink-wrapping we need to make some changes to the interface.
When trying to extend shrink-wrapping I came across the following limitations in terms of interface:
- We cannot have multiple save points.
- We cannot have multiple restore points.
- We cannot choose which registers are being saved / restored for each block.
- We cannot separate the Prologue / Epilogue emission from the callee saved register storing / loading.
- We cannot reuse the same algorithm to shrink-wrap anything else than CSRs and stack usage.
The goal here would be to provide an interface that would allow us to develop shrink-wrapping extensions in-tree, and being able to switch between implementations without actually having to handle both interfaces in PEI.
In order to be able to simply re-use this, we need to know which basic blocks use which element (as an input), and which basic blocks are the best suited for saving / restoring each element (as an output).
For that, the interface is split in two parts:
ShrinkWrapInfo: it is an interface that determines the "uses" to be used by the shrink-wrapping algorithm.
Example of uses:
- For shrink-wrapping CSR save / restore points: uses/defs of CSRs
- For shrink-wrapping stack setup code: uses/defs of SP, FP, FrameIndex operands
In order to have individual shrink-wrapping for each element, we map every different element to an index and store it in a BitVector. If there is an use of the element in a basic block, the bit at the index of the element is set. The interface provides uses per basic block:
const BitVector *getUses(unsigned MBBNum) const
In order to choose what a "use" is, this class has to be sub-classed and it has to compute the uses per basic block that are consumed by getUses().
- Example 1: Callee saved registers:
0: elements: r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 BitVector index: 0 1 2 3 4 5 6 7 8 9 BitVector values: 1 0 0 0 0 0 0 0 0 1 This means that r0 and r9 are used in %bb.0.
- Example 2: Stack + CSRs:
0: elements: Stack r0 r1 BitVector index: 0 1 2 BitVector values: 1 0 1 4: elements: Stack r0 r1 BitVector index: 0 1 2 BitVector values: 0 1 0
This means that r0 is used in %bb.4 and the Stack and r0 are used in %bb.0.
Providing this as an input to the shrink-wrapping algorithm allows the algorithm to be independent of the meaning of the input.
ShrinkWrapper: it is an interface for the shrink-wrapping algorithm. It makes it easy to switch between the current one (dominator-based) and other ones like Fred Chow's. This interface provides a result in the following format:
- DenseMap<unsigned, BitVector> Saves, Restores.
- It maps a MachineBasicBlock number to a BitVector containing the elements that need to be saved / restored.
- Example 1: Saves:
0: elements: r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 BitVector index: 0 1 2 3 4 5 6 7 8 9 BitVector values: 1 0 0 0 0 0 0 0 0 1
This means that r0 and r9 need to be saved in %bb.0.
- Example 2: Saves:
0: elements: Stack r0 r1 BitVector index: 0 1 2 BitVector values: 1 0 1 4: elements: Stack r0 r1 BitVector index: 0 1 2 BitVector values: 0 1 0
This means that we have to set up the stack and save r1 in %bb.0, and save r0 in %bb.4.
In order to implement an algorithm, this class has to be sub-classed and compute the results of Saves and Restores in the constructor.
The current dominator-based algorithm port to this interface can allow us to experiment with shrink-wrapping registers and stack uses individually, through the ShrinkWrapInfo class.
This class is meant to be used from PEI to collect better save / restore points, but for now I'll keep it in the ShrinkWrap pass to simplify the review of the patch and focus on the design of the interface.
For this review, I recommend to start by reviewing ShrinkWrapper.h and the ShrinkWrapper / ShrinkWrapInfo abstract classes. The DominatorShrinkWrapper is the port of the current shrink-wrapper to this new interface, and the CSRFIShrinkWrapInfo collects all the basic blocks that use any CSRs or the stack.
This means that r0 is used in %bb.4 and the Stack and r1 are used in