| //===------ 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; |
| } |