| //===- MachineInstrTest.cpp -----------------------------------------------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/CodeGen/MachineInstr.h" | |
| #include "llvm/CodeGen/MachineFunction.h" | |
| #include "llvm/CodeGen/MachineModuleInfo.h" | |
| #include "llvm/CodeGen/TargetFrameLowering.h" | |
| #include "llvm/CodeGen/TargetInstrInfo.h" | |
| #include "llvm/CodeGen/TargetLowering.h" | |
| #include "llvm/CodeGen/TargetSubtargetInfo.h" | |
| #include "llvm/IR/DebugInfoMetadata.h" | |
| #include "llvm/IR/ModuleSlotTracker.h" | |
| #include "llvm/Support/TargetRegistry.h" | |
| #include "llvm/Support/TargetSelect.h" | |
| #include "llvm/Target/TargetMachine.h" | |
| #include "llvm/Target/TargetOptions.h" | |
| #include "gtest/gtest.h" | |
| using namespace llvm; | |
| namespace { | |
| // Add a few Bogus backend classes so we can create MachineInstrs without | |
| // depending on a real target. | |
| class BogusTargetLowering : public TargetLowering { | |
| public: | |
| BogusTargetLowering(TargetMachine &TM) : TargetLowering(TM) {} | |
| }; | |
| class BogusFrameLowering : public TargetFrameLowering { | |
| public: | |
| BogusFrameLowering() | |
| : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, 4, 4) {} | |
| void emitPrologue(MachineFunction &MF, | |
| MachineBasicBlock &MBB) const override {} | |
| void emitEpilogue(MachineFunction &MF, | |
| MachineBasicBlock &MBB) const override {} | |
| bool hasFP(const MachineFunction &MF) const override { return false; } | |
| }; | |
| class BogusSubtarget : public TargetSubtargetInfo { | |
| public: | |
| BogusSubtarget(TargetMachine &TM) | |
| : TargetSubtargetInfo(Triple(""), "", "", {}, {}, nullptr, nullptr, | |
| nullptr, nullptr, nullptr, nullptr, nullptr), | |
| FL(), TL(TM) {} | |
| ~BogusSubtarget() override {} | |
| const TargetFrameLowering *getFrameLowering() const override { return &FL; } | |
| const TargetLowering *getTargetLowering() const override { return &TL; } | |
| const TargetInstrInfo *getInstrInfo() const override { return &TII; } | |
| private: | |
| BogusFrameLowering FL; | |
| BogusTargetLowering TL; | |
| TargetInstrInfo TII; | |
| }; | |
| class BogusTargetMachine : public LLVMTargetMachine { | |
| public: | |
| BogusTargetMachine() | |
| : LLVMTargetMachine(Target(), "", Triple(""), "", "", TargetOptions(), | |
| Reloc::Static, CodeModel::Small, CodeGenOpt::Default), | |
| ST(*this) {} | |
| ~BogusTargetMachine() override {} | |
| const TargetSubtargetInfo *getSubtargetImpl(const Function &) const override { | |
| return &ST; | |
| } | |
| private: | |
| BogusSubtarget ST; | |
| }; | |
| std::unique_ptr<BogusTargetMachine> createTargetMachine() { | |
| return llvm::make_unique<BogusTargetMachine>(); | |
| } | |
| std::unique_ptr<MachineFunction> createMachineFunction() { | |
| LLVMContext Ctx; | |
| Module M("Module", Ctx); | |
| auto Type = FunctionType::get(Type::getVoidTy(Ctx), false); | |
| auto F = Function::Create(Type, GlobalValue::ExternalLinkage, "Test", &M); | |
| auto TM = createTargetMachine(); | |
| unsigned FunctionNum = 42; | |
| MachineModuleInfo MMI(TM.get()); | |
| const TargetSubtargetInfo &STI = *TM->getSubtargetImpl(*F); | |
| return llvm::make_unique<MachineFunction>(*F, *TM, STI, FunctionNum, MMI); | |
| } | |
| // This test makes sure that MachineInstr::isIdenticalTo handles Defs correctly | |
| // for various combinations of IgnoreDefs, and also that it is symmetrical. | |
| TEST(IsIdenticalToTest, DifferentDefs) { | |
| auto MF = createMachineFunction(); | |
| unsigned short NumOps = 2; | |
| unsigned char NumDefs = 1; | |
| MCOperandInfo OpInfo[] = { | |
| {0, 0, MCOI::OPERAND_REGISTER, 0}, | |
| {0, 1 << MCOI::OptionalDef, MCOI::OPERAND_REGISTER, 0}}; | |
| MCInstrDesc MCID = { | |
| 0, NumOps, NumDefs, 0, 0, 1ULL << MCID::HasOptionalDef, | |
| 0, nullptr, nullptr, OpInfo, 0, nullptr}; | |
| // Create two MIs with different virtual reg defs and the same uses. | |
| unsigned VirtualDef1 = -42; // The value doesn't matter, but the sign does. | |
| unsigned VirtualDef2 = -43; | |
| unsigned VirtualUse = -44; | |
| auto MI1 = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| MI1->addOperand(*MF, MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true)); | |
| MI1->addOperand(*MF, MachineOperand::CreateReg(VirtualUse, /*isDef*/ false)); | |
| auto MI2 = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| MI2->addOperand(*MF, MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true)); | |
| MI2->addOperand(*MF, MachineOperand::CreateReg(VirtualUse, /*isDef*/ false)); | |
| // Check that they are identical when we ignore virtual register defs, but not | |
| // when we check defs. | |
| ASSERT_FALSE(MI1->isIdenticalTo(*MI2, MachineInstr::CheckDefs)); | |
| ASSERT_FALSE(MI2->isIdenticalTo(*MI1, MachineInstr::CheckDefs)); | |
| ASSERT_TRUE(MI1->isIdenticalTo(*MI2, MachineInstr::IgnoreVRegDefs)); | |
| ASSERT_TRUE(MI2->isIdenticalTo(*MI1, MachineInstr::IgnoreVRegDefs)); | |
| // Create two MIs with different virtual reg defs, and a def or use of a | |
| // sentinel register. | |
| unsigned SentinelReg = 0; | |
| auto MI3 = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| MI3->addOperand(*MF, MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true)); | |
| MI3->addOperand(*MF, MachineOperand::CreateReg(SentinelReg, /*isDef*/ true)); | |
| auto MI4 = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| MI4->addOperand(*MF, MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true)); | |
| MI4->addOperand(*MF, MachineOperand::CreateReg(SentinelReg, /*isDef*/ false)); | |
| // Check that they are never identical. | |
| ASSERT_FALSE(MI3->isIdenticalTo(*MI4, MachineInstr::CheckDefs)); | |
| ASSERT_FALSE(MI4->isIdenticalTo(*MI3, MachineInstr::CheckDefs)); | |
| ASSERT_FALSE(MI3->isIdenticalTo(*MI4, MachineInstr::IgnoreVRegDefs)); | |
| ASSERT_FALSE(MI4->isIdenticalTo(*MI3, MachineInstr::IgnoreVRegDefs)); | |
| } | |
| // Check that MachineInstrExpressionTrait::isEqual is symmetric and in sync with | |
| // MachineInstrExpressionTrait::getHashValue | |
| void checkHashAndIsEqualMatch(MachineInstr *MI1, MachineInstr *MI2) { | |
| bool IsEqual1 = MachineInstrExpressionTrait::isEqual(MI1, MI2); | |
| bool IsEqual2 = MachineInstrExpressionTrait::isEqual(MI2, MI1); | |
| ASSERT_EQ(IsEqual1, IsEqual2); | |
| auto Hash1 = MachineInstrExpressionTrait::getHashValue(MI1); | |
| auto Hash2 = MachineInstrExpressionTrait::getHashValue(MI2); | |
| ASSERT_EQ(IsEqual1, Hash1 == Hash2); | |
| } | |
| // This test makes sure that MachineInstrExpressionTraits::isEqual is in sync | |
| // with MachineInstrExpressionTraits::getHashValue. | |
| TEST(MachineInstrExpressionTraitTest, IsEqualAgreesWithGetHashValue) { | |
| auto MF = createMachineFunction(); | |
| unsigned short NumOps = 2; | |
| unsigned char NumDefs = 1; | |
| MCOperandInfo OpInfo[] = { | |
| {0, 0, MCOI::OPERAND_REGISTER, 0}, | |
| {0, 1 << MCOI::OptionalDef, MCOI::OPERAND_REGISTER, 0}}; | |
| MCInstrDesc MCID = { | |
| 0, NumOps, NumDefs, 0, 0, 1ULL << MCID::HasOptionalDef, | |
| 0, nullptr, nullptr, OpInfo, 0, nullptr}; | |
| // Define a series of instructions with different kinds of operands and make | |
| // sure that the hash function is consistent with isEqual for various | |
| // combinations of them. | |
| unsigned VirtualDef1 = -42; | |
| unsigned VirtualDef2 = -43; | |
| unsigned VirtualReg = -44; | |
| unsigned SentinelReg = 0; | |
| unsigned PhysicalReg = 45; | |
| auto VD1VU = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| VD1VU->addOperand(*MF, | |
| MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true)); | |
| VD1VU->addOperand(*MF, | |
| MachineOperand::CreateReg(VirtualReg, /*isDef*/ false)); | |
| auto VD2VU = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| VD2VU->addOperand(*MF, | |
| MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true)); | |
| VD2VU->addOperand(*MF, | |
| MachineOperand::CreateReg(VirtualReg, /*isDef*/ false)); | |
| auto VD1SU = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| VD1SU->addOperand(*MF, | |
| MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true)); | |
| VD1SU->addOperand(*MF, | |
| MachineOperand::CreateReg(SentinelReg, /*isDef*/ false)); | |
| auto VD1SD = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| VD1SD->addOperand(*MF, | |
| MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true)); | |
| VD1SD->addOperand(*MF, | |
| MachineOperand::CreateReg(SentinelReg, /*isDef*/ true)); | |
| auto VD2PU = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| VD2PU->addOperand(*MF, | |
| MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true)); | |
| VD2PU->addOperand(*MF, | |
| MachineOperand::CreateReg(PhysicalReg, /*isDef*/ false)); | |
| auto VD2PD = MF->CreateMachineInstr(MCID, DebugLoc()); | |
| VD2PD->addOperand(*MF, | |
| MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true)); | |
| VD2PD->addOperand(*MF, | |
| MachineOperand::CreateReg(PhysicalReg, /*isDef*/ true)); | |
| checkHashAndIsEqualMatch(VD1VU, VD2VU); | |
| checkHashAndIsEqualMatch(VD1VU, VD1SU); | |
| checkHashAndIsEqualMatch(VD1VU, VD1SD); | |
| checkHashAndIsEqualMatch(VD1VU, VD2PU); | |
| checkHashAndIsEqualMatch(VD1VU, VD2PD); | |
| checkHashAndIsEqualMatch(VD2VU, VD1SU); | |
| checkHashAndIsEqualMatch(VD2VU, VD1SD); | |
| checkHashAndIsEqualMatch(VD2VU, VD2PU); | |
| checkHashAndIsEqualMatch(VD2VU, VD2PD); | |
| checkHashAndIsEqualMatch(VD1SU, VD1SD); | |
| checkHashAndIsEqualMatch(VD1SU, VD2PU); | |
| checkHashAndIsEqualMatch(VD1SU, VD2PD); | |
| checkHashAndIsEqualMatch(VD1SD, VD2PU); | |
| checkHashAndIsEqualMatch(VD1SD, VD2PD); | |
| checkHashAndIsEqualMatch(VD2PU, VD2PD); | |
| } | |
| TEST(MachineInstrPrintingTest, DebugLocPrinting) { | |
| auto MF = createMachineFunction(); | |
| MCOperandInfo OpInfo{0, 0, MCOI::OPERAND_REGISTER, 0}; | |
| MCInstrDesc MCID = {0, 1, 1, 0, 0, 0, | |
| 0, nullptr, nullptr, &OpInfo, 0, nullptr}; | |
| LLVMContext Ctx; | |
| DIFile *DIF = DIFile::getDistinct(Ctx, "filename", ""); | |
| DISubprogram *DIS = DISubprogram::getDistinct( | |
| Ctx, nullptr, "", "", DIF, 0, nullptr, false, false, 0, nullptr, 0, 0, 0, | |
| DINode::FlagZero, false, nullptr); | |
| DILocation *DIL = DILocation::get(Ctx, 1, 5, DIS); | |
| DebugLoc DL(DIL); | |
| MachineInstr *MI = MF->CreateMachineInstr(MCID, DL); | |
| MI->addOperand(*MF, MachineOperand::CreateReg(0, /*isDef*/ true)); | |
| std::string str; | |
| raw_string_ostream OS(str); | |
| MI->print(OS); | |
| ASSERT_TRUE( | |
| StringRef(OS.str()).startswith("$noreg = UNKNOWN debug-location ")); | |
| ASSERT_TRUE( | |
| StringRef(OS.str()).endswith("filename:1:5")); | |
| } | |
| } // end namespace |