1*b4ad28daSMomchil Velikov //===------ CFIFixup.cpp - Insert CFI remember/restore instructions -------===//
2*b4ad28daSMomchil Velikov //
3*b4ad28daSMomchil Velikov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*b4ad28daSMomchil Velikov // See https://llvm.org/LICENSE.txt for license information.
5*b4ad28daSMomchil Velikov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*b4ad28daSMomchil Velikov //
7*b4ad28daSMomchil Velikov //===----------------------------------------------------------------------===//
8*b4ad28daSMomchil Velikov //
9*b4ad28daSMomchil Velikov
10*b4ad28daSMomchil Velikov // This pass inserts the necessary instructions to adjust for the inconsistency
11*b4ad28daSMomchil Velikov // of the call-frame information caused by final machine basic block layout.
12*b4ad28daSMomchil Velikov // The pass relies in constraints LLVM imposes on the placement of
13*b4ad28daSMomchil Velikov // save/restore points (cf. ShrinkWrap):
14*b4ad28daSMomchil Velikov // * there is a single basic block, containing the function prologue
15*b4ad28daSMomchil Velikov // * possibly multiple epilogue blocks, where each epilogue block is
16*b4ad28daSMomchil Velikov // complete and self-contained, i.e. CSR restore instructions (and the
17*b4ad28daSMomchil Velikov // corresponding CFI instructions are not split across two or more blocks.
18*b4ad28daSMomchil Velikov // * prologue and epilogue blocks are outside of any loops
19*b4ad28daSMomchil Velikov // Thus, during execution, at the beginning and at the end of each basic block
20*b4ad28daSMomchil Velikov // the function can be in one of two states:
21*b4ad28daSMomchil Velikov // - "has a call frame", if the function has executed the prologue, and
22*b4ad28daSMomchil Velikov // has not executed any epilogue
23*b4ad28daSMomchil Velikov // - "does not have a call frame", if the function has not executed the
24*b4ad28daSMomchil Velikov // prologue, or has executed an epilogue
25*b4ad28daSMomchil Velikov // which can be computed by a single RPO traversal.
26*b4ad28daSMomchil Velikov
27*b4ad28daSMomchil Velikov // In order to accommodate backends which do not generate unwind info in
28*b4ad28daSMomchil Velikov // epilogues we compute an additional property "strong no call frame on entry",
29*b4ad28daSMomchil Velikov // which is set for the entry point of the function and for every block
30*b4ad28daSMomchil Velikov // reachable from the entry along a path that does not execute the prologue. If
31*b4ad28daSMomchil Velikov // this property holds, it takes precedence over the "has a call frame"
32*b4ad28daSMomchil Velikov // property.
33*b4ad28daSMomchil Velikov
34*b4ad28daSMomchil Velikov // From the point of view of the unwind tables, the "has/does not have call
35*b4ad28daSMomchil Velikov // frame" state at beginning of each block is determined by the state at the end
36*b4ad28daSMomchil Velikov // of the previous block, in layout order. Where these states differ, we insert
37*b4ad28daSMomchil Velikov // compensating CFI instructions, which come in two flavours:
38*b4ad28daSMomchil Velikov
39*b4ad28daSMomchil Velikov // - CFI instructions, which reset the unwind table state to the initial one.
40*b4ad28daSMomchil Velikov // This is done by a target specific hook and is expected to be trivial
41*b4ad28daSMomchil Velikov // to implement, for example it could be:
42*b4ad28daSMomchil Velikov // .cfi_def_cfa <sp>, 0
43*b4ad28daSMomchil Velikov // .cfi_same_value <rN>
44*b4ad28daSMomchil Velikov // .cfi_same_value <rN-1>
45*b4ad28daSMomchil Velikov // ...
46*b4ad28daSMomchil Velikov // where <rN> are the callee-saved registers.
47*b4ad28daSMomchil Velikov // - CFI instructions, which reset the unwind table state to the one
48*b4ad28daSMomchil Velikov // created by the function prologue. These are
49*b4ad28daSMomchil Velikov // .cfi_restore_state
50*b4ad28daSMomchil Velikov // .cfi_remember_state
51*b4ad28daSMomchil Velikov // In this case we also insert a `.cfi_remember_state` after the last CFI
52*b4ad28daSMomchil Velikov // instruction in the function prologue.
53*b4ad28daSMomchil Velikov //
54*b4ad28daSMomchil Velikov // Known limitations:
55*b4ad28daSMomchil Velikov // * the pass cannot handle an epilogue preceding the prologue in the basic
56*b4ad28daSMomchil Velikov // block layout
57*b4ad28daSMomchil Velikov // * the pass does not handle functions where SP is used as a frame pointer and
58*b4ad28daSMomchil Velikov // SP adjustments up and down are done in different basic blocks (TODO)
59*b4ad28daSMomchil Velikov //===----------------------------------------------------------------------===//
60*b4ad28daSMomchil Velikov
61*b4ad28daSMomchil Velikov #include "llvm/CodeGen/CFIFixup.h"
62*b4ad28daSMomchil Velikov
63*b4ad28daSMomchil Velikov #include "llvm/ADT/PostOrderIterator.h"
64*b4ad28daSMomchil Velikov #include "llvm/ADT/SmallBitVector.h"
65*b4ad28daSMomchil Velikov #include "llvm/CodeGen/Passes.h"
66*b4ad28daSMomchil Velikov #include "llvm/CodeGen/TargetFrameLowering.h"
67*b4ad28daSMomchil Velikov #include "llvm/CodeGen/TargetInstrInfo.h"
68*b4ad28daSMomchil Velikov #include "llvm/CodeGen/TargetSubtargetInfo.h"
69*b4ad28daSMomchil Velikov #include "llvm/MC/MCAsmInfo.h"
70*b4ad28daSMomchil Velikov #include "llvm/MC/MCDwarf.h"
71*b4ad28daSMomchil Velikov #include "llvm/Target/TargetMachine.h"
72*b4ad28daSMomchil Velikov
73*b4ad28daSMomchil Velikov using namespace llvm;
74*b4ad28daSMomchil Velikov
75*b4ad28daSMomchil Velikov #define DEBUG_TYPE "cfi-fixup"
76*b4ad28daSMomchil Velikov
77*b4ad28daSMomchil Velikov char CFIFixup::ID = 0;
78*b4ad28daSMomchil Velikov
79*b4ad28daSMomchil Velikov INITIALIZE_PASS(CFIFixup, "cfi-fixup",
80*b4ad28daSMomchil Velikov "Insert CFI remember/restore state instructions", false, false)
createCFIFixup()81*b4ad28daSMomchil Velikov FunctionPass *llvm::createCFIFixup() { return new CFIFixup(); }
82*b4ad28daSMomchil Velikov
isPrologueCFIInstruction(const MachineInstr & MI)83*b4ad28daSMomchil Velikov static bool isPrologueCFIInstruction(const MachineInstr &MI) {
84*b4ad28daSMomchil Velikov return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION &&
85*b4ad28daSMomchil Velikov MI.getFlag(MachineInstr::FrameSetup);
86*b4ad28daSMomchil Velikov }
87*b4ad28daSMomchil Velikov
containsPrologue(const MachineBasicBlock & MBB)88*b4ad28daSMomchil Velikov static bool containsPrologue(const MachineBasicBlock &MBB) {
89*b4ad28daSMomchil Velikov return llvm::any_of(MBB.instrs(), isPrologueCFIInstruction);
90*b4ad28daSMomchil Velikov }
91*b4ad28daSMomchil Velikov
containsEpilogue(const MachineBasicBlock & MBB)92*b4ad28daSMomchil Velikov static bool containsEpilogue(const MachineBasicBlock &MBB) {
93*b4ad28daSMomchil Velikov return llvm::any_of(llvm::reverse(MBB), [](const auto &MI) {
94*b4ad28daSMomchil Velikov return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION &&
95*b4ad28daSMomchil Velikov MI.getFlag(MachineInstr::FrameDestroy);
96*b4ad28daSMomchil Velikov });
97*b4ad28daSMomchil Velikov }
98*b4ad28daSMomchil Velikov
runOnMachineFunction(MachineFunction & MF)99*b4ad28daSMomchil Velikov bool CFIFixup::runOnMachineFunction(MachineFunction &MF) {
100*b4ad28daSMomchil Velikov const TargetFrameLowering &TFL = *MF.getSubtarget().getFrameLowering();
101*b4ad28daSMomchil Velikov if (!TFL.enableCFIFixup(MF))
102*b4ad28daSMomchil Velikov return false;
103*b4ad28daSMomchil Velikov
104*b4ad28daSMomchil Velikov const unsigned NumBlocks = MF.getNumBlockIDs();
105*b4ad28daSMomchil Velikov if (NumBlocks < 2)
106*b4ad28daSMomchil Velikov return false;
107*b4ad28daSMomchil Velikov
108*b4ad28daSMomchil Velikov struct BlockFlags {
109*b4ad28daSMomchil Velikov bool Reachable : 1;
110*b4ad28daSMomchil Velikov bool StrongNoFrameOnEntry : 1;
111*b4ad28daSMomchil Velikov bool HasFrameOnEntry : 1;
112*b4ad28daSMomchil Velikov bool HasFrameOnExit : 1;
113*b4ad28daSMomchil Velikov };
114*b4ad28daSMomchil Velikov SmallVector<BlockFlags, 32> BlockInfo(NumBlocks, {false, false, false, false});
115*b4ad28daSMomchil Velikov BlockInfo[0].Reachable = true;
116*b4ad28daSMomchil Velikov BlockInfo[0].StrongNoFrameOnEntry = true;
117*b4ad28daSMomchil Velikov
118*b4ad28daSMomchil Velikov // Compute the presence/absence of frame at each basic block.
119*b4ad28daSMomchil Velikov MachineBasicBlock *PrologueBlock = nullptr;
120*b4ad28daSMomchil Velikov ReversePostOrderTraversal<MachineBasicBlock *> RPOT(&*MF.begin());
121*b4ad28daSMomchil Velikov for (MachineBasicBlock *MBB : RPOT) {
122*b4ad28daSMomchil Velikov BlockFlags &Info = BlockInfo[MBB->getNumber()];
123*b4ad28daSMomchil Velikov
124*b4ad28daSMomchil Velikov // Set to true if the current block contains the prologue or the epilogue,
125*b4ad28daSMomchil Velikov // respectively.
126*b4ad28daSMomchil Velikov bool HasPrologue = false;
127*b4ad28daSMomchil Velikov bool HasEpilogue = false;
128*b4ad28daSMomchil Velikov
129*b4ad28daSMomchil Velikov if (!PrologueBlock && !Info.HasFrameOnEntry && containsPrologue(*MBB)) {
130*b4ad28daSMomchil Velikov PrologueBlock = MBB;
131*b4ad28daSMomchil Velikov HasPrologue = true;
132*b4ad28daSMomchil Velikov }
133*b4ad28daSMomchil Velikov
134*b4ad28daSMomchil Velikov if (Info.HasFrameOnEntry || HasPrologue)
135*b4ad28daSMomchil Velikov HasEpilogue = containsEpilogue(*MBB);
136*b4ad28daSMomchil Velikov
137*b4ad28daSMomchil Velikov // If the function has a call frame at the entry of the current block or the
138*b4ad28daSMomchil Velikov // current block contains the prologue, then the function has a call frame
139*b4ad28daSMomchil Velikov // at the exit of the block, unless the block contains the epilogue.
140*b4ad28daSMomchil Velikov Info.HasFrameOnExit = (Info.HasFrameOnEntry || HasPrologue) && !HasEpilogue;
141*b4ad28daSMomchil Velikov
142*b4ad28daSMomchil Velikov // Set the successors' state on entry.
143*b4ad28daSMomchil Velikov for (MachineBasicBlock *Succ : MBB->successors()) {
144*b4ad28daSMomchil Velikov BlockFlags &SuccInfo = BlockInfo[Succ->getNumber()];
145*b4ad28daSMomchil Velikov SuccInfo.Reachable = true;
146*b4ad28daSMomchil Velikov SuccInfo.StrongNoFrameOnEntry |=
147*b4ad28daSMomchil Velikov Info.StrongNoFrameOnEntry && !HasPrologue;
148*b4ad28daSMomchil Velikov SuccInfo.HasFrameOnEntry = Info.HasFrameOnExit;
149*b4ad28daSMomchil Velikov }
150*b4ad28daSMomchil Velikov }
151*b4ad28daSMomchil Velikov
152*b4ad28daSMomchil Velikov if (!PrologueBlock)
153*b4ad28daSMomchil Velikov return false;
154*b4ad28daSMomchil Velikov
155*b4ad28daSMomchil Velikov // Walk the blocks of the function in "physical" order.
156*b4ad28daSMomchil Velikov // Every block inherits the frame state (as recorded in the unwind tables)
157*b4ad28daSMomchil Velikov // of the previous block. If the intended frame state is different, insert
158*b4ad28daSMomchil Velikov // compensating CFI instructions.
159*b4ad28daSMomchil Velikov const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
160*b4ad28daSMomchil Velikov bool Change = false;
161*b4ad28daSMomchil Velikov // `InsertPt` always points to the point in a preceding block where we have to
162*b4ad28daSMomchil Velikov // insert a `.cfi_remember_state`, in the case that the current block needs a
163*b4ad28daSMomchil Velikov // `.cfi_restore_state`.
164*b4ad28daSMomchil Velikov MachineBasicBlock *InsertMBB = PrologueBlock;
165*b4ad28daSMomchil Velikov MachineBasicBlock::iterator InsertPt = PrologueBlock->begin();
166*b4ad28daSMomchil Velikov for (MachineInstr &MI : *PrologueBlock)
167*b4ad28daSMomchil Velikov if (isPrologueCFIInstruction(MI))
168*b4ad28daSMomchil Velikov InsertPt = std::next(MI.getIterator());
169*b4ad28daSMomchil Velikov
170*b4ad28daSMomchil Velikov assert(InsertPt != PrologueBlock->begin() &&
171*b4ad28daSMomchil Velikov "Inconsistent notion of \"prologue block\"");
172*b4ad28daSMomchil Velikov
173*b4ad28daSMomchil Velikov // No point starting before the prologue block.
174*b4ad28daSMomchil Velikov // TODO: the unwind tables will still be incorrect if an epilogue physically
175*b4ad28daSMomchil Velikov // preceeds the prologue.
176*b4ad28daSMomchil Velikov MachineFunction::iterator CurrBB = std::next(PrologueBlock->getIterator());
177*b4ad28daSMomchil Velikov bool HasFrame = BlockInfo[PrologueBlock->getNumber()].HasFrameOnExit;
178*b4ad28daSMomchil Velikov while (CurrBB != MF.end()) {
179*b4ad28daSMomchil Velikov const BlockFlags &Info = BlockInfo[CurrBB->getNumber()];
180*b4ad28daSMomchil Velikov if (!Info.Reachable) {
181*b4ad28daSMomchil Velikov ++CurrBB;
182*b4ad28daSMomchil Velikov continue;
183*b4ad28daSMomchil Velikov }
184*b4ad28daSMomchil Velikov
185*b4ad28daSMomchil Velikov #ifndef NDEBUG
186*b4ad28daSMomchil Velikov if (!Info.StrongNoFrameOnEntry) {
187*b4ad28daSMomchil Velikov for (auto *Pred : CurrBB->predecessors()) {
188*b4ad28daSMomchil Velikov BlockFlags &PredInfo = BlockInfo[Pred->getNumber()];
189*b4ad28daSMomchil Velikov assert((!PredInfo.Reachable ||
190*b4ad28daSMomchil Velikov Info.HasFrameOnEntry == PredInfo.HasFrameOnExit) &&
191*b4ad28daSMomchil Velikov "Inconsistent call frame state");
192*b4ad28daSMomchil Velikov }
193*b4ad28daSMomchil Velikov }
194*b4ad28daSMomchil Velikov #endif
195*b4ad28daSMomchil Velikov if (!Info.StrongNoFrameOnEntry && Info.HasFrameOnEntry && !HasFrame) {
196*b4ad28daSMomchil Velikov // Reset to the "after prologue" state.
197*b4ad28daSMomchil Velikov
198*b4ad28daSMomchil Velikov // Insert a `.cfi_remember_state` into the last block known to have a
199*b4ad28daSMomchil Velikov // stack frame.
200*b4ad28daSMomchil Velikov unsigned CFIIndex =
201*b4ad28daSMomchil Velikov MF.addFrameInst(MCCFIInstruction::createRememberState(nullptr));
202*b4ad28daSMomchil Velikov BuildMI(*InsertMBB, InsertPt, DebugLoc(),
203*b4ad28daSMomchil Velikov TII.get(TargetOpcode::CFI_INSTRUCTION))
204*b4ad28daSMomchil Velikov .addCFIIndex(CFIIndex);
205*b4ad28daSMomchil Velikov // Insert a `.cfi_restore_state` at the beginning of the current block.
206*b4ad28daSMomchil Velikov CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestoreState(nullptr));
207*b4ad28daSMomchil Velikov InsertPt = BuildMI(*CurrBB, CurrBB->begin(), DebugLoc(),
208*b4ad28daSMomchil Velikov TII.get(TargetOpcode::CFI_INSTRUCTION))
209*b4ad28daSMomchil Velikov .addCFIIndex(CFIIndex);
210*b4ad28daSMomchil Velikov ++InsertPt;
211*b4ad28daSMomchil Velikov InsertMBB = &*CurrBB;
212*b4ad28daSMomchil Velikov Change = true;
213*b4ad28daSMomchil Velikov } else if ((Info.StrongNoFrameOnEntry || !Info.HasFrameOnEntry) &&
214*b4ad28daSMomchil Velikov HasFrame) {
215*b4ad28daSMomchil Velikov // Reset to the state upon function entry.
216*b4ad28daSMomchil Velikov TFL.resetCFIToInitialState(*CurrBB);
217*b4ad28daSMomchil Velikov Change = true;
218*b4ad28daSMomchil Velikov }
219*b4ad28daSMomchil Velikov
220*b4ad28daSMomchil Velikov HasFrame = Info.HasFrameOnExit;
221*b4ad28daSMomchil Velikov ++CurrBB;
222*b4ad28daSMomchil Velikov }
223*b4ad28daSMomchil Velikov
224*b4ad28daSMomchil Velikov return Change;
225*b4ad28daSMomchil Velikov }
226