aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/CodeGen/CFIFixup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/CodeGen/CFIFixup.cpp')
-rw-r--r--llvm/lib/CodeGen/CFIFixup.cpp225
1 files changed, 225 insertions, 0 deletions
diff --git a/llvm/lib/CodeGen/CFIFixup.cpp b/llvm/lib/CodeGen/CFIFixup.cpp
new file mode 100644
index 000000000000..837dbd77d073
--- /dev/null
+++ b/llvm/lib/CodeGen/CFIFixup.cpp
@@ -0,0 +1,225 @@
+//===------ 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;
+}