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