//===----------------------------------------------------------------------===//
#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();
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<MachineInstr *>
+WebAssemblyDebugValueManager::getSinkableDebugValues(
+ MachineInstr *Insert) const {
+ if (DbgValues.empty())
+ return {};
+ // DBG_VALUEs between Def and Insert
+ SmallVector<MachineInstr *, 8> 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<DebugVariable, SmallVector<MachineInstr *, 2>>
+ 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<MachineInstr *, 1> 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<MachineInstr *, 1> SinkableDbgValues =
+ getSinkableDebugValues(Insert);
+
+ // Sink Def first.
+ MBB->splice(Insert, Def->getParent(), Def);
+
+ if (DbgValues.empty())
+ return;
+
+ // Clone sinkable DBG_VALUEs and insert them.
+ SmallVector<MachineInstr *, 1> 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<MachineInstr *> 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<MachineInstr *, 1> 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) {
MO.ChangeToTargetIndex(IndexType, LocalId);
}
}
+
+// Remove Def, and set its DBG_VALUEs to undef.
+void WebAssemblyDebugValueManager::removeDef() {
+ Def->removeFromParent();
+ for (MachineInstr *DV : DbgValues)
+ DV->setDebugValueUndef();
+}
}
// 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;
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);
// 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);
MFI.stackifyVReg(MRI, NewReg);
- DefDIs.updateReg(NewReg);
-
LLVM_DEBUG(dbgs() << " - Replaced register: "; Def->dump());
}
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(
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);
}
// 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;
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);
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());
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
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;
--- /dev/null
+# 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
+...