diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h index e46fb3d311ad6..773223b728740 100644 --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -279,6 +279,50 @@ inline unsigned GetDefaultP2Align(unsigned Opc) { return Align; } +inline bool isConst(unsigned Opc) { + switch (Opc) { + case WebAssembly::CONST_I32: + case WebAssembly::CONST_I32_S: + case WebAssembly::CONST_I64: + case WebAssembly::CONST_I64_S: + case WebAssembly::CONST_F32: + case WebAssembly::CONST_F32_S: + case WebAssembly::CONST_F64: + case WebAssembly::CONST_F64_S: + case WebAssembly::CONST_V128_I8x16: + case WebAssembly::CONST_V128_I8x16_S: + case WebAssembly::CONST_V128_I16x8: + case WebAssembly::CONST_V128_I16x8_S: + case WebAssembly::CONST_V128_I32x4: + case WebAssembly::CONST_V128_I32x4_S: + case WebAssembly::CONST_V128_I64x2: + case WebAssembly::CONST_V128_I64x2_S: + case WebAssembly::CONST_V128_F32x4: + case WebAssembly::CONST_V128_F32x4_S: + case WebAssembly::CONST_V128_F64x2: + case WebAssembly::CONST_V128_F64x2_S: + return true; + default: + return false; + } +} + +inline bool isScalarConst(unsigned Opc) { + switch (Opc) { + case WebAssembly::CONST_I32: + case WebAssembly::CONST_I32_S: + case WebAssembly::CONST_I64: + case WebAssembly::CONST_I64_S: + case WebAssembly::CONST_F32: + case WebAssembly::CONST_F32_S: + case WebAssembly::CONST_F64: + case WebAssembly::CONST_F64_S: + return true; + default: + return false; + } +} + inline bool isArgument(unsigned Opc) { switch (Opc) { case WebAssembly::ARGUMENT_i32: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp index 45502a577e4e2..c036ef02a6d8b 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp @@ -12,15 +12,18 @@ //===----------------------------------------------------------------------===// #include "WebAssemblyDebugValueManager.h" +#include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssembly.h" #include "WebAssemblyMachineFunctionInfo.h" #include "llvm/CodeGen/MachineInstr.h" using namespace llvm; -WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(MachineInstr *Def) { +WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(MachineInstr *Def) + : Def(Def) { // This code differs from MachineInstr::collectDebugValues in that it scans - // the whole BB, not just contiguous DBG_VALUEs. + // the whole BB, not just contiguous DBG_VALUEs, until another definition to + // the same register is encountered. if (!Def->getOperand(0).isReg()) return; CurrentReg = Def->getOperand(0).getReg(); @@ -28,34 +31,317 @@ WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(MachineInstr *Def) { for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), ME = Def->getParent()->end(); MI != ME; ++MI) { + // If another definition appears, stop + if (MI->definesRegister(CurrentReg)) + break; if (MI->isDebugValue() && MI->hasDebugOperandForReg(CurrentReg)) DbgValues.push_back(&*MI); } } -void WebAssemblyDebugValueManager::move(MachineInstr *Insert) { +// Returns true if both A and B are the same CONST_I32/I64/F32/F64 instructions. +// Doesn't include CONST_V128. +static bool isSameScalarConst(const MachineInstr *A, const MachineInstr *B) { + if (A->getOpcode() != B->getOpcode() || + !WebAssembly::isScalarConst(A->getOpcode()) || + !WebAssembly::isScalarConst(B->getOpcode())) + return false; + const MachineOperand &OpA = A->getOperand(1), &OpB = B->getOperand(1); + if ((OpA.isImm() && OpB.isImm() && OpA.getImm() == OpB.getImm()) || + (OpA.isFPImm() && OpB.isFPImm() && OpA.getFPImm() == OpB.getFPImm()) || + (OpA.isGlobal() && OpB.isGlobal() && OpA.getGlobal() == OpB.getGlobal())) + return true; + return false; +} + +SmallVector +WebAssemblyDebugValueManager::getSinkableDebugValues( + MachineInstr *Insert) const { + if (DbgValues.empty()) + return {}; + // DBG_VALUEs between Def and Insert + SmallVector DbgValuesInBetween; + + if (Def->getParent() == Insert->getParent()) { + // When Def and Insert are within the same BB, check if Insert comes after + // Def, because we only support sinking. + bool DefFirst = false; + for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), + ME = Def->getParent()->end(); + MI != ME; ++MI) { + if (&*MI == Insert) { + DefFirst = true; + break; + } + if (MI->isDebugValue()) + DbgValuesInBetween.push_back(&*MI); + } + if (!DefFirst) // Not a sink + return {}; + + } else { // Def and Insert are in different BBs + // If Def and Insert are in different BBs, we only handle a simple case in + // which Insert's BB is a successor of Def's BB. + if (!Def->getParent()->isSuccessor(Insert->getParent())) + return {}; + + // Gather DBG_VALUEs between 'Def~Def BB's end' and + // 'Insert BB's begin~Insert' + for (MachineBasicBlock::iterator MI = std::next(Def->getIterator()), + ME = Def->getParent()->end(); + MI != ME; ++MI) { + if (MI->isDebugValue()) + DbgValuesInBetween.push_back(&*MI); + } + for (MachineBasicBlock::iterator MI = Insert->getParent()->begin(), + ME = Insert->getIterator(); + MI != ME; ++MI) { + if (MI->isDebugValue()) + DbgValuesInBetween.push_back(&*MI); + } + } + + // Gather DebugVariables that are seen between Def and Insert, excluding our + // own DBG_VALUEs in DbgValues. + SmallDenseMap> + SeenDbgVarToDbgValues; + for (auto *DV : DbgValuesInBetween) { + if (std::find(DbgValues.begin(), DbgValues.end(), DV) == DbgValues.end()) { + DebugVariable Var(DV->getDebugVariable(), DV->getDebugExpression(), + DV->getDebugLoc()->getInlinedAt()); + SeenDbgVarToDbgValues[Var].push_back(DV); + } + } + + // Gather sinkable DBG_VALUEs. We should not sink a DBG_VALUE if there is + // another DBG_VALUE between Def and Insert referring to the same + // DebugVariable. For example, + // %0 = someinst + // DBG_VALUE %0, !"a", !DIExpression() // Should not sink with %0 + // %1 = anotherinst + // DBG_VALUE %1, !"a", !DIExpression() + // Where if %0 were to sink, the DBG_VAUE should not sink with it, as that + // would re-order assignments. + SmallVector SinkableDbgValues; + MachineRegisterInfo &MRI = Def->getParent()->getParent()->getRegInfo(); + for (auto *DV : DbgValues) { + DebugVariable Var(DV->getDebugVariable(), DV->getDebugExpression(), + DV->getDebugLoc()->getInlinedAt()); + auto It = SeenDbgVarToDbgValues.find(Var); + if (It == SeenDbgVarToDbgValues.end()) { + SinkableDbgValues.push_back(DV); + continue; + } + if (!WebAssembly::isScalarConst(Def->getOpcode())) + continue; + auto &OverlappingDbgValues = It->second; + bool Sinkable = true; + for (auto *OverlappingDV : OverlappingDbgValues) { + MachineOperand &DbgOp = OverlappingDV->getDebugOperand(0); + if (!DbgOp.isReg()) { + Sinkable = false; + break; + } + Register OtherReg = DbgOp.getReg(); + MachineInstr *OtherDef = MRI.getUniqueVRegDef(OtherReg); + // We have an exception to allow encoutering other DBG_VALUEs with the + // smae DebugVariables, only when they are referring to the same scalar + // CONST instruction. For example, + // %0 = CONST_I32 1 + // DBG_VALUE %0, !"a", !DIExpression() // Can sink with %0 + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"a", !DIExpression() + // When %0 were to be sunk/cloneed, the DBG_VALUE can be sunk/cloned with + // it because even though the second DBG_VALUE refers to the same + // DebugVariable, its value in effect is the same CONST instruction. + // + // This is to allow a case that can happen with RegStackify's + // "rematerializeCheapDef". For example, we have this program with two + // BBs: + // bb0: + // %0 = CONST_I32 1 + // DBG_VALUE %0, !"a", ... + // ... + // INST0 ..., $0 ... + // bb1: + // INST1 ..., $0 ... + // INST2 ..., $0 ... + // + // We process bb0 first. Because %0 is used multiple times, %0 is cloned + // before INST0: + // bb0: + // %0 = CONST_I32 1 + // DBG_VALUE %0, !"a", ... + // ... + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"a", ... + // INST0 ..., $1 ... + // + // And when we process bb1, we clone %0 and its DBG_VALUE again: + // bb0: + // %0 = CONST_I32 1 + // DBG_VALUE %0, !"a", ... + // ... + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"a", ... + // INST0 ..., $1 ... + // bb1: + // %2 = CONST_I32 1 + // DBG_VALUE %2, !"a", ... // !!! + // INST1 ..., $2 ... + // %3 = CONST_I32 1 + // DBG_VALUE %3, !"a", ... // !!! + // INST2 ..., $3 ... + // + // But (without this exception) the cloned DBG_VALUEs marked with !!! are + // not possible to be cloned, because there is a previously cloned + // 'DBG_VALUE %1, !"a"' at the end of bb0 referring to the same + // DebugVariable "a". But in this case they are OK to be cloned, because + // the interfering DBG_VALUE is pointing to the same 'CONST_I32 1', + // because it was cloned from the same instruction. + if (!OtherDef || !isSameScalarConst(Def, OtherDef)) { + Sinkable = false; + break; + } + } + if (Sinkable) + SinkableDbgValues.push_back(DV); + } + return SinkableDbgValues; +} + +// Sink 'Def', and also sink its eligible DBG_VALUEs to the place before +// 'Insert'. Convert the original DBG_VALUEs into undefs. +// +// For DBG_VALUEs to sink properly, if 'Def' and 'Insert' are within the same +// BB, 'Insert' should be below 'Def'; if they are in different BBs, 'Insert' +// should be in one of 'Def's BBs successors. Def will be sunk regardless of the +// location. +// +// This DebugValueManager's new Def and DbgValues will be updated to the newly +// sinked Def + DBG_VALUEs. +void WebAssemblyDebugValueManager::sink(MachineInstr *Insert) { MachineBasicBlock *MBB = Insert->getParent(); - for (MachineInstr *DBI : reverse(DbgValues)) - MBB->splice(Insert, DBI->getParent(), DBI); + MachineFunction *MF = MBB->getParent(); + + // Get the list of sinkable DBG_VALUEs. This should be done before sinking + // Def, because we need to examine instructions between Def and Insert. + SmallVector SinkableDbgValues = + getSinkableDebugValues(Insert); + + // Sink Def first. + MBB->splice(Insert, Def->getParent(), Def); + + if (DbgValues.empty()) + return; + + // Clone sinkable DBG_VALUEs and insert them. + SmallVector NewDbgValues; + for (MachineInstr *DV : SinkableDbgValues) { + MachineInstr *Clone = MF->CloneMachineInstr(DV); + MBB->insert(Insert, Clone); + NewDbgValues.push_back(Clone); + } + + // When sinking a Def and its DBG_VALUEs, we shouldn't just remove the + // original DBG_VALUE instructions; we should set them to undef not to create + // an impossible combination of variable assignments in the original program. + // For example, this is the original program in order: + // %0 = CONST_I32 0 + // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 + // %2 = CONST_I32 2 + // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 1 + // %3 = CONST_I32 3 + // DBG_VALUE %3, !"b", !DIExpression() // a = 2, b = 3 + // + // If %2 were to sink below %3, if we just sink DBG_VALUE %1 with it, the + // debug info will show the variable "b" is updated to 2, creating the + // variable assignment combination of (a = 0, b = 3), which is not possible in + // the original program: + // %0 = CONST_I32 0 + // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 + // %3 = CONST_I32 3 + // DBG_VALUE %3, !"b", !DIExpression() // a = 0, b = 3 (Incorrect!) + // %2 = CONST_I32 2 + // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 3 + // + // To fix this,we leave an undef DBG_VALUE in its original place, so that the + // result will be + // %0 = CONST_I32 0 + // DBG_VALUE %0, !"a", !DIExpression() // a = 0, b = ? + // %1 = CONST_I32 1 + // DBG_VALUE %1, !"b", !DIExpression() // a = 0, b = 1 + // DBG_VALUE $noreg, !"a", !DIExpression() // a = ?, b = 1 + // %3 = CONST_I32 3 + // DBG_VALUE %3, !"b", !DIExpression() // a = ?, b = 3 + // %2 = CONST_I32 2 + // DBG_VALUE %2, !"a", !DIExpression() // a = 2, b = 3 + // Now in the middle "a" will be shown as "optimized out", but it wouldn't + // show the impossible combination of (a = 0, b = 3). + for (MachineInstr *DV : DbgValues) + DV->setDebugValueUndef(); + + DbgValues.swap(NewDbgValues); + return; } -void WebAssemblyDebugValueManager::clone(MachineInstr *Insert, - Register NewReg) { +// Clone 'Def', and also clone its eligible DBG_VALUEs to the place before +// 'Insert'. +// +// For DBG_VALUEs to be cloned properly, if 'Def' and 'Insert' are within the +// same BB, 'Insert' should be below 'Def'; if they are in different BBs, +// 'Insert' should be in one of 'Def's BBs successors. Def will be cloned +// regardless of the location. +// +// If NewReg is not $noreg, the newly cloned DBG_VALUEs will have the new +// register as its operand. +void WebAssemblyDebugValueManager::cloneSink(MachineInstr *Insert, + Register NewReg, + bool CloneDef) const { MachineBasicBlock *MBB = Insert->getParent(); MachineFunction *MF = MBB->getParent(); - for (MachineInstr *DBI : reverse(DbgValues)) { - MachineInstr *Clone = MF->CloneMachineInstr(DBI); - for (auto &MO : Clone->getDebugOperandsForReg(CurrentReg)) - MO.setReg(NewReg); + + SmallVector SinkableDbgValues = + getSinkableDebugValues(Insert); + + // Clone Def first. + if (CloneDef) { + MachineInstr *Clone = MF->CloneMachineInstr(Def); + if (NewReg != CurrentReg && NewReg.isValid()) + Clone->getOperand(0).setReg(NewReg); MBB->insert(Insert, Clone); } + + if (DbgValues.empty()) + return; + + // Clone sinkable DBG_VALUEs and insert them. + SmallVector NewDbgValues; + for (MachineInstr *DV : SinkableDbgValues) { + MachineInstr *Clone = MF->CloneMachineInstr(DV); + MBB->insert(Insert, Clone); + NewDbgValues.push_back(Clone); + } + + if (NewReg != CurrentReg && NewReg.isValid()) + for (auto *DBI : NewDbgValues) + for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) + MO.setReg(NewReg); } +// Update the register for Def and DBG_VALUEs. void WebAssemblyDebugValueManager::updateReg(Register Reg) { - for (auto *DBI : DbgValues) - for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) - MO.setReg(Reg); - CurrentReg = Reg; + if (Reg != CurrentReg && Reg.isValid()) { + for (auto *DBI : DbgValues) + for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) + MO.setReg(Reg); + CurrentReg = Reg; + Def->getOperand(0).setReg(Reg); + } } void WebAssemblyDebugValueManager::replaceWithLocal(unsigned LocalId) { @@ -67,3 +353,10 @@ void WebAssemblyDebugValueManager::replaceWithLocal(unsigned LocalId) { MO.ChangeToTargetIndex(IndexType, LocalId); } } + +// Remove Def, and set its DBG_VALUEs to undef. +void WebAssemblyDebugValueManager::removeDef() { + Def->removeFromParent(); + for (MachineInstr *DV : DbgValues) + DV->setDebugValueUndef(); +} diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h index 4c63af21406e1..3ca9290ecd21d 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h @@ -26,19 +26,29 @@ namespace llvm { class MachineInstr; class WebAssemblyDebugValueManager { + MachineInstr *Def; SmallVector DbgValues; Register CurrentReg; + SmallVector + getSinkableDebugValues(MachineInstr *Insert) const; public: WebAssemblyDebugValueManager(MachineInstr *Def); - void move(MachineInstr *Insert); - void clone(MachineInstr *Insert, Register NewReg); + // Sink 'Def', and also sink its eligible DBG_VALUEs to the place before + // 'Insert'. Convert the original DBG_VALUEs into undefs. + void sink(MachineInstr *Insert); + // Clone 'Def' (optionally), and also clone its eligible DBG_VALUEs to the + // place before 'Insert'. + void cloneSink(MachineInstr *Insert, Register NewReg = Register(), + bool CloneDef = true) const; // Update the register for Def and DBG_VALUEs. void updateReg(Register Reg); // Replace the current register in DBG_VALUEs with the given LocalId target // index. void replaceWithLocal(unsigned LocalId); + // Remove Def, and set its DBG_VALUEs to undef. + void removeDef(); }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp index 4b24f7fdb118c..2e0df3c478411 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -278,12 +278,13 @@ static MachineInstr *getVRegDef(unsigned Reg, const MachineInstr *Insert, } // Test whether Reg, as defined at Def, has exactly one use. This is a -// generalization of MachineRegisterInfo::hasOneUse that uses LiveIntervals -// to handle complex cases. -static bool hasOneUse(unsigned Reg, MachineInstr *Def, MachineRegisterInfo &MRI, - MachineDominatorTree &MDT, LiveIntervals &LIS) { +// generalization of MachineRegisterInfo::hasOneNonDBGUse that uses +// LiveIntervals to handle complex cases. +static bool hasOneNonDBGUse(unsigned Reg, MachineInstr *Def, + MachineRegisterInfo &MRI, MachineDominatorTree &MDT, + LiveIntervals &LIS) { // Most registers are in SSA form here so we try a quick MRI query first. - if (MRI.hasOneUse(Reg)) + if (MRI.hasOneNonDBGUse(Reg)) return true; bool HasOne = false; @@ -525,11 +526,10 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, LLVM_DEBUG(dbgs() << "Move for single use: "; Def->dump()); WebAssemblyDebugValueManager DefDIs(Def); - MBB.splice(Insert, &MBB, Def); - DefDIs.move(Insert); + DefDIs.sink(Insert); LIS.handleMove(*Def); - if (MRI.hasOneDef(Reg) && MRI.hasOneUse(Reg)) { + if (MRI.hasOneDef(Reg) && MRI.hasOneNonDBGUse(Reg)) { // No one else is using this register for anything so we can just stackify // it in place. MFI.stackifyVReg(MRI, Reg); @@ -537,8 +537,8 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, // The register may have unrelated uses or defs; create a new register for // just our one def and use so that we can stackify it. Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); - Def->getOperand(0).setReg(NewReg); Op.setReg(NewReg); + DefDIs.updateReg(NewReg); // Tell LiveIntervals about the new register. LIS.createAndComputeVirtRegInterval(NewReg); @@ -551,8 +551,6 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, MFI.stackifyVReg(MRI, NewReg); - DefDIs.updateReg(NewReg); - LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump()); } @@ -560,6 +558,13 @@ static MachineInstr *moveForSingleUse(unsigned Reg, MachineOperand &Op, return Def; } +static MachineInstr *getPrevNonDebugInst(MachineInstr *MI) { + for (auto *I = MI->getPrevNode(); I; I = I->getPrevNode()) + if (!I->isDebugInstr()) + return I; + return nullptr; +} + /// A trivially cloneable instruction; clone it and nest the new copy with the /// current instruction. static MachineInstr *rematerializeCheapDef( @@ -573,9 +578,10 @@ static MachineInstr *rematerializeCheapDef( WebAssemblyDebugValueManager DefDIs(&Def); Register NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); - TII->reMaterialize(MBB, Insert, NewReg, 0, Def, *TRI); + DefDIs.cloneSink(&*Insert, NewReg); Op.setReg(NewReg); - MachineInstr *Clone = &*std::prev(Insert); + MachineInstr *Clone = getPrevNonDebugInst(&*Insert); + assert(Clone); LIS.InsertMachineInstrInMaps(*Clone); LIS.createAndComputeVirtRegInterval(NewReg); MFI.stackifyVReg(MRI, NewReg); @@ -592,19 +598,13 @@ static MachineInstr *rematerializeCheapDef( } // If that was the last use of the original, delete the original. - // Move or clone corresponding DBG_VALUEs to the 'Insert' location. if (IsDead) { LLVM_DEBUG(dbgs() << " - Deleting original\n"); SlotIndex Idx = LIS.getInstructionIndex(Def).getRegSlot(); LIS.removePhysRegDefAt(MCRegister::from(WebAssembly::ARGUMENTS), Idx); LIS.removeInterval(Reg); LIS.RemoveMachineInstrFromMaps(Def); - Def.eraseFromParent(); - - DefDIs.move(&*Insert); - DefDIs.updateReg(NewReg); - } else { - DefDIs.clone(&*Insert, NewReg); + DefDIs.removeDef(); } return Clone; @@ -636,28 +636,26 @@ static MachineInstr *moveAndTeeForMultiUse( MachineRegisterInfo &MRI, const WebAssemblyInstrInfo *TII) { LLVM_DEBUG(dbgs() << "Move and tee for multi-use:"; Def->dump()); - WebAssemblyDebugValueManager DefDIs(Def); + const auto *RegClass = MRI.getRegClass(Reg); + Register TeeReg = MRI.createVirtualRegister(RegClass); + Register DefReg = MRI.createVirtualRegister(RegClass); // Move Def into place. - MBB.splice(Insert, &MBB, Def); + WebAssemblyDebugValueManager DefDIs(Def); + DefDIs.sink(Insert); LIS.handleMove(*Def); // Create the Tee and attach the registers. - const auto *RegClass = MRI.getRegClass(Reg); - Register TeeReg = MRI.createVirtualRegister(RegClass); - Register DefReg = MRI.createVirtualRegister(RegClass); MachineOperand &DefMO = Def->getOperand(0); MachineInstr *Tee = BuildMI(MBB, Insert, Insert->getDebugLoc(), TII->get(getTeeOpcode(RegClass)), TeeReg) .addReg(Reg, RegState::Define) .addReg(DefReg, getUndefRegState(DefMO.isDead())); Op.setReg(TeeReg); - DefMO.setReg(DefReg); + DefDIs.updateReg(DefReg); SlotIndex TeeIdx = LIS.InsertMachineInstrInMaps(*Tee).getRegSlot(); SlotIndex DefIdx = LIS.getInstructionIndex(*Def).getRegSlot(); - DefDIs.move(Insert); - // Tell LiveIntervals we moved the original vreg def from Def to Tee. LiveInterval &LI = LIS.getInterval(Reg); LiveInterval::iterator I = LI.FindSegmentContaining(DefIdx); @@ -674,8 +672,11 @@ static MachineInstr *moveAndTeeForMultiUse( imposeStackOrdering(Def); imposeStackOrdering(Tee); - DefDIs.clone(Tee, DefReg); - DefDIs.clone(Insert, TeeReg); + // Even though 'TeeReg, Reg = TEE ...', has two defs, we don't need to clone + // DBG_VALUEs for both of them, given that the latter will cancel the former + // anyway. Here we only clone DBG_VALUEs for TeeReg, which will be converted + // to a local index in ExplicitLocals pass. + DefDIs.cloneSink(Insert, TeeReg, /* CloneDef */ false); LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump()); LLVM_DEBUG(dbgs() << " - Tee instruction: "; Tee->dump()); @@ -876,7 +877,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) { bool SameBlock = DefI->getParent() == &MBB; bool CanMove = SameBlock && isSafeToMove(Def, &Use, Insert, MFI, MRI) && !TreeWalker.isOnStack(Reg); - if (CanMove && hasOneUse(Reg, DefI, MRI, MDT, LIS)) { + if (CanMove && hasOneNonDBGUse(Reg, DefI, MRI, MDT, LIS)) { Insert = moveForSingleUse(Reg, Use, DefI, MBB, Insert, LIS, MFI, MRI); // If we are removing the frame base reg completely, remove the debug @@ -913,7 +914,7 @@ bool WebAssemblyRegStackify::runOnMachineFunction(MachineFunction &MF) { Register DefReg = SubsequentDef->getReg(); Register UseReg = SubsequentUse->getReg(); // TODO: This single-use restriction could be relaxed by using tees - if (DefReg != UseReg || !MRI.hasOneUse(DefReg)) + if (DefReg != UseReg || !MRI.hasOneNonDBGUse(DefReg)) break; MFI.stackifyVReg(MRI, DefReg); ++SubsequentDef; diff --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll b/llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll index a8d60b591d6d8..5eccb316778c6 100644 --- a/llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll +++ b/llvm/test/DebugInfo/WebAssembly/dbg-value-move-2.ll @@ -3,8 +3,8 @@ ; CHECK: {{.*}}After WebAssembly Register Stackify ; CHECK: bb.2.for.body: -; CHECK: [[REG:%[0-9]+]]:i32 = TEE_I32 {{.*}} fib2.c:6:7 -; CHECK-NEXT: DBG_VALUE [[REG]]:i32, $noreg, !"a", {{.*}} fib2.c:2:13 +; CHECK: [[TEEREG:%[0-9]+]]:i32, %{{[0-9]+}}:i32 = TEE_I32 {{.*}} fib2.c:6:7 +; CHECK-NEXT: DBG_VALUE [[TEEREG]]:i32, $noreg, !"a", {{.*}} fib2.c:2:13 ; ModuleID = 'fib2.bc' ; The test generated via: clang --target=wasm32-unknown-unknown-wasm fib2.c -g -O2 diff --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-move-reg-stackify.mir b/llvm/test/DebugInfo/WebAssembly/dbg-value-move-reg-stackify.mir deleted file mode 100644 index 041707ef8632e..0000000000000 --- a/llvm/test/DebugInfo/WebAssembly/dbg-value-move-reg-stackify.mir +++ /dev/null @@ -1,60 +0,0 @@ -# RUN: llc < %s -run-pass=wasm-reg-stackify -x=mir 2>&1 | FileCheck %s - -# CHECK: body: -# CHECK: dead %3:i32 = I32_WRAP_I64 %0, -# CHECK-NEXT: DBG_VALUE %1:i32 -# CHECK-NEXT: dead %2:i32 = CALL @bar, -# CHECK-NEXT: DBG_VALUE %1:i32, -# CHECK-NEXT: %[[NEWREG:.*]]:i32 = CALL @bar, -# CHECK-NEXT: DBG_VALUE %[[NEWREG]], -# CHECK-NEXT: CALL @foo, %[[NEWREG]], - ---- | - target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" - target triple = "wasm32-unknown-unknown" - - declare void @foo(i32) - declare i32 @bar() - - define void @test(i64 %arg) { - unreachable - } - - !llvm.dbg.cu = !{!0} - !llvm.module.flags = !{!4} - !0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !2, producer: "clang LLVM (rustc version 1.30.0-dev)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !1, globals: !1) - !1 = !{} - !2 = !DIFile(filename: "", directory: "") - !3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "&str", file: !2, size: 64, align: 32, elements: !{}, identifier: "111094d970b097647de579f9c509ef08") - !4 = !{i32 2, !"Debug Info Version", i32 3} - !5 = distinct !DILexicalBlock(scope: !6, file: !2, line: 357, column: 8) - !6 = distinct !DISubprogram(name: "testfoo", linkageName: "_testba", scope: !7, file: !2, line: 353, type: !8, isLocal: true, isDefinition: true, scopeLine: 353, flags: DIFlagPrototyped, isOptimized: true, unit: !0, templateParams: !1, retainedNodes: !9) - !7 = !DINamespace(name: "ptr", scope: null) - !8 = !DISubroutineType(types: !1) - !9 = !{!10} - !10 = !DILocalVariable(name: "val0", scope: !5, file: !2, line: 357, type: !3, align: 4) - !11 = !DILocalVariable(name: "val1", scope: !5, file: !2, line: 358, type: !3, align: 4) - !12 = !DILocalVariable(name: "val2", scope: !5, file: !2, line: 359, type: !3, align: 4) - !13 = !DILocation(line: 357, column: 12, scope: !5) - !14 = !DILocation(line: 358, column: 12, scope: !5) - !15 = !DILocation(line: 359, column: 12, scope: !5) - ---- -name: test -liveins: - - { reg: '$arguments' } -tracksRegLiveness: true -body: | - bb.0: - liveins: $arguments - %0:i64 = ARGUMENT_i64 0, implicit $arguments - %1:i32 = I32_WRAP_I64 %0:i64, implicit-def dead $arguments - DBG_VALUE %1:i32, $noreg, !10, !DIExpression(), debug-location !13; :357:12 line no:357 - %1:i32 = CALL @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64 - DBG_VALUE %1:i32, $noreg, !11, !DIExpression(), debug-location !14; :357:12 line no:357 - %1:i32 = CALL @bar, implicit-def dead $arguments, implicit $sp32, implicit $sp64 - DBG_VALUE %1:i32, $noreg, !12, !DIExpression(), debug-location !15; :357:12 line no:357 - CALL @foo, %1:i32, implicit-def dead $arguments, implicit $sp32, implicit $sp64 - RETURN implicit-def dead $arguments - -... diff --git a/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-stackify.mir b/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-stackify.mir new file mode 100644 index 0000000000000..cb78abd54cbd8 --- /dev/null +++ b/llvm/test/DebugInfo/WebAssembly/dbg-value-reg-stackify.mir @@ -0,0 +1,514 @@ +# RUN: llc -run-pass wasm-reg-stackify %s -o - | FileCheck %s + +# Tests for DBG_VALUE hanlding in RegStackify + DebugValueManager + +--- | + target triple = "wasm32-unknown-unknown" + + declare void @use(i32) + declare void @use_2(i32, i32) + + define void @sink_simple() { + call void @llvm.dbg.value(metadata i32 0, metadata !5, metadata !DIExpression()), !dbg !10 + call void @llvm.dbg.value(metadata i32 0, metadata !11, metadata !DIExpression()), !dbg !10 + call void @llvm.dbg.value(metadata i32 0, metadata !12, metadata !DIExpression()), !dbg !10 + call void @llvm.dbg.value(metadata i32 0, metadata !13, metadata !DIExpression()), !dbg !10 + ret void + } + define void @sink_non_consecutive() { + unreachable + } + define void @dont_sink_above_def() { + unreachable + } + define void @sink_to_same_place() { + unreachable + } + define void @cannot_sink_across_same_variable() { + unreachable + } + define void @cannot_sink_across_same_variable2() { + unreachable + } + define void @can_sink_across_same_variable_with_same_const() { + unreachable + } + define void @sink_multiple_defs() { + unreachable + } + define void @clone_same_bb() { + unreachable + } + define void @clone_different_bb() { + unreachable + } + define void @tee_with_two_use_insts() { + unreachable + } + define void @tee_with_one_inst_with_two_uses() { + unreachable + } + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2, !3, !4} + + ; Note the current mapping variable metadata and their names, which we will + ; use in all functions in ths file: + ; - "var_a" / VAR_A: !5 + ; - "var_b" / VAR_B: !11 + ; - "var_c" / VAR_C: !12 + ; - "var_d" / VAR_D: !13 + ; We will use VAR_? in the CHECK lines for robustness in case of metadata + ; renumbering, but currently in mir tests we cannot use variable names like + ; "var_a" directly in the input, which can be confusing to read. + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug) + !1 = !DIFile(filename: "test.c", directory: "") + !2 = !{i32 7, !"Dwarf Version", i32 5} + !3 = !{i32 2, !"Debug Info Version", i32 3} + !4 = !{i32 1, !"wchar_size", i32 4} + !5 = !DILocalVariable(name: "var_a", scope: !6, file: !1, line: 2, type: !9) + ; CHECK: ![[VAR_A:[0-9]+]] = !DILocalVariable(name: "var_a" + !6 = distinct !DISubprogram(name: "sink_simple", scope: !1, file: !1, line: 1, type: !7, scopeLine: 1, unit: !0) + !7 = !DISubroutineType(types: !8) + !8 = !{null} + !9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !10 = !DILocation(line: 0, scope: !6) + !11 = !DILocalVariable(name: "var_b", scope: !6, file: !1, line: 2, type: !9) + ; CHECK: ![[VAR_B:[0-9]+]] = !DILocalVariable(name: "var_b" + !12 = !DILocalVariable(name: "var_c", scope: !6, file: !1, line: 2, type: !9) + ; CHECK: ![[VAR_C:[0-9]+]] = !DILocalVariable(name: "var_c" + !13 = !DILocalVariable(name: "var_d", scope: !6, file: !1, line: 2, type: !9) + ; CHECK: ![[VAR_D:[0-9]+]] = !DILocalVariable(name: "var_d" +... + +--- +# A simple sinking example. +# '%0 = CONST_I32 1' will sink to the place before 'CALL %use', and the two +# DBG_VALUEs will sink with it, leaving the original DBG_VALUEs to be set to +# undef (= DBG_VALUE $noreg). +# CHECK-LABEL: name: sink_simple +name: sink_simple +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# Sinking when DBG_VALUEs are non-consecutive. +# '%0 = CONST_I32 1' will sink to the place before 'CALL %use', and the two +# DBG_VALUEs will sink with it, even though they are not consecutive. The +# original DBG_VALUEs will be set to undef. +# CHECK-LABEL: name: sink_non_consecutive +name: sink_non_consecutive +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# Only DBG_VALUEs following a def should be sunk together. +# '%0 = CONST_I32 1' will sink to the place before 'CALL %use', but the +# DBG_VALUE above it should be untouched. +# CHECK-LABEL: name: dont_sink_above_def +name: dont_sink_above_def +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + %0:i32 = CONST_I32 1, implicit-def $arguments + NOP implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# A sink no-op case. +# '%0 = CONST_I32 1' will sink to the place before 'CALL %use', but it's already +# right before the CALL so it should be effectively a no-op. But currently +# sinking happens anyway so this will create unnecessary two undef DBG_VALUEs. +# This increases the number of DBG_VALUEs but doesn't hurt the coverage or +# generate incorrect debug info. TODO Improve this? +# CHECK-LABEL: name: sink_to_same_place +name: sink_to_same_place +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# A DBG_VALUE cannot be sunk across another DBG_VALUE that has the same +# DebugVariable, because it will reorder assignments. +# '%0 = CONST_I32 1' will sink to the place before 'CALL %use'. But from the two +# DBG_VALUEs following it, only the DBG_VALUE for "var_b" can sink with it, +# because there is another 'DBG_VALUE 10' for "var_a" in the middle. +# CHECK-LABEL: name: cannot_sink_across_same_variable +name: cannot_sink_across_same_variable +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + DBG_VALUE 10, $noreg, !5, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE 10, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# Another case in which a DBG_VALUE cannot be sunk across another DBG_VALUE with +# the same DebugVariable, because it will reorder assignments. +# '%0 = CONST_I32 1' will sink to the place before 'CALL %use'. But from the two +# DBG_VALUEs following it, only the DBG_VALUE for "var_b" can sink with it, +# because there is another 'DBG_VALUE %1, "var_a"' in the middle. +# CHECK-LABEL: name: cannot_sink_across_same_variable2 +name: cannot_sink_across_same_variable2 +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + %1:i32 = CONST_I32 2, implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + DBG_VALUE %1:i32, $noreg, !5, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: %1:i32 = CONST_I32 2, implicit-def $arguments + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# There is a exception in which a DBG_VALUE can be sunk across another DBG_VALUE +# with the same DebugVariable: when the interfering DBG_VALUE refers to the same +# CONST_[I32/I64/F32/F64] instruction, in which case we don't reorder +# assignments. +# +# This is the same test with the previous one with one difference: %1 has the +# same CONST instruction with %0 'CONST_I32 1'. We can sink the DBG_VALUE for +# "var_a" here as well. +# CHECK-LABEL: name: can_sink_across_same_variable_with_same_const +name: can_sink_across_same_variable_with_same_const +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + %1:i32 = CONST_I32 1, implicit-def $arguments ; Same CONST_I32 + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + DBG_VALUE %1:i32, $noreg, !5, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: %1:i32 = CONST_I32 1, implicit-def $arguments + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# Both %0 and %1 will be sunk to the place before ADD_I32. DBG_VALUEs associated +# with those two defs will be sunk as well, leaving the original DBG_VALUEs set +# to undef. +# CHECK-LABEL: name: sink_multiple_defs +name: sink_multiple_defs +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + %1:i32 = CONST_I32 2, implicit-def $arguments + DBG_VALUE %1:i32, $noreg, !12, !DIExpression(), debug-location !10 + DBG_VALUE %1:i32, $noreg, !13, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + %2:i32 = ADD_I32 %0:i32, %1:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_C]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_D]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: %1:i32 = CONST_I32 2, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_C]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_D]], !DIExpression() + ; CHECK-NEXT: dead %2:i32 = ADD_I32 %0, %1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# A simple cloning example. +# When processing the second 'CALL @use', because %0 has multiple uses, the def +# '%0 = CONST_I32 1' is cloned before the CALL, along with its DBG_VALUEs. And +# then when processing the first 'CALL @use', by that time %0 has only one use +# remaining, so it is just sink with the DBG_VALUEs, leaving the original +# DBG_VALUEs undef. +# CHECK-LABEL: name: clone_same_bb +name: clone_same_bb +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %0:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %0, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: %1:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# Cloning across different BBs. +# First, when bb.0's 'CALL @use' is procssed, '%0 = CONST_I32 1' and its +# DBG_VALUEs are cloned before the CALL. And when bb.1's 'CALL @use' is +# processed, '%0 = CONST_I32 1' and its DBG_VALUEs are cloned to bb.1 this time. +# Even though there are (previously cloned) DBG_VALUEs for "var_a" and "var_b" +# in the middle, it's fine because they point to the same 'CONST_I32 1' +# instruction. +# After the second cloning, the original '%0 = CONST_I32 1' is removed because +# it doesn't have any users anymore, leaving its original DBG_VALUEs as undef. +# CHECK-LABEL: name: clone_different_bb +name: clone_different_bb +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + successors: %bb.1 + liveins: $arguments + %0:i32 = CONST_I32 1, implicit-def $arguments + DBG_VALUE %0:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %0:i32, $noreg, !11, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use, %0:i32, implicit-def $arguments + BR %bb.1, implicit-def $arguments + + ; CHECK: bb.0: + ; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %1:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %1, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: BR %bb.1, implicit-def $arguments + + bb.1: + ; predecessors: %bb.0 + CALL @use, %0:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: bb.1: + ; CHECK: %2:i32 = CONST_I32 1, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %2, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %2, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %2, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# TEE conversion example. +# Convert this form: +# Reg = INST ... // Def +# DBG_VALUE Reg, ... +# INST ..., Reg, ... // Insert +# INST ..., Reg, ... +# to +# DefReg = INST ... // Def (to become the new Insert) +# DBG_VALUE DefReg, ... +# TeeReg, Reg = TEE_... DefReg +# DBG_VALUE TeeReg, ... +# INST ..., TeeReg, ... // Insert +# INST ..., Reg, ... +# CHECK-LABEL: name: tee_with_two_use_insts +name: tee_with_two_use_insts +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = ARGUMENT_i32 0, implicit $arguments + %1:i32 = ARGUMENT_i32 1, implicit $arguments + %2:i32 = MUL_I32 %1:i32, %0:i32, implicit-def $arguments + DBG_VALUE %2:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %2:i32, $noreg, !11, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use, %2:i32, implicit-def $arguments + CALL @use, %2:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: %0:i32 = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: %1:i32 = ARGUMENT_i32 1, implicit $arguments + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %4:i32 = MUL_I32 %1, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %4, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %4, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: %3:i32, %2:i32 = TEE_I32 %4, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %3, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %3, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use, %3, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: CALL @use, %2, implicit-def $arguments + ; CHECK-NEXT: RETURN implicit-def $arguments +... + +--- +# Another TEE conversion example. The previous example had two instructions +# that use a single register, whereas this has one instructions that has two +# same use operands. The resulting transformation is the same. +# CHECK-LABEL: name: tee_with_one_inst_with_two_uses +name: tee_with_one_inst_with_two_uses +liveins: + - { reg: '$arguments' } +tracksRegLiveness: true +body: | + bb.0: + liveins: $arguments + %0:i32 = ARGUMENT_i32 0, implicit $arguments + %1:i32 = ARGUMENT_i32 1, implicit $arguments + %2:i32 = MUL_I32 %1:i32, %0:i32, implicit-def $arguments + DBG_VALUE %2:i32, $noreg, !5, !DIExpression(), debug-location !10 + DBG_VALUE %2:i32, $noreg, !11, !DIExpression(), debug-location !10 + NOP implicit-def $arguments + CALL @use_2, %2:i32, %2:i32, implicit-def $arguments + RETURN implicit-def $arguments + + ; CHECK: %0:i32 = ARGUMENT_i32 0, implicit $arguments + ; CHECK-NEXT: %1:i32 = ARGUMENT_i32 1, implicit $arguments + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: NOP implicit-def $arguments + ; CHECK-NEXT: %4:i32 = MUL_I32 %1, %0, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %4, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %4, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: %3:i32, %2:i32 = TEE_I32 %4, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: DBG_VALUE %3, $noreg, ![[VAR_A]], !DIExpression() + ; CHECK-NEXT: DBG_VALUE %3, $noreg, ![[VAR_B]], !DIExpression() + ; CHECK-NEXT: CALL @use_2, %3, %2, implicit-def $arguments, implicit-def $value_stack, implicit $value_stack + ; CHECK-NEXT: RETURN implicit-def $arguments +...