diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h index 3910e77d13de7..d629d7fd18071 100644 --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -428,6 +428,22 @@ class LLVM_ABI TargetInstrInfo : public MCInstrInfo { return ~0U; } + enum class InstSizeVerifyMode { + /// Do not verify instruction size. + NoVerify, + /// Check that the instruction size matches exactly. + ExactSize, + /// Allow the reported instruction size to be larger than the actual size. + AllowOverEstimate, + }; + + /// Determine whether/how the instruction size returned by + /// getInstSizeInBytes() should be verified. + virtual InstSizeVerifyMode + getInstSizeVerifyMode(const MachineInstr &MI) const { + return InstSizeVerifyMode::NoVerify; + } + /// Return true if the instruction is as cheap as a move instruction. /// /// Targets for different archs need to override this, and different diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index db4fc5888f6d4..ea4908b205241 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2147,6 +2147,11 @@ void AsmPrinter::emitFunctionBody() { if (isVerbose()) emitComments(MI, STI, OutStreamer->getCommentOS()); +#ifndef NDEBUG + MCFragment *OldFragment = OutStreamer->getCurrentFragment(); + size_t OldFragSize = OldFragment->getFixedSize(); +#endif + switch (MI.getOpcode()) { case TargetOpcode::CFI_INSTRUCTION: emitCFIInstruction(MI); @@ -2265,6 +2270,36 @@ void AsmPrinter::emitFunctionBody() { break; } +#ifndef NDEBUG + // Verify that the instruction size reported by InstrInfo matches the + // actually emitted size. Many backends performing branch relaxation + // on the MIR level rely on this for correctness. + if (OutStreamer->isObj()) { + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + MCFragment *NewFragment = OutStreamer->getCurrentFragment(); + TargetInstrInfo::InstSizeVerifyMode Mode = + TII->getInstSizeVerifyMode(MI); + // Don't try to handle fragment splitting cases. + if (NewFragment == OldFragment && + Mode != TargetInstrInfo::InstSizeVerifyMode::NoVerify) { + unsigned ExpectedSize = TII->getInstSizeInBytes(MI); + unsigned ActualSize = NewFragment->getFixedSize() - OldFragSize; + bool AllowOverEstimate = + Mode == TargetInstrInfo::InstSizeVerifyMode::AllowOverEstimate; + bool Valid = AllowOverEstimate ? ActualSize <= ExpectedSize + : ActualSize == ExpectedSize; + if (!Valid) { + dbgs() << "In function: " << MF->getName() << "\n"; + dbgs() << "Size mismatch for: " << MI; + dbgs() << "Expected " << (AllowOverEstimate ? "maximum" : "exact") + << " size: " << ExpectedSize << "\n"; + dbgs() << "Actual size: " << ActualSize << "\n"; + abort(); + } + } + } +#endif + if (MI.isCall()) { if (MF->getTarget().Options.BBAddrMap) OutStreamer->emitLabel(createCallsiteEndSymbol(MBB)); diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp index f5d56c1e8e264..6d1ee6bb111fe 100644 --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -27,7 +27,6 @@ #include "PPCTargetMachine.h" #include "TargetInfo/PowerPCTargetInfo.h" #include "llvm/ADT/MapVector.h" -#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" @@ -946,31 +945,6 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) { return PPC::S_None; }; -#ifndef NDEBUG - // Instruction sizes must be correct for PPCBranchSelector to pick the - // right branch kind. Verify that the reported sizes and the actually - // emitted sizes match. - unsigned ExpectedSize = Subtarget->getInstrInfo()->getInstSizeInBytes(*MI); - MCFragment *OldFragment = OutStreamer->getCurrentFragment(); - size_t OldFragSize = OldFragment->getFixedSize(); - scope_exit VerifyInstSize([&]() { - if (!OutStreamer->isObj()) - return; // Can only verify size when streaming to object. - MCFragment *NewFragment = OutStreamer->getCurrentFragment(); - if (NewFragment != OldFragment) - return; // Don't try to handle fragment splitting cases. - unsigned ActualSize = NewFragment->getFixedSize() - OldFragSize; - // FIXME: InstrInfo currently over-estimates the size of STACKMAP. - if (ActualSize != ExpectedSize && - MI->getOpcode() != TargetOpcode::STACKMAP) { - dbgs() << "Size mismatch for: " << *MI << "\n"; - dbgs() << "Expected size: " << ExpectedSize << "\n"; - dbgs() << "Actual size: " << ActualSize << "\n"; - abort(); - } - }); -#endif - // Lower multi-instruction pseudo operations. switch (MI->getOpcode()) { default: break; diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp b/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp index 278fd37d4c229..d1c7bfde4d506 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.cpp @@ -3034,6 +3034,14 @@ unsigned PPCInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { } } +TargetInstrInfo::InstSizeVerifyMode +PPCInstrInfo::getInstSizeVerifyMode(const MachineInstr &MI) const { + // FIXME: The size of STACKMAP is currently over-estimated. + return MI.getOpcode() != TargetOpcode::STACKMAP + ? InstSizeVerifyMode::AllowOverEstimate + : InstSizeVerifyMode::ExactSize; +} + std::pair PPCInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const { // PPC always uses a direct mask. diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.h b/llvm/lib/Target/PowerPC/PPCInstrInfo.h index 9fe5706e7b1e0..adfae11d4cac4 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.h +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.h @@ -700,6 +700,9 @@ class PPCInstrInfo : public PPCGenInstrInfo { /// unsigned getInstSizeInBytes(const MachineInstr &MI) const override; + InstSizeVerifyMode + getInstSizeVerifyMode(const MachineInstr &MI) const override; + MCInst getNop() const override; std::pair