diff options
Diffstat (limited to 'unittests/CodeGen')
| -rw-r--r-- | unittests/CodeGen/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | unittests/CodeGen/GlobalISel/CMakeLists.txt | 8 | ||||
| -rw-r--r-- | unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp | 121 | ||||
| -rw-r--r-- | unittests/CodeGen/LowLevelTypeTest.cpp | 78 | ||||
| -rw-r--r-- | unittests/CodeGen/MachineInstrTest.cpp | 247 | ||||
| -rw-r--r-- | unittests/CodeGen/MachineOperandTest.cpp | 339 |
6 files changed, 692 insertions, 106 deletions
diff --git a/unittests/CodeGen/CMakeLists.txt b/unittests/CodeGen/CMakeLists.txt index e944f6c9e3b9..05b72ce05b7b 100644 --- a/unittests/CodeGen/CMakeLists.txt +++ b/unittests/CodeGen/CMakeLists.txt @@ -2,13 +2,18 @@ set(LLVM_LINK_COMPONENTS AsmPrinter CodeGen Core + MC + SelectionDAG Support + Target ) set(CodeGenSources DIEHashTest.cpp LowLevelTypeTest.cpp MachineInstrBundleIteratorTest.cpp + MachineInstrTest.cpp + MachineOperandTest.cpp ScalableVectorMVTsTest.cpp ) diff --git a/unittests/CodeGen/GlobalISel/CMakeLists.txt b/unittests/CodeGen/GlobalISel/CMakeLists.txt index 94e31159c6bb..075bb44bc330 100644 --- a/unittests/CodeGen/GlobalISel/CMakeLists.txt +++ b/unittests/CodeGen/GlobalISel/CMakeLists.txt @@ -3,8 +3,6 @@ set(LLVM_LINK_COMPONENTS CodeGen ) -if(LLVM_BUILD_GLOBAL_ISEL) - add_llvm_unittest(GlobalISelTests - LegalizerInfoTest.cpp - ) -endif() +add_llvm_unittest(GlobalISelTests + LegalizerInfoTest.cpp + ) diff --git a/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp b/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp index 0e881759656d..ca7b47e87eda 100644 --- a/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp +++ b/unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" -#include "llvm/Target/TargetOpcodes.h" +#include "llvm/CodeGen/TargetOpcodes.h" #include "gtest/gtest.h" using namespace llvm; @@ -49,66 +49,91 @@ TEST(LegalizerInfoTest, ScalarRISC) { using namespace TargetOpcode; LegalizerInfo L; // Typical RISCy set of operations based on AArch64. - L.setAction({G_ADD, LLT::scalar(8)}, LegalizerInfo::WidenScalar); - L.setAction({G_ADD, LLT::scalar(16)}, LegalizerInfo::WidenScalar); - L.setAction({G_ADD, LLT::scalar(32)}, LegalizerInfo::Legal); - L.setAction({G_ADD, LLT::scalar(64)}, LegalizerInfo::Legal); + for (unsigned Op : {G_ADD, G_SUB}) { + for (unsigned Size : {32, 64}) + L.setAction({Op, 0, LLT::scalar(Size)}, LegalizerInfo::Legal); + L.setLegalizeScalarToDifferentSizeStrategy( + Op, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest); + } + L.computeTables(); - // Check we infer the correct types and actually do what we're told. - ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(8)}), - std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); - ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(16)}), - std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); - ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(32)}), - std::make_pair(LegalizerInfo::Legal, LLT::scalar(32))); - ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(64)}), - std::make_pair(LegalizerInfo::Legal, LLT::scalar(64))); - - // Make sure the default for over-sized types applies. - ASSERT_EQ(L.getAction({G_ADD, LLT::scalar(128)}), - std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64))); + for (unsigned opcode : {G_ADD, G_SUB}) { + // Check we infer the correct types and actually do what we're told. + ASSERT_EQ(L.getAction({opcode, LLT::scalar(8)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); + ASSERT_EQ(L.getAction({opcode, LLT::scalar(16)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); + ASSERT_EQ(L.getAction({opcode, LLT::scalar(32)}), + std::make_pair(LegalizerInfo::Legal, LLT::scalar(32))); + ASSERT_EQ(L.getAction({opcode, LLT::scalar(64)}), + std::make_pair(LegalizerInfo::Legal, LLT::scalar(64))); + + // Make sure the default for over-sized types applies. + ASSERT_EQ(L.getAction({opcode, LLT::scalar(128)}), + std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64))); + // Make sure we also handle unusual sizes + ASSERT_EQ(L.getAction({opcode, LLT::scalar(1)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); + ASSERT_EQ(L.getAction({opcode, LLT::scalar(31)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); + ASSERT_EQ(L.getAction({opcode, LLT::scalar(33)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(64))); + ASSERT_EQ(L.getAction({opcode, LLT::scalar(63)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(64))); + ASSERT_EQ(L.getAction({opcode, LLT::scalar(65)}), + std::make_pair(LegalizerInfo::NarrowScalar, LLT::scalar(64))); + } } TEST(LegalizerInfoTest, VectorRISC) { using namespace TargetOpcode; LegalizerInfo L; // Typical RISCy set of operations based on ARM. - L.setScalarInVectorAction(G_ADD, LLT::scalar(8), LegalizerInfo::Legal); - L.setScalarInVectorAction(G_ADD, LLT::scalar(16), LegalizerInfo::Legal); - L.setScalarInVectorAction(G_ADD, LLT::scalar(32), LegalizerInfo::Legal); - L.setAction({G_ADD, LLT::vector(8, 8)}, LegalizerInfo::Legal); L.setAction({G_ADD, LLT::vector(16, 8)}, LegalizerInfo::Legal); L.setAction({G_ADD, LLT::vector(4, 16)}, LegalizerInfo::Legal); L.setAction({G_ADD, LLT::vector(8, 16)}, LegalizerInfo::Legal); L.setAction({G_ADD, LLT::vector(2, 32)}, LegalizerInfo::Legal); L.setAction({G_ADD, LLT::vector(4, 32)}, LegalizerInfo::Legal); + + L.setLegalizeVectorElementToDifferentSizeStrategy( + G_ADD, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise); + + L.setAction({G_ADD, 0, LLT::scalar(32)}, LegalizerInfo::Legal); + L.computeTables(); // Check we infer the correct types and actually do what we're told for some // simple cases. - ASSERT_EQ(L.getAction({G_ADD, LLT::vector(2, 8)}), - std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8))); ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 8)}), std::make_pair(LegalizerInfo::Legal, LLT::vector(8, 8))); - ASSERT_EQ( - L.getAction({G_ADD, LLT::vector(8, 32)}), - std::make_pair(LegalizerInfo::FewerElements, LLT::vector(4, 32))); + ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 7)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::vector(8, 8))); + ASSERT_EQ(L.getAction({G_ADD, LLT::vector(2, 8)}), + std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8))); + ASSERT_EQ(L.getAction({G_ADD, LLT::vector(8, 32)}), + std::make_pair(LegalizerInfo::FewerElements, LLT::vector(4, 32))); + // Check a few non-power-of-2 sizes: + ASSERT_EQ(L.getAction({G_ADD, LLT::vector(3, 3)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::vector(3, 8))); + ASSERT_EQ(L.getAction({G_ADD, LLT::vector(3, 8)}), + std::make_pair(LegalizerInfo::MoreElements, LLT::vector(8, 8))); } TEST(LegalizerInfoTest, MultipleTypes) { using namespace TargetOpcode; LegalizerInfo L; LLT p0 = LLT::pointer(0, 64); - LLT s32 = LLT::scalar(32); LLT s64 = LLT::scalar(64); // Typical RISCy set of operations based on AArch64. L.setAction({G_PTRTOINT, 0, s64}, LegalizerInfo::Legal); L.setAction({G_PTRTOINT, 1, p0}, LegalizerInfo::Legal); - L.setAction({G_PTRTOINT, 0, s32}, LegalizerInfo::WidenScalar); + L.setLegalizeScalarToDifferentSizeStrategy( + G_PTRTOINT, 0, LegalizerInfo::widenToLargerTypesAndNarrowToLargest); + L.computeTables(); // Check we infer the correct types and actually do what we're told. @@ -116,16 +141,21 @@ TEST(LegalizerInfoTest, MultipleTypes) { std::make_pair(LegalizerInfo::Legal, s64)); ASSERT_EQ(L.getAction({G_PTRTOINT, 1, p0}), std::make_pair(LegalizerInfo::Legal, p0)); + // Make sure we also handle unusual sizes + ASSERT_EQ(L.getAction({G_PTRTOINT, 0, LLT::scalar(65)}), + std::make_pair(LegalizerInfo::NarrowScalar, s64)); + ASSERT_EQ(L.getAction({G_PTRTOINT, 1, LLT::pointer(0, 32)}), + std::make_pair(LegalizerInfo::Unsupported, LLT::pointer(0, 32))); } TEST(LegalizerInfoTest, MultipleSteps) { using namespace TargetOpcode; LegalizerInfo L; - LLT s16 = LLT::scalar(16); LLT s32 = LLT::scalar(32); LLT s64 = LLT::scalar(64); - L.setAction({G_UREM, 0, s16}, LegalizerInfo::WidenScalar); + L.setLegalizeScalarToDifferentSizeStrategy( + G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise); L.setAction({G_UREM, 0, s32}, LegalizerInfo::Lower); L.setAction({G_UREM, 0, s64}, LegalizerInfo::Lower); @@ -136,4 +166,33 @@ TEST(LegalizerInfoTest, MultipleSteps) { ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(32)}), std::make_pair(LegalizerInfo::Lower, LLT::scalar(32))); } + +TEST(LegalizerInfoTest, SizeChangeStrategy) { + using namespace TargetOpcode; + LegalizerInfo L; + for (unsigned Size : {1, 8, 16, 32}) + L.setAction({G_UREM, 0, LLT::scalar(Size)}, LegalizerInfo::Legal); + + L.setLegalizeScalarToDifferentSizeStrategy( + G_UREM, 0, LegalizerInfo::widenToLargerTypesUnsupportedOtherwise); + L.computeTables(); + + // Check we infer the correct types and actually do what we're told. + for (unsigned Size : {1, 8, 16, 32}) { + ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(Size)}), + std::make_pair(LegalizerInfo::Legal, LLT::scalar(Size))); + } + ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(2)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(8))); + ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(7)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(8))); + ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(9)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(16))); + ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(17)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); + ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(31)}), + std::make_pair(LegalizerInfo::WidenScalar, LLT::scalar(32))); + ASSERT_EQ(L.getAction({G_UREM, LLT::scalar(33)}), + std::make_pair(LegalizerInfo::Unsupported, LLT::scalar(33))); +} } diff --git a/unittests/CodeGen/LowLevelTypeTest.cpp b/unittests/CodeGen/LowLevelTypeTest.cpp index 115554642907..a4765d998562 100644 --- a/unittests/CodeGen/LowLevelTypeTest.cpp +++ b/unittests/CodeGen/LowLevelTypeTest.cpp @@ -36,36 +36,22 @@ TEST(LowLevelTypeTest, Scalar) { for (unsigned S : {1U, 17U, 32U, 64U, 0xfffffU}) { const LLT Ty = LLT::scalar(S); - const LLT HalfTy = (S % 2) == 0 ? Ty.halfScalarSize() : Ty; - const LLT DoubleTy = Ty.doubleScalarSize(); // Test kind. - for (const LLT TestTy : {Ty, HalfTy, DoubleTy}) { - ASSERT_TRUE(TestTy.isValid()); - ASSERT_TRUE(TestTy.isScalar()); + ASSERT_TRUE(Ty.isValid()); + ASSERT_TRUE(Ty.isScalar()); - ASSERT_FALSE(TestTy.isPointer()); - ASSERT_FALSE(TestTy.isVector()); - } + ASSERT_FALSE(Ty.isPointer()); + ASSERT_FALSE(Ty.isVector()); // Test sizes. EXPECT_EQ(S, Ty.getSizeInBits()); EXPECT_EQ(S, Ty.getScalarSizeInBits()); - EXPECT_EQ(S*2, DoubleTy.getSizeInBits()); - EXPECT_EQ(S*2, DoubleTy.getScalarSizeInBits()); - - if ((S % 2) == 0) { - EXPECT_EQ(S/2, HalfTy.getSizeInBits()); - EXPECT_EQ(S/2, HalfTy.getScalarSizeInBits()); - } - // Test equality operators. EXPECT_TRUE(Ty == Ty); EXPECT_FALSE(Ty != Ty); - EXPECT_NE(Ty, DoubleTy); - // Test Type->LLT conversion. Type *IRTy = IntegerType::get(C, S); EXPECT_EQ(Ty, getLLTForType(*IRTy, DL)); @@ -90,62 +76,18 @@ TEST(LowLevelTypeTest, Vector) { // Test getElementType(). EXPECT_EQ(STy, VTy.getElementType()); - const LLT HalfSzTy = ((S % 2) == 0) ? VTy.halfScalarSize() : VTy; - const LLT DoubleSzTy = VTy.doubleScalarSize(); - - // halfElements requires an even number of elements. - const LLT HalfEltIfEvenTy = ((Elts % 2) == 0) ? VTy.halfElements() : VTy; - const LLT DoubleEltTy = VTy.doubleElements(); - // Test kind. - for (const LLT TestTy : {VTy, HalfSzTy, DoubleSzTy, DoubleEltTy}) { - ASSERT_TRUE(TestTy.isValid()); - ASSERT_TRUE(TestTy.isVector()); - - ASSERT_FALSE(TestTy.isScalar()); - ASSERT_FALSE(TestTy.isPointer()); - } - - // Test halving elements to a scalar. - { - ASSERT_TRUE(HalfEltIfEvenTy.isValid()); - ASSERT_FALSE(HalfEltIfEvenTy.isPointer()); - if (Elts > 2) { - ASSERT_TRUE(HalfEltIfEvenTy.isVector()); - } else { - ASSERT_FALSE(HalfEltIfEvenTy.isVector()); - EXPECT_EQ(STy, HalfEltIfEvenTy); - } - } + ASSERT_TRUE(VTy.isValid()); + ASSERT_TRUE(VTy.isVector()); + ASSERT_FALSE(VTy.isScalar()); + ASSERT_FALSE(VTy.isPointer()); // Test sizes. EXPECT_EQ(S * Elts, VTy.getSizeInBits()); EXPECT_EQ(S, VTy.getScalarSizeInBits()); EXPECT_EQ(Elts, VTy.getNumElements()); - if ((S % 2) == 0) { - EXPECT_EQ((S / 2) * Elts, HalfSzTy.getSizeInBits()); - EXPECT_EQ(S / 2, HalfSzTy.getScalarSizeInBits()); - EXPECT_EQ(Elts, HalfSzTy.getNumElements()); - } - - EXPECT_EQ((S * 2) * Elts, DoubleSzTy.getSizeInBits()); - EXPECT_EQ(S * 2, DoubleSzTy.getScalarSizeInBits()); - EXPECT_EQ(Elts, DoubleSzTy.getNumElements()); - - if ((Elts % 2) == 0) { - EXPECT_EQ(S * (Elts / 2), HalfEltIfEvenTy.getSizeInBits()); - EXPECT_EQ(S, HalfEltIfEvenTy.getScalarSizeInBits()); - if (Elts > 2) { - EXPECT_EQ(Elts / 2, HalfEltIfEvenTy.getNumElements()); - } - } - - EXPECT_EQ(S * (Elts * 2), DoubleEltTy.getSizeInBits()); - EXPECT_EQ(S, DoubleEltTy.getScalarSizeInBits()); - EXPECT_EQ(Elts * 2, DoubleEltTy.getNumElements()); - // Test equality operators. EXPECT_TRUE(VTy == VTy); EXPECT_FALSE(VTy != VTy); @@ -153,10 +95,6 @@ TEST(LowLevelTypeTest, Vector) { // Test inequality operators on.. // ..different kind. EXPECT_NE(VTy, STy); - // ..different #elts. - EXPECT_NE(VTy, DoubleEltTy); - // ..different scalar size. - EXPECT_NE(VTy, DoubleSzTy); // Test Type->LLT conversion. Type *IRSTy = IntegerType::get(C, S); diff --git a/unittests/CodeGen/MachineInstrTest.cpp b/unittests/CodeGen/MachineInstrTest.cpp new file mode 100644 index 000000000000..aca640ebcf35 --- /dev/null +++ b/unittests/CodeGen/MachineInstrTest.cpp @@ -0,0 +1,247 @@ +//===- 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/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); +} +} // end namespace diff --git a/unittests/CodeGen/MachineOperandTest.cpp b/unittests/CodeGen/MachineOperandTest.cpp new file mode 100644 index 000000000000..e51207b95716 --- /dev/null +++ b/unittests/CodeGen/MachineOperandTest.cpp @@ -0,0 +1,339 @@ +//===- MachineOperandTest.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/MachineOperand.h" +#include "llvm/ADT/ilist_node.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/ModuleSlotTracker.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +TEST(MachineOperandTest, ChangeToTargetIndexTest) { + // Creating a MachineOperand to change it to TargetIndex + MachineOperand MO = MachineOperand::CreateImm(50); + + // Checking some precondition on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isImm()); + ASSERT_TRUE(MO.getImm() == 50); + ASSERT_FALSE(MO.isTargetIndex()); + + // Changing to TargetIndex with some arbitrary values + // for index, offset and flags. + MO.ChangeToTargetIndex(74, 57, 12); + + // Checking that the mutation to TargetIndex happened + // correctly. + ASSERT_TRUE(MO.isTargetIndex()); + ASSERT_TRUE(MO.getIndex() == 74); + ASSERT_TRUE(MO.getOffset() == 57); + ASSERT_TRUE(MO.getTargetFlags() == 12); +} + +TEST(MachineOperandTest, PrintRegisterMask) { + uint32_t Dummy; + MachineOperand MO = MachineOperand::CreateRegMask(&Dummy); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isRegMask()); + ASSERT_TRUE(MO.getRegMask() == &Dummy); + + // Print a MachineOperand containing a RegMask. Here we check that without a + // TRI and IntrinsicInfo we still print a less detailed regmask. + std::string str; + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "<regmask ...>"); +} + +TEST(MachineOperandTest, PrintSubReg) { + // Create a MachineOperand with RegNum=1 and SubReg=5. + MachineOperand MO = MachineOperand::CreateReg( + /*Reg=*/1, /*isDef=*/false, /*isImp=*/false, /*isKill=*/false, + /*isDead=*/false, /*isUndef=*/false, /*isEarlyClobber=*/false, + /*SubReg=*/5, /*isDebug=*/false, /*isInternalRead=*/false); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isReg()); + ASSERT_TRUE(MO.getReg() == 1); + ASSERT_TRUE(MO.getSubReg() == 5); + + // Print a MachineOperand containing a SubReg. Here we check that without a + // TRI and IntrinsicInfo we can still print the subreg index. + std::string str; + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "%physreg1.subreg5"); +} + +TEST(MachineOperandTest, PrintCImm) { + LLVMContext Context; + APInt Int(128, UINT64_MAX); + ++Int; + ConstantInt *CImm = ConstantInt::get(Context, Int); + // Create a MachineOperand with an Imm=(UINT64_MAX + 1) + MachineOperand MO = MachineOperand::CreateCImm(CImm); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isCImm()); + ASSERT_TRUE(MO.getCImm() == CImm); + ASSERT_TRUE(MO.getCImm()->getValue() == Int); + + // Print a MachineOperand containing a SubReg. Here we check that without a + // TRI and IntrinsicInfo we can still print the subreg index. + std::string str; + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "i128 18446744073709551616"); +} + +TEST(MachineOperandTest, PrintSubRegIndex) { + // Create a MachineOperand with an immediate and print it as a subreg index. + MachineOperand MO = MachineOperand::CreateImm(3); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isImm()); + ASSERT_TRUE(MO.getImm() == 3); + + // Print a MachineOperand containing a SubRegIdx. Here we check that without a + // TRI and IntrinsicInfo we can print the operand as a subreg index. + std::string str; + raw_string_ostream OS(str); + ModuleSlotTracker DummyMST(nullptr); + MachineOperand::printSubregIdx(OS, MO.getImm(), nullptr); + ASSERT_TRUE(OS.str() == "%subreg.3"); +} + +TEST(MachineOperandTest, PrintCPI) { + // Create a MachineOperand with a constant pool index and print it. + MachineOperand MO = MachineOperand::CreateCPI(0, 8); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isCPI()); + ASSERT_TRUE(MO.getIndex() == 0); + ASSERT_TRUE(MO.getOffset() == 8); + + // Print a MachineOperand containing a constant pool index and a positive + // offset. + std::string str; + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "%const.0 + 8"); + } + + str.clear(); + + MO.setOffset(-12); + + // Print a MachineOperand containing a constant pool index and a negative + // offset. + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "%const.0 - 12"); + } +} + +TEST(MachineOperandTest, PrintTargetIndexName) { + // Create a MachineOperand with a target index and print it. + MachineOperand MO = MachineOperand::CreateTargetIndex(0, 8); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isTargetIndex()); + ASSERT_TRUE(MO.getIndex() == 0); + ASSERT_TRUE(MO.getOffset() == 8); + + // Print a MachineOperand containing a target index and a positive offset. + std::string str; + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "target-index(<unknown>) + 8"); + } + + str.clear(); + + MO.setOffset(-12); + + // Print a MachineOperand containing a target index and a negative offset. + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "target-index(<unknown>) - 12"); + } +} + +TEST(MachineOperandTest, PrintJumpTableIndex) { + // Create a MachineOperand with a jump-table index and print it. + MachineOperand MO = MachineOperand::CreateJTI(3); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isJTI()); + ASSERT_TRUE(MO.getIndex() == 3); + + // Print a MachineOperand containing a jump-table index. + std::string str; + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "%jump-table.3"); +} + +TEST(MachineOperandTest, PrintExternalSymbol) { + // Create a MachineOperand with an external symbol and print it. + MachineOperand MO = MachineOperand::CreateES("foo"); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isSymbol()); + ASSERT_TRUE(MO.getSymbolName() == StringRef("foo")); + + // Print a MachineOperand containing an external symbol and no offset. + std::string str; + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "$foo"); + } + + str.clear(); + MO.setOffset(12); + + // Print a MachineOperand containing an external symbol and a positive offset. + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "$foo + 12"); + } + + str.clear(); + MO.setOffset(-12); + + // Print a MachineOperand containing an external symbol and a negative offset. + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "$foo - 12"); + } +} + +TEST(MachineOperandTest, PrintGlobalAddress) { + LLVMContext Ctx; + Module M("MachineOperandGVTest", Ctx); + M.getOrInsertGlobal("foo", Type::getInt32Ty(Ctx)); + + GlobalValue *GV = M.getNamedValue("foo"); + + // Create a MachineOperand with a global address and a positive offset and + // print it. + MachineOperand MO = MachineOperand::CreateGA(GV, 12); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isGlobal()); + ASSERT_TRUE(MO.getGlobal() == GV); + ASSERT_TRUE(MO.getOffset() == 12); + + std::string str; + // Print a MachineOperand containing a global address and a positive offset. + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "@foo + 12"); + } + + str.clear(); + MO.setOffset(-12); + + // Print a MachineOperand containing a global address and a negative offset. + { + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "@foo - 12"); + } +} + +TEST(MachineOperandTest, PrintRegisterLiveOut) { + // Create a MachineOperand with a register live out list and print it. + uint32_t Mask = 0; + MachineOperand MO = MachineOperand::CreateRegLiveOut(&Mask); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isRegLiveOut()); + ASSERT_TRUE(MO.getRegLiveOut() == &Mask); + + std::string str; + // Print a MachineOperand containing a register live out list without a TRI. + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "liveout(<unknown>)"); +} + +TEST(MachineOperandTest, PrintMetadata) { + LLVMContext Ctx; + Module M("MachineOperandMDNodeTest", Ctx); + NamedMDNode *MD = M.getOrInsertNamedMetadata("namedmd"); + ModuleSlotTracker DummyMST(&M); + Metadata *MDS = MDString::get(Ctx, "foo"); + MDNode *Node = MDNode::get(Ctx, MDS); + MD->addOperand(Node); + + // Create a MachineOperand with a metadata and print it. + MachineOperand MO = MachineOperand::CreateMetadata(Node); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isMetadata()); + ASSERT_TRUE(MO.getMetadata() == Node); + + std::string str; + // Print a MachineOperand containing a metadata node. + raw_string_ostream OS(str); + MO.print(OS, DummyMST, LLT{}, false, false, 0, /*TRI=*/nullptr, + /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "!0"); +} + +TEST(MachineOperandTest, PrintMCSymbol) { + MCAsmInfo MAI; + MCContext Ctx(&MAI, /*MRI=*/nullptr, /*MOFI=*/nullptr); + MCSymbol *Sym = Ctx.getOrCreateSymbol("foo"); + + // Create a MachineOperand with a metadata and print it. + MachineOperand MO = MachineOperand::CreateMCSymbol(Sym); + + // Checking some preconditions on the newly created + // MachineOperand. + ASSERT_TRUE(MO.isMCSymbol()); + ASSERT_TRUE(MO.getMCSymbol() == Sym); + + std::string str; + // Print a MachineOperand containing a metadata node. + raw_string_ostream OS(str); + MO.print(OS, /*TRI=*/nullptr, /*IntrinsicInfo=*/nullptr); + ASSERT_TRUE(OS.str() == "<mcsymbol foo>"); +} + +} // end namespace |
