//===------ 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 , 0 // .cfi_same_value // .cfi_same_value // ... // where 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 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 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; }