#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/MIRParser/MIRParser.h"
#include "llvm/CodeGen/MIRPrinter.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineModuleSlotTracker.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/FileCheck/FileCheck.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/ModuleSlotTracker.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "gtest/gtest.h"
using namespace llvm;
class MachineMetadataTest : public testing::Test {
public:
MachineMetadataTest() {}
protected:
LLVMContext Context;
std::unique_ptr<Module> M;
std::unique_ptr<MIRParser> MIR;
static void SetUpTestCase() {
InitializeAllTargetInfos();
InitializeAllTargets();
InitializeAllTargetMCs();
}
void SetUp() override { M = std::make_unique<Module>("Dummy", Context); }
void addHooks(ModuleSlotTracker &MST, const MachineOperand &MO) {
MST.setProcessHook([&MO](AbstractSlotTrackerStorage *AST, const Module *M,
bool ShouldInitializeAllMetadata) {
if (ShouldInitializeAllMetadata) {
if (MO.isMetadata())
AST->createMetadataSlot(MO.getMetadata());
}
});
MST.setProcessHook([&MO](AbstractSlotTrackerStorage *AST, const Function *F,
bool ShouldInitializeAllMetadata) {
if (!ShouldInitializeAllMetadata) {
if (MO.isMetadata())
AST->createMetadataSlot(MO.getMetadata());
}
});
}
std::unique_ptr<LLVMTargetMachine>
createTargetMachine(std::string TT, StringRef CPU, StringRef FS) {
std::string Error;
const Target *T = TargetRegistry::lookupTarget(TT, Error);
if (!T)
return nullptr;
TargetOptions Options;
return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(
T->createTargetMachine(TT, CPU, FS, Options, None, None)));
}
std::unique_ptr<Module> parseMIR(const TargetMachine &TM, StringRef MIRCode,
const char *FnName, MachineModuleInfo &MMI) {
SMDiagnostic Diagnostic;
std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);
MIR = createMIRParser(std::move(MBuffer), Context);
if (!MIR)
return nullptr;
std::unique_ptr<Module> Mod = MIR->parseIRModule();
if (!Mod)
return nullptr;
Mod->setDataLayout(TM.createDataLayout());
if (MIR->parseMachineFunctions(*Mod, MMI)) {
M.reset();
return nullptr;
}
return Mod;
}
};
static std::string print(std::function<void(raw_ostream &OS)> PrintFn) {
std::string Str;
raw_string_ostream OS(Str);
PrintFn(OS);
OS.flush();
return Str;
}
TEST_F(MachineMetadataTest, TrivialHook) {
ASSERT_TRUE(M);
Metadata *MDS = MDString::get(Context, "foo");
MDNode *Node = MDNode::get(Context, MDS);
MachineOperand MO = MachineOperand::CreateMetadata(Node);
ASSERT_TRUE(MO.isMetadata());
ASSERT_EQ(MO.getMetadata(), Node);
ModuleSlotTracker MST(M.get());
addHooks(MST, MO);
EXPECT_EQ("!0", print([&](raw_ostream &OS) {
MO.print(OS, MST, LLT{}, ~0U, false,
false,
false, 0,
nullptr,
nullptr);
}));
EXPECT_EQ("!0 = !{!\"foo\"}",
print([&](raw_ostream &OS) { Node->print(OS, MST); }));
}
TEST_F(MachineMetadataTest, BasicHook) {
ASSERT_TRUE(M);
Metadata *MachineMDS = MDString::get(Context, "foo");
MDNode *MachineNode = MDNode::get(Context, MachineMDS);
MachineOperand MO = MachineOperand::CreateMetadata(MachineNode);
ASSERT_TRUE(MO.isMetadata());
ASSERT_EQ(MO.getMetadata(), MachineNode);
NamedMDNode *MD = M->getOrInsertNamedMetadata("namedmd");
Metadata *MDS = MDString::get(Context, "bar");
MDNode *Node = MDNode::get(Context, MDS);
MD->addOperand(Node);
ModuleSlotTracker MST(M.get());
addHooks(MST, MO);
EXPECT_EQ("!1", print([&](raw_ostream &OS) {
MO.print(OS, MST, LLT{}, ~0U, false,
false,
false, 0,
nullptr,
nullptr);
}));
EXPECT_EQ("!0 = !{!\"bar\"}",
print([&](raw_ostream &OS) { Node->print(OS, MST); }));
EXPECT_EQ("!1 = !{!\"foo\"}",
print([&](raw_ostream &OS) { MachineNode->print(OS, MST); }));
}
static bool checkOutput(std::string CheckString, std::string Output) {
auto CheckBuffer = MemoryBuffer::getMemBuffer(CheckString, "");
auto OutputBuffer = MemoryBuffer::getMemBuffer(Output, "Output", false);
SmallString<4096> CheckFileBuffer;
FileCheckRequest Req;
FileCheck FC(Req);
StringRef CheckFileText =
FC.CanonicalizeFile(*CheckBuffer.get(), CheckFileBuffer);
SourceMgr SM;
SM.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(CheckFileText, "CheckFile"),
SMLoc());
Regex PrefixRE = FC.buildCheckPrefixRegex();
if (FC.readCheckFile(SM, CheckFileText, PrefixRE))
return false;
auto OutBuffer = OutputBuffer->getBuffer();
SM.AddNewSourceBuffer(std::move(OutputBuffer), SMLoc());
return FC.checkInput(SM, OutBuffer);
}
TEST_F(MachineMetadataTest, MMSlotTrackerAArch64) {
auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");
if (!TM)
GTEST_SKIP();
StringRef MIRString = R"MIR(
--- |
define i32 @test0(i32* %p) {
%r = load i32, i32* %p, align 4
ret i32 %r
}
...
---
name: test0
liveins:
- { reg: '$x0', virtual-reg: '%0' }
body: |
bb.0 (%ir-block.0):
liveins: $x0
%0:gpr64common = COPY $x0
%1:gpr32 = LDRWui %0, 0 :: (load (s32) from %ir.p)
...
)MIR";
MachineModuleInfo MMI(TM.get());
M = parseMIR(*TM, MIRString, "test0", MMI);
ASSERT_TRUE(M);
auto *MF = MMI.getMachineFunction(*M->getFunction("test0"));
auto *MBB = MF->getBlockNumbered(0);
auto &MI = MBB->back();
ASSERT_TRUE(MI.hasOneMemOperand());
MDBuilder MDB(Context);
MDNode *Domain = MDB.createAnonymousAliasScopeDomain("domain");
MDNode *Scope0 = MDB.createAnonymousAliasScope(Domain, "scope0");
MDNode *Scope1 = MDB.createAnonymousAliasScope(Domain, "scope1");
MDNode *Set0 = MDNode::get(Context, {Scope0});
MDNode *Set1 = MDNode::get(Context, {Scope1});
AAMDNodes AAInfo;
AAInfo.TBAA = AAInfo.TBAAStruct = nullptr;
AAInfo.Scope = Set0;
AAInfo.NoAlias = Set1;
auto *OldMMO = MI.memoperands().front();
auto *NewMMO = MF->getMachineMemOperand(OldMMO, AAInfo);
MI.setMemRefs(*MF, NewMMO);
MachineModuleSlotTracker MST(MF);
EXPECT_EQ("%1:gpr32 = LDRWui %0, 0 :: (load (s32) from %ir.p, "
"!alias.scope !0, !noalias !3)",
print([&](raw_ostream &OS) {
MI.print(OS, MST, false, false,
false, false);
}));
std::vector<const MDNode *> Generated{Domain, Scope0, Scope1, Set0, Set1};
std::vector<const MDNode *> Collected;
MachineModuleSlotTracker::MachineMDNodeListType MDList;
MST.collectMachineMDNodes(MDList);
for (auto &MD : MDList)
Collected.push_back(MD.second);
llvm::sort(Generated);
llvm::sort(Collected);
EXPECT_EQ(Collected, Generated);
std::string Output = print([&](raw_ostream &OS) { printMIR(OS, *MF); });
std::string CheckString = R"(
CHECK: machineMetadataNodes:
CHECK-DAG: ![[MMDOMAIN:[0-9]+]] = distinct !{!{{[0-9]+}}, !"domain"}
CHECK-DAG: ![[MMSCOPE0:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope0"}
CHECK-DAG: ![[MMSCOPE1:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope1"}
CHECK-DAG: ![[MMSET0:[0-9]+]] = !{![[MMSCOPE0]]}
CHECK-DAG: ![[MMSET1:[0-9]+]] = !{![[MMSCOPE1]]}
CHECK: body:
CHECK: %1:gpr32 = LDRWui %0, 0 :: (load (s32) from %ir.p, !alias.scope ![[MMSET0]], !noalias ![[MMSET1]])
)";
EXPECT_TRUE(checkOutput(CheckString, Output));
}
TEST_F(MachineMetadataTest, isMetaInstruction) {
auto TM = createTargetMachine(Triple::normalize("x86_64--"), "", "");
if (!TM)
GTEST_SKIP();
StringRef MIRString = R"MIR(
--- |
define void @test0(i32 %b) {
ret void
}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "a.c", directory: "/tmp")
!2 = !{i32 7, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{i32 7, !"uwtable", i32 1}
!6 = !{i32 7, !"frame-pointer", i32 2}
!7 = !{!""}
!8 = distinct !DISubprogram(name: "test0", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !12)
!9 = !DISubroutineType(types: !10)
!10 = !{null, !11}
!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!12 = !{}
!13 = !DILocalVariable(name: "b", arg: 1, scope: !8, file: !1, line: 1, type: !11)
!14 = !DILocation(line: 1, column: 16, scope: !8)
...
---
name: test0
machineFunctionInfo
body: |
bb.0:
$rdi = IMPLICIT_DEF
KILL $rsi
CFI_INSTRUCTION undefined $rax
EH_LABEL 0
GC_LABEL 0
DBG_VALUE $rax, $noreg, !13, !DIExpression(), debug-location !14
DBG_LABEL 0
LIFETIME_START 0
LIFETIME_END 0
PSEUDO_PROBE 6699318081062747564, 1, 0, 0
$xmm0 = ARITH_FENCE $xmm0
Int_MemBarrier
...
)MIR";
MachineModuleInfo MMI(TM.get());
M = parseMIR(*TM, MIRString, "test0", MMI);
ASSERT_TRUE(M);
auto *MF = MMI.getMachineFunction(*M->getFunction("test0"));
auto *MBB = MF->getBlockNumbered(0);
for (auto It = MBB->begin(); It != MBB->end(); ++It) {
MachineInstr &MI = *It;
ASSERT_TRUE(MI.isMetaInstruction());
}
}
TEST_F(MachineMetadataTest, MMSlotTrackerX64) {
auto TM = createTargetMachine(Triple::normalize("x86_64--"), "", "");
if (!TM)
GTEST_SKIP();
StringRef MIRString = R"MIR(
--- |
define i32 @test0(i32* %p) {
%r = load i32, i32* %p, align 4
ret i32 %r
}
...
---
name: test0
liveins:
- { reg: '$rdi', virtual-reg: '%0' }
body: |
bb.0 (%ir-block.0):
liveins: $rdi
%0:gr64 = COPY $rdi
%1:gr32 = MOV32rm %0, 1, $noreg, 0, $noreg :: (load (s32) from %ir.p)
...
)MIR";
MachineModuleInfo MMI(TM.get());
M = parseMIR(*TM, MIRString, "test0", MMI);
ASSERT_TRUE(M);
auto *MF = MMI.getMachineFunction(*M->getFunction("test0"));
auto *MBB = MF->getBlockNumbered(0);
auto &MI = MBB->back();
ASSERT_FALSE(MI.memoperands_empty());
ASSERT_TRUE(MI.hasOneMemOperand());
MDBuilder MDB(Context);
MDNode *Domain = MDB.createAnonymousAliasScopeDomain("domain");
MDNode *Scope0 = MDB.createAnonymousAliasScope(Domain, "scope0");
MDNode *Scope1 = MDB.createAnonymousAliasScope(Domain, "scope1");
MDNode *Set0 = MDNode::get(Context, {Scope0});
MDNode *Set1 = MDNode::get(Context, {Scope1});
AAMDNodes AAInfo;
AAInfo.TBAA = AAInfo.TBAAStruct = nullptr;
AAInfo.Scope = Set0;
AAInfo.NoAlias = Set1;
auto *OldMMO = MI.memoperands().front();
auto *NewMMO = MF->getMachineMemOperand(OldMMO, AAInfo);
MI.setMemRefs(*MF, NewMMO);
MachineModuleSlotTracker MST(MF);
EXPECT_EQ("%1:gr32 = MOV32rm %0, 1, $noreg, 0, $noreg :: (load (s32) from %ir.p, "
"!alias.scope !0, !noalias !3)",
print([&](raw_ostream &OS) {
MI.print(OS, MST, false, false,
false, false);
}));
std::vector<const MDNode *> Generated{Domain, Scope0, Scope1, Set0, Set1};
std::vector<const MDNode *> Collected;
MachineModuleSlotTracker::MachineMDNodeListType MDList;
MST.collectMachineMDNodes(MDList);
for (auto &MD : MDList)
Collected.push_back(MD.second);
llvm::sort(Generated);
llvm::sort(Collected);
EXPECT_EQ(Collected, Generated);
std::string Output = print([&](raw_ostream &OS) { printMIR(OS, *MF); });
std::string CheckString = R"(
CHECK: machineMetadataNodes:
CHECK-DAG: ![[MMDOMAIN:[0-9]+]] = distinct !{!{{[0-9]+}}, !"domain"}
CHECK-DAG: ![[MMSCOPE0:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope0"}
CHECK-DAG: ![[MMSCOPE1:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope1"}
CHECK-DAG: ![[MMSET0:[0-9]+]] = !{![[MMSCOPE0]]}
CHECK-DAG: ![[MMSET1:[0-9]+]] = !{![[MMSCOPE1]]}
CHECK: body:
CHECK: %1:gr32 = MOV32rm %0, 1, $noreg, 0, $noreg :: (load (s32) from %ir.p, !alias.scope ![[MMSET0]], !noalias ![[MMSET1]])
)";
EXPECT_TRUE(checkOutput(CheckString, Output));
}
TEST_F(MachineMetadataTest, MMSlotTrackerAMDGPU) {
auto TM = createTargetMachine(Triple::normalize("amdgcn-amd-amdhsa"),
"gfx1010", "");
if (!TM)
GTEST_SKIP();
StringRef MIRString = R"MIR(
--- |
define i32 @test0(i32* %p) {
%r = load i32, i32* %p, align 4
ret i32 %r
}
...
---
name: test0
liveins:
- { reg: '$vgpr0', virtual-reg: '%0' }
- { reg: '$vgpr1', virtual-reg: '%1' }
- { reg: '$sgpr30_sgpr31', virtual-reg: '%2' }
body: |
bb.0 (%ir-block.0):
liveins: $vgpr0, $vgpr1, $sgpr30_sgpr31
%2:sreg_64 = COPY $sgpr30_sgpr31
%1:vgpr_32 = COPY $vgpr1
%0:vgpr_32 = COPY $vgpr0
%8:vreg_64 = REG_SEQUENCE %0, %subreg.sub0, %1, %subreg.sub1
%6:vreg_64 = COPY %8
%5:vgpr_32 = FLAT_LOAD_DWORD killed %6, 0, 0, implicit $exec, implicit $flat_scr :: (load (s32) from %ir.p)
...
)MIR";
MachineModuleInfo MMI(TM.get());
M = parseMIR(*TM, MIRString, "test0", MMI);
ASSERT_TRUE(M);
auto *MF = MMI.getMachineFunction(*M->getFunction("test0"));
auto *MBB = MF->getBlockNumbered(0);
auto &MI = MBB->back();
ASSERT_FALSE(MI.memoperands_empty());
ASSERT_TRUE(MI.hasOneMemOperand());
MDBuilder MDB(Context);
MDNode *Domain = MDB.createAnonymousAliasScopeDomain("domain");
MDNode *Scope0 = MDB.createAnonymousAliasScope(Domain, "scope0");
MDNode *Scope1 = MDB.createAnonymousAliasScope(Domain, "scope1");
MDNode *Set0 = MDNode::get(Context, {Scope0});
MDNode *Set1 = MDNode::get(Context, {Scope1});
AAMDNodes AAInfo;
AAInfo.TBAA = AAInfo.TBAAStruct = nullptr;
AAInfo.Scope = Set0;
AAInfo.NoAlias = Set1;
auto *OldMMO = MI.memoperands().front();
auto *NewMMO = MF->getMachineMemOperand(OldMMO, AAInfo);
MI.setMemRefs(*MF, NewMMO);
MachineModuleSlotTracker MST(MF);
EXPECT_EQ(
"%5:vgpr_32 = FLAT_LOAD_DWORD killed %4, 0, 0, implicit $exec, implicit "
"$flat_scr :: (load (s32) from %ir.p, !alias.scope !0, !noalias !3)",
print([&](raw_ostream &OS) {
MI.print(OS, MST, false, false,
false, false);
}));
std::vector<const MDNode *> Generated{Domain, Scope0, Scope1, Set0, Set1};
std::vector<const MDNode *> Collected;
MachineModuleSlotTracker::MachineMDNodeListType MDList;
MST.collectMachineMDNodes(MDList);
for (auto &MD : MDList)
Collected.push_back(MD.second);
llvm::sort(Generated);
llvm::sort(Collected);
EXPECT_EQ(Collected, Generated);
std::string Output = print([&](raw_ostream &OS) { printMIR(OS, *MF); });
std::string CheckString = R"(
CHECK: machineMetadataNodes:
CHECK-DAG: ![[MMDOMAIN:[0-9]+]] = distinct !{!{{[0-9]+}}, !"domain"}
CHECK-DAG: ![[MMSCOPE0:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope0"}
CHECK-DAG: ![[MMSCOPE1:[0-9]+]] = distinct !{!{{[0-9]+}}, ![[MMDOMAIN]], !"scope1"}
CHECK-DAG: ![[MMSET0:[0-9]+]] = !{![[MMSCOPE0]]}
CHECK-DAG: ![[MMSET1:[0-9]+]] = !{![[MMSCOPE1]]}
CHECK: body:
CHECK: %5:vgpr_32 = FLAT_LOAD_DWORD killed %4, 0, 0, implicit $exec, implicit $flat_scr :: (load (s32) from %ir.p, !alias.scope ![[MMSET0]], !noalias ![[MMSET1]])
)";
EXPECT_TRUE(checkOutput(CheckString, Output));
}
TEST_F(MachineMetadataTest, TiedOpsRewritten) {
auto TM = createTargetMachine(Triple::normalize("powerpc64--"), "", "");
if (!TM)
GTEST_SKIP();
StringRef MIRString = R"MIR(
---
name: foo
alignment: 16
tracksRegLiveness: true
frameInfo:
maxAlignment: 16
machineFunctionInfo: {}
body: |
bb.0:
liveins: $r3
%0:gprc = COPY $r3
%0 = RLWIMI killed %0, $r3, 1, 0, 30
$r3 = COPY %0
BLR8 implicit $r3, implicit $lr8, implicit $rm
...
)MIR";
MachineModuleInfo MMI(TM.get());
M = parseMIR(*TM, MIRString, "foo", MMI);
ASSERT_TRUE(M);
auto *MF = MMI.getMachineFunction(*M->getFunction("foo"));
MachineFunctionProperties &Properties = MF->getProperties();
ASSERT_TRUE(Properties.hasProperty(
MachineFunctionProperties::Property::TiedOpsRewritten));
}
TEST_F(MachineMetadataTest, NoTiedOpsRewritten) {
auto TM = createTargetMachine(Triple::normalize("powerpc64--"), "", "");
if (!TM)
GTEST_SKIP();
StringRef MIRString = R"MIR(
---
name: foo
alignment: 16
tracksRegLiveness: true
frameInfo:
maxAlignment: 16
machineFunctionInfo: {}
body: |
bb.0:
liveins: $r3
%0:gprc = COPY $r3
%1:gprc = RLWIMI killed %0, $r3, 1, 0, 30
$r3 = COPY %1
BLR8 implicit $r3, implicit $lr8, implicit $rm
...
)MIR";
MachineModuleInfo MMI(TM.get());
M = parseMIR(*TM, MIRString, "foo", MMI);
ASSERT_TRUE(M);
auto *MF = MMI.getMachineFunction(*M->getFunction("foo"));
MachineFunctionProperties &Properties = MF->getProperties();
ASSERT_FALSE(Properties.hasProperty(
MachineFunctionProperties::Property::TiedOpsRewritten));
}