[DebugInstrRef][1/3] Track PHI values through register allocation

This patch introduces "DBG_PHI" instructions, a marker of where a PHI
instruction used to be, before PHI elimination. Under the instruction
referencing model, we want to know where every value in the function is
defined -- and a PHI, even if implicit, is such a place.

Just like instruction numbers, we can use this to identify a value to be
used as a variable value, but we don't need to know what instruction
defines that value, for example:

bb1:
   DBG_PHI $rax, 1
   [... more insts ... ]
bb2:
   DBG_INSTR_REF 1, 0, !1234, !DIExpression()

This specifies that on entry to bb1, whatever value is in $rax is known
as value number one -- and the later DBG_INSTR_REF marks the position
where variable !1234 should take on value number one.

PHI locations are stored in MachineFunction for the duration of the
regalloc phase in the DebugPHIPositions map. The map is populated by
PHIElimination, and then flushed back into the instruction stream by
virtregrewriter. A small amount of maintenence is needed in
LiveDebugVariables to account for registers being split, but only for
individual positions, not for entire ranges of blocks.

Differential Revision: https://reviews.llvm.org/D86812
This commit is contained in:
Jeremy Morse 2021-05-26 19:53:33 +01:00
parent 3b9a1bb1af
commit 8496fc2ec8
10 changed files with 457 additions and 2 deletions

View File

@ -457,6 +457,24 @@ public:
std::map<DebugInstrOperandPair, DebugInstrOperandPair>
DebugValueSubstitutions;
/// Location of a PHI instruction that is also a debug-info variable value,
/// for the duration of register allocation. Loaded by the PHI-elimination
/// pass, and emitted as DBG_PHI instructions during VirtRegRewriter, with
/// maintenance applied by intermediate passes that edit registers (such as
/// coalescing and the allocator passes).
class DebugPHIRegallocPos {
public:
MachineBasicBlock *MBB; ///< Block where this PHI was originally located.
Register Reg; ///< VReg where the control-flow-merge happens.
unsigned SubReg; ///< Optional subreg qualifier within Reg.
DebugPHIRegallocPos(MachineBasicBlock *MBB, Register Reg, unsigned SubReg)
: MBB(MBB), Reg(Reg), SubReg(SubReg) {}
};
/// Map of debug instruction numbers to the position of their PHI instructions
/// during register allocation. See DebugPHIRegallocPos.
DenseMap<unsigned, DebugPHIRegallocPos> DebugPHIPositions;
/// Create a substitution between one <instr,operand> value to a different,
/// new value.
void makeDebugValueSubstitution(DebugInstrOperandPair, DebugInstrOperandPair);

View File

@ -1207,8 +1207,9 @@ public:
}
bool isDebugLabel() const { return getOpcode() == TargetOpcode::DBG_LABEL; }
bool isDebugRef() const { return getOpcode() == TargetOpcode::DBG_INSTR_REF; }
bool isDebugPHI() const { return getOpcode() == TargetOpcode::DBG_PHI; }
bool isDebugInstr() const {
return isDebugValue() || isDebugLabel() || isDebugRef();
return isDebugValue() || isDebugLabel() || isDebugRef() || isDebugPHI();
}
bool isDebugOrPseudoInstr() const {
return isDebugInstr() || isPseudoProbe();
@ -1314,6 +1315,7 @@ public:
case TargetOpcode::DBG_VALUE:
case TargetOpcode::DBG_VALUE_LIST:
case TargetOpcode::DBG_INSTR_REF:
case TargetOpcode::DBG_PHI:
case TargetOpcode::DBG_LABEL:
case TargetOpcode::LIFETIME_START:
case TargetOpcode::LIFETIME_END:

View File

@ -85,6 +85,10 @@ HANDLE_TARGET_OPCODE(DBG_VALUE_LIST)
/// that defines the value, rather than a virtual register.
HANDLE_TARGET_OPCODE(DBG_INSTR_REF)
/// DBG_PHI - remainder of a PHI, identifies a program point where values
/// merge under control flow.
HANDLE_TARGET_OPCODE(DBG_PHI)
/// DBG_LABEL - a mapping of the llvm.dbg.label intrinsic
HANDLE_TARGET_OPCODE(DBG_LABEL)

View File

@ -1125,6 +1125,12 @@ def DBG_INSTR_REF : StandardPseudoInstruction {
let AsmString = "DBG_INSTR_REF";
let hasSideEffects = false;
}
def DBG_PHI : StandardPseudoInstruction {
let OutOperandList = (outs);
let InOperandList = (ins variable_ops);
let AsmString = "DBG_PHI";
let hasSideEffects = 0;
}
def DBG_LABEL : StandardPseudoInstruction {
let OutOperandList = (outs);
let InOperandList = (ins unknown:$label);

View File

@ -1313,6 +1313,10 @@ void AsmPrinter::emitFunctionBody() {
// location, and a nearby DBG_VALUE created. We can safely ignore
// the instruction reference.
break;
case TargetOpcode::DBG_PHI:
// This instruction is only used to label a program point, it's purely
// meta information.
break;
case TargetOpcode::DBG_LABEL:
if (isVerbose()) {
if (!emitDebugLabelComment(&MI, *this))

View File

@ -538,6 +538,21 @@ class LDVImpl {
using StashedInstrRef =
std::tuple<unsigned, unsigned, const DILocalVariable *,
const DIExpression *, DebugLoc>;
/// Position and VReg of a PHI instruction during register allocation.
struct PHIValPos {
SlotIndex SI; /// Slot where this PHI occurs.
Register Reg; /// VReg this PHI occurs in.
unsigned SubReg; /// Qualifiying subregister for Reg.
};
/// Map from debug instruction number to PHI position during allocation.
std::map<unsigned, PHIValPos> PHIValToPos;
/// Index of, for each VReg, which debug instruction numbers and corresponding
/// PHIs are sensitive to splitting. Each VReg may have multiple PHI defs,
/// at different positions.
DenseMap<Register, std::vector<unsigned>> RegToPHIIdx;
std::map<SlotIndex, std::vector<StashedInstrRef>> StashedInstrReferences;
/// Whether emitDebugValues is called.
@ -614,6 +629,8 @@ public:
/// Release all memory.
void clear() {
MF = nullptr;
PHIValToPos.clear();
RegToPHIIdx.clear();
StashedInstrReferences.clear();
userValues.clear();
userLabels.clear();
@ -629,6 +646,10 @@ public:
/// Map virtual register to an equivalence class.
void mapVirtReg(Register VirtReg, UserValue *EC);
/// Replace any PHI referring to OldReg with its corresponding NewReg, if
/// present.
void splitPHIRegister(Register OldReg, ArrayRef<Register> NewRegs);
/// Replace all references to OldReg with NewRegs.
void splitRegister(Register OldReg, ArrayRef<Register> NewRegs);
@ -1224,6 +1245,21 @@ bool LDVImpl::runOnMachineFunction(MachineFunction &mf) {
bool Changed = collectDebugValues(mf);
computeIntervals();
LLVM_DEBUG(print(dbgs()));
// Collect the set of VReg / SlotIndexs where PHIs occur; index the sensitive
// VRegs too, for when we're notified of a range split.
SlotIndexes *Slots = LIS->getSlotIndexes();
for (const auto &PHIIt : MF->DebugPHIPositions) {
const MachineFunction::DebugPHIRegallocPos &Position = PHIIt.second;
MachineBasicBlock *MBB = Position.MBB;
Register Reg = Position.Reg;
unsigned SubReg = Position.SubReg;
SlotIndex SI = Slots->getMBBStartIdx(MBB);
PHIValPos VP = {SI, Reg, SubReg};
PHIValToPos.insert(std::make_pair(PHIIt.first, VP));
RegToPHIIdx[Reg].push_back(PHIIt.first);
}
ModifiedMF = Changed;
return Changed;
}
@ -1382,7 +1418,50 @@ UserValue::splitRegister(Register OldReg, ArrayRef<Register> NewRegs,
return DidChange;
}
void LDVImpl::splitPHIRegister(Register OldReg, ArrayRef<Register> NewRegs) {
auto RegIt = RegToPHIIdx.find(OldReg);
if (RegIt == RegToPHIIdx.end())
return;
std::vector<std::pair<Register, unsigned>> NewRegIdxes;
// Iterate over all the debug instruction numbers affected by this split.
for (unsigned InstrID : RegIt->second) {
auto PHIIt = PHIValToPos.find(InstrID);
assert(PHIIt != PHIValToPos.end());
const SlotIndex &Slot = PHIIt->second.SI;
assert(OldReg == PHIIt->second.Reg);
// Find the new register that covers this position.
for (auto NewReg : NewRegs) {
const LiveInterval &LI = LIS->getInterval(NewReg);
auto LII = LI.find(Slot);
if (LII != LI.end() && LII->start <= Slot) {
// This new register covers this PHI position, record this for indexing.
NewRegIdxes.push_back(std::make_pair(NewReg, InstrID));
// Record that this value lives in a different VReg now.
PHIIt->second.Reg = NewReg;
break;
}
}
// If we do not find a new register covering this PHI, then register
// allocation has dropped its location, for example because it's not live.
// The old VReg will not be mapped to a physreg, and the instruction
// number will have been optimized out.
}
// Re-create register index using the new register numbers.
RegToPHIIdx.erase(RegIt);
for (auto &RegAndInstr : NewRegIdxes)
RegToPHIIdx[RegAndInstr.first].push_back(RegAndInstr.second);
}
void LDVImpl::splitRegister(Register OldReg, ArrayRef<Register> NewRegs) {
// Consider whether this split range affects any PHI locations.
splitPHIRegister(OldReg, NewRegs);
// Check whether any intervals mapped by a DBG_VALUE were split and need
// updating.
bool DidChange = false;
for (UserValue *UV = lookupVirtReg(OldReg); UV; UV = UV->getNext())
DidChange |= UV->splitRegister(OldReg, NewRegs, *LIS);
@ -1722,11 +1801,53 @@ void LDVImpl::emitDebugValues(VirtRegMap *VRM) {
userLabel->emitDebugLabel(*LIS, *TII, BBSkipInstsMap);
}
LLVM_DEBUG(dbgs() << "********** EMITTING DEBUG PHIS **********\n");
auto Slots = LIS->getSlotIndexes();
for (auto &It : PHIValToPos) {
// For each ex-PHI, identify its physreg location or stack slot, and emit
// a DBG_PHI for it.
unsigned InstNum = It.first;
auto Slot = It.second.SI;
Register Reg = It.second.Reg;
unsigned SubReg = It.second.SubReg;
MachineBasicBlock *OrigMBB = Slots->getMBBFromIndex(Slot);
if (VRM->isAssignedReg(Reg) &&
Register::isPhysicalRegister(VRM->getPhys(Reg))) {
unsigned PhysReg = VRM->getPhys(Reg);
if (SubReg != 0)
PhysReg = TRI->getSubReg(PhysReg, SubReg);
auto Builder = BuildMI(*OrigMBB, OrigMBB->begin(), DebugLoc(),
TII->get(TargetOpcode::DBG_PHI));
Builder.addReg(PhysReg);
Builder.addImm(InstNum);
} else if (VRM->getStackSlot(Reg) != VirtRegMap::NO_STACK_SLOT) {
const MachineRegisterInfo &MRI = MF->getRegInfo();
const TargetRegisterClass *TRC = MRI.getRegClass(Reg);
unsigned SpillSize, SpillOffset;
// Test whether this location is legal with the given subreg.
bool Success =
TII->getStackSlotRange(TRC, SubReg, SpillSize, SpillOffset, *MF);
if (Success) {
auto Builder = BuildMI(*OrigMBB, OrigMBB->begin(), DebugLoc(),
TII->get(TargetOpcode::DBG_PHI));
Builder.addFrameIndex(VRM->getStackSlot(Reg));
Builder.addImm(InstNum);
}
}
// If there was no mapping for a value ID, it's optimized out. Create no
// DBG_PHI, and any variables using this value will become optimized out.
}
MF->DebugPHIPositions.clear();
LLVM_DEBUG(dbgs() << "********** EMITTING INSTR REFERENCES **********\n");
// Re-insert any DBG_INSTR_REFs back in the position they were. Ordering
// is preserved by vector.
auto Slots = LIS->getSlotIndexes();
const MCInstrDesc &RefII = TII->get(TargetOpcode::DBG_INSTR_REF);
for (auto &P : StashedInstrReferences) {
const SlotIndex &Idx = P.first;

View File

@ -316,6 +316,16 @@ void PHIElimination::LowerPHINode(MachineBasicBlock &MBB,
IncomingReg, DestReg);
}
if (MPhi->peekDebugInstrNum()) {
// If referred to by debug-info, store where this PHI was.
MachineFunction *MF = MBB.getParent();
unsigned ID = MPhi->peekDebugInstrNum();
auto P = MachineFunction::DebugPHIRegallocPos(&MBB, IncomingReg, 0);
auto Res = MF->DebugPHIPositions.insert({ID, P});
assert(Res.second);
(void)Res;
}
// Update live variable information if there is any.
if (LV) {
if (IncomingReg) {

View File

@ -1269,6 +1269,9 @@ void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF,
}
MI.getDebugExpressionOp().setMetadata(DIExpr);
continue;
} else if (MI.isDebugPHI()) {
// Allow stack ref to continue onwards.
continue;
}
// TODO: This code should be commoned with the code for

View File

@ -0,0 +1,153 @@
# RUN: llc %s -o - -mtriple=x86_64-unknown-unknown \
# RUN: -experimental-debug-variable-locations \
# RUN: -run-pass=phi-node-elimination,livedebugvars,greedy,virtregrewriter \
# RUN: | FileCheck %s
#
# Like phi-through-regalloc.mir, pass a PHI node into register allocation, and
# test that it correctly comes out, in a stack slot. Fifteen spurious PHIs have
# been added to force the register allocator to spill one.
--- |
; ModuleID = 'promoted.ll'
source_filename = "test.c"
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"
define dso_local i32 @foo(i32 %bar, i32 %baz) !dbg !7 {
entry:
ret i32 0, !dbg !19
}
declare dso_local i32 @ext(i32)
; 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_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "test.c", 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 = !{!"."}
!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10, !10}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !DILocalVariable(name: "bar", arg: 1, scope: !7, file: !1, line: 2, type: !10)
!12 = !DILocation(line: 0, scope: !7)
!13 = !DILocalVariable(name: "baz", arg: 2, scope: !7, file: !1, line: 2, type: !10)
!14 = !DILocalVariable(name: "either", scope: !7, file: !1, line: 3, type: !10)
!15 = !DILocation(line: 4, column: 7, scope: !16)
!16 = distinct !DILexicalBlock(scope: !7, file: !1, line: 4, column: 7)
!17 = !DILocation(line: 4, column: 7, scope: !7)
!18 = !DILocation(line: 0, scope: !16)
!19 = !DILocation(line: 9, column: 3, scope: !7)
...
---
name: foo
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr32 }
- { id: 1, class: gr32 }
- { id: 2, class: gr32 }
- { id: 3, class: gr32 }
- { id: 4, class: gr32 }
liveins:
- { reg: '$edi', virtual-reg: '%1' }
- { reg: '$esi', virtual-reg: '%2' }
frameInfo:
maxAlignment: 1
hasCalls: true
machineFunctionInfo: {}
body: |
; CHECK-LABEL: bb.0:
; CHECK: renamable $ebp = COPY $edi
; CHECK: MOV32mr %stack.1, 1, $noreg, 0, $noreg, killed renamable $ebp
bb.0:
successors: %bb.2(0x50000000), %bb.1(0x30000000)
liveins: $edi, $esi
DBG_VALUE $edi, $noreg, !11, !DIExpression(), debug-location !12
DBG_VALUE $esi, $noreg, !13, !DIExpression(), debug-location !12
%2:gr32 = COPY killed $esi
DBG_VALUE %2, $noreg, !13, !DIExpression(), debug-location !12
%1:gr32 = COPY killed $edi
DBG_VALUE %1, $noreg, !11, !DIExpression(), debug-location !12
DBG_VALUE 0, $noreg, !14, !DIExpression(), debug-location !12
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !15
%3:gr32 = MOV32r0 implicit-def dead $eflags
$edi = COPY killed %3, debug-location !15
CALL64pcrel32 @ext, csr_64, implicit $rsp, implicit $ssp, implicit killed $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !15
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !15
%4:gr32 = COPY killed $eax, debug-location !15
%10:gr32 = MOV32ri 0
%11:gr32 = MOV32ri 1
%12:gr32 = MOV32ri 2
%13:gr32 = MOV32ri 3
%14:gr32 = MOV32ri 4
%15:gr32 = MOV32ri 5
%16:gr32 = MOV32ri 6
%17:gr32 = MOV32ri 7
%18:gr32 = MOV32ri 8
%19:gr32 = MOV32ri 9
%20:gr32 = MOV32ri 10
%21:gr32 = MOV32ri 11
%22:gr32 = MOV32ri 12
%23:gr32 = MOV32ri 13
%24:gr32 = MOV32ri 14
TEST32rr killed %4, %4, implicit-def $eflags, debug-location !15
JCC_1 %bb.2, 5, implicit killed $eflags, debug-location !17
JMP_1 %bb.1, debug-location !17
bb.1:
DBG_VALUE %2, $noreg, !14, !DIExpression(), debug-location !12
%30:gr32 = MOV32ri 0
%31:gr32 = MOV32ri 1
%32:gr32 = MOV32ri 2
%33:gr32 = MOV32ri 3
%34:gr32 = MOV32ri 4
%35:gr32 = MOV32ri 5
%36:gr32 = MOV32ri 6
%37:gr32 = MOV32ri 7
%38:gr32 = MOV32ri 8
%39:gr32 = MOV32ri 9
%40:gr32 = MOV32ri 10
%41:gr32 = MOV32ri 11
%42:gr32 = MOV32ri 12
%43:gr32 = MOV32ri 13
%44:gr32 = MOV32ri 14
; CHECK-LABEL: bb.2:
bb.2:
%0:gr32 = PHI %1, %bb.0, %2, %bb.1, debug-instr-number 1, debug-location !18
%50:gr32 = PHI %10, %bb.0, %30, %bb.1, debug-location !18
%51:gr32 = PHI %11, %bb.0, %31, %bb.1, debug-location !18
%52:gr32 = PHI %12, %bb.0, %32, %bb.1, debug-location !18
%53:gr32 = PHI %13, %bb.0, %33, %bb.1, debug-location !18
%54:gr32 = PHI %14, %bb.0, %34, %bb.1, debug-location !18
%55:gr32 = PHI %15, %bb.0, %35, %bb.1, debug-location !18
%56:gr32 = PHI %16, %bb.0, %36, %bb.1, debug-location !18
%57:gr32 = PHI %17, %bb.0, %37, %bb.1, debug-location !18
%58:gr32 = PHI %18, %bb.0, %38, %bb.1, debug-location !18
%59:gr32 = PHI %19, %bb.0, %39, %bb.1, debug-location !18
%60:gr32 = PHI %20, %bb.0, %40, %bb.1, debug-location !18
%61:gr32 = PHI %21, %bb.0, %41, %bb.1, debug-location !18
%62:gr32 = PHI %22, %bb.0, %42, %bb.1, debug-location !18
%63:gr32 = PHI %23, %bb.0, %43, %bb.1, debug-location !18
%64:gr32 = PHI %24, %bb.0, %44, %bb.1, debug-location !18
DBG_INSTR_REF 1, 0, !14, !DIExpression(), debug-location !12
; CHECK: DBG_PHI %stack.1, 1
; CHECK: renamable $eax = MOV32rm %stack.1,
; CHECK: DBG_INSTR_REF 1, 0
$eax = COPY killed %0, debug-location !19
RET 0, killed $eax, debug-location !19
...

View File

@ -0,0 +1,134 @@
# RUN: llc %s -o - -mtriple=x86_64-unknown-unknown \
# RUN: -experimental-debug-variable-locations \
# RUN: -run-pass=phi-node-elimination,livedebugvars,greedy,virtregrewriter \
# RUN: | FileCheck %s
#
# This test checks that for a very simple PHI, we produce a corresponding
# DBG_PHI instruction, that a DBG_INSTR_REF refers to. Tests the path through
# phi-node-elimination, live debug variables, and then placement after register
# allocation.
#
# Original code, compiled with only -mem2reg, then fed to llc -stop-before...
#
# int ext(int);
# int foo(int bar, int baz) {
# int either = 0;
# if (ext(0))
# either = bar;
# else
# either = baz;
#
# return either;
# }
#
--- |
; ModuleID = 'promoted.ll'
source_filename = "test.c"
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"
define dso_local i32 @foo(i32 %bar, i32 %baz) !dbg !7 {
entry:
call void @llvm.dbg.value(metadata i32 %bar, metadata !11, metadata !DIExpression()), !dbg !12
call void @llvm.dbg.value(metadata i32 %baz, metadata !13, metadata !DIExpression()), !dbg !12
call void @llvm.dbg.value(metadata i32 0, metadata !14, metadata !DIExpression()), !dbg !12
%call = call i32 @ext(i32 0), !dbg !15
%tobool = icmp ne i32 %call, 0, !dbg !15
br i1 %tobool, label %if.end, label %if.else, !dbg !17
if.else: ; preds = %entry
call void @llvm.dbg.value(metadata i32 %baz, metadata !14, metadata !DIExpression()), !dbg !12
br label %if.end
if.end: ; preds = %entry, %if.else
%either.0 = phi i32 [ %baz, %if.else ], [ %bar, %entry ], !dbg !18
call void @llvm.dbg.value(metadata i32 %either.0, metadata !14, metadata !DIExpression()), !dbg !12
ret i32 %either.0, !dbg !19
}
declare dso_local i32 @ext(i32)
; 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_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "test.c", 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 = !{!"."}
!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10, !10}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !DILocalVariable(name: "bar", arg: 1, scope: !7, file: !1, line: 2, type: !10)
!12 = !DILocation(line: 0, scope: !7)
!13 = !DILocalVariable(name: "baz", arg: 2, scope: !7, file: !1, line: 2, type: !10)
!14 = !DILocalVariable(name: "either", scope: !7, file: !1, line: 3, type: !10)
!15 = !DILocation(line: 4, column: 7, scope: !16)
!16 = distinct !DILexicalBlock(scope: !7, file: !1, line: 4, column: 7)
!17 = !DILocation(line: 4, column: 7, scope: !7)
!18 = !DILocation(line: 0, scope: !16)
!19 = !DILocation(line: 9, column: 3, scope: !7)
...
---
name: foo
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr32 }
- { id: 1, class: gr32 }
- { id: 2, class: gr32 }
- { id: 3, class: gr32 }
- { id: 4, class: gr32 }
liveins:
- { reg: '$edi', virtual-reg: '%1' }
- { reg: '$esi', virtual-reg: '%2' }
frameInfo:
maxAlignment: 1
hasCalls: true
machineFunctionInfo: {}
body: |
bb.0.entry:
successors: %bb.2(0x50000000), %bb.1(0x30000000)
liveins: $edi, $esi
DBG_VALUE $edi, $noreg, !11, !DIExpression(), debug-location !12
DBG_VALUE $esi, $noreg, !13, !DIExpression(), debug-location !12
%2:gr32 = COPY killed $esi
DBG_VALUE %2, $noreg, !13, !DIExpression(), debug-location !12
%1:gr32 = COPY killed $edi
DBG_VALUE %1, $noreg, !11, !DIExpression(), debug-location !12
DBG_VALUE 0, $noreg, !14, !DIExpression(), debug-location !12
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !15
%3:gr32 = MOV32r0 implicit-def dead $eflags
$edi = COPY killed %3, debug-location !15
CALL64pcrel32 @ext, csr_64, implicit $rsp, implicit $ssp, implicit killed $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !15
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !15
%4:gr32 = COPY killed $eax, debug-location !15
TEST32rr killed %4, %4, implicit-def $eflags, debug-location !15
JCC_1 %bb.2, 5, implicit killed $eflags, debug-location !17
JMP_1 %bb.1, debug-location !17
bb.1.if.else:
DBG_VALUE %2, $noreg, !14, !DIExpression(), debug-location !12
; CHECK-LABEL: bb.2.if.end:
bb.2.if.end:
%0:gr32 = PHI %1, %bb.0, %2, %bb.1, debug-instr-number 1, debug-location !18
DBG_INSTR_REF 1, 0, !14, !DIExpression(), debug-location !12
; CHECK: DBG_PHI $ebp, 1
; CHECK: DBG_INSTR_REF 1, 0
$eax = COPY killed %0, debug-location !19
; Confirm that %0 is allocated in $ebp,
; CHECK: $eax = COPY killed renamable $ebp
RET 0, killed $eax, debug-location !19
...