
As alluded to in #20571, it would be nice if we could mutate operand lists of MachineInstr's more safely. Add an insert method that together with removeOperand allows for easier splicing of operands. Splitting this patch off early to get feedback; I need to either: - mutate an INLINEASM{_BR} MachinInstr's MachineOperands from being registers (physical or virtual) to memory (MachineOperandType::MO_FrameIndex). These are not 1:1 operand replacements, but N:M operand replacements. i.e. we need to update 2 MachineOperands into the middle of the operand list to 5 (at least for x86_64). - copy, modify, write a new MachineInstr which has its relevant operands replaced. Either approaches are hazarded by existing references to either the operands being moved, or the instruction being removed+replaced. For my purposes in regalloc, either seem to work for me, so hopefully reviewers can help me determine which approach is preferable. The second would involve no new methods on MachineInstr. One question I had while looking at this was: "why does MachineInstr have BOTH a NumOperands member AND a MCInstrDesc member that itself has a NumOperands member? How many operands can a MachineInstr have? Do I need to update BOTH (keeping them in sync)?" FWICT, only "variadic" MachineInstrs have MCInstrDesc with NumOperands (of the MCInstrDesc) set to zero. If the MCInstrDesc's NumOperands is non-zero, then the NumOperands on the MachineInstr itself cannot exceed this value (IIUC) else an assert will be triggered. For most non-psuedo instructions (or at least non-varidic instructions), insert is less likely to be useful. To run the newly added unittest: $ pushd llvm/build; ninja CodeGenTests; popd $ ./llvm/build/unittests/CodeGen/CodeGenTests \ --gtest_filter=MachineInstrTest.SpliceOperands This is meant to mirror `MCInst::insert`.
565 lines
21 KiB
C++
565 lines
21 KiB
C++
//===- MachineInstrTest.cpp -----------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
#include "llvm/CodeGen/MachineBasicBlock.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineMemOperand.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/IRBuilder.h"
|
|
#include "llvm/IR/ModuleSlotTracker.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCSymbol.h"
|
|
#include "llvm/MC/TargetRegistry.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
#include "llvm/Target/TargetOptions.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
// Include helper functions to ease the manipulation of MachineFunctions.
|
|
#include "MFCommon.inc"
|
|
|
|
std::unique_ptr<MCContext> createMCContext(MCAsmInfo *AsmInfo) {
|
|
Triple TheTriple(/*ArchStr=*/"", /*VendorStr=*/"", /*OSStr=*/"",
|
|
/*EnvironmentStr=*/"elf");
|
|
return std::make_unique<MCContext>(TheTriple, AsmInfo, nullptr, nullptr,
|
|
nullptr, nullptr, false);
|
|
}
|
|
|
|
// This test makes sure that MachineInstr::isIdenticalTo handles Defs correctly
|
|
// for various combinations of IgnoreDefs, and also that it is symmetrical.
|
|
TEST(IsIdenticalToTest, DifferentDefs) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
|
|
unsigned short NumOps = 2;
|
|
unsigned char NumDefs = 1;
|
|
struct {
|
|
MCInstrDesc MCID;
|
|
MCOperandInfo OpInfo[2];
|
|
} Table = {
|
|
{0, NumOps, NumDefs, 0, 0, 0, 0, 0, 0, 1ULL << MCID::HasOptionalDef, 0},
|
|
{{0, 0, MCOI::OPERAND_REGISTER, 0},
|
|
{0, 1 << MCOI::OptionalDef, MCOI::OPERAND_REGISTER, 0}}};
|
|
|
|
// 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(Table.MCID, DebugLoc());
|
|
MI1->addOperand(*MF, MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));
|
|
MI1->addOperand(*MF, MachineOperand::CreateReg(VirtualUse, /*isDef*/ false));
|
|
|
|
auto MI2 = MF->CreateMachineInstr(Table.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(Table.MCID, DebugLoc());
|
|
MI3->addOperand(*MF, MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));
|
|
MI3->addOperand(*MF, MachineOperand::CreateReg(SentinelReg, /*isDef*/ true));
|
|
|
|
auto MI4 = MF->CreateMachineInstr(Table.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) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
|
|
unsigned short NumOps = 2;
|
|
unsigned char NumDefs = 1;
|
|
struct {
|
|
MCInstrDesc MCID;
|
|
MCOperandInfo OpInfo[2];
|
|
} Table = {
|
|
{0, NumOps, NumDefs, 0, 0, 0, 0, 0, 0, 1ULL << MCID::HasOptionalDef, 0},
|
|
{{0, 0, MCOI::OPERAND_REGISTER, 0},
|
|
{0, 1 << MCOI::OptionalDef, MCOI::OPERAND_REGISTER, 0}}};
|
|
|
|
// 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(Table.MCID, DebugLoc());
|
|
VD1VU->addOperand(*MF,
|
|
MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));
|
|
VD1VU->addOperand(*MF,
|
|
MachineOperand::CreateReg(VirtualReg, /*isDef*/ false));
|
|
|
|
auto VD2VU = MF->CreateMachineInstr(Table.MCID, DebugLoc());
|
|
VD2VU->addOperand(*MF,
|
|
MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true));
|
|
VD2VU->addOperand(*MF,
|
|
MachineOperand::CreateReg(VirtualReg, /*isDef*/ false));
|
|
|
|
auto VD1SU = MF->CreateMachineInstr(Table.MCID, DebugLoc());
|
|
VD1SU->addOperand(*MF,
|
|
MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));
|
|
VD1SU->addOperand(*MF,
|
|
MachineOperand::CreateReg(SentinelReg, /*isDef*/ false));
|
|
|
|
auto VD1SD = MF->CreateMachineInstr(Table.MCID, DebugLoc());
|
|
VD1SD->addOperand(*MF,
|
|
MachineOperand::CreateReg(VirtualDef1, /*isDef*/ true));
|
|
VD1SD->addOperand(*MF,
|
|
MachineOperand::CreateReg(SentinelReg, /*isDef*/ true));
|
|
|
|
auto VD2PU = MF->CreateMachineInstr(Table.MCID, DebugLoc());
|
|
VD2PU->addOperand(*MF,
|
|
MachineOperand::CreateReg(VirtualDef2, /*isDef*/ true));
|
|
VD2PU->addOperand(*MF,
|
|
MachineOperand::CreateReg(PhysicalReg, /*isDef*/ false));
|
|
|
|
auto VD2PD = MF->CreateMachineInstr(Table.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) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
|
|
struct {
|
|
MCInstrDesc MCID;
|
|
MCOperandInfo OpInfo;
|
|
} Table = {{0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, MCOI::OPERAND_REGISTER, 0}};
|
|
|
|
DIFile *DIF = DIFile::getDistinct(Ctx, "filename", "");
|
|
DISubprogram *DIS = DISubprogram::getDistinct(
|
|
Ctx, nullptr, "", "", DIF, 0, nullptr, 0, nullptr, 0, 0, DINode::FlagZero,
|
|
DISubprogram::SPFlagZero, nullptr);
|
|
DILocation *DIL = DILocation::get(Ctx, 1, 5, DIS);
|
|
DebugLoc DL(DIL);
|
|
MachineInstr *MI = MF->CreateMachineInstr(Table.MCID, DL);
|
|
MI->addOperand(*MF, MachineOperand::CreateReg(0, /*isDef*/ true));
|
|
|
|
std::string str;
|
|
raw_string_ostream OS(str);
|
|
MI->print(OS, /*IsStandalone*/true, /*SkipOpers*/false, /*SkipDebugLoc*/false,
|
|
/*AddNewLine*/false);
|
|
ASSERT_TRUE(
|
|
StringRef(OS.str()).startswith("$noreg = UNKNOWN debug-location "));
|
|
ASSERT_TRUE(
|
|
StringRef(OS.str()).endswith("filename:1:5"));
|
|
}
|
|
|
|
TEST(MachineInstrSpan, DistanceBegin) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
auto MBB = MF->CreateMachineBasicBlock();
|
|
|
|
MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
auto MII = MBB->begin();
|
|
MachineInstrSpan MIS(MII, MBB);
|
|
ASSERT_TRUE(MIS.empty());
|
|
|
|
auto MI = MF->CreateMachineInstr(MCID, DebugLoc());
|
|
MBB->insert(MII, MI);
|
|
ASSERT_TRUE(std::distance(MIS.begin(), MII) == 1);
|
|
}
|
|
|
|
TEST(MachineInstrSpan, DistanceEnd) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
auto MBB = MF->CreateMachineBasicBlock();
|
|
|
|
MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
auto MII = MBB->end();
|
|
MachineInstrSpan MIS(MII, MBB);
|
|
ASSERT_TRUE(MIS.empty());
|
|
|
|
auto MI = MF->CreateMachineInstr(MCID, DebugLoc());
|
|
MBB->insert(MII, MI);
|
|
ASSERT_TRUE(std::distance(MIS.begin(), MII) == 1);
|
|
}
|
|
|
|
TEST(MachineInstrExtraInfo, AddExtraInfo) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
auto MI = MF->CreateMachineInstr(MCID, DebugLoc());
|
|
auto MAI = MCAsmInfo();
|
|
auto MC = createMCContext(&MAI);
|
|
auto MMO = MF->getMachineMemOperand(MachinePointerInfo(),
|
|
MachineMemOperand::MOLoad, 8, Align(8));
|
|
SmallVector<MachineMemOperand *, 2> MMOs;
|
|
MMOs.push_back(MMO);
|
|
MCSymbol *Sym1 = MC->createTempSymbol("pre_label", false);
|
|
MCSymbol *Sym2 = MC->createTempSymbol("post_label", false);
|
|
MDNode *HAM = MDNode::getDistinct(Ctx, std::nullopt);
|
|
MDNode *PCS = MDNode::getDistinct(Ctx, std::nullopt);
|
|
|
|
ASSERT_TRUE(MI->memoperands_empty());
|
|
ASSERT_FALSE(MI->getPreInstrSymbol());
|
|
ASSERT_FALSE(MI->getPostInstrSymbol());
|
|
ASSERT_FALSE(MI->getHeapAllocMarker());
|
|
ASSERT_FALSE(MI->getPCSections());
|
|
|
|
MI->setMemRefs(*MF, MMOs);
|
|
ASSERT_TRUE(MI->memoperands().size() == 1);
|
|
ASSERT_FALSE(MI->getPreInstrSymbol());
|
|
ASSERT_FALSE(MI->getPostInstrSymbol());
|
|
ASSERT_FALSE(MI->getHeapAllocMarker());
|
|
ASSERT_FALSE(MI->getPCSections());
|
|
|
|
MI->setPreInstrSymbol(*MF, Sym1);
|
|
ASSERT_TRUE(MI->memoperands().size() == 1);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_FALSE(MI->getPostInstrSymbol());
|
|
ASSERT_FALSE(MI->getHeapAllocMarker());
|
|
ASSERT_FALSE(MI->getPCSections());
|
|
|
|
MI->setPostInstrSymbol(*MF, Sym2);
|
|
ASSERT_TRUE(MI->memoperands().size() == 1);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2);
|
|
ASSERT_FALSE(MI->getHeapAllocMarker());
|
|
ASSERT_FALSE(MI->getPCSections());
|
|
|
|
MI->setHeapAllocMarker(*MF, HAM);
|
|
ASSERT_TRUE(MI->memoperands().size() == 1);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2);
|
|
ASSERT_TRUE(MI->getHeapAllocMarker() == HAM);
|
|
ASSERT_FALSE(MI->getPCSections());
|
|
|
|
MI->setPCSections(*MF, PCS);
|
|
ASSERT_TRUE(MI->memoperands().size() == 1);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2);
|
|
ASSERT_TRUE(MI->getHeapAllocMarker() == HAM);
|
|
ASSERT_TRUE(MI->getPCSections() == PCS);
|
|
}
|
|
|
|
TEST(MachineInstrExtraInfo, ChangeExtraInfo) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
auto MI = MF->CreateMachineInstr(MCID, DebugLoc());
|
|
auto MAI = MCAsmInfo();
|
|
auto MC = createMCContext(&MAI);
|
|
auto MMO = MF->getMachineMemOperand(MachinePointerInfo(),
|
|
MachineMemOperand::MOLoad, 8, Align(8));
|
|
SmallVector<MachineMemOperand *, 2> MMOs;
|
|
MMOs.push_back(MMO);
|
|
MCSymbol *Sym1 = MC->createTempSymbol("pre_label", false);
|
|
MCSymbol *Sym2 = MC->createTempSymbol("post_label", false);
|
|
MDNode *HAM = MDNode::getDistinct(Ctx, std::nullopt);
|
|
MDNode *PCS = MDNode::getDistinct(Ctx, std::nullopt);
|
|
|
|
MI->setMemRefs(*MF, MMOs);
|
|
MI->setPreInstrSymbol(*MF, Sym1);
|
|
MI->setPostInstrSymbol(*MF, Sym2);
|
|
MI->setHeapAllocMarker(*MF, HAM);
|
|
MI->setPCSections(*MF, PCS);
|
|
|
|
MMOs.push_back(MMO);
|
|
|
|
MI->setMemRefs(*MF, MMOs);
|
|
ASSERT_TRUE(MI->memoperands().size() == 2);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_TRUE(MI->getPostInstrSymbol() == Sym2);
|
|
ASSERT_TRUE(MI->getHeapAllocMarker() == HAM);
|
|
ASSERT_TRUE(MI->getPCSections() == PCS);
|
|
|
|
MI->setPostInstrSymbol(*MF, Sym1);
|
|
ASSERT_TRUE(MI->memoperands().size() == 2);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_TRUE(MI->getPostInstrSymbol() == Sym1);
|
|
ASSERT_TRUE(MI->getHeapAllocMarker() == HAM);
|
|
ASSERT_TRUE(MI->getPCSections() == PCS);
|
|
}
|
|
|
|
TEST(MachineInstrExtraInfo, RemoveExtraInfo) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
auto MI = MF->CreateMachineInstr(MCID, DebugLoc());
|
|
auto MAI = MCAsmInfo();
|
|
auto MC = createMCContext(&MAI);
|
|
auto MMO = MF->getMachineMemOperand(MachinePointerInfo(),
|
|
MachineMemOperand::MOLoad, 8, Align(8));
|
|
SmallVector<MachineMemOperand *, 2> MMOs;
|
|
MMOs.push_back(MMO);
|
|
MMOs.push_back(MMO);
|
|
MCSymbol *Sym1 = MC->createTempSymbol("pre_label", false);
|
|
MCSymbol *Sym2 = MC->createTempSymbol("post_label", false);
|
|
MDNode *HAM = MDNode::getDistinct(Ctx, std::nullopt);
|
|
MDNode *PCS = MDNode::getDistinct(Ctx, std::nullopt);
|
|
|
|
MI->setMemRefs(*MF, MMOs);
|
|
MI->setPreInstrSymbol(*MF, Sym1);
|
|
MI->setPostInstrSymbol(*MF, Sym2);
|
|
MI->setHeapAllocMarker(*MF, HAM);
|
|
MI->setPCSections(*MF, PCS);
|
|
|
|
MI->setPostInstrSymbol(*MF, nullptr);
|
|
ASSERT_TRUE(MI->memoperands().size() == 2);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_FALSE(MI->getPostInstrSymbol());
|
|
ASSERT_TRUE(MI->getHeapAllocMarker() == HAM);
|
|
ASSERT_TRUE(MI->getPCSections() == PCS);
|
|
|
|
MI->setHeapAllocMarker(*MF, nullptr);
|
|
ASSERT_TRUE(MI->memoperands().size() == 2);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_FALSE(MI->getPostInstrSymbol());
|
|
ASSERT_FALSE(MI->getHeapAllocMarker());
|
|
ASSERT_TRUE(MI->getPCSections() == PCS);
|
|
|
|
MI->setPCSections(*MF, nullptr);
|
|
ASSERT_TRUE(MI->memoperands().size() == 2);
|
|
ASSERT_TRUE(MI->getPreInstrSymbol() == Sym1);
|
|
ASSERT_FALSE(MI->getPostInstrSymbol());
|
|
ASSERT_FALSE(MI->getHeapAllocMarker());
|
|
ASSERT_FALSE(MI->getPCSections());
|
|
|
|
MI->setPreInstrSymbol(*MF, nullptr);
|
|
ASSERT_TRUE(MI->memoperands().size() == 2);
|
|
ASSERT_FALSE(MI->getPreInstrSymbol());
|
|
ASSERT_FALSE(MI->getPostInstrSymbol());
|
|
ASSERT_FALSE(MI->getHeapAllocMarker());
|
|
ASSERT_FALSE(MI->getPCSections());
|
|
|
|
MI->setMemRefs(*MF, {});
|
|
ASSERT_TRUE(MI->memoperands_empty());
|
|
ASSERT_FALSE(MI->getPreInstrSymbol());
|
|
ASSERT_FALSE(MI->getPostInstrSymbol());
|
|
ASSERT_FALSE(MI->getHeapAllocMarker());
|
|
ASSERT_FALSE(MI->getPCSections());
|
|
}
|
|
|
|
TEST(MachineInstrDebugValue, AddDebugValueOperand) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
|
|
for (const unsigned short Opcode :
|
|
{TargetOpcode::DBG_VALUE, TargetOpcode::DBG_VALUE_LIST,
|
|
TargetOpcode::DBG_INSTR_REF, TargetOpcode::DBG_PHI,
|
|
TargetOpcode::DBG_LABEL}) {
|
|
const MCInstrDesc MCID = {
|
|
Opcode, 0, 0, 0, 0,
|
|
0, 0, 0, 0, (1ULL << MCID::Pseudo) | (1ULL << MCID::Variadic),
|
|
0};
|
|
|
|
auto *MI = MF->CreateMachineInstr(MCID, DebugLoc());
|
|
MI->addOperand(*MF, MachineOperand::CreateReg(0, /*isDef*/ false));
|
|
|
|
MI->addOperand(*MF, MachineOperand::CreateImm(0));
|
|
MI->getOperand(1).ChangeToRegister(0, false);
|
|
|
|
ASSERT_TRUE(MI->getOperand(0).isDebug());
|
|
ASSERT_TRUE(MI->getOperand(1).isDebug());
|
|
}
|
|
}
|
|
|
|
MATCHER_P(HasMIMetadata, MIMD, "") {
|
|
return arg->getDebugLoc() == MIMD.getDL() &&
|
|
arg->getPCSections() == MIMD.getPCSections();
|
|
}
|
|
|
|
TEST(MachineInstrBuilder, BuildMI) {
|
|
LLVMContext Ctx;
|
|
MDNode *PCS = MDNode::getDistinct(Ctx, std::nullopt);
|
|
MDNode *DI = MDNode::getDistinct(Ctx, std::nullopt);
|
|
DebugLoc DL(DI);
|
|
MIMetadata MIMD(DL, PCS);
|
|
EXPECT_EQ(MIMD.getDL(), DL);
|
|
EXPECT_EQ(MIMD.getPCSections(), PCS);
|
|
// Check common BuildMI() overloads propagate MIMetadata.
|
|
Module Mod("Module", Ctx);
|
|
auto MF = createMachineFunction(Ctx, Mod);
|
|
auto MBB = MF->CreateMachineBasicBlock();
|
|
MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
EXPECT_THAT(BuildMI(*MF, MIMD, MCID), HasMIMetadata(MIMD));
|
|
EXPECT_THAT(BuildMI(*MF, MIMD, MCID), HasMIMetadata(MIMD));
|
|
EXPECT_THAT(BuildMI(*MBB, MBB->end(), MIMD, MCID), HasMIMetadata(MIMD));
|
|
EXPECT_THAT(BuildMI(*MBB, MBB->end(), MIMD, MCID), HasMIMetadata(MIMD));
|
|
EXPECT_THAT(BuildMI(*MBB, MBB->instr_end(), MIMD, MCID), HasMIMetadata(MIMD));
|
|
EXPECT_THAT(BuildMI(*MBB, *MBB->begin(), MIMD, MCID), HasMIMetadata(MIMD));
|
|
EXPECT_THAT(BuildMI(*MBB, &*MBB->begin(), MIMD, MCID), HasMIMetadata(MIMD));
|
|
EXPECT_THAT(BuildMI(MBB, MIMD, MCID), HasMIMetadata(MIMD));
|
|
}
|
|
|
|
static_assert(std::is_trivially_copyable_v<MCOperand>, "trivially copyable");
|
|
|
|
TEST(MachineInstrTest, SpliceOperands) {
|
|
LLVMContext Ctx;
|
|
Module Mod("Module", Ctx);
|
|
std::unique_ptr<MachineFunction> MF = createMachineFunction(Ctx, Mod);
|
|
MachineBasicBlock *MBB = MF->CreateMachineBasicBlock();
|
|
MCInstrDesc MCID = {TargetOpcode::INLINEASM,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
(1ULL << MCID::Pseudo) | (1ULL << MCID::Variadic),
|
|
0};
|
|
MachineInstr *MI = MF->CreateMachineInstr(MCID, DebugLoc());
|
|
MBB->insert(MBB->begin(), MI);
|
|
MI->addOperand(MachineOperand::CreateImm(0));
|
|
MI->addOperand(MachineOperand::CreateImm(1));
|
|
MI->addOperand(MachineOperand::CreateImm(2));
|
|
MI->addOperand(MachineOperand::CreateImm(3));
|
|
MI->addOperand(MachineOperand::CreateImm(4));
|
|
|
|
MI->removeOperand(1);
|
|
EXPECT_EQ(MI->getOperand(1).getImm(), MachineOperand::CreateImm(2).getImm());
|
|
EXPECT_EQ(MI->getNumOperands(), 4U);
|
|
|
|
MachineOperand Ops[] = {
|
|
MachineOperand::CreateImm(42), MachineOperand::CreateImm(1024),
|
|
MachineOperand::CreateImm(2048), MachineOperand::CreateImm(4096),
|
|
MachineOperand::CreateImm(8192),
|
|
};
|
|
auto *It = MI->operands_begin();
|
|
++It;
|
|
MI->insert(It, Ops);
|
|
|
|
EXPECT_EQ(MI->getNumOperands(), 9U);
|
|
EXPECT_EQ(MI->getOperand(0).getImm(), MachineOperand::CreateImm(0).getImm());
|
|
EXPECT_EQ(MI->getOperand(1).getImm(), MachineOperand::CreateImm(42).getImm());
|
|
EXPECT_EQ(MI->getOperand(2).getImm(),
|
|
MachineOperand::CreateImm(1024).getImm());
|
|
EXPECT_EQ(MI->getOperand(3).getImm(),
|
|
MachineOperand::CreateImm(2048).getImm());
|
|
EXPECT_EQ(MI->getOperand(4).getImm(),
|
|
MachineOperand::CreateImm(4096).getImm());
|
|
EXPECT_EQ(MI->getOperand(5).getImm(),
|
|
MachineOperand::CreateImm(8192).getImm());
|
|
EXPECT_EQ(MI->getOperand(6).getImm(), MachineOperand::CreateImm(2).getImm());
|
|
EXPECT_EQ(MI->getOperand(7).getImm(), MachineOperand::CreateImm(3).getImm());
|
|
EXPECT_EQ(MI->getOperand(8).getImm(), MachineOperand::CreateImm(4).getImm());
|
|
|
|
// test tied operands
|
|
MCRegisterClass MRC{0, 0, 0, 0, 0, 0, 0, 0, /*Allocatable=*/true};
|
|
TargetRegisterClass RC{&MRC, 0, 0, {}, 0, 0, 0, 0, 0, 0, 0};
|
|
// MachineRegisterInfo will be very upset if these registers aren't
|
|
// allocatable.
|
|
assert(RC.isAllocatable() && "unusable TargetRegisterClass");
|
|
MachineRegisterInfo &MRI = MF->getRegInfo();
|
|
Register A = MRI.createVirtualRegister(&RC);
|
|
Register B = MRI.createVirtualRegister(&RC);
|
|
MI->getOperand(0).ChangeToRegister(A, /*isDef=*/true);
|
|
MI->getOperand(1).ChangeToRegister(B, /*isDef=*/false);
|
|
MI->tieOperands(0, 1);
|
|
EXPECT_TRUE(MI->getOperand(0).isTied());
|
|
EXPECT_TRUE(MI->getOperand(1).isTied());
|
|
EXPECT_EQ(MI->findTiedOperandIdx(0), 1U);
|
|
EXPECT_EQ(MI->findTiedOperandIdx(1), 0U);
|
|
MI->insert(&MI->getOperand(1), {MachineOperand::CreateImm(7)});
|
|
EXPECT_TRUE(MI->getOperand(0).isTied());
|
|
EXPECT_TRUE(MI->getOperand(1).isImm());
|
|
EXPECT_TRUE(MI->getOperand(2).isTied());
|
|
EXPECT_EQ(MI->findTiedOperandIdx(0), 2U);
|
|
EXPECT_EQ(MI->findTiedOperandIdx(2), 0U);
|
|
EXPECT_EQ(MI->getOperand(0).getReg(), A);
|
|
EXPECT_EQ(MI->getOperand(2).getReg(), B);
|
|
|
|
// bad inputs
|
|
EXPECT_EQ(MI->getNumOperands(), 10U);
|
|
MI->insert(MI->operands_begin(), {});
|
|
EXPECT_EQ(MI->getNumOperands(), 10U);
|
|
}
|
|
|
|
} // end namespace
|