diff --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h --- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h +++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h @@ -380,12 +380,6 @@ 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 @@ 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 @@ /// 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 @@ /// 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/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp @@ -362,7 +362,7 @@ // 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 different debug instruction defined the variable value / location @@ -493,12 +493,12 @@ // 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 @@ 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 @@ Optional 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 @@ } 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 @@ // 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::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 different - // 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 @@ // 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 @@ 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/unittests/CodeGen/InstrRefLDVTest.cpp b/llvm/unittests/CodeGen/InstrRefLDVTest.cpp --- 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 @@ using MLocTransferMap = InstrRefBasedLDV::MLocTransferMap; LLVMContext Ctx; - Module Mod; + std::unique_ptr Mod; std::unique_ptr Machine; std::unique_ptr MF; std::unique_ptr DomTree; + std::unique_ptr MMI; DICompileUnit *OurCU; DIFile *OurFile; DISubprogram *OurFunc; @@ -55,13 +58,15 @@ std::unique_ptr MTracker; std::unique_ptr VTracker; - InstrRefLDVTest() : Ctx(), Mod("beehives", Ctx) { - } + SmallString<256> MIRStr; + + InstrRefLDVTest() : Ctx(), Mod(std::make_unique("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 @@ GTEST_SKIP(); TargetOptions Options; - Machine = std::unique_ptr(T->createTargetMachine( - "X86", "", "", Options, None, None, CodeGenOpt::Aggressive)); + Machine = std::unique_ptr( + 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((LLVMTargetMachine *)&*Machine); const TargetSubtargetInfo &STI = *Machine->getSubtargetImpl(*F); - MF = std::make_unique(*F, (LLVMTargetMachine&)*Machine, STI, FunctionNum, MMI); + MF = std::make_unique(*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 @@ 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(); const TargetSubtargetInfo &STI = MF->getSubtarget(); @@ -144,12 +152,11 @@ // 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 @@ MF->insert(MF->end(), MBB0); MF->RenumberBlocks(); - setupLDVObj(); + setupLDVObj(&*MF); } void setupDiamondBlocks() { @@ -245,7 +252,7 @@ MBB2->addSuccessor(MBB3); MF->RenumberBlocks(); - setupLDVObj(); + setupLDVObj(&*MF); } void setupSimpleLoop() { @@ -275,7 +282,7 @@ MBB1->addSuccessor(MBB1); MF->RenumberBlocks(); - setupLDVObj(); + setupLDVObj(&*MF); } void setupNestedLoops() { @@ -320,7 +327,7 @@ MBB3->addSuccessor(MBB4); MF->RenumberBlocks(); - setupLDVObj(); + setupLDVObj(&*MF); } void setupNoDominatingLoop() { @@ -365,7 +372,7 @@ MBB3->addSuccessor(MBB4); MF->RenumberBlocks(); - setupLDVObj(); + setupLDVObj(&*MF); } void setupBadlyNestedLoops() { @@ -416,10 +423,307 @@ 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, ""); + 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 &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 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 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 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 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();