summaryrefslogtreecommitdiff
path: root/unittests/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/CodeGen')
-rw-r--r--unittests/CodeGen/CMakeLists.txt5
-rw-r--r--unittests/CodeGen/GlobalISel/CMakeLists.txt8
-rw-r--r--unittests/CodeGen/GlobalISel/LegalizerInfoTest.cpp121
-rw-r--r--unittests/CodeGen/LowLevelTypeTest.cpp78
-rw-r--r--unittests/CodeGen/MachineInstrTest.cpp247
-rw-r--r--unittests/CodeGen/MachineOperandTest.cpp339
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