[llvm] d9eebe3 - [DebugInfo][InstrRef] Add unit tests for transfer-function building

Jeremy Morse via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 22 10:29:27 PDT 2021


Author: Jeremy Morse
Date: 2021-10-22T18:29:03+01:00
New Revision: d9eebe3cd7832b69471b27e68f0ee6f1374ed759

URL: https://github.com/llvm/llvm-project/commit/d9eebe3cd7832b69471b27e68f0ee6f1374ed759
DIFF: https://github.com/llvm/llvm-project/commit/d9eebe3cd7832b69471b27e68f0ee6f1374ed759.diff

LOG: [DebugInfo][InstrRef] Add unit tests for transfer-function building

This patch adds some unit tests for the machine-location transfer-function
building parts of InstrRefBasedLDV: i.e., test that if we feed some MIR
into the transfer-function building code, does it create the correct
transfer function.

There are a number of minor defects that get corrected in the process:
 * The unit test was selecting the x86 (i.e. 32 bit) backend rather than
   x86_64's 64 bit backend,
 * COPY instructions weren't actually having their subregister values
   correctly represented in the transfer function. Subregisters were being
   defined by the COPY, rather than taking the value in the source register.
 * SP aliases were at risk of being clobbered, if an SP subregister was
   clobbered.

Differential Revision: https://reviews.llvm.org/D112006

Added: 
    

Modified: 
    llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
    llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
    llvm/unittests/CodeGen/InstrRefLDVTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
index d286aa903547..4f151d721f1d 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
@@ -362,7 +362,7 @@ class TransferTracker {
       // instruction or similar with an instruction number, where it doesn't
       // actually define a new value, instead it moves a value. In case this
       // happens, discard.
-      if (MTracker->LocIdxToIDNum[L] != Use.ID)
+      if (MTracker->readMLoc(L) != Use.ID)
         continue;
 
       // If a 
diff erent debug instruction defined the variable value / location
@@ -493,12 +493,12 @@ class TransferTracker {
     // Check whether our local copy of values-by-location in #VarLocs is out of
     // date. Wipe old tracking data for the location if it's been clobbered in
     // the meantime.
-    if (MTracker->getNumAtPos(NewLoc) != VarLocs[NewLoc.asU64()]) {
+    if (MTracker->readMLoc(NewLoc) != VarLocs[NewLoc.asU64()]) {
       for (auto &P : ActiveMLocs[NewLoc]) {
         ActiveVLocs.erase(P);
       }
       ActiveMLocs[NewLoc.asU64()].clear();
-      VarLocs[NewLoc.asU64()] = MTracker->getNumAtPos(NewLoc);
+      VarLocs[NewLoc.asU64()] = MTracker->readMLoc(NewLoc);
     }
 
     ActiveMLocs[NewLoc].insert(Var);
@@ -586,7 +586,7 @@ class TransferTracker {
   void transferMlocs(LocIdx Src, LocIdx Dst, MachineBasicBlock::iterator Pos) {
     // Does Src still contain the value num we expect? If not, it's been
     // clobbered in the meantime, and our variable locations are stale.
-    if (VarLocs[Src.asU64()] != MTracker->getNumAtPos(Src))
+    if (VarLocs[Src.asU64()] != MTracker->readMLoc(Src))
       return;
 
     // assert(ActiveMLocs[Dst].size() == 0);
@@ -1066,7 +1066,7 @@ bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI,
   Optional<LocIdx> FoundLoc = None;
   for (auto Location : MTracker->locations()) {
     LocIdx CurL = Location.Idx;
-    ValueIDNum ID = MTracker->LocIdxToIDNum[CurL];
+    ValueIDNum ID = MTracker->readMLoc(CurL);
     if (NewID && ID == NewID) {
       // If this is the first location with that value, pick it. Otherwise,
       // consider whether it's a "longer term" location.
@@ -1176,10 +1176,6 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
   } else if (MI.isMetaInstruction())
     return;
 
-  MachineFunction *MF = MI.getMF();
-  const TargetLowering *TLI = MF->getSubtarget().getTargetLowering();
-  Register SP = TLI->getStackPointerRegisterToSaveRestore();
-
   // Find the regs killed by MI, and find regmasks of preserved regs.
   // Max out the number of statically allocated elements in `DeadRegs`, as this
   // prevents fallback to std::set::count() operations.
@@ -1190,7 +1186,7 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
     // Determine whether the operand is a register def.
     if (MO.isReg() && MO.isDef() && MO.getReg() &&
         Register::isPhysicalRegister(MO.getReg()) &&
-        !(MI.isCall() && MO.getReg() == SP)) {
+        !(MI.isCall() && MTracker->SPAliases.count(MO.getReg()))) {
       // Remove ranges of all aliased registers.
       for (MCRegAliasIterator RAI(MO.getReg(), TRI, true); RAI.isValid(); ++RAI)
         // FIXME: Can we break out of this loop early if no insertion occurs?
@@ -1236,29 +1232,14 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
 }
 
 void InstrRefBasedLDV::performCopy(Register SrcRegNum, Register DstRegNum) {
-  ValueIDNum SrcValue = MTracker->readReg(SrcRegNum);
+  // In all circumstances, re-def all aliases. It's definitely a new value now.
+  for (MCRegAliasIterator RAI(DstRegNum, TRI, true); RAI.isValid(); ++RAI)
+    MTracker->defReg(*RAI, CurBB, CurInst);
 
+  ValueIDNum SrcValue = MTracker->readReg(SrcRegNum);
   MTracker->setReg(DstRegNum, SrcValue);
 
-  // In all circumstances, re-def the super registers. It's definitely a new
-  // value now. This doesn't uniquely identify the composition of subregs, for
-  // example, two identical values in subregisters composed in 
diff erent
-  // places would not get equal value numbers.
-  for (MCSuperRegIterator SRI(DstRegNum, TRI); SRI.isValid(); ++SRI)
-    MTracker->defReg(*SRI, CurBB, CurInst);
-
-  // If we're emulating VarLocBasedImpl, just define all the subregisters.
-  // DBG_VALUEs of them will expect to be tracked from the DBG_VALUE, not
-  // through prior copies.
-  if (EmulateOldLDV) {
-    for (MCSubRegIndexIterator DRI(DstRegNum, TRI); DRI.isValid(); ++DRI)
-      MTracker->defReg(DRI.getSubReg(), CurBB, CurInst);
-    return;
-  }
-
-  // Otherwise, actually copy subregisters from one location to another.
-  // XXX: in addition, any subregisters of DstRegNum that don't line up with
-  // the source register should be def'd.
+  // Copy subregisters from one location to another.
   for (MCSubRegIndexIterator SRI(SrcRegNum, TRI); SRI.isValid(); ++SRI) {
     unsigned SrcSubReg = SRI.getSubReg();
     unsigned SubRegIdx = SRI.getSubRegIndex();
@@ -1269,15 +1250,13 @@ void InstrRefBasedLDV::performCopy(Register SrcRegNum, Register DstRegNum) {
     // Do copy. There are two matching subregisters, the source value should
     // have been def'd when the super-reg was, the latter might not be tracked
     // yet.
-    // This will force SrcSubReg to be tracked, if it isn't yet.
-    (void)MTracker->readReg(SrcSubReg);
-    LocIdx SrcL = MTracker->getRegMLoc(SrcSubReg);
-    assert(SrcL.asU64());
-    (void)MTracker->readReg(DstSubReg);
-    LocIdx DstL = MTracker->getRegMLoc(DstSubReg);
-    assert(DstL.asU64());
+    // This will force SrcSubReg to be tracked, if it isn't yet. Will read
+    // mphi values if it wasn't tracked.
+    LocIdx SrcL = MTracker->lookupOrTrackRegister(SrcSubReg);
+    LocIdx DstL = MTracker->lookupOrTrackRegister(DstSubReg);
+    (void)SrcL;
     (void)DstL;
-    ValueIDNum CpyValue = {SrcValue.getBlock(), SrcValue.getInst(), SrcL};
+    ValueIDNum CpyValue = MTracker->readReg(SrcSubReg);
 
     MTracker->setReg(DstSubReg, CpyValue);
   }
@@ -1911,7 +1890,7 @@ void InstrRefBasedLDV::buildMLocValueMap(
       for (auto &P : MLocTransfer[CurBB]) {
         if (P.second.getBlock() == CurBB && P.second.isPHI()) {
           // This is a movement of whatever was live in. Read it.
-          ValueIDNum NewID = MTracker->getNumAtPos(P.second.getLoc());
+          ValueIDNum NewID = MTracker->readMLoc(P.second.getLoc());
           ToRemap.push_back(std::make_pair(P.first, NewID));
         } else {
           // It's a def. Just set it.

diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
index 0de8ed5fc352..b211154489f4 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
@@ -380,12 +380,6 @@ class MLocTracker {
     return (isSpill) ? RegOrSpill.id() + NumRegs - 1 : RegOrSpill.id();
   }
 
-  /// Accessor for reading the value at Idx.
-  ValueIDNum getNumAtPos(LocIdx Idx) const {
-    assert(Idx.asU64() < LocIdxToIDNum.size());
-    return LocIdxToIDNum[Idx];
-  }
-
   unsigned getNumLocs(void) const { return LocIdxToIDNum.size(); }
 
   /// Reset all locations to contain a PHI value at the designated block. Used
@@ -435,6 +429,12 @@ class MLocTracker {
     LocIdxToIDNum[L] = Num;
   }
 
+  /// Read the value of a particular location
+  ValueIDNum readMLoc(LocIdx L) {
+    assert(L.asU64() < LocIdxToIDNum.size());
+    return LocIdxToIDNum[L];
+  }
+
   /// Create a LocIdx for an untracked register ID. Initialize it to either an
   /// mphi value representing a live-in, or a recent register mask clobber.
   LocIdx trackRegister(unsigned ID);
@@ -664,7 +664,7 @@ class InstrRefBasedLDV : public LDVImpl {
 
   /// Object to track machine locations as we step through a block. Could
   /// probably be a field rather than a pointer, as it's always used.
-  MLocTracker *MTracker;
+  MLocTracker *MTracker = nullptr;
 
   /// Number of the current block LiveDebugValues is stepping through.
   unsigned CurBB;
@@ -675,12 +675,12 @@ class InstrRefBasedLDV : public LDVImpl {
   /// Variable tracker -- listens to DBG_VALUEs occurring as InstrRefBasedImpl
   /// steps through a block. Reads the values at each location from the
   /// MLocTracker object.
-  VLocTracker *VTracker;
+  VLocTracker *VTracker = nullptr;
 
   /// Tracker for transfers, listens to DBG_VALUEs and transfers of values
   /// between locations during stepping, creates new DBG_VALUEs when values move
   /// location.
-  TransferTracker *TTracker;
+  TransferTracker *TTracker = nullptr;
 
   /// Blocks which are artificial, i.e. blocks which exclusively contain
   /// instructions without DebugLocs, or with line 0 locations.

diff  --git a/llvm/unittests/CodeGen/InstrRefLDVTest.cpp b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
index c6420b5b5a02..79043205112d 100644
--- a/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
+++ b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/CodeGen/MIRParser/MIRParser.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/TargetLowering.h"
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
@@ -13,6 +14,7 @@
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Target/TargetMachine.h"
 #include "llvm/Target/TargetOptions.h"
@@ -33,10 +35,11 @@ class InstrRefLDVTest : public testing::Test {
   using MLocTransferMap = InstrRefBasedLDV::MLocTransferMap;
 
   LLVMContext Ctx;
-  Module Mod;
+  std::unique_ptr<Module> Mod;
   std::unique_ptr<TargetMachine> Machine;
   std::unique_ptr<MachineFunction> MF;
   std::unique_ptr<MachineDominatorTree> DomTree;
+  std::unique_ptr<MachineModuleInfo> MMI;
   DICompileUnit *OurCU;
   DIFile *OurFile;
   DISubprogram *OurFunc;
@@ -55,13 +58,15 @@ class InstrRefLDVTest : public testing::Test {
   std::unique_ptr<MLocTracker> MTracker;
   std::unique_ptr<VLocTracker> VTracker;
 
-  InstrRefLDVTest() : Ctx(), Mod("beehives", Ctx) {
-  }
+  SmallString<256> MIRStr;
+
+  InstrRefLDVTest() : Ctx(), Mod(std::make_unique<Module>("beehives", Ctx)) {}
 
   void SetUp() {
     // Boilerplate that creates a MachineFunction and associated blocks.
 
-    Mod.setDataLayout("e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128");
+    Mod->setDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-"
+                       "n8:16:32:64-S128");
     Triple TargetTriple("x86_64--");
     std::string Error;
     const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);
@@ -69,21 +74,24 @@ class InstrRefLDVTest : public testing::Test {
       GTEST_SKIP();
 
     TargetOptions Options;
-    Machine = std::unique_ptr<TargetMachine>(T->createTargetMachine(
-        "X86", "", "", Options, None, None, CodeGenOpt::Aggressive));
+    Machine = std::unique_ptr<TargetMachine>(
+        T->createTargetMachine(Triple::normalize("x86_64--"), "", "", Options,
+                               None, None, CodeGenOpt::Aggressive));
 
     auto Type = FunctionType::get(Type::getVoidTy(Ctx), false);
-    auto F = Function::Create(Type, GlobalValue::ExternalLinkage, "Test", &Mod);
+    auto F =
+        Function::Create(Type, GlobalValue::ExternalLinkage, "Test", &*Mod);
 
     unsigned FunctionNum = 42;
-    MachineModuleInfo MMI((LLVMTargetMachine*)&*Machine);
+    MMI = std::make_unique<MachineModuleInfo>((LLVMTargetMachine *)&*Machine);
     const TargetSubtargetInfo &STI = *Machine->getSubtargetImpl(*F);
 
-    MF = std::make_unique<MachineFunction>(*F, (LLVMTargetMachine&)*Machine, STI, FunctionNum, MMI);
+    MF = std::make_unique<MachineFunction>(*F, (LLVMTargetMachine &)*Machine,
+                                           STI, FunctionNum, *MMI);
 
     // Create metadata: CU, subprogram, some blocks and an inline function
     // scope.
-    DIBuilder DIB(Mod);
+    DIBuilder DIB(*Mod);
     OurFile = DIB.createFile("xyzzy.c", "/cave");
     OurCU =
         DIB.createCompileUnit(dwarf::DW_LANG_C99, OurFile, "nou", false, "", 0);
@@ -126,7 +134,7 @@ class InstrRefLDVTest : public testing::Test {
     llvm_unreachable("Can't find register by name");
   }
 
-  InstrRefBasedLDV *setupLDVObj() {
+  InstrRefBasedLDV *setupLDVObj(MachineFunction *MF) {
     // Create a new LDV object, and plug some relevant object ptrs into it.
     LDV = std::make_unique<InstrRefBasedLDV>();
     const TargetSubtargetInfo &STI = MF->getSubtarget();
@@ -144,12 +152,11 @@ class InstrRefLDVTest : public testing::Test {
     // mappings.
     LDV->initialSetup(*MF);
     LDV->LS.initialize(*MF);
-    addMTracker();
-    addVTracker();
+    addMTracker(MF);
     return &*LDV;
   }
 
-  void addMTracker() {
+  void addMTracker(MachineFunction *MF) {
     ASSERT_TRUE(LDV);
     // Add a machine-location-tracking object to LDV. Don't initialize any
     // register locations within it though.
@@ -212,7 +219,7 @@ class InstrRefLDVTest : public testing::Test {
     MF->insert(MF->end(), MBB0);
     MF->RenumberBlocks();
 
-    setupLDVObj();
+    setupLDVObj(&*MF);
   }
 
   void setupDiamondBlocks() {
@@ -245,7 +252,7 @@ class InstrRefLDVTest : public testing::Test {
     MBB2->addSuccessor(MBB3);
     MF->RenumberBlocks();
 
-    setupLDVObj();
+    setupLDVObj(&*MF);
   }
 
   void setupSimpleLoop() {
@@ -275,7 +282,7 @@ class InstrRefLDVTest : public testing::Test {
     MBB1->addSuccessor(MBB1);
     MF->RenumberBlocks();
 
-    setupLDVObj();
+    setupLDVObj(&*MF);
   }
 
   void setupNestedLoops() {
@@ -320,7 +327,7 @@ class InstrRefLDVTest : public testing::Test {
     MBB3->addSuccessor(MBB4);
     MF->RenumberBlocks();
 
-    setupLDVObj();
+    setupLDVObj(&*MF);
   }
 
   void setupNoDominatingLoop() {
@@ -365,7 +372,7 @@ class InstrRefLDVTest : public testing::Test {
     MBB3->addSuccessor(MBB4);
     MF->RenumberBlocks();
 
-    setupLDVObj();
+    setupLDVObj(&*MF);
   }
 
   void setupBadlyNestedLoops() {
@@ -416,10 +423,307 @@ class InstrRefLDVTest : public testing::Test {
     MBB3->addSuccessor(MBB4);
     MF->RenumberBlocks();
 
-    setupLDVObj();
+    setupLDVObj(&*MF);
+  }
+
+  MachineFunction *readMIRBlock(const char *Input) {
+    MIRStr.clear();
+    StringRef S = Twine(Twine(R"MIR(
+--- |
+  target triple = "x86_64-unknown-linux-gnu"
+  define void @test() { ret void }
+...
+---
+name: test
+tracksRegLiveness: true
+stack:
+  - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+body:  |
+   bb.0:
+    liveins: $rdi, $rsi
+)MIR") + Twine(Input) + Twine("...\n"))
+                      .toNullTerminatedStringRef(MIRStr);
+    ;
+
+    // Clear the "test" function from MMI if it's still present.
+    if (Function *Fn = Mod->getFunction("test"))
+      MMI->deleteMachineFunctionFor(*Fn);
+
+    auto MemBuf = MemoryBuffer::getMemBuffer(S, "<input>");
+    auto MIRParse = createMIRParser(std::move(MemBuf), Ctx);
+    Mod = MIRParse->parseIRModule();
+    assert(Mod);
+    Mod->setDataLayout("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-"
+                       "n8:16:32:64-S128");
+
+    bool Result = MIRParse->parseMachineFunctions(*Mod, *MMI);
+    assert(!Result && "Failed to parse unit test machine function?");
+    (void)Result;
+
+    Function *Fn = Mod->getFunction("test");
+    assert(Fn && "Failed to parse a unit test module string?");
+    Fn->setSubprogram(OurFunc);
+    return MMI->getMachineFunction(*Fn);
+  }
+
+  void
+  produceMLocTransferFunction(MachineFunction &MF,
+                              SmallVectorImpl<MLocTransferMap> &MLocTransfer,
+                              unsigned MaxNumBlocks) {
+    LDV->produceMLocTransferFunction(MF, MLocTransfer, MaxNumBlocks);
   }
 };
 
+TEST_F(InstrRefLDVTest, MTransferDefs) {
+  MachineFunction *MF = readMIRBlock(
+   "    $rax = MOV64ri 0\n"
+   "    RETQ $rax\n");
+  setupLDVObj(MF);
+
+  // We should start with only SP tracked.
+  EXPECT_TRUE(MTracker->getNumLocs() == 1);
+
+  SmallVector<MLocTransferMap, 1> TransferMap;
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  // Code contains only one register write: that should assign to each of the
+  // aliasing registers. Test that all of them get locations, and have a
+  // corresponding def at the first instr in the function.
+  const char *RegNames[] = {"RAX", "HAX", "EAX", "AX", "AH", "AL"};
+  EXPECT_TRUE(MTracker->getNumLocs() == 7);
+  for (const char *RegName : RegNames) {
+    Register R = getRegByName(RegName);
+    ASSERT_TRUE(MTracker->isRegisterTracked(R));
+    LocIdx L = MTracker->getRegMLoc(R);
+    ValueIDNum V = MTracker->readReg(R);
+    // Value of this register should be: block zero, instruction 1, and the
+    // location it's defined in is itself.
+    ValueIDNum ToCmp(0, 1, L);
+    EXPECT_EQ(V, ToCmp);
+  }
+
+  // Do the same again, but with an aliasing write. This should write to all
+  // the same registers again, except $ah and $hax (the upper 8 bits of $ax
+  // and 32 bits of $rax resp.).
+  MF = readMIRBlock(
+   "    $rax = MOV64ri 0\n"
+   "    $al = MOV8ri 0\n"
+   "    RETQ $rax\n");
+  setupLDVObj(MF);
+  TransferMap.clear();
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  auto TestRegSetSite = [&](const char *Name, unsigned InstrNum) {
+    Register R = getRegByName(Name);
+    ASSERT_TRUE(MTracker->isRegisterTracked(R));
+    LocIdx L = MTracker->getRegMLoc(R);
+    ValueIDNum V = MTracker->readMLoc(L);
+    ValueIDNum ToCmp(0, InstrNum, L);
+    EXPECT_EQ(V, ToCmp);
+  };
+
+  TestRegSetSite("AL", 2);
+  TestRegSetSite("AH", 1);
+  TestRegSetSite("AX", 2);
+  TestRegSetSite("EAX", 2);
+  TestRegSetSite("HAX", 1);
+  TestRegSetSite("RAX", 2);
+
+  // This call should:
+  //  * Def rax via the implicit-def,
+  //  * Clobber rsi/rdi and all their subregs, via the register mask
+  //  * Same for rcx, despite it not being a use in the instr, it's in the mask
+  //  * NOT clobber $rsp / $esp $ sp, LiveDebugValues deliberately ignores
+  //    these.
+  //  * NOT clobber $rbx, because it's non-volatile
+  //  * Not track every other register in the machine, only those needed.
+ MF = readMIRBlock(
+   "    $rax = MOV64ri 0\n" // instr 1
+   "    $rbx = MOV64ri 0\n" // instr 2
+   "    $rcx = MOV64ri 0\n" // instr 3
+   "    $rdi = MOV64ri 0\n" // instr 4
+   "    $rsi = MOV64ri 0\n" // instr 5
+   "    CALL64r $rax, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit-def $rsp, implicit-def $ssp, implicit-def $rax, implicit-def $esp, implicit-def $sp\n\n\n\n" // instr 6
+   "    RETQ $rax\n"); // instr 7
+  setupLDVObj(MF);
+  TransferMap.clear();
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  const char *RegsSetInCall[] = {"AL",  "AH",  "AX", "EAX", "HAX", "RAX",
+                                 "DIL", "DIH", "DI", "EDI", "HDI", "RDI",
+                                 "SIL", "SIH", "SI", "ESI", "HSI", "RSI",
+                                 "CL",  "CH",  "CX", "ECX", "HCX", "RCX"};
+  for (const char *RegSetInCall : RegsSetInCall)
+    TestRegSetSite(RegSetInCall, 6);
+
+  const char *RegsLeftAlone[] = {"BL", "BH", "BX", "EBX", "HBX", "RBX"};
+  for (const char *RegLeftAlone : RegsLeftAlone)
+    TestRegSetSite(RegLeftAlone, 2);
+
+  // Stack pointer should be the live-in to the function, instruction zero.
+  TestRegSetSite("RSP", 0);
+  // These stack regs should not be tracked either. Nor the (fake) subregs.
+  EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("ESP")));
+  EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("SP")));
+  EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("SPL")));
+  EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("SPH")));
+  EXPECT_FALSE(MTracker->isRegisterTracked(getRegByName("HSP")));
+
+  // Should only be tracking: 6 x {A, B, C, DI, SI} registers = 30,
+  // Plus RSP, SSP = 32.
+  EXPECT_EQ(32u, MTracker->getNumLocs());
+
+
+  // When we DBG_PHI something, we should track all its subregs.
+  MF = readMIRBlock(
+   "    DBG_PHI $rdi, 0\n"
+   "    RETQ\n");
+  setupLDVObj(MF);
+  TransferMap.clear();
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  // All DI regs and RSP tracked.
+  EXPECT_EQ(7u, MTracker->getNumLocs());
+
+  // All the DI registers should have block live-in values, i.e. the argument
+  // to the function.
+  const char *DIRegs[] = {"DIL", "DIH", "DI", "EDI", "HDI", "RDI"};
+  for (const char *DIReg : DIRegs)
+    TestRegSetSite(DIReg, 0);
+}
+
+TEST_F(InstrRefLDVTest, MTransferMeta) {
+  // Meta instructions should not have any effect on register values.
+  SmallVector<MLocTransferMap, 1> TransferMap;
+  MachineFunction *MF = readMIRBlock(
+   "    $rax = MOV64ri 0\n"
+   "    $rax = IMPLICIT_DEF\n"
+   "    $rax = KILL killed $rax\n"
+   "    RETQ $rax\n");
+  setupLDVObj(MF);
+  TransferMap.clear();
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  LocIdx RaxLoc = MTracker->getRegMLoc(getRegByName("RAX"));
+  ValueIDNum V = MTracker->readMLoc(RaxLoc);
+  // Def of rax should be from instruction 1, i.e., unmodified.
+  ValueIDNum Cmp(0, 1, RaxLoc);
+  EXPECT_EQ(Cmp, V);
+}
+
+TEST_F(InstrRefLDVTest, MTransferCopies) {
+  SmallVector<MLocTransferMap, 1> TransferMap;
+  // This memory spill should be recognised, and a spill slot created.
+  MachineFunction *MF = readMIRBlock(
+   "    $rax = MOV64ri 0\n"
+   "    MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0)\n"
+   "    RETQ $rax\n");
+  setupLDVObj(MF);
+  TransferMap.clear();
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  // Check that the spill location contains the value defined in rax by
+  // instruction 1. The MIR header says -16 offset, but it's stored as -8;
+  // it's not completely clear why, but here we only care about correctly
+  // identifying the slot, not that all the surrounding data is correct.
+  SpillLoc L = {getRegByName("RSP"), StackOffset::getFixed(-8)};
+  Optional<ValueIDNum> V = MTracker->readSpill(L);
+  ASSERT_TRUE(V);
+  Register RAX = getRegByName("RAX");
+  LocIdx RaxLoc = MTracker->getRegMLoc(RAX);
+  ValueIDNum Cmp(0, 1, RaxLoc);
+  EXPECT_EQ(*V, Cmp);
+
+  // A spill and restore should be recognised.
+  MF = readMIRBlock(
+   "    $rax = MOV64ri 0\n"
+   "    MOV64mr $rsp, 1, $noreg, 16, $noreg, $rax :: (store 8 into %stack.0)\n"
+   "    $rbx = MOV64rm $rsp, 1, $noreg, 0, $noreg :: (load 8 from %stack.0)\n"
+   "    RETQ\n");
+  setupLDVObj(MF);
+  TransferMap.clear();
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  // Test that rbx contains rax from instruction 1.
+  RAX = getRegByName("RAX");
+  RaxLoc = MTracker->getRegMLoc(RAX);
+  Register RBX = getRegByName("RBX");
+  LocIdx RbxLoc = MTracker->getRegMLoc(RBX);
+  Cmp = ValueIDNum(0, 1, RaxLoc);
+  ValueIDNum RbxVal = MTracker->readMLoc(RbxLoc);
+  EXPECT_EQ(RbxVal, Cmp);
+
+  // FIXME: future work, make sure all the subregisters are transferred too.
+
+  // Copies and x86 movs should be recognised and honoured. In addition, all
+  // of the subregisters should be copied across too.
+  MF = readMIRBlock(
+   "    $rax = MOV64ri 0\n"
+   "    $rcx = COPY $rax\n"
+   "    $rbx = MOV64rr $rcx\n"
+   "    RETQ\n");
+  setupLDVObj(MF);
+  TransferMap.clear();
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  const char *ARegs[] = {"AL", "AH", "AX", "EAX", "HAX", "RAX"};
+  const char *BRegs[] = {"BL", "BH", "BX", "EBX", "HBX", "RBX"};
+  const char *CRegs[] = {"CL", "CH", "CX", "ECX", "HCX", "RCX"};
+  auto CheckReg = [&](unsigned int I) {
+    LocIdx A = MTracker->getRegMLoc(getRegByName(ARegs[I]));
+    LocIdx B = MTracker->getRegMLoc(getRegByName(BRegs[I]));
+    LocIdx C = MTracker->getRegMLoc(getRegByName(CRegs[I]));
+    ValueIDNum ARefVal(0, 1, A);
+    ValueIDNum AVal = MTracker->readMLoc(A);
+    ValueIDNum BVal = MTracker->readMLoc(B);
+    ValueIDNum CVal = MTracker->readMLoc(C);
+    EXPECT_EQ(ARefVal, AVal);
+    EXPECT_EQ(ARefVal, BVal);
+    EXPECT_EQ(ARefVal, CVal);
+  };
+
+  for (unsigned int I = 0; I < 6; ++I)
+    CheckReg(I);
+
+  // When we copy to a subregister, the super-register should be def'd too: it's
+  // value will have changed.
+  MF = readMIRBlock(
+   "    $rax = MOV64ri 0\n"
+   "    $ecx = COPY $eax\n"
+   "    RETQ\n");
+  setupLDVObj(MF);
+  TransferMap.clear();
+  TransferMap.resize(1);
+  produceMLocTransferFunction(*MF, TransferMap, 1);
+
+  // First four regs [al, ah, ax, eax] should be copied to *cx.
+  for (unsigned int I = 0; I < 4; ++I) {
+    LocIdx A = MTracker->getRegMLoc(getRegByName(ARegs[I]));
+    LocIdx C = MTracker->getRegMLoc(getRegByName(CRegs[I]));
+    ValueIDNum ARefVal(0, 1, A);
+    ValueIDNum AVal = MTracker->readMLoc(A);
+    ValueIDNum CVal = MTracker->readMLoc(C);
+    EXPECT_EQ(ARefVal, AVal);
+    EXPECT_EQ(ARefVal, CVal);
+  }
+
+  // But rcx should contain a value defined by the COPY.
+  LocIdx RcxLoc = MTracker->getRegMLoc(getRegByName("RCX"));
+  ValueIDNum RcxVal = MTracker->readMLoc(RcxLoc);
+  ValueIDNum RcxDefVal(0, 2, RcxLoc); // instr 2 -> the copy
+  EXPECT_EQ(RcxVal, RcxDefVal);
+}
+
 TEST_F(InstrRefLDVTest, MLocSingleBlock) {
   // Test some very simple properties about interpreting the transfer function.
   setupSingleBlock();


        


More information about the llvm-commits mailing list