Path: blob/main/contrib/llvm-project/llvm/lib/CodeGen/CFIInstrInserter.cpp
35234 views
//===------ CFIInstrInserter.cpp - Insert additional CFI instructions -----===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8/// \file This pass verifies incoming and outgoing CFA information of basic9/// blocks. CFA information is information about offset and register set by CFI10/// directives, valid at the start and end of a basic block. This pass checks11/// that outgoing information of predecessors matches incoming information of12/// their successors. Then it checks if blocks have correct CFA calculation rule13/// set and inserts additional CFI instruction at their beginnings if they14/// don't. CFI instructions are inserted if basic blocks have incorrect offset15/// or register set by previous blocks, as a result of a non-linear layout of16/// blocks in a function.17//===----------------------------------------------------------------------===//1819#include "llvm/ADT/DepthFirstIterator.h"20#include "llvm/CodeGen/MachineFunctionPass.h"21#include "llvm/CodeGen/MachineInstrBuilder.h"22#include "llvm/CodeGen/Passes.h"23#include "llvm/CodeGen/TargetFrameLowering.h"24#include "llvm/CodeGen/TargetInstrInfo.h"25#include "llvm/CodeGen/TargetSubtargetInfo.h"26#include "llvm/InitializePasses.h"27#include "llvm/MC/MCDwarf.h"28using namespace llvm;2930static cl::opt<bool> VerifyCFI("verify-cfiinstrs",31cl::desc("Verify Call Frame Information instructions"),32cl::init(false),33cl::Hidden);3435namespace {36class CFIInstrInserter : public MachineFunctionPass {37public:38static char ID;3940CFIInstrInserter() : MachineFunctionPass(ID) {41initializeCFIInstrInserterPass(*PassRegistry::getPassRegistry());42}4344void getAnalysisUsage(AnalysisUsage &AU) const override {45AU.setPreservesAll();46MachineFunctionPass::getAnalysisUsage(AU);47}4849bool runOnMachineFunction(MachineFunction &MF) override {50if (!MF.needsFrameMoves())51return false;5253MBBVector.resize(MF.getNumBlockIDs());54calculateCFAInfo(MF);5556if (VerifyCFI) {57if (unsigned ErrorNum = verify(MF))58report_fatal_error("Found " + Twine(ErrorNum) +59" in/out CFI information errors.");60}61bool insertedCFI = insertCFIInstrs(MF);62MBBVector.clear();63return insertedCFI;64}6566private:67struct MBBCFAInfo {68MachineBasicBlock *MBB;69/// Value of cfa offset valid at basic block entry.70int64_t IncomingCFAOffset = -1;71/// Value of cfa offset valid at basic block exit.72int64_t OutgoingCFAOffset = -1;73/// Value of cfa register valid at basic block entry.74unsigned IncomingCFARegister = 0;75/// Value of cfa register valid at basic block exit.76unsigned OutgoingCFARegister = 0;77/// Set of callee saved registers saved at basic block entry.78BitVector IncomingCSRSaved;79/// Set of callee saved registers saved at basic block exit.80BitVector OutgoingCSRSaved;81/// If in/out cfa offset and register values for this block have already82/// been set or not.83bool Processed = false;84};8586#define INVALID_REG UINT_MAX87#define INVALID_OFFSET INT_MAX88/// contains the location where CSR register is saved.89struct CSRSavedLocation {90CSRSavedLocation(std::optional<unsigned> R, std::optional<int> O)91: Reg(R), Offset(O) {}92std::optional<unsigned> Reg;93std::optional<int> Offset;94};9596/// Contains cfa offset and register values valid at entry and exit of basic97/// blocks.98std::vector<MBBCFAInfo> MBBVector;99100/// Map the callee save registers to the locations where they are saved.101SmallDenseMap<unsigned, CSRSavedLocation, 16> CSRLocMap;102103/// Calculate cfa offset and register values valid at entry and exit for all104/// basic blocks in a function.105void calculateCFAInfo(MachineFunction &MF);106/// Calculate cfa offset and register values valid at basic block exit by107/// checking the block for CFI instructions. Block's incoming CFA info remains108/// the same.109void calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo);110/// Update in/out cfa offset and register values for successors of the basic111/// block.112void updateSuccCFAInfo(MBBCFAInfo &MBBInfo);113114/// Check if incoming CFA information of a basic block matches outgoing CFA115/// information of the previous block. If it doesn't, insert CFI instruction116/// at the beginning of the block that corrects the CFA calculation rule for117/// that block.118bool insertCFIInstrs(MachineFunction &MF);119/// Return the cfa offset value that should be set at the beginning of a MBB120/// if needed. The negated value is needed when creating CFI instructions that121/// set absolute offset.122int64_t getCorrectCFAOffset(MachineBasicBlock *MBB) {123return MBBVector[MBB->getNumber()].IncomingCFAOffset;124}125126void reportCFAError(const MBBCFAInfo &Pred, const MBBCFAInfo &Succ);127void reportCSRError(const MBBCFAInfo &Pred, const MBBCFAInfo &Succ);128/// Go through each MBB in a function and check that outgoing offset and129/// register of its predecessors match incoming offset and register of that130/// MBB, as well as that incoming offset and register of its successors match131/// outgoing offset and register of the MBB.132unsigned verify(MachineFunction &MF);133};134} // namespace135136char CFIInstrInserter::ID = 0;137INITIALIZE_PASS(CFIInstrInserter, "cfi-instr-inserter",138"Check CFA info and insert CFI instructions if needed", false,139false)140FunctionPass *llvm::createCFIInstrInserter() { return new CFIInstrInserter(); }141142void CFIInstrInserter::calculateCFAInfo(MachineFunction &MF) {143const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo();144// Initial CFA offset value i.e. the one valid at the beginning of the145// function.146int InitialOffset =147MF.getSubtarget().getFrameLowering()->getInitialCFAOffset(MF);148// Initial CFA register value i.e. the one valid at the beginning of the149// function.150Register InitialRegister =151MF.getSubtarget().getFrameLowering()->getInitialCFARegister(MF);152InitialRegister = TRI.getDwarfRegNum(InitialRegister, true);153unsigned NumRegs = TRI.getNumSupportedRegs(MF);154155// Initialize MBBMap.156for (MachineBasicBlock &MBB : MF) {157MBBCFAInfo &MBBInfo = MBBVector[MBB.getNumber()];158MBBInfo.MBB = &MBB;159MBBInfo.IncomingCFAOffset = InitialOffset;160MBBInfo.OutgoingCFAOffset = InitialOffset;161MBBInfo.IncomingCFARegister = InitialRegister;162MBBInfo.OutgoingCFARegister = InitialRegister;163MBBInfo.IncomingCSRSaved.resize(NumRegs);164MBBInfo.OutgoingCSRSaved.resize(NumRegs);165}166CSRLocMap.clear();167168// Set in/out cfa info for all blocks in the function. This traversal is based169// on the assumption that the first block in the function is the entry block170// i.e. that it has initial cfa offset and register values as incoming CFA171// information.172updateSuccCFAInfo(MBBVector[MF.front().getNumber()]);173}174175void CFIInstrInserter::calculateOutgoingCFAInfo(MBBCFAInfo &MBBInfo) {176// Outgoing cfa offset set by the block.177int64_t SetOffset = MBBInfo.IncomingCFAOffset;178// Outgoing cfa register set by the block.179unsigned SetRegister = MBBInfo.IncomingCFARegister;180MachineFunction *MF = MBBInfo.MBB->getParent();181const std::vector<MCCFIInstruction> &Instrs = MF->getFrameInstructions();182const TargetRegisterInfo &TRI = *MF->getSubtarget().getRegisterInfo();183unsigned NumRegs = TRI.getNumSupportedRegs(*MF);184BitVector CSRSaved(NumRegs), CSRRestored(NumRegs);185186// Determine cfa offset and register set by the block.187for (MachineInstr &MI : *MBBInfo.MBB) {188if (MI.isCFIInstruction()) {189std::optional<unsigned> CSRReg;190std::optional<int64_t> CSROffset;191unsigned CFIIndex = MI.getOperand(0).getCFIIndex();192const MCCFIInstruction &CFI = Instrs[CFIIndex];193switch (CFI.getOperation()) {194case MCCFIInstruction::OpDefCfaRegister:195SetRegister = CFI.getRegister();196break;197case MCCFIInstruction::OpDefCfaOffset:198SetOffset = CFI.getOffset();199break;200case MCCFIInstruction::OpAdjustCfaOffset:201SetOffset += CFI.getOffset();202break;203case MCCFIInstruction::OpDefCfa:204SetRegister = CFI.getRegister();205SetOffset = CFI.getOffset();206break;207case MCCFIInstruction::OpOffset:208CSROffset = CFI.getOffset();209break;210case MCCFIInstruction::OpRegister:211CSRReg = CFI.getRegister2();212break;213case MCCFIInstruction::OpRelOffset:214CSROffset = CFI.getOffset() - SetOffset;215break;216case MCCFIInstruction::OpRestore:217CSRRestored.set(CFI.getRegister());218break;219case MCCFIInstruction::OpLLVMDefAspaceCfa:220// TODO: Add support for handling cfi_def_aspace_cfa.221#ifndef NDEBUG222report_fatal_error(223"Support for cfi_llvm_def_aspace_cfa not implemented! Value of CFA "224"may be incorrect!\n");225#endif226break;227case MCCFIInstruction::OpRememberState:228// TODO: Add support for handling cfi_remember_state.229#ifndef NDEBUG230report_fatal_error(231"Support for cfi_remember_state not implemented! Value of CFA "232"may be incorrect!\n");233#endif234break;235case MCCFIInstruction::OpRestoreState:236// TODO: Add support for handling cfi_restore_state.237#ifndef NDEBUG238report_fatal_error(239"Support for cfi_restore_state not implemented! Value of CFA may "240"be incorrect!\n");241#endif242break;243// Other CFI directives do not affect CFA value.244case MCCFIInstruction::OpUndefined:245case MCCFIInstruction::OpSameValue:246case MCCFIInstruction::OpEscape:247case MCCFIInstruction::OpWindowSave:248case MCCFIInstruction::OpNegateRAState:249case MCCFIInstruction::OpGnuArgsSize:250case MCCFIInstruction::OpLabel:251break;252}253if (CSRReg || CSROffset) {254auto It = CSRLocMap.find(CFI.getRegister());255if (It == CSRLocMap.end()) {256CSRLocMap.insert(257{CFI.getRegister(), CSRSavedLocation(CSRReg, CSROffset)});258} else if (It->second.Reg != CSRReg || It->second.Offset != CSROffset) {259llvm_unreachable("Different saved locations for the same CSR");260}261CSRSaved.set(CFI.getRegister());262}263}264}265266MBBInfo.Processed = true;267268// Update outgoing CFA info.269MBBInfo.OutgoingCFAOffset = SetOffset;270MBBInfo.OutgoingCFARegister = SetRegister;271272// Update outgoing CSR info.273BitVector::apply([](auto x, auto y, auto z) { return (x | y) & ~z; },274MBBInfo.OutgoingCSRSaved, MBBInfo.IncomingCSRSaved, CSRSaved,275CSRRestored);276}277278void CFIInstrInserter::updateSuccCFAInfo(MBBCFAInfo &MBBInfo) {279SmallVector<MachineBasicBlock *, 4> Stack;280Stack.push_back(MBBInfo.MBB);281282do {283MachineBasicBlock *Current = Stack.pop_back_val();284MBBCFAInfo &CurrentInfo = MBBVector[Current->getNumber()];285calculateOutgoingCFAInfo(CurrentInfo);286for (auto *Succ : CurrentInfo.MBB->successors()) {287MBBCFAInfo &SuccInfo = MBBVector[Succ->getNumber()];288if (!SuccInfo.Processed) {289SuccInfo.IncomingCFAOffset = CurrentInfo.OutgoingCFAOffset;290SuccInfo.IncomingCFARegister = CurrentInfo.OutgoingCFARegister;291SuccInfo.IncomingCSRSaved = CurrentInfo.OutgoingCSRSaved;292Stack.push_back(Succ);293}294}295} while (!Stack.empty());296}297298bool CFIInstrInserter::insertCFIInstrs(MachineFunction &MF) {299const MBBCFAInfo *PrevMBBInfo = &MBBVector[MF.front().getNumber()];300const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();301bool InsertedCFIInstr = false;302303BitVector SetDifference;304for (MachineBasicBlock &MBB : MF) {305// Skip the first MBB in a function306if (MBB.getNumber() == MF.front().getNumber()) continue;307308const MBBCFAInfo &MBBInfo = MBBVector[MBB.getNumber()];309auto MBBI = MBBInfo.MBB->begin();310DebugLoc DL = MBBInfo.MBB->findDebugLoc(MBBI);311312// If the current MBB will be placed in a unique section, a full DefCfa313// must be emitted.314const bool ForceFullCFA = MBB.isBeginSection();315316if ((PrevMBBInfo->OutgoingCFAOffset != MBBInfo.IncomingCFAOffset &&317PrevMBBInfo->OutgoingCFARegister != MBBInfo.IncomingCFARegister) ||318ForceFullCFA) {319// If both outgoing offset and register of a previous block don't match320// incoming offset and register of this block, or if this block begins a321// section, add a def_cfa instruction with the correct offset and322// register for this block.323unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa(324nullptr, MBBInfo.IncomingCFARegister, getCorrectCFAOffset(&MBB)));325BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))326.addCFIIndex(CFIIndex);327InsertedCFIInstr = true;328} else if (PrevMBBInfo->OutgoingCFAOffset != MBBInfo.IncomingCFAOffset) {329// If outgoing offset of a previous block doesn't match incoming offset330// of this block, add a def_cfa_offset instruction with the correct331// offset for this block.332unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(333nullptr, getCorrectCFAOffset(&MBB)));334BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))335.addCFIIndex(CFIIndex);336InsertedCFIInstr = true;337} else if (PrevMBBInfo->OutgoingCFARegister !=338MBBInfo.IncomingCFARegister) {339unsigned CFIIndex =340MF.addFrameInst(MCCFIInstruction::createDefCfaRegister(341nullptr, MBBInfo.IncomingCFARegister));342BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))343.addCFIIndex(CFIIndex);344InsertedCFIInstr = true;345}346347if (ForceFullCFA) {348MF.getSubtarget().getFrameLowering()->emitCalleeSavedFrameMovesFullCFA(349*MBBInfo.MBB, MBBI);350InsertedCFIInstr = true;351PrevMBBInfo = &MBBInfo;352continue;353}354355BitVector::apply([](auto x, auto y) { return x & ~y; }, SetDifference,356PrevMBBInfo->OutgoingCSRSaved, MBBInfo.IncomingCSRSaved);357for (int Reg : SetDifference.set_bits()) {358unsigned CFIIndex =359MF.addFrameInst(MCCFIInstruction::createRestore(nullptr, Reg));360BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))361.addCFIIndex(CFIIndex);362InsertedCFIInstr = true;363}364365BitVector::apply([](auto x, auto y) { return x & ~y; }, SetDifference,366MBBInfo.IncomingCSRSaved, PrevMBBInfo->OutgoingCSRSaved);367for (int Reg : SetDifference.set_bits()) {368auto it = CSRLocMap.find(Reg);369assert(it != CSRLocMap.end() && "Reg should have an entry in CSRLocMap");370unsigned CFIIndex;371CSRSavedLocation RO = it->second;372if (!RO.Reg && RO.Offset) {373CFIIndex = MF.addFrameInst(374MCCFIInstruction::createOffset(nullptr, Reg, *RO.Offset));375} else if (RO.Reg && !RO.Offset) {376CFIIndex = MF.addFrameInst(377MCCFIInstruction::createRegister(nullptr, Reg, *RO.Reg));378} else {379llvm_unreachable("RO.Reg and RO.Offset cannot both be valid/invalid");380}381BuildMI(*MBBInfo.MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION))382.addCFIIndex(CFIIndex);383InsertedCFIInstr = true;384}385386PrevMBBInfo = &MBBInfo;387}388return InsertedCFIInstr;389}390391void CFIInstrInserter::reportCFAError(const MBBCFAInfo &Pred,392const MBBCFAInfo &Succ) {393errs() << "*** Inconsistent CFA register and/or offset between pred and succ "394"***\n";395errs() << "Pred: " << Pred.MBB->getName() << " #" << Pred.MBB->getNumber()396<< " in " << Pred.MBB->getParent()->getName()397<< " outgoing CFA Reg:" << Pred.OutgoingCFARegister << "\n";398errs() << "Pred: " << Pred.MBB->getName() << " #" << Pred.MBB->getNumber()399<< " in " << Pred.MBB->getParent()->getName()400<< " outgoing CFA Offset:" << Pred.OutgoingCFAOffset << "\n";401errs() << "Succ: " << Succ.MBB->getName() << " #" << Succ.MBB->getNumber()402<< " incoming CFA Reg:" << Succ.IncomingCFARegister << "\n";403errs() << "Succ: " << Succ.MBB->getName() << " #" << Succ.MBB->getNumber()404<< " incoming CFA Offset:" << Succ.IncomingCFAOffset << "\n";405}406407void CFIInstrInserter::reportCSRError(const MBBCFAInfo &Pred,408const MBBCFAInfo &Succ) {409errs() << "*** Inconsistent CSR Saved between pred and succ in function "410<< Pred.MBB->getParent()->getName() << " ***\n";411errs() << "Pred: " << Pred.MBB->getName() << " #" << Pred.MBB->getNumber()412<< " outgoing CSR Saved: ";413for (int Reg : Pred.OutgoingCSRSaved.set_bits())414errs() << Reg << " ";415errs() << "\n";416errs() << "Succ: " << Succ.MBB->getName() << " #" << Succ.MBB->getNumber()417<< " incoming CSR Saved: ";418for (int Reg : Succ.IncomingCSRSaved.set_bits())419errs() << Reg << " ";420errs() << "\n";421}422423unsigned CFIInstrInserter::verify(MachineFunction &MF) {424unsigned ErrorNum = 0;425for (auto *CurrMBB : depth_first(&MF)) {426const MBBCFAInfo &CurrMBBInfo = MBBVector[CurrMBB->getNumber()];427for (MachineBasicBlock *Succ : CurrMBB->successors()) {428const MBBCFAInfo &SuccMBBInfo = MBBVector[Succ->getNumber()];429// Check that incoming offset and register values of successors match the430// outgoing offset and register values of CurrMBB431if (SuccMBBInfo.IncomingCFAOffset != CurrMBBInfo.OutgoingCFAOffset ||432SuccMBBInfo.IncomingCFARegister != CurrMBBInfo.OutgoingCFARegister) {433// Inconsistent offsets/registers are ok for 'noreturn' blocks because434// we don't generate epilogues inside such blocks.435if (SuccMBBInfo.MBB->succ_empty() && !SuccMBBInfo.MBB->isReturnBlock())436continue;437reportCFAError(CurrMBBInfo, SuccMBBInfo);438ErrorNum++;439}440// Check that IncomingCSRSaved of every successor matches the441// OutgoingCSRSaved of CurrMBB442if (SuccMBBInfo.IncomingCSRSaved != CurrMBBInfo.OutgoingCSRSaved) {443reportCSRError(CurrMBBInfo, SuccMBBInfo);444ErrorNum++;445}446}447}448return ErrorNum;449}450451452