1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 02:33:06 +01:00

Reapply "[DebugInfo] Add new instruction and DIExpression operator for variadic debug values"

Rewrites test to use correct architecture triple; fixes incorrect
reference in SourceLevelDebugging doc; simplifies `spillReg` behaviour
so as to not be dependent on changes elsewhere in the patch stack.

This reverts commit d2000b45d033c06dc7973f59909a0ad12887ff51.
This commit is contained in:
Stephen Tozer 2021-03-04 12:02:28 +00:00
parent 4a1dabde23
commit e0cb677eb6
23 changed files with 467 additions and 139 deletions

View File

@ -5324,6 +5324,14 @@ The current supported opcode vocabulary is limited:
``AsmPrinter`` pass when a call site parameter value
(``DW_AT_call_site_parameter_value``) is represented as entry value
of the parameter.
- ``DW_OP_LLVM_arg, N`` is used in debug intrinsics that refer to more than one
value, such as one that calculates the sum of two registers. This is always
used in combination with an ordered list of values, such that
``DW_OP_LLVM_arg, N`` refers to the ``N``th element in that list. For
example, ``!DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_minus,
DW_OP_stack_value)`` used with the list ``(%reg1, %reg2)`` would evaluate to
``%reg1 - reg2``. This list of values should be provided by the containing
intrinsic/instruction.
- ``DW_OP_breg`` (or ``DW_OP_bregx``) represents a content on the provided
signed offset of the specified register. The opcode is only generated by the
``AsmPrinter`` pass to describe call site parameter value which requires an

View File

@ -576,13 +576,15 @@ the equivalent location is used.
After MIR locations are assigned to each variable, machine pseudo-instructions
corresponding to each ``llvm.dbg.value`` and ``llvm.dbg.addr`` intrinsic are
inserted. These ``DBG_VALUE`` instructions appear thus:
inserted. There are two forms of this type of instruction.
The first form, ``DBG_VALUE``, appears thus:
.. code-block:: text
DBG_VALUE %1, $noreg, !123, !DIExpression()
And have the following operands:
And has the following operands:
* The first operand can record the variable location as a register,
a frame index, an immediate, or the base address register if the original
debug intrinsic referred to memory. ``$noreg`` indicates the variable
@ -594,6 +596,22 @@ And have the following operands:
* Operand 3 is the Variable field of the original debug intrinsic.
* Operand 4 is the Expression field of the original debug intrinsic.
The second form, ``DBG_VALUE_LIST``, appears thus:
.. code-block:: text
DBG_VALUE_LIST !123, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), %1, %2
And has the following operands:
* The first operand is the Variable field of the original debug intrinsic.
* The second operand is the Expression field of the original debug intrinsic.
* Any number of operands, from the 3rd onwards, record a sequence of variable
location operands, which may take any of the same values as the first
operand of the ``DBG_VALUE`` instruction above. These variable location
operands are inserted into the final DWARF Expression in positions indicated
by the DW_OP_LLVM_arg operator in the `DIExpression
<LangRef.html#diexpression>`.
The position at which the DBG_VALUEs are inserted should correspond to the
positions of their matching ``llvm.dbg.value`` intrinsics in the IR block. As
with optimization, LLVM aims to preserve the order in which variable

View File

@ -144,6 +144,7 @@ enum LocationAtom {
DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata.
DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata.
DW_OP_LLVM_implicit_pointer = 0x1004, ///< Only used in LLVM metadata.
DW_OP_LLVM_arg = 0x1005, ///< Only used in LLVM metadata.
};
enum TypeKind : uint8_t {

View File

@ -420,11 +420,11 @@ public:
/// instruction is indirect; will be an invalid register if this value is
/// not indirect, and an immediate with value 0 otherwise.
const MachineOperand &getDebugOffset() const {
assert(isDebugValue() && "not a DBG_VALUE");
assert(isNonListDebugValue() && "not a DBG_VALUE");
return getOperand(1);
}
MachineOperand &getDebugOffset() {
assert(isDebugValue() && "not a DBG_VALUE");
assert(isNonListDebugValue() && "not a DBG_VALUE");
return getOperand(1);
}
@ -439,6 +439,7 @@ public:
/// Return the operand for the complex address expression referenced by
/// this DBG_VALUE instruction.
const MachineOperand &getDebugExpressionOp() const;
MachineOperand &getDebugExpressionOp();
/// Return the complex address expression referenced by
@ -501,26 +502,43 @@ public:
return *(debug_operands().begin() + Index);
}
/// Returns a pointer to the operand corresponding to a debug use of Reg, or
/// nullptr if Reg is not used in any debug operand.
const MachineOperand *getDebugOperandForReg(Register Reg) const {
const MachineOperand *RegOp =
find_if(debug_operands(), [Reg](const MachineOperand &Op) {
return Op.isReg() && Op.getReg() == Reg;
});
return RegOp == adl_end(debug_operands()) ? nullptr : RegOp;
/// Returns whether this debug value has at least one debug operand with the
/// register \p Reg.
bool hasDebugOperandForReg(Register Reg) const {
return any_of(debug_operands(), [Reg](const MachineOperand &Op) {
return Op.isReg() && Op.getReg() == Reg;
});
}
MachineOperand *getDebugOperandForReg(Register Reg) {
MachineOperand *RegOp =
find_if(debug_operands(), [Reg](const MachineOperand &Op) {
return Op.isReg() && Op.getReg() == Reg;
});
return RegOp == adl_end(debug_operands()) ? nullptr : RegOp;
/// Returns a range of all of the operands that correspond to a debug use of
/// \p Reg.
template <typename Operand, typename Instruction>
static iterator_range<
filter_iterator<Operand *, std::function<bool(Operand &Op)>>>
getDebugOperandsForReg(Instruction *MI, Register Reg) {
std::function<bool(Operand & Op)> OpUsesReg(
[Reg](Operand &Op) { return Op.isReg() && Op.getReg() == Reg; });
return make_filter_range(MI->debug_operands(), OpUsesReg);
}
iterator_range<filter_iterator<const MachineOperand *,
std::function<bool(const MachineOperand &Op)>>>
getDebugOperandsForReg(Register Reg) const {
return MachineInstr::getDebugOperandsForReg<const MachineOperand,
const MachineInstr>(this, Reg);
}
iterator_range<filter_iterator<MachineOperand *,
std::function<bool(MachineOperand &Op)>>>
getDebugOperandsForReg(Register Reg) {
return MachineInstr::getDebugOperandsForReg<MachineOperand, MachineInstr>(
this, Reg);
}
bool isDebugOperand(const MachineOperand *Op) const {
return Op >= adl_begin(debug_operands()) && Op <= adl_end(debug_operands());
}
unsigned getDebugOperandIndex(const MachineOperand *Op) const {
assert(Op >= adl_begin(debug_operands()) &&
Op <= adl_end(debug_operands()) && "Expected a debug operand.");
assert(isDebugOperand(Op) && "Expected a debug operand.");
return std::distance(adl_begin(debug_operands()), Op);
}
@ -600,12 +618,16 @@ public:
/// location for this DBG_VALUE instruction.
iterator_range<mop_iterator> debug_operands() {
assert(isDebugValue() && "Must be a debug value instruction.");
return make_range(operands_begin(), operands_begin() + 1);
return isDebugValueList()
? make_range(operands_begin() + 2, operands_end())
: make_range(operands_begin(), operands_begin() + 1);
}
/// \copydoc debug_operands()
iterator_range<const_mop_iterator> debug_operands() const {
assert(isDebugValue() && "Must be a debug value instruction.");
return make_range(operands_begin(), operands_begin() + 1);
return isDebugValueList()
? make_range(operands_begin() + 2, operands_end())
: make_range(operands_begin(), operands_begin() + 1);
}
/// Returns a range over all explicit operands that are register definitions.
/// Implicit definition are not included!
@ -1164,7 +1186,15 @@ public:
// True if the instruction represents a position in the function.
bool isPosition() const { return isLabel() || isCFIInstruction(); }
bool isDebugValue() const { return getOpcode() == TargetOpcode::DBG_VALUE; }
bool isNonListDebugValue() const {
return getOpcode() == TargetOpcode::DBG_VALUE;
}
bool isDebugValueList() const {
return getOpcode() == TargetOpcode::DBG_VALUE_LIST;
}
bool isDebugValue() const {
return isNonListDebugValue() || isDebugValueList();
}
bool isDebugLabel() const { return getOpcode() == TargetOpcode::DBG_LABEL; }
bool isDebugRef() const { return getOpcode() == TargetOpcode::DBG_INSTR_REF; }
bool isDebugInstr() const {
@ -1174,12 +1204,14 @@ public:
return isDebugInstr() || isPseudoProbe();
}
bool isDebugOffsetImm() const { return getDebugOffset().isImm(); }
bool isDebugOffsetImm() const {
return isNonListDebugValue() && getDebugOffset().isImm();
}
/// A DBG_VALUE is indirect iff the location operand is a register and
/// the offset operand is an immediate.
bool isIndirectDebugValue() const {
return isDebugValue() && getDebugOperand(0).isReg() && isDebugOffsetImm();
return isDebugOffsetImm() && getDebugOperand(0).isReg();
}
/// A DBG_VALUE is an entry value iff its debug expression contains the
@ -1189,8 +1221,13 @@ public:
/// Return true if the instruction is a debug value which describes a part of
/// a variable as unavailable.
bool isUndefDebugValue() const {
return isDebugValue() && getDebugOperand(0).isReg() &&
!getDebugOperand(0).getReg().isValid();
if (!isDebugValue())
return false;
// If any $noreg locations are given, this DV is undef.
for (const MachineOperand &Op : debug_operands())
if (Op.isReg() && !Op.getReg().isValid())
return true;
return false;
}
bool isPHI() const {
@ -1265,6 +1302,7 @@ public:
case TargetOpcode::EH_LABEL:
case TargetOpcode::GC_LABEL:
case TargetOpcode::DBG_VALUE:
case TargetOpcode::DBG_VALUE_LIST:
case TargetOpcode::DBG_INSTR_REF:
case TargetOpcode::DBG_LABEL:
case TargetOpcode::LIFETIME_START:

View File

@ -451,9 +451,16 @@ MachineInstrBuilder BuildMI(MachineFunction &MF, const DebugLoc &DL,
/// for a MachineOperand.
MachineInstrBuilder BuildMI(MachineFunction &MF, const DebugLoc &DL,
const MCInstrDesc &MCID, bool IsIndirect,
MachineOperand &MO, const MDNode *Variable,
const MachineOperand &MO, const MDNode *Variable,
const MDNode *Expr);
/// This version of the builder builds a DBG_VALUE or DBG_VALUE_LIST intrinsic
/// for a MachineOperand.
MachineInstrBuilder BuildMI(MachineFunction &MF, const DebugLoc &DL,
const MCInstrDesc &MCID, bool IsIndirect,
ArrayRef<MachineOperand> MOs,
const MDNode *Variable, const MDNode *Expr);
/// This version of the builder builds a DBG_VALUE intrinsic
/// for either a value in a register or a register-indirect
/// address and inserts it at position I.
@ -471,6 +478,14 @@ MachineInstrBuilder BuildMI(MachineBasicBlock &BB,
MachineOperand &MO, const MDNode *Variable,
const MDNode *Expr);
/// This version of the builder builds a DBG_VALUE or DBG_VALUE_LIST intrinsic
/// for a machine operand and inserts it at position I.
MachineInstrBuilder BuildMI(MachineBasicBlock &BB,
MachineBasicBlock::iterator I, const DebugLoc &DL,
const MCInstrDesc &MCID, bool IsIndirect,
ArrayRef<MachineOperand> MOs,
const MDNode *Variable, const MDNode *Expr);
/// Clone a DBG_VALUE whose value has been spilled to FrameIndex.
MachineInstr *buildDbgValueForSpill(MachineBasicBlock &BB,
MachineBasicBlock::iterator I,

View File

@ -2771,6 +2771,14 @@ public:
static DIExpression *appendToStack(const DIExpression *Expr,
ArrayRef<uint64_t> Ops);
/// Create a copy of \p Expr by appending the given list of \p Ops to each
/// instance of the operand `DW_OP_LLVM_arg, \p ArgNo`. This is used to
/// modify a specific location used by \p Expr, such as when salvaging that
/// location.
static DIExpression *appendOpsToArg(const DIExpression *Expr,
ArrayRef<uint64_t> Ops, unsigned ArgNo,
bool StackValue = false);
/// Create a DIExpression to describe one part of an aggregate variable that
/// is fragmented across multiple Values. The DW_OP_LLVM_fragment operation
/// will be appended to the elements of \c Expr. If \c Expr already contains

View File

@ -77,6 +77,10 @@ HANDLE_TARGET_OPCODE(SUBREG_TO_REG)
/// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic
HANDLE_TARGET_OPCODE(DBG_VALUE)
/// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic with a variadic
/// list of locations
HANDLE_TARGET_OPCODE(DBG_VALUE_LIST)
/// DBG_INSTR_REF - A mapping of llvm.dbg.value referring to the instruction
/// that defines the value, rather than a virtual register.
HANDLE_TARGET_OPCODE(DBG_INSTR_REF)

View File

@ -1107,6 +1107,12 @@ def DBG_VALUE : StandardPseudoInstruction {
let AsmString = "DBG_VALUE";
let hasSideEffects = false;
}
def DBG_VALUE_LIST : StandardPseudoInstruction {
let OutOperandList = (outs);
let InOperandList = (ins variable_ops);
let AsmString = "DBG_VALUE_LIST";
let hasSideEffects = 0;
}
def DBG_INSTR_REF : StandardPseudoInstruction {
let OutOperandList = (outs);
let InOperandList = (ins variable_ops);

View File

@ -153,6 +153,8 @@ StringRef llvm::dwarf::OperationEncodingString(unsigned Encoding) {
return "DW_OP_LLVM_entry_value";
case DW_OP_LLVM_implicit_pointer:
return "DW_OP_LLVM_implicit_pointer";
case DW_OP_LLVM_arg:
return "DW_OP_LLVM_arg";
}
}
@ -166,6 +168,7 @@ unsigned llvm::dwarf::getOperationEncoding(StringRef OperationEncodingString) {
.Case("DW_OP_LLVM_tag_offset", DW_OP_LLVM_tag_offset)
.Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value)
.Case("DW_OP_LLVM_implicit_pointer", DW_OP_LLVM_implicit_pointer)
.Case("DW_OP_LLVM_arg", DW_OP_LLVM_arg)
.Default(0);
}

View File

@ -905,7 +905,7 @@ static void emitKill(const MachineInstr *MI, AsmPrinter &AP) {
/// means the target will need to handle MI in EmitInstruction.
static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) {
// This code handles only the 4-operand target-independent form.
if (MI->getNumOperands() != 4)
if (MI->isNonListDebugValue() && MI->getNumOperands() != 4)
return false;
SmallString<128> Str;
@ -1228,6 +1228,7 @@ void AsmPrinter::emitFunctionBody() {
emitInlineAsm(&MI);
break;
case TargetOpcode::DBG_VALUE:
case TargetOpcode::DBG_VALUE_LIST:
if (isVerbose()) {
if (!emitDebugValueComment(&MI, *this))
emitInstruction(&MI);

View File

@ -235,7 +235,7 @@ bool LiveRangeShrink::runOnMachineFunction(MachineFunction &MF) {
MachineBasicBlock::iterator EndIter = std::next(MI.getIterator());
if (MI.getOperand(0).isReg())
for (; EndIter != MBB.end() && EndIter->isDebugValue() &&
EndIter->getDebugOperandForReg(MI.getOperand(0).getReg());
EndIter->hasDebugOperandForReg(MI.getOperand(0).getReg());
++EndIter, ++Next)
IOM[&*EndIter] = NewOrder;
MBB.splice(I, &MBB, MI.getIterator(), EndIter);

View File

@ -987,7 +987,9 @@ bool MIParser::parse(MachineInstr *&MI) {
Optional<unsigned> TiedDefIdx;
if (parseMachineOperandAndTargetFlags(OpCode, Operands.size(), MO, TiedDefIdx))
return true;
if (OpCode == TargetOpcode::DBG_VALUE && MO.isReg())
if ((OpCode == TargetOpcode::DBG_VALUE ||
OpCode == TargetOpcode::DBG_VALUE_LIST) &&
MO.isReg())
MO.setIsDebug();
Operands.push_back(
ParsedMachineOperand(MO, Loc, Token.location(), TiedDefIdx));

View File

@ -841,28 +841,35 @@ const DILabel *MachineInstr::getDebugLabel() const {
}
const MachineOperand &MachineInstr::getDebugVariableOp() const {
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE");
return getOperand(2);
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE*");
unsigned VariableOp = isDebugValueList() ? 0 : 2;
return getOperand(VariableOp);
}
MachineOperand &MachineInstr::getDebugVariableOp() {
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE");
return getOperand(2);
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE*");
unsigned VariableOp = isDebugValueList() ? 0 : 2;
return getOperand(VariableOp);
}
const DILocalVariable *MachineInstr::getDebugVariable() const {
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE");
return cast<DILocalVariable>(getOperand(2).getMetadata());
return cast<DILocalVariable>(getDebugVariableOp().getMetadata());
}
const MachineOperand &MachineInstr::getDebugExpressionOp() const {
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE*");
unsigned ExpressionOp = isDebugValueList() ? 1 : 3;
return getOperand(ExpressionOp);
}
MachineOperand &MachineInstr::getDebugExpressionOp() {
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE");
return getOperand(3);
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE*");
unsigned ExpressionOp = isDebugValueList() ? 1 : 3;
return getOperand(ExpressionOp);
}
const DIExpression *MachineInstr::getDebugExpression() const {
assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE");
return cast<DIExpression>(getOperand(3).getMetadata());
return cast<DIExpression>(getDebugExpressionOp().getMetadata());
}
bool MachineInstr::isDebugEntryValue() const {
@ -1712,7 +1719,7 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST,
OS << " ";
if (isDebugValue() && MO.isMetadata()) {
// Pretty print DBG_VALUE instructions.
// Pretty print DBG_VALUE* instructions.
auto *DIV = dyn_cast<DILocalVariable>(MO.getMetadata());
if (DIV && !DIV->getName().empty())
OS << "!\"" << DIV->getName() << '\"';
@ -2116,8 +2123,8 @@ MachineInstrBuilder llvm::BuildMI(MachineFunction &MF, const DebugLoc &DL,
MachineInstrBuilder llvm::BuildMI(MachineFunction &MF, const DebugLoc &DL,
const MCInstrDesc &MCID, bool IsIndirect,
MachineOperand &MO, const MDNode *Variable,
const MDNode *Expr) {
const MachineOperand &MO,
const MDNode *Variable, const MDNode *Expr) {
assert(isa<DILocalVariable>(Variable) && "not a variable");
assert(cast<DIExpression>(Expr)->isValid() && "not an expression");
assert(cast<DILocalVariable>(Variable)->isValidLocationForIntrinsic(DL) &&
@ -2131,7 +2138,28 @@ MachineInstrBuilder llvm::BuildMI(MachineFunction &MF, const DebugLoc &DL,
else
MIB.addReg(0U, RegState::Debug);
return MIB.addMetadata(Variable).addMetadata(Expr);
}
}
MachineInstrBuilder llvm::BuildMI(MachineFunction &MF, const DebugLoc &DL,
const MCInstrDesc &MCID, bool IsIndirect,
ArrayRef<MachineOperand> MOs,
const MDNode *Variable, const MDNode *Expr) {
assert(isa<DILocalVariable>(Variable) && "not a variable");
assert(cast<DIExpression>(Expr)->isValid() && "not an expression");
assert(cast<DILocalVariable>(Variable)->isValidLocationForIntrinsic(DL) &&
"Expected inlined-at fields to agree");
if (MCID.Opcode == TargetOpcode::DBG_VALUE)
return BuildMI(MF, DL, MCID, IsIndirect, MOs[0], Variable, Expr);
auto MIB = BuildMI(MF, DL, MCID);
MIB.addMetadata(Variable).addMetadata(Expr);
for (const MachineOperand &MO : MOs)
if (MO.isReg())
MIB.addReg(MO.getReg(), RegState::Debug);
else
MIB.add(MO);
return MIB;
}
MachineInstrBuilder llvm::BuildMI(MachineBasicBlock &BB,
MachineBasicBlock::iterator I,
@ -2155,10 +2183,22 @@ MachineInstrBuilder llvm::BuildMI(MachineBasicBlock &BB,
return MachineInstrBuilder(MF, *MI);
}
MachineInstrBuilder llvm::BuildMI(MachineBasicBlock &BB,
MachineBasicBlock::iterator I,
const DebugLoc &DL, const MCInstrDesc &MCID,
bool IsIndirect, ArrayRef<MachineOperand> MOs,
const MDNode *Variable, const MDNode *Expr) {
MachineFunction &MF = *BB.getParent();
MachineInstr *MI = BuildMI(MF, DL, MCID, IsIndirect, MOs, Variable, Expr);
BB.insert(I, MI);
return MachineInstrBuilder(MF, *MI);
}
/// Compute the new DIExpression to use with a DBG_VALUE for a spill slot.
/// This prepends DW_OP_deref when spilling an indirect DBG_VALUE.
static const DIExpression *computeExprForSpill(const MachineInstr &MI) {
assert(MI.getOperand(0).isReg() && "can't spill non-register");
static const DIExpression *computeExprForSpill(const MachineInstr &MI,
Register SpillReg) {
assert(MI.hasDebugOperandForReg(SpillReg) && "Spill Reg is not used in MI.");
assert(MI.getDebugVariable()->isValidLocationForIntrinsic(MI.getDebugLoc()) &&
"Expected inlined-at fields to agree");
@ -2167,6 +2207,14 @@ static const DIExpression *computeExprForSpill(const MachineInstr &MI) {
assert(MI.getDebugOffset().getImm() == 0 &&
"DBG_VALUE with nonzero offset");
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
} else if (MI.isDebugValueList()) {
// We will replace the spilled register with a frame index, so
// immediately deref all references to the spilled register.
std::array<uint64_t, 1> Ops{{dwarf::DW_OP_deref}};
for (const MachineOperand &Op : MI.getDebugOperandsForReg(SpillReg)) {
unsigned OpIdx = MI.getDebugOperandIndex(&Op);
Expr = DIExpression::appendOpsToArg(Expr, Ops, OpIdx);
}
}
return Expr;
}
@ -2175,18 +2223,32 @@ MachineInstr *llvm::buildDbgValueForSpill(MachineBasicBlock &BB,
MachineBasicBlock::iterator I,
const MachineInstr &Orig,
int FrameIndex) {
const DIExpression *Expr = computeExprForSpill(Orig);
return BuildMI(BB, I, Orig.getDebugLoc(), Orig.getDesc())
.addFrameIndex(FrameIndex)
.addImm(0U)
.addMetadata(Orig.getDebugVariable())
.addMetadata(Expr);
Register SpillReg = Orig.getDebugOperand(0).getReg();
const DIExpression *Expr = computeExprForSpill(Orig, SpillReg);
MachineInstrBuilder NewMI =
BuildMI(BB, I, Orig.getDebugLoc(), Orig.getDesc());
// Non-Variadic Operands: Location, Offset, Variable, Expression
// Variadic Operands: Variable, Expression, Locations...
if (Orig.isNonListDebugValue())
NewMI.addFrameIndex(FrameIndex).addImm(0U);
NewMI.addMetadata(Orig.getDebugVariable()).addMetadata(Expr);
if (Orig.isDebugValueList()) {
for (const MachineOperand &Op : Orig.debug_operands())
if (Op.isReg() && Op.getReg() == SpillReg)
NewMI.addFrameIndex(FrameIndex);
else
NewMI.add(MachineOperand(Op));
}
return NewMI;
}
void llvm::updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex) {
const DIExpression *Expr = computeExprForSpill(Orig);
Orig.getDebugOperand(0).ChangeToFrameIndex(FrameIndex);
Orig.getDebugOffset().ChangeToImmediate(0U);
Register SpillReg = Orig.getDebugOperand(0).getReg();
const DIExpression *Expr = computeExprForSpill(Orig, SpillReg);
if (Orig.isNonListDebugValue())
Orig.getDebugOffset().ChangeToImmediate(0U);
for (MachineOperand &Op : Orig.getDebugOperandsForReg(SpillReg))
Op.ChangeToFrameIndex(FrameIndex);
Orig.getDebugExpressionOp().setMetadata(Expr);
}
@ -2201,7 +2263,7 @@ void MachineInstr::collectDebugValues(
DI != DE; ++DI) {
if (!DI->isDebugValue())
return;
if (DI->getDebugOperandForReg(MI.getOperand(0).getReg()))
if (DI->hasDebugOperandForReg(MI.getOperand(0).getReg()))
DbgValues.push_back(&*DI);
}
}
@ -2219,14 +2281,15 @@ void MachineInstr::changeDebugValuesDefReg(Register Reg) {
auto *DI = MO.getParent();
if (!DI->isDebugValue())
continue;
if (DI->getDebugOperandForReg(DefReg)) {
if (DI->hasDebugOperandForReg(DefReg)) {
DbgValues.push_back(DI);
}
}
// Propagate Reg to debug value instructions.
for (auto *DBI : DbgValues)
DBI->getDebugOperandForReg(DefReg)->setReg(Reg);
for (MachineOperand &Op : DBI->getDebugOperandsForReg(DefReg))
Op.setReg(Reg);
}
using MMOList = SmallVector<const MachineMemOperand *, 2>;

View File

@ -530,11 +530,11 @@ bool MachineRegisterInfo::isConstantPhysReg(MCRegister PhysReg) const {
/// specified register as undefined which causes the DBG_VALUE to be
/// deleted during LiveDebugVariables analysis.
void MachineRegisterInfo::markUsesInDebugValueAsUndef(Register Reg) const {
// Mark any DBG_VALUE that uses Reg as undef (but don't delete it.)
// Mark any DBG_VALUE* that uses Reg as undef (but don't delete it.)
// We use make_early_inc_range because setReg invalidates the iterator.
for (MachineInstr &UseMI : llvm::make_early_inc_range(use_instructions(Reg))) {
if (UseMI.isDebugValue())
UseMI.getDebugOperandForReg(Reg)->setReg(0U);
if (UseMI.isDebugValue() && UseMI.hasDebugOperandForReg(Reg))
UseMI.setDebugValueUndef();
}
}

View File

@ -1233,23 +1233,33 @@ void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF,
// complex location that is interpreted as being a memory address.
// This changes a pointer-valued variable to dereference that pointer,
// which is incorrect. Fix by adding DW_OP_stack_value.
unsigned PrependFlags = DIExpression::ApplyOffset;
if (!MI.isIndirectDebugValue() && !DIExpr->isComplex())
PrependFlags |= DIExpression::StackValue;
// If we have DBG_VALUE that is indirect and has a Implicit location
// expression need to insert a deref before prepending a Memory
// location expression. Also after doing this we change the DBG_VALUE
// to be direct.
if (MI.isIndirectDebugValue() && DIExpr->isImplicit()) {
SmallVector<uint64_t, 2> Ops = {dwarf::DW_OP_deref_size, Size};
bool WithStackValue = true;
DIExpr = DIExpression::prependOpcodes(DIExpr, Ops, WithStackValue);
// Make the DBG_VALUE direct.
MI.getDebugOffset().ChangeToRegister(0, false);
if (MI.isNonListDebugValue()) {
unsigned PrependFlags = DIExpression::ApplyOffset;
if (!MI.isIndirectDebugValue() && !DIExpr->isComplex())
PrependFlags |= DIExpression::StackValue;
// If we have DBG_VALUE that is indirect and has a Implicit location
// expression need to insert a deref before prepending a Memory
// location expression. Also after doing this we change the DBG_VALUE
// to be direct.
if (MI.isIndirectDebugValue() && DIExpr->isImplicit()) {
SmallVector<uint64_t, 2> Ops = {dwarf::DW_OP_deref_size, Size};
bool WithStackValue = true;
DIExpr = DIExpression::prependOpcodes(DIExpr, Ops, WithStackValue);
// Make the DBG_VALUE direct.
MI.getDebugOffset().ChangeToRegister(0, false);
}
DIExpr = TRI.prependOffsetExpression(DIExpr, PrependFlags, Offset);
} else {
// The debug operand at DebugOpIndex was a frame index at offset
// `Offset`; now the operand has been replaced with the frame
// register, we must add Offset with `register x, plus Offset`.
unsigned DebugOpIndex = MI.getDebugOperandIndex(&MI.getOperand(i));
SmallVector<uint64_t, 3> Ops;
TRI.getOffsetOpcodes(Offset, Ops);
DIExpr = DIExpression::appendOpsToArg(DIExpr, Ops, DebugOpIndex);
}
DIExpr = TRI.prependOffsetExpression(DIExpr, PrependFlags, Offset);
MI.getDebugExpressionOp().setMetadata(DIExpr);
continue;
}

View File

@ -424,9 +424,14 @@ void RegAllocFast::spill(MachineBasicBlock::iterator Before, Register VirtReg,
}
// Rewrite unassigned dbg_values to use the stack slot.
MachineOperand &MO = DBG->getOperand(0);
if (MO.isReg() && MO.getReg() == 0)
updateDbgValueForSpill(*DBG, FI);
// TODO We can potentially do this for list debug values as well if we know
// how the dbg_values are getting unassigned.
if (DBG->isNonListDebugValue()) {
MachineOperand &MO = DBG->getDebugOperand(0);
if (MO.isReg() && MO.getReg() == 0) {
updateDbgValueForSpill(*DBG, FI);
}
}
}
// Now this register is spilled there is should not be any DBG_VALUE
// pointing to this register because they are all pointing to spilled value
@ -623,8 +628,7 @@ void RegAllocFast::assignDanglingDebugValues(MachineInstr &Definition,
SmallVectorImpl<MachineInstr*> &Dangling = UDBGValIter->second;
for (MachineInstr *DbgValue : Dangling) {
assert(DbgValue->isDebugValue());
MachineOperand &MO = DbgValue->getOperand(0);
if (!MO.isReg())
if (!DbgValue->hasDebugOperandForReg(VirtReg))
continue;
// Test whether the physreg survives from the definition to the DBG_VALUE.
@ -639,9 +643,11 @@ void RegAllocFast::assignDanglingDebugValues(MachineInstr &Definition,
break;
}
}
MO.setReg(SetToReg);
if (SetToReg != 0)
MO.setIsRenamable();
for (MachineOperand &MO : DbgValue->getDebugOperandsForReg(VirtReg)) {
MO.setReg(SetToReg);
if (SetToReg != 0)
MO.setIsRenamable();
}
}
Dangling.clear();
}
@ -1360,37 +1366,44 @@ void RegAllocFast::allocateInstruction(MachineInstr &MI) {
}
void RegAllocFast::handleDebugValue(MachineInstr &MI) {
MachineOperand &MO = MI.getDebugOperand(0);
SmallSet<Register, 4> SeenRegisters;
for (MachineOperand &MO : MI.debug_operands()) {
// Ignore DBG_VALUEs that aren't based on virtual registers. These are
// mostly constants and frame indices.
if (!MO.isReg())
continue;
Register Reg = MO.getReg();
if (!Register::isVirtualRegister(Reg))
continue;
// Only process each register once per MI, each use of that register will
// be updated if necessary.
if (!SeenRegisters.insert(Reg).second)
continue;
// Ignore DBG_VALUEs that aren't based on virtual registers. These are
// mostly constants and frame indices.
if (!MO.isReg())
return;
Register Reg = MO.getReg();
if (!Register::isVirtualRegister(Reg))
return;
// Already spilled to a stackslot?
int SS = StackSlotForVirtReg[Reg];
if (SS != -1) {
// Modify DBG_VALUE now that the value is in a spill slot.
updateDbgValueForSpill(MI, SS);
LLVM_DEBUG(dbgs() << "Rewrite DBG_VALUE for spilled memory: " << MI);
continue;
}
// Already spilled to a stackslot?
int SS = StackSlotForVirtReg[Reg];
if (SS != -1) {
// Modify DBG_VALUE now that the value is in a spill slot.
updateDbgValueForSpill(MI, SS);
LLVM_DEBUG(dbgs() << "Rewrite DBG_VALUE for spilled memory: " << MI);
return;
// See if this virtual register has already been allocated to a physical
// register or spilled to a stack slot.
LiveRegMap::iterator LRI = findLiveVirtReg(Reg);
if (LRI != LiveVirtRegs.end() && LRI->PhysReg) {
// Update every use of Reg within MI.
for (auto &RegMO : MI.getDebugOperandsForReg(Reg))
setPhysReg(MI, RegMO, LRI->PhysReg);
} else {
DanglingDbgValues[Reg].push_back(&MI);
}
// If Reg hasn't been spilled, put this DBG_VALUE in LiveDbgValueMap so
// that future spills of Reg will have DBG_VALUEs.
LiveDbgValueMap[Reg].push_back(&MI);
}
// See if this virtual register has already been allocated to a physical
// register or spilled to a stack slot.
LiveRegMap::iterator LRI = findLiveVirtReg(Reg);
if (LRI != LiveVirtRegs.end() && LRI->PhysReg) {
setPhysReg(MI, MO, LRI->PhysReg);
} else {
DanglingDbgValues[Reg].push_back(&MI);
}
// If Reg hasn't been spilled, put this DBG_VALUE in LiveDbgValueMap so
// that future spills of Reg will have DBG_VALUEs.
LiveDbgValueMap[Reg].push_back(&MI);
}
void RegAllocFast::handleBundle(MachineInstr &MI) {
@ -1472,13 +1485,12 @@ void RegAllocFast::allocateBasicBlock(MachineBasicBlock &MBB) {
for (auto &UDBGPair : DanglingDbgValues) {
for (MachineInstr *DbgValue : UDBGPair.second) {
assert(DbgValue->isDebugValue() && "expected DBG_VALUE");
MachineOperand &MO = DbgValue->getOperand(0);
// Nothing to do if the vreg was spilled in the meantime.
if (!MO.isReg())
if (!DbgValue->hasDebugOperandForReg(UDBGPair.first))
continue;
LLVM_DEBUG(dbgs() << "Register did not survive for " << *DbgValue
<< '\n');
MO.setReg(0);
DbgValue->setDebugValueUndef();
}
}
DanglingDbgValues.clear();

View File

@ -1059,6 +1059,7 @@ unsigned DIExpression::ExprOperand::getSize() const {
case dwarf::DW_OP_plus_uconst:
case dwarf::DW_OP_LLVM_tag_offset:
case dwarf::DW_OP_LLVM_entry_value:
case dwarf::DW_OP_LLVM_arg:
case dwarf::DW_OP_regx:
return 2;
default:
@ -1115,6 +1116,7 @@ bool DIExpression::isValid() const {
}
case dwarf::DW_OP_LLVM_implicit_pointer:
case dwarf::DW_OP_LLVM_convert:
case dwarf::DW_OP_LLVM_arg:
case dwarf::DW_OP_LLVM_tag_offset:
case dwarf::DW_OP_constu:
case dwarf::DW_OP_plus_uconst:
@ -1270,6 +1272,30 @@ DIExpression *DIExpression::prepend(const DIExpression *Expr, uint8_t Flags,
return prependOpcodes(Expr, Ops, StackValue, EntryValue);
}
DIExpression *DIExpression::appendOpsToArg(const DIExpression *Expr,
ArrayRef<uint64_t> Ops,
unsigned ArgNo, bool StackValue) {
assert(Expr && "Can't add ops to this expression");
// Handle non-variadic intrinsics by prepending the opcodes.
if (!any_of(Expr->expr_ops(),
[](auto Op) { return Op.getOp() == dwarf::DW_OP_LLVM_arg; })) {
assert(ArgNo == 0 &&
"Location Index must be 0 for a non-variadic expression.");
SmallVector<uint64_t, 8> NewOps(Ops.begin(), Ops.end());
return DIExpression::prependOpcodes(Expr, NewOps, StackValue);
}
SmallVector<uint64_t, 8> NewOps;
for (auto Op : Expr->expr_ops()) {
Op.appendToVector(NewOps);
if (Op.getOp() == dwarf::DW_OP_LLVM_arg && Op.getArg(0) == ArgNo)
NewOps.insert(NewOps.end(), Ops.begin(), Ops.end());
}
return DIExpression::get(Expr->getContext(), NewOps);
}
DIExpression *DIExpression::prependOpcodes(const DIExpression *Expr,
SmallVectorImpl<uint64_t> &Ops,
bool StackValue,

View File

@ -1210,7 +1210,8 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
}
break;
case AArch64::DBG_VALUE: {
case AArch64::DBG_VALUE:
case AArch64::DBG_VALUE_LIST: {
if (isVerbose() && OutStreamer->hasRawTextSupport()) {
SmallString<128> TmpStr;
raw_svector_ostream OS(TmpStr);
@ -1231,7 +1232,7 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
OutStreamer->emitCFIBKeyFrame();
return;
}
}
}
// Tail calls use pseudo instructions so they have the proper code-gen
// attributes (isCall, isReturn, etc.). We lower them to the real

View File

@ -65,16 +65,25 @@ bool NVPTXPrologEpilogPass::runOnMachineFunction(MachineFunction &MF) {
// way with simply the frame index and offset rather than any
// target-specific addressing mode.
if (MI.isDebugValue()) {
assert(i == 0 && "Frame indices can only appear as the first "
"operand of a DBG_VALUE machine instruction");
MachineOperand &Op = MI.getOperand(i);
assert(
MI.isDebugOperand(&Op) &&
"Frame indices can only appear as a debug operand in a DBG_VALUE*"
" machine instruction");
Register Reg;
int64_t Offset =
TFI.getFrameIndexReference(MF, MI.getOperand(0).getIndex(), Reg)
.getFixed();
MI.getOperand(0).ChangeToRegister(Reg, /*isDef=*/false);
MI.getOperand(0).setIsDebug();
auto *DIExpr = DIExpression::prepend(
MI.getDebugExpression(), DIExpression::ApplyOffset, Offset);
auto Offset =
TFI.getFrameIndexReference(MF, Op.getIndex(), Reg);
Op.ChangeToRegister(Reg, /*isDef=*/false);
Op.setIsDebug();
const DIExpression *DIExpr = MI.getDebugExpression();
if (MI.isNonListDebugValue()) {
DIExpr = TRI.prependOffsetExpression(MI.getDebugExpression(), DIExpression::ApplyOffset, Offset);
} else {
SmallVector<uint64_t, 3> Ops;
TRI.getOffsetOpcodes(Offset, Ops);
unsigned OpIdx = MI.getDebugOperandIndex(&Op);
DIExpr = DIExpression::appendOpsToArg(DIExpr, Ops, OpIdx);
}
MI.getDebugExpressionOp().setMetadata(DIExpr);
continue;
}

View File

@ -9,12 +9,13 @@
#include "SystemZRegisterInfo.h"
#include "SystemZInstrInfo.h"
#include "SystemZSubtarget.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/CodeGen/VirtRegMap.h"
#include "llvm/IR/DebugInfoMetadata.h"
using namespace llvm;
@ -273,7 +274,16 @@ SystemZRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator MI,
// Special handling of dbg_value instructions.
if (MI->isDebugValue()) {
MI->getOperand(FIOperandNum).ChangeToRegister(BasePtr, /*isDef*/ false);
MI->getDebugOffset().ChangeToImmediate(Offset);
if (MI->isNonListDebugValue()) {
MI->getDebugOffset().ChangeToImmediate(Offset);
} else {
unsigned OpIdx = MI->getDebugOperandIndex(&MI->getOperand(FIOperandNum));
SmallVector<uint64_t, 3> Ops;
DIExpression::appendOffset(
Ops, TFI->getFrameIndexReference(MF, FrameIndex, BasePtr).getFixed());
MI->getDebugExpressionOp().setMetadata(
DIExpression::appendOpsToArg(MI->getDebugExpression(), Ops, OpIdx));
}
return;
}

View File

@ -30,7 +30,7 @@ WebAssemblyDebugValueManager::WebAssemblyDebugValueManager(
for (MachineBasicBlock::iterator DE = Instr->getParent()->end(); DI != DE;
++DI) {
if (DI->isDebugValue() &&
DI->getDebugOperandForReg(Instr->getOperand(0).getReg()))
DI->hasDebugOperandForReg(Instr->getOperand(0).getReg()))
DbgValues.push_back(&*DI);
}
}

View File

@ -295,8 +295,8 @@ private:
/// Replace debug value MI with a new debug value instruction using register
/// VReg with an appropriate offset and DIExpression to incorporate the
/// address displacement AddrDispShift. Return new debug value instruction.
MachineInstr *replaceDebugValue(MachineInstr &MI, unsigned VReg,
int64_t AddrDispShift);
MachineInstr *replaceDebugValue(MachineInstr &MI, unsigned OldReg,
unsigned NewReg, int64_t AddrDispShift);
/// Removes LEAs which calculate similar addresses.
bool removeRedundantLEAs(MemOpMap &LEAs);
@ -576,21 +576,50 @@ bool X86OptimizeLEAPass::removeRedundantAddrCalc(MemOpMap &LEAs) {
}
MachineInstr *X86OptimizeLEAPass::replaceDebugValue(MachineInstr &MI,
unsigned VReg,
unsigned OldReg,
unsigned NewReg,
int64_t AddrDispShift) {
const DIExpression *Expr = MI.getDebugExpression();
if (AddrDispShift != 0)
Expr = DIExpression::prepend(Expr, DIExpression::StackValue, AddrDispShift);
if (AddrDispShift != 0) {
if (MI.isNonListDebugValue()) {
Expr =
DIExpression::prepend(Expr, DIExpression::StackValue, AddrDispShift);
} else {
// Update the Expression, appending an offset of `AddrDispShift` to the
// Op corresponding to `OldReg`.
SmallVector<uint64_t, 3> Ops;
DIExpression::appendOffset(Ops, AddrDispShift);
for (MachineOperand &Op : MI.getDebugOperandsForReg(OldReg)) {
unsigned OpIdx = MI.getDebugOperandIndex(&Op);
Expr = DIExpression::appendOpsToArg(Expr, Ops, OpIdx);
}
}
}
// Replace DBG_VALUE instruction with modified version.
MachineBasicBlock *MBB = MI.getParent();
DebugLoc DL = MI.getDebugLoc();
bool IsIndirect = MI.isIndirectDebugValue();
const MDNode *Var = MI.getDebugVariable();
unsigned Opcode = MI.isNonListDebugValue() ? TargetOpcode::DBG_VALUE
: TargetOpcode::DBG_VALUE_LIST;
if (IsIndirect)
assert(MI.getOperand(1).getImm() == 0 && "DBG_VALUE with nonzero offset");
return BuildMI(*MBB, MBB->erase(&MI), DL, TII->get(TargetOpcode::DBG_VALUE),
IsIndirect, VReg, Var, Expr);
assert(MI.getDebugOffset().getImm() == 0 &&
"DBG_VALUE with nonzero offset");
SmallVector<MachineOperand, 4> NewOps;
// If we encounter an operand using the old register, replace it with an
// operand that uses the new register; otherwise keep the old operand.
auto replaceOldReg = [OldReg, NewReg](const MachineOperand &Op) {
if (Op.isReg() && Op.getReg() == OldReg)
return MachineOperand::CreateReg(NewReg, false, false, false, false,
false, false, false, false, 0,
/*IsRenamable*/ true);
return Op;
};
for (const MachineOperand &Op : MI.debug_operands())
NewOps.push_back(replaceOldReg(Op));
return BuildMI(*MBB, MBB->erase(&MI), DL, TII->get(Opcode), IsIndirect,
NewOps, Var, Expr);
}
// Try to find similar LEAs in the list and replace one with another.
@ -635,7 +664,7 @@ bool X86OptimizeLEAPass::removeRedundantLEAs(MemOpMap &LEAs) {
// Replace DBG_VALUE instruction with modified version using the
// register from the replacing LEA and the address displacement
// between the LEA instructions.
replaceDebugValue(MI, FirstVReg, AddrDispShift);
replaceDebugValue(MI, LastVReg, FirstVReg, AddrDispShift);
continue;
}

View File

@ -0,0 +1,64 @@
# RUN: llc -march=x86-64 -run-pass machineverifier -o - %s | FileCheck %s
# Simple round-trip test for DBG_VALUE_LIST.
# CHECK: [[VAR_C:![0-9]+]] = !DILocalVariable(name: "c"
# CHECK: DBG_VALUE_LIST [[VAR_C]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $edi, $esi, debug-location
--- |
; ModuleID = 'test.cpp'
source_filename = "test.cpp"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: norecurse nounwind readnone uwtable
define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 {
entry:
call void @llvm.dbg.value(metadata i32 %a, metadata !12, metadata !DIExpression()), !dbg !15
call void @llvm.dbg.value(metadata i32 %b, metadata !13, metadata !DIExpression()), !dbg !15
call void @llvm.dbg.value(metadata i32 undef, metadata !14, metadata !DIExpression()), !dbg !15
%mul = mul nsw i32 %b, %a, !dbg !16
ret i32 %mul, !dbg !17
}
; Function Attrs: nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!llvm.ident = !{!6}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "test.cpp", directory: "/")
!2 = !{}
!3 = !{i32 7, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{!"clang version 11.0.0"}
!7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10, !10}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !{!12, !13, !14}
!12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10)
!13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10)
!14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10)
!15 = !DILocation(line: 0, scope: !7)
!16 = !DILocation(line: 4, column: 12, scope: !7)
!17 = !DILocation(line: 4, column: 3, scope: !7)
...
---
name: _Z3fooii
body: |
bb.0.entry:
liveins: $edi, $esi
DBG_VALUE $edi, $noreg, !12, !DIExpression(), debug-location !15
DBG_VALUE $esi, $noreg, !13, !DIExpression(), debug-location !15
$eax = MOV32rr $edi
DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $edi, $esi, debug-location !15
DBG_VALUE $esi, $noreg, !13, !DIExpression(), debug-location !15
DBG_VALUE $eax, $noreg, !12, !DIExpression(), debug-location !15
renamable $eax = nsw IMUL32rr killed renamable $eax, killed renamable $esi, implicit-def dead $eflags, debug-location !16
RETQ $eax, debug-location !17
...