|  | //===------ CFIFixup.cpp - Insert CFI remember/restore instructions -------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  |  | 
|  | // This pass inserts the necessary  instructions to adjust for the inconsistency | 
|  | // of the call-frame information caused by final machine basic block layout. | 
|  | // The pass relies in constraints LLVM imposes on the placement of | 
|  | // save/restore points (cf. ShrinkWrap): | 
|  | // * there is a single basic block, containing the function prologue | 
|  | // * possibly multiple epilogue blocks, where each epilogue block is | 
|  | //   complete and self-contained, i.e. CSR restore instructions (and the | 
|  | //   corresponding CFI instructions are not split across two or more blocks. | 
|  | // * prologue and epilogue blocks are outside of any loops | 
|  | // Thus, during execution, at the beginning and at the end of each basic block | 
|  | // the function can be in one of two states: | 
|  | //  - "has a call frame", if the function has executed the prologue, and | 
|  | //    has not executed any epilogue | 
|  | //  - "does not have a call frame", if the function has not executed the | 
|  | //    prologue, or has executed an epilogue | 
|  | // which can be computed by a single RPO traversal. | 
|  |  | 
|  | // In order to accommodate backends which do not generate unwind info in | 
|  | // epilogues we compute an additional property "strong no call frame on entry", | 
|  | // which is set for the entry point of the function and for every block | 
|  | // reachable from the entry along a path that does not execute the prologue. If | 
|  | // this property holds, it takes precedence over the "has a call frame" | 
|  | // property. | 
|  |  | 
|  | // From the point of view of the unwind tables, the "has/does not have call | 
|  | // frame" state at beginning of each block is determined by the state at the end | 
|  | // of the previous block, in layout order. Where these states differ, we insert | 
|  | // compensating CFI instructions, which come in two flavours: | 
|  |  | 
|  | //   - CFI instructions, which reset the unwind table state to the initial one. | 
|  | //     This is done by a target specific hook and is expected to be trivial | 
|  | //     to implement, for example it could be: | 
|  | //       .cfi_def_cfa <sp>, 0 | 
|  | //       .cfi_same_value <rN> | 
|  | //       .cfi_same_value <rN-1> | 
|  | //       ... | 
|  | //     where <rN> are the callee-saved registers. | 
|  | //   - CFI instructions, which reset the unwind table state to the one | 
|  | //     created by the function prologue. These are | 
|  | //       .cfi_restore_state | 
|  | //       .cfi_remember_state | 
|  | //     In this case we also insert a `.cfi_remember_state` after the last CFI | 
|  | //     instruction in the function prologue. | 
|  | // | 
|  | // Known limitations: | 
|  | //  * the pass cannot handle an epilogue preceding the prologue in the basic | 
|  | //    block layout | 
|  | //  * the pass does not handle functions where SP is used as a frame pointer and | 
|  | //    SP adjustments up and down are done in different basic blocks (TODO) | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/CodeGen/CFIFixup.h" | 
|  |  | 
|  | #include "llvm/ADT/PostOrderIterator.h" | 
|  | #include "llvm/ADT/SmallBitVector.h" | 
|  | #include "llvm/CodeGen/Passes.h" | 
|  | #include "llvm/CodeGen/TargetFrameLowering.h" | 
|  | #include "llvm/CodeGen/TargetInstrInfo.h" | 
|  | #include "llvm/CodeGen/TargetSubtargetInfo.h" | 
|  | #include "llvm/MC/MCAsmInfo.h" | 
|  | #include "llvm/MC/MCDwarf.h" | 
|  | #include "llvm/Target/TargetMachine.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | #define DEBUG_TYPE "cfi-fixup" | 
|  |  | 
|  | char CFIFixup::ID = 0; | 
|  |  | 
|  | INITIALIZE_PASS(CFIFixup, "cfi-fixup", | 
|  | "Insert CFI remember/restore state instructions", false, false) | 
|  | FunctionPass *llvm::createCFIFixup() { return new CFIFixup(); } | 
|  |  | 
|  | static bool isPrologueCFIInstruction(const MachineInstr &MI) { | 
|  | return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION && | 
|  | MI.getFlag(MachineInstr::FrameSetup); | 
|  | } | 
|  |  | 
|  | static bool containsPrologue(const MachineBasicBlock &MBB) { | 
|  | return llvm::any_of(MBB.instrs(), isPrologueCFIInstruction); | 
|  | } | 
|  |  | 
|  | static bool containsEpilogue(const MachineBasicBlock &MBB) { | 
|  | return llvm::any_of(llvm::reverse(MBB), [](const auto &MI) { | 
|  | return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION && | 
|  | MI.getFlag(MachineInstr::FrameDestroy); | 
|  | }); | 
|  | } | 
|  |  | 
|  | bool CFIFixup::runOnMachineFunction(MachineFunction &MF) { | 
|  | const TargetFrameLowering &TFL = *MF.getSubtarget().getFrameLowering(); | 
|  | if (!TFL.enableCFIFixup(MF)) | 
|  | return false; | 
|  |  | 
|  | const unsigned NumBlocks = MF.getNumBlockIDs(); | 
|  | if (NumBlocks < 2) | 
|  | return false; | 
|  |  | 
|  | struct BlockFlags { | 
|  | bool Reachable : 1; | 
|  | bool StrongNoFrameOnEntry : 1; | 
|  | bool HasFrameOnEntry : 1; | 
|  | bool HasFrameOnExit : 1; | 
|  | }; | 
|  | SmallVector<BlockFlags, 32> BlockInfo(NumBlocks, {false, false, false, false}); | 
|  | BlockInfo[0].Reachable = true; | 
|  | BlockInfo[0].StrongNoFrameOnEntry = true; | 
|  |  | 
|  | // Compute the presence/absence of frame at each basic block. | 
|  | MachineBasicBlock *PrologueBlock = nullptr; | 
|  | ReversePostOrderTraversal<MachineBasicBlock *> RPOT(&*MF.begin()); | 
|  | for (MachineBasicBlock *MBB : RPOT) { | 
|  | BlockFlags &Info = BlockInfo[MBB->getNumber()]; | 
|  |  | 
|  | // Set to true if the current block contains the prologue or the epilogue, | 
|  | // respectively. | 
|  | bool HasPrologue = false; | 
|  | bool HasEpilogue = false; | 
|  |  | 
|  | if (!PrologueBlock && !Info.HasFrameOnEntry && containsPrologue(*MBB)) { | 
|  | PrologueBlock = MBB; | 
|  | HasPrologue = true; | 
|  | } | 
|  |  | 
|  | if (Info.HasFrameOnEntry || HasPrologue) | 
|  | HasEpilogue = containsEpilogue(*MBB); | 
|  |  | 
|  | // If the function has a call frame at the entry of the current block or the | 
|  | // current block contains the prologue, then the function has a call frame | 
|  | // at the exit of the block, unless the block contains the epilogue. | 
|  | Info.HasFrameOnExit = (Info.HasFrameOnEntry || HasPrologue) && !HasEpilogue; | 
|  |  | 
|  | // Set the successors' state on entry. | 
|  | for (MachineBasicBlock *Succ : MBB->successors()) { | 
|  | BlockFlags &SuccInfo = BlockInfo[Succ->getNumber()]; | 
|  | SuccInfo.Reachable = true; | 
|  | SuccInfo.StrongNoFrameOnEntry |= | 
|  | Info.StrongNoFrameOnEntry && !HasPrologue; | 
|  | SuccInfo.HasFrameOnEntry = Info.HasFrameOnExit; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!PrologueBlock) | 
|  | return false; | 
|  |  | 
|  | // Walk the blocks of the function in "physical" order. | 
|  | // Every block inherits the frame state (as recorded in the unwind tables) | 
|  | // of the previous block. If the intended frame state is different, insert | 
|  | // compensating CFI instructions. | 
|  | const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); | 
|  | bool Change = false; | 
|  | // `InsertPt` always points to the point in a preceding block where we have to | 
|  | // insert a `.cfi_remember_state`, in the case that the current block needs a | 
|  | // `.cfi_restore_state`. | 
|  | MachineBasicBlock *InsertMBB = PrologueBlock; | 
|  | MachineBasicBlock::iterator InsertPt = PrologueBlock->begin(); | 
|  | for (MachineInstr &MI : *PrologueBlock) | 
|  | if (isPrologueCFIInstruction(MI)) | 
|  | InsertPt = std::next(MI.getIterator()); | 
|  |  | 
|  | assert(InsertPt != PrologueBlock->begin() && | 
|  | "Inconsistent notion of \"prologue block\""); | 
|  |  | 
|  | // No point starting before the prologue block. | 
|  | // TODO: the unwind tables will still be incorrect if an epilogue physically | 
|  | // preceeds the prologue. | 
|  | MachineFunction::iterator CurrBB = std::next(PrologueBlock->getIterator()); | 
|  | bool HasFrame = BlockInfo[PrologueBlock->getNumber()].HasFrameOnExit; | 
|  | while (CurrBB != MF.end()) { | 
|  | const BlockFlags &Info = BlockInfo[CurrBB->getNumber()]; | 
|  | if (!Info.Reachable) { | 
|  | ++CurrBB; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | #ifndef NDEBUG | 
|  | if (!Info.StrongNoFrameOnEntry) { | 
|  | for (auto *Pred : CurrBB->predecessors()) { | 
|  | BlockFlags &PredInfo = BlockInfo[Pred->getNumber()]; | 
|  | assert((!PredInfo.Reachable || | 
|  | Info.HasFrameOnEntry == PredInfo.HasFrameOnExit) && | 
|  | "Inconsistent call frame state"); | 
|  | } | 
|  | } | 
|  | #endif | 
|  | if (!Info.StrongNoFrameOnEntry && Info.HasFrameOnEntry && !HasFrame) { | 
|  | // Reset to the "after prologue" state. | 
|  |  | 
|  | // Insert a `.cfi_remember_state` into the last block known to have a | 
|  | // stack frame. | 
|  | unsigned CFIIndex = | 
|  | MF.addFrameInst(MCCFIInstruction::createRememberState(nullptr)); | 
|  | BuildMI(*InsertMBB, InsertPt, DebugLoc(), | 
|  | TII.get(TargetOpcode::CFI_INSTRUCTION)) | 
|  | .addCFIIndex(CFIIndex); | 
|  | // Insert a `.cfi_restore_state` at the beginning of the current block. | 
|  | CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestoreState(nullptr)); | 
|  | InsertPt = BuildMI(*CurrBB, CurrBB->begin(), DebugLoc(), | 
|  | TII.get(TargetOpcode::CFI_INSTRUCTION)) | 
|  | .addCFIIndex(CFIIndex); | 
|  | ++InsertPt; | 
|  | InsertMBB = &*CurrBB; | 
|  | Change = true; | 
|  | } else if ((Info.StrongNoFrameOnEntry || !Info.HasFrameOnEntry) && | 
|  | HasFrame) { | 
|  | // Reset to the state upon function entry. | 
|  | TFL.resetCFIToInitialState(*CurrBB); | 
|  | Change = true; | 
|  | } | 
|  |  | 
|  | HasFrame = Info.HasFrameOnExit; | 
|  | ++CurrBB; | 
|  | } | 
|  |  | 
|  | return Change; | 
|  | } |