[DebugInfo][InstrRef] Track values fused into stack spills

During register allocation, some instructions can have stack spills fused
into them. It means that when vregs are allocated on the stack we can
convert:

    SETCCr %0
    DBG_VALUE %0

to

    SETCCm %stack.0
    DBG_VALUE %stack.0

Unfortunately instruction referencing finds this harder: a store to the
stack doesn't have a specific operand number, therefore we don't substitute
the old operand for a new operand, and the location is dropped. This patch
implements a solution: just recognise the memory operand attached to an
instruction with a Special Number (TM), and record a substitution between
the old value and the new one.

This patch adds substitution code to InlineSpiller to record such fused
spills, and tracking in InstrRefBasedLDV to recognise such values, and
produce the value numbers for them. Everything to do with the movement of
stack-defined values is already handled in InstrRefBasedLDV.

Differential Revision: https://reviews.llvm.org/D111317
This commit is contained in:
Jeremy Morse
2021-10-25 15:09:42 +01:00
parent 2d9ee590b6
commit ee3eee71e4
9 changed files with 776 additions and 8 deletions

View File

@@ -544,6 +544,10 @@ public:
/// instruction referencing.
bool useDebugInstrRef() const;
/// A reserved operand number representing the instructions memory operand,
/// for instructions that have a stack spill fused into them.
const static unsigned int DebugOperandMemNumber;
MachineFunction(Function &F, const LLVMTargetMachine &Target,
const TargetSubtargetInfo &STI, unsigned FunctionNum,
MachineModuleInfo &MMI);

View File

@@ -928,6 +928,39 @@ foldMemoryOperand(ArrayRef<std::pair<MachineInstr *, unsigned>> Ops,
// Update the call site info.
if (MI->isCandidateForCallSiteEntry())
MI->getMF()->moveCallSiteInfo(MI, FoldMI);
// If we've folded a store into an instruction labelled with debug-info,
// record a substitution from the old operand to the memory operand. Handle
// the simple common case where operand 0 is the one being folded, plus when
// the destination operand is also a tied def. More values could be
// substituted / preserved with more analysis.
if (MI->peekDebugInstrNum() && Ops[0].second == 0) {
// Helper lambda.
auto MakeSubstitution = [this,FoldMI,MI,&Ops]() {
// Substitute old operand zero to the new instructions memory operand.
unsigned OldOperandNum = Ops[0].second;
unsigned NewNum = FoldMI->getDebugInstrNum();
unsigned OldNum = MI->getDebugInstrNum();
MF.makeDebugValueSubstitution({OldNum, OldOperandNum},
{NewNum, MachineFunction::DebugOperandMemNumber});
};
const MachineOperand &Op0 = MI->getOperand(Ops[0].second);
if (Ops.size() == 1 && Op0.isDef()) {
MakeSubstitution();
} else if (Ops.size() == 2 && Op0.isDef() && MI->getOperand(1).isTied() &&
Op0.getReg() == MI->getOperand(1).getReg()) {
MakeSubstitution();
}
} else if (MI->peekDebugInstrNum()) {
// This is a debug-labelled instruction, but the operand being folded isn't
// at operand zero. Most likely this means it's a load being folded in.
// Substitute any register defs from operand zero up to the one being
// folded -- past that point, we don't know what the new operand indexes
// will be.
MF.substituteDebugValuesForInst(*MI, *FoldMI, Ops[0].second);
}
MI->eraseFromParent();
// Insert any new instructions other than FoldMI into the LIS maps.

View File

@@ -884,6 +884,27 @@ InstrRefBasedLDV::extractSpillBaseRegAndOffset(const MachineInstr &MI) {
return MTracker->getOrTrackSpillLoc({Reg, Offset});
}
Optional<LocIdx> InstrRefBasedLDV::findLocationForMemOperand(const MachineInstr &MI) {
SpillLocationNo SpillLoc = extractSpillBaseRegAndOffset(MI);
// Where in the stack slot is this value defined -- i.e., what size of value
// is this? An important question, because it could be loaded into a register
// from the stack at some point. Happily the memory operand will tell us
// the size written to the stack.
auto *MemOperand = *MI.memoperands_begin();
unsigned SizeInBits = MemOperand->getSizeInBits();
// Find that position in the stack indexes we're tracking.
auto IdxIt = MTracker->StackSlotIdxes.find({SizeInBits, 0});
if (IdxIt == MTracker->StackSlotIdxes.end())
// That index is not tracked. This is suprising, and unlikely to ever
// occur, but the safe action is to indicate the variable is optimised out.
return None;
unsigned SpillID = MTracker->getSpillIDWithIdx(SpillLoc, IdxIt->second);
return MTracker->getSpillMLoc(SpillID);
}
/// End all previous ranges related to @MI and start a new range from @MI
/// if it is a DBG_VALUE instr.
bool InstrRefBasedLDV::transferDebugValue(const MachineInstr &MI) {
@@ -1010,16 +1031,25 @@ bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI,
const MachineInstr &TargetInstr = *InstrIt->second.first;
uint64_t BlockNo = TargetInstr.getParent()->getNumber();
// Pick out the designated operand.
assert(OpNo < TargetInstr.getNumOperands());
const MachineOperand &MO = TargetInstr.getOperand(OpNo);
// Pick out the designated operand. It might be a memory reference, if
// a register def was folded into a stack store.
if (OpNo == MachineFunction::DebugOperandMemNumber &&
TargetInstr.hasOneMemOperand()) {
Optional<LocIdx> L = findLocationForMemOperand(TargetInstr);
if (L)
NewID = ValueIDNum(BlockNo, InstrIt->second.second, *L);
} else if (OpNo != MachineFunction::DebugOperandMemNumber) {
assert(OpNo < TargetInstr.getNumOperands());
const MachineOperand &MO = TargetInstr.getOperand(OpNo);
// Today, this can only be a register.
assert(MO.isReg() && MO.isDef());
// Today, this can only be a register.
assert(MO.isReg() && MO.isDef());
unsigned LocID = MTracker->getLocID(MO.getReg());
LocIdx L = MTracker->LocIDToLocIdx[LocID];
NewID = ValueIDNum(BlockNo, InstrIt->second.second, L);
unsigned LocID = MTracker->getLocID(MO.getReg());
LocIdx L = MTracker->LocIDToLocIdx[LocID];
NewID = ValueIDNum(BlockNo, InstrIt->second.second, L);
}
// else: NewID is left as None.
} else if (PHIIt != DebugPHINumToValue.end() && PHIIt->InstrNum == InstNo) {
// It's actually a PHI value. Which value it is might not be obvious, use
// the resolver helper to find out.
@@ -1278,6 +1308,16 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
for (auto *MO : RegMaskPtrs)
MTracker->writeRegMask(MO, CurBB, CurInst);
// If this instruction writes to a spill slot, def that slot.
if (hasFoldedStackStore(MI)) {
SpillLocationNo SpillNo = extractSpillBaseRegAndOffset(MI);
for (unsigned int I = 0; I < MTracker->NumSlotIdxes; ++I) {
unsigned SpillID = MTracker->getSpillIDWithIdx(SpillNo, I);
LocIdx L = MTracker->getSpillMLoc(SpillID);
MTracker->setMLoc(L, ValueIDNum(CurBB, CurInst, L));
}
}
if (!TTracker)
return;
@@ -1303,6 +1343,16 @@ void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) {
if (MO->clobbersPhysReg(Reg))
TTracker->clobberMloc(L.Idx, MI.getIterator(), false);
}
// Tell TTracker about any folded stack store.
if (hasFoldedStackStore(MI)) {
SpillLocationNo SpillNo = extractSpillBaseRegAndOffset(MI);
for (unsigned int I = 0; I < MTracker->NumSlotIdxes; ++I) {
unsigned SpillID = MTracker->getSpillIDWithIdx(SpillNo, I);
LocIdx L = MTracker->getSpillMLoc(SpillID);
TTracker->clobberMloc(L, MI.getIterator(), true);
}
}
}
void InstrRefBasedLDV::performCopy(Register SrcRegNum, Register DstRegNum) {
@@ -1342,6 +1392,12 @@ bool InstrRefBasedLDV::isSpillInstruction(const MachineInstr &MI,
if (!MI.hasOneMemOperand())
return false;
// Reject any memory operand that's aliased -- we can't guarantee its value.
auto MMOI = MI.memoperands_begin();
const PseudoSourceValue *PVal = (*MMOI)->getPseudoValue();
if (PVal->isAliased(MFI))
return false;
if (!MI.getSpillSize(TII) && !MI.getFoldedSpillSize(TII))
return false; // This is not a spill instruction, since no valid size was
// returned from either function.
@@ -1393,6 +1449,13 @@ bool InstrRefBasedLDV::transferSpillOrRestoreInst(MachineInstr &MI) {
LLVM_DEBUG(dbgs() << "Examining instruction: "; MI.dump(););
// Strictly limit ourselves to plain loads and stores, not all instructions
// that can access the stack.
int FIDummy;
if (!TII->isStoreToStackSlotPostFE(MI, FIDummy) &&
!TII->isLoadFromStackSlotPostFE(MI, FIDummy))
return false;
// First, if there are any DBG_VALUEs pointing at a spill slot that is
// written to, terminate that variable location. The value in memory
// will have changed. DbgEntityHistoryCalculator doesn't try to detect this.

View File

@@ -984,6 +984,21 @@ public:
void dump_mloc_transfer(const MLocTransferMap &mloc_transfer) const;
bool isCalleeSaved(LocIdx L) const;
bool hasFoldedStackStore(const MachineInstr &MI) {
// Instruction must have a memory operand that's a stack slot, and isn't
// aliased, meaning it's a spill from regalloc instead of a variable.
// If it's aliased, we can't guarantee its value.
if (!MI.hasOneMemOperand())
return false;
auto *MemOperand = *MI.memoperands_begin();
return MemOperand->isStore() &&
MemOperand->getPseudoValue() &&
MemOperand->getPseudoValue()->kind() == PseudoSourceValue::FixedStack
&& !MemOperand->getPseudoValue()->isAliased(MFI);
}
Optional<LocIdx> findLocationForMemOperand(const MachineInstr &MI);
};
} // namespace LiveDebugValues

View File

@@ -974,6 +974,9 @@ void MachineFunction::makeDebugValueSubstitution(DebugInstrOperandPair A,
unsigned Subreg) {
// Catch any accidental self-loops.
assert(A.first != B.first);
// Don't allow any substitutions _from_ the memory operand number.
assert(A.second != DebugOperandMemNumber);
DebugValueSubstitutions.push_back({A, B, Subreg});
}
@@ -1239,6 +1242,9 @@ bool MachineFunction::useDebugInstrRef() const {
return false;
}
// Use one million as a high / reserved number.
const unsigned MachineFunction::DebugOperandMemNumber = 1000000;
/// \}
//===----------------------------------------------------------------------===//

View File

@@ -0,0 +1,175 @@
# RUN: llc %s -o - -experimental-debug-variable-locations \
# RUN: -start-before=x86-flags-copy-lowering -stop-after=virtregrewriter \
# RUN: | FileCheck %s
#
# This test is for stack spill folding -- the INC32r near the end of the MIR
# below show be morphed into an INC32m by the register allocator, making it
# load-operate-store to %stack.0. We should track this fact in the substitution
# table, by adding a substitution to the memory-operand operand number.
#
# The INC32r is a tied-def instruction.
#
# NB: This test would more ideally start at phi-node-elimination, where
# register allocation begins, however for some reason the INC32r gets
# transformed into a different instruction if we do that. Start at the last
# optimisation pass before regalloc instead.
#
# CHECK: debugValueSubstitutions:
# CHECK-NEXT: - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 1000000, subreg: 0 }
# CHECK-LABEL: bb.5:
# CHECK: INC32m %stack.0, {{.*}} debug-instr-number 2,
--- |
; ModuleID = 'reduced.ll'
source_filename = "reduced.ll"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
%"class.llvm::APInt" = type { i32, %union.anon }
%union.anon = type { i64 }
define void @_ZNK4llvm5APInt5magicEv() local_unnamed_addr align 2 !dbg !7 {
ret void
}
!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 = !{!"clang"}
!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !11, !11}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed)
!12 = !DILocalVariable(name: "bar", arg: 1, scope: !7, file: !1, line: 3, type: !11)
!13 = !DILocation(line: 0, scope: !7)
!14 = !DILocalVariable(name: "baz", arg: 2, scope: !7, file: !1, line: 3, type: !11)
!15 = distinct !DILexicalBlock(scope: !7, file: !1, line: 8, column: 7)
...
---
name: _ZNK4llvm5APInt5magicEv
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr32 }
- { id: 2, class: gr64 }
- { id: 3, class: gr32 }
- { id: 4, class: gr64 }
- { id: 5, class: gr32 }
- { id: 6, class: gr32 }
- { id: 7, class: gr32 }
- { id: 8, class: gr64 }
- { id: 9, class: gr32 }
- { id: 10, class: gr64 }
- { id: 11, class: gr32 }
- { id: 12, class: gr64 }
- { id: 13, class: gr32 }
- { id: 14, class: gr64 }
- { id: 15, class: gr32 }
- { id: 16, class: gr64 }
- { id: 17, class: gr64 }
- { id: 18, class: gr64 }
- { id: 19, class: gr32 }
- { id: 20, class: gr8 }
- { id: 21, class: gr32 }
- { id: 22, class: gr64 }
- { id: 23, class: gr64 }
- { id: 24, class: gr64 }
- { id: 25, class: gr64 }
- { id: 26, class: gr64 }
- { id: 27, class: gr32 }
- { id: 28, class: gr8 }
- { id: 29, class: gr64 }
- { id: 30, class: gr64 }
- { id: 31, class: gr64 }
- { id: 32, class: gr64 }
- { id: 33, class: gr64 }
- { id: 34, class: gr64 }
- { id: 35, class: gr64 }
- { id: 36, class: gr64 }
- { id: 37, class: gr64 }
- { id: 38, class: gr8 }
frameInfo:
maxAlignment: 1
hasCalls: true
machineFunctionInfo: {}
body: |
bb.0:
%15:gr32 = IMPLICIT_DEF
%14:gr64 = IMPLICIT_DEF
%17:gr64 = IMPLICIT_DEF
%18:gr64 = IMPLICIT_DEF
%19:gr32 = MOV32r0 implicit-def dead $eflags
%22:gr64 = IMPLICIT_DEF
%23:gr64 = IMPLICIT_DEF
%24:gr64 = IMPLICIT_DEF
%25:gr64 = IMPLICIT_DEF
%26:gr64 = IMPLICIT_DEF
%30:gr64 = IMPLICIT_DEF
%31:gr64 = IMPLICIT_DEF
%32:gr64 = IMPLICIT_DEF
%33:gr64 = IMPLICIT_DEF
%34:gr64 = IMPLICIT_DEF
%35:gr64 = IMPLICIT_DEF
%36:gr64 = IMPLICIT_DEF
%37:gr64 = IMPLICIT_DEF
%21:gr32 = IMPLICIT_DEF
bb.1:
%0:gr64 = PHI %14, %bb.0, %12, %bb.5
%1:gr32 = PHI %15, %bb.0, %11, %bb.5
%2:gr64 = PHI %14, %bb.0, %10, %bb.5
%3:gr32 = PHI %15, %bb.0, %9, %bb.5
%4:gr64 = PHI %14, %bb.0, %8, %bb.5
%5:gr32 = PHI %15, %bb.0, %7, %bb.5, debug-location !13
%6:gr32 = PHI %15, %bb.0, %13, %bb.5, debug-location !13
%16:gr64 = ADD64rr %4, %4, implicit-def dead $eflags, debug-location !13
MOV32mr %17, 1, $noreg, 0, $noreg, %5, debug-location !13 :: (store (s32) into `i32* undef`, align 8)
MOV64mr %18, 1, $noreg, 0, $noreg, killed %16, debug-location !13 :: (store (s64) into `i64* undef`)
%20:gr8 = COPY %19.sub_8bit
TEST8rr %20, %20, implicit-def $eflags, debug-location !13
JCC_1 %bb.3, 5, implicit $eflags, debug-location !13
JMP_1 %bb.2, debug-location !13
bb.2:
bb.3:
successors: %bb.4, %bb.5
%7:gr32 = PHI %5, %bb.1, %21, %bb.2, debug-location !13
MOV32mr %22, 1, $noreg, 0, $noreg, %7, debug-location !13 :: (store (s32) into `i32* undef`, align 8)
%8:gr64 = MOV64rm %23, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s64) from `i64* undef`)
MOV32mr %24, 1, $noreg, 0, $noreg, %3, debug-location !13 :: (store (s32) into `i32* undef`, align 8)
MOV64mi32 %25, 1, $noreg, 0, $noreg, 0, debug-location !13 :: (store (s64) into `i64* undef`)
%28:gr8 = COPY %19.sub_8bit
TEST8rr %28, %28, implicit-def $eflags, debug-location !13
JCC_1 %bb.5, 5, implicit $eflags, debug-location !13
JMP_1 %bb.4, debug-location !13
bb.4:
%29:gr64 = ADD64rr %2, %2, implicit-def dead $eflags, debug-location !13
MOV64mr %30, 1, $noreg, 0, $noreg, killed %29, debug-location !13 :: (store (s64) into `i64* undef`)
bb.5:
%9:gr32 = MOV32rm %26, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s32) from `i32* undef`, align 8)
%10:gr64 = MOV64rm %31, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s64) from `i64* undef`)
%12:gr64 = ADD64rr %0, %0, implicit-def dead $eflags, debug-location !13
MOV32mr %32, 1, $noreg, 0, $noreg, %1, debug-location !13 :: (store (s32) into `i32* undef`, align 8)
MOV64mr %33, 1, $noreg, 0, $noreg, %12, debug-location !13 :: (store (s64) into `i64* undef`)
%11:gr32 = MOV32rm %34, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s32) from `i32* undef`, align 8)
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13
$rdi = COPY %35, debug-location !13
$rsi = COPY %36, debug-location !13
CALL64r %37, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit-def $rsp, implicit-def $ssp, implicit-def $al, debug-location !13
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13
%13:gr32 = INC32r %6, implicit-def dead $eflags, debug-instr-number 1, debug-location !13
DBG_INSTR_REF 1, 0, !12, !DIExpression(), debug-location !13
JMP_1 %bb.1, debug-location !13
...

View File

@@ -0,0 +1,259 @@
# RUN: llc %s -o - -experimental-debug-variable-locations \
# RUN: -start-before=x86-flags-copy-lowering -stop-after=virtregrewriter \
# RUN: | FileCheck %s
#
# This test is for stack spill folding -- the SETCC near the start of the MIR
# below show be morphed into an SETCCm by the register allocator, making it
# store to %stack.0. We should track this fact in the substitution table, by
# adding a substitution to the memory-operand operand number.
#
# This is a single operand spill -- there's a separate test for tied def ones.
#
# Ideally this test would be shorter; however, it needs to be sufficiently
# complex to force the register allocator to spill something, so there's a
# limit.
#
# CHECK: debugValueSubstitutions:
# CHECK-NEXT: - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 1000000, subreg: 0 }
# CHECK-LABEL: bb.0:
# CHECK: SETCCm %stack.0, {{.*}} debug-instr-number 2
--- |
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
define internal fastcc void @beans(i32 %Kind) unnamed_addr align 2 !dbg !7 {
ret void
}
!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 = !{!"clang"}
!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !11, !11}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed)
!12 = !DILocalVariable(name: "bar", arg: 1, scope: !7, file: !1, line: 3, type: !11)
!13 = !DILocation(line: 0, scope: !7)
!14 = !DILocalVariable(name: "baz", arg: 2, scope: !7, file: !1, line: 3, type: !11)
!15 = distinct !DILexicalBlock(scope: !7, file: !1, line: 8, column: 7)
...
---
name: beans
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr8 }
- { id: 1, class: gr8 }
- { id: 2, class: gr8 }
- { id: 3, class: gr32 }
- { id: 4, class: gr64 }
- { id: 5, class: gr8 }
- { id: 6, class: gr32 }
- { id: 7, class: gr32 }
- { id: 8, class: gr32 }
- { id: 9, class: gr32 }
- { id: 10, class: gr32 }
- { id: 11, class: gr64 }
- { id: 12, class: gr64 }
- { id: 13, class: gr32 }
- { id: 14, class: gr8 }
- { id: 15, class: gr8 }
- { id: 16, class: gr32 }
- { id: 17, class: gr64 }
- { id: 18, class: gr8 }
- { id: 19, class: gr8 }
- { id: 20, class: gr32 }
- { id: 21, class: gr64 }
- { id: 22, class: gr32 }
- { id: 23, class: gr32 }
- { id: 24, class: gr64 }
- { id: 25, class: gr32 }
- { id: 26, class: gr64 }
- { id: 27, class: gr8 }
- { id: 28, class: gr64_nosp }
- { id: 29, class: gr32 }
- { id: 30, class: gr8 }
- { id: 31, class: gr8 }
- { id: 32, class: gr64 }
- { id: 33, class: gr8 }
- { id: 34, class: gr64 }
- { id: 35, class: gr64 }
- { id: 36, class: gr8 }
- { id: 37, class: gr64 }
- { id: 38, class: gr8 }
- { id: 39, class: gr32 }
- { id: 40, class: gr32 }
- { id: 41, class: gr8 }
- { id: 42, class: gr8 }
- { id: 43, class: gr8 }
liveins:
- { reg: '$edi', virtual-reg: '%7' }
frameInfo:
maxAlignment: 1
hasCalls: true
machineFunctionInfo: {}
jumpTable:
kind: block-address
entries:
- id: 0
blocks: [ '%bb.11', '%bb.7', '%bb.11', '%bb.7', '%bb.12',
'%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7',
'%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.13',
'%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7',
'%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7',
'%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7',
'%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7',
'%bb.8' ]
body: |
bb.0:
successors: %bb.2(0x20000000), %bb.14(0x60000000)
liveins: $edi
%7:gr32 = COPY $edi
CMP32ri8 %7, 4, implicit-def $eflags
%0:gr8 = SETCCr 4, implicit $eflags, debug-instr-number 1
CMP32ri8 %7, 2, implicit-def $eflags
%1:gr8 = SETCCr 4, implicit $eflags
CMP32ri8 %7, 1, implicit-def $eflags
%2:gr8 = SETCCr 4, implicit $eflags
%11:gr64 = IMPLICIT_DEF
%12:gr64 = IMPLICIT_DEF
%5:gr8 = MOV8rm %12, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s8) from `i8* undef`, align 8)
%13:gr32 = MOV32r0 implicit-def dead $eflags
%14:gr8 = COPY %13.sub_8bit
TEST8rr %14, %14, implicit-def $eflags, debug-location !13
JCC_1 %bb.2, 5, implicit $eflags, debug-location !13
JMP_1 %bb.14, debug-location !13
bb.14:
successors: %bb.2(0x2aaaaaab), %bb.1(0x55555555)
CMP8ri %5, 70, implicit-def $eflags, debug-location !13
JCC_1 %bb.2, 5, implicit $eflags, debug-location !13
JMP_1 %bb.1, debug-location !13
bb.1:
bb.2:
successors: %bb.4(0x20000000), %bb.15(0x60000000)
%4:gr64 = MOV64rm %11, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s64) from `%"class.llvm::Instruction"** undef`)
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13
%16:gr32 = IMPLICIT_DEF
$edi = COPY %16, debug-location !13
%17:gr64 = IMPLICIT_DEF
CALL64r killed %17, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $al, debug-location !13
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13
%18:gr8 = COPY $al, debug-location !13
TEST8ri %18, 1, implicit-def $eflags, debug-location !13
JCC_1 %bb.4, 5, implicit $eflags, debug-location !13
JMP_1 %bb.15, debug-location !13
bb.15:
successors: %bb.4(0x2aaaaaab), %bb.3(0x55555555)
CMP8ri %5, 70, implicit-def $eflags, debug-location !13
JCC_1 %bb.4, 4, implicit $eflags, debug-location !13
JMP_1 %bb.3, debug-location !13
bb.3:
bb.4:
successors: %bb.5, %bb.6
%21:gr64 = IMPLICIT_DEF
%20:gr32 = MOVZX32rm8 killed %21, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s8) from `i32* undef`, align 8)
%6:gr32 = nsw DEC32r %20, implicit-def dead $eflags, debug-location !13
CMP32ri8 %6, 5, implicit-def $eflags, debug-location !13
JCC_1 %bb.6, 7, implicit $eflags, debug-location !13
JMP_1 %bb.5, debug-location !13
bb.5:
%24:gr64 = IMPLICIT_DEF
%23:gr32 = MOVZX32rm8 %24, 1, $noreg, 0, $noreg, debug-location !13 :: (load (s8) from `i8* undef`, align 8)
%25:gr32 = nsw ADD32ri8 %23, -22, implicit-def dead $eflags, debug-location !13
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13
$edi = COPY %25, debug-location !13
%26:gr64 = IMPLICIT_DEF
CALL64r %26, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $al, debug-location !13
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !13
bb.6:
successors: %bb.7(0x0aaaaaab), %bb.16(0x75555555)
%31:gr8 = IMPLICIT_DEF
CMP8ri %31, 40, implicit-def $eflags, debug-location !13
JCC_1 %bb.7, 7, implicit $eflags, debug-location !13
bb.16:
successors: %bb.11(0x2e8ba2ea), %bb.7(0x0ba2e8ba), %bb.12(0x1745d174), %bb.13(0x1745d174), %bb.8(0x1745d174)
%29:gr32 = MOV32r0 implicit-def dead $eflags
%28:gr64_nosp = SUBREG_TO_REG 0, %29, %subreg.sub_32bit
JMP64m $noreg, 8, %28, %jump-table.0, $noreg :: (load (s64) from jump-table)
bb.7:
%43:gr8 = IMPLICIT_DEF
$al = COPY %43
RET 0, $al
bb.8:
successors: %bb.10(0x20000000), %bb.17(0x60000000)
CMP32ri8 %6, 5, implicit-def $eflags, debug-location !13
JCC_1 %bb.10, 7, implicit $eflags, debug-location !13
JMP_1 %bb.17, debug-location !13
bb.17:
successors: %bb.10(0x2aaaaaab), %bb.9(0x55555555)
%3:gr32 = ADD32ri8 %7, -7, implicit-def dead $eflags
CMP32ri8 %3, 3, implicit-def $eflags, debug-location !13
JCC_1 %bb.10, 2, implicit $eflags, debug-location !13
JMP_1 %bb.9, debug-location !13
bb.9:
%41:gr8 = IMPLICIT_DEF
$al = COPY %41
RET 0, $al
bb.10:
%42:gr8 = IMPLICIT_DEF
$al = COPY %42
RET 0, $al
bb.11:
%37:gr64 = IMPLICIT_DEF
MOV8mr killed %37, 1, $noreg, 0, $noreg, %2, debug-location !13 :: (store (s8) into `i8* undef`, align 8)
%38:gr8 = IMPLICIT_DEF
$al = COPY %38
RET 0, $al
bb.12:
%34:gr64 = IMPLICIT_DEF
MOV8mr %34, 1, $noreg, 0, $noreg, %1, debug-location !13 :: (store (s8) into `i8* undef`, align 8)
%35:gr64 = IMPLICIT_DEF
MOV64mr %35, 1, $noreg, 0, $noreg, %4, debug-location !13 :: (store (s64) into `%"class.llvm::Instruction"** undef`)
%36:gr8 = IMPLICIT_DEF
$al = COPY %36
RET 0, $al
bb.13:
DBG_INSTR_REF 1, 0, !12, !DIExpression(), debug-location !13
%32:gr64 = IMPLICIT_DEF
MOV8mr killed %32, 1, $noreg, 0, $noreg, %0, debug-location !13 :: (store (s8) into `i8* undef`, align 8)
%33:gr8 = IMPLICIT_DEF
$al = COPY %33
RET 0, $al
...

View File

@@ -0,0 +1,131 @@
# RUN: llc %s -o - -experimental-debug-variable-locations \
# RUN: -start-before=phi-node-elimination -stop-after=virtregrewriter \
# RUN: | FileCheck %s
#
# Test that when a load gets folded into an instruction (the CVTTSS2SIrr below)
# that the debug-instr-number is preserved, with a substitution.
#
# CHECK: debugValueSubstitutions:
# CHECK-NEXT: - { srcinst: 1, srcop: 0, dstinst: 2, dstop: 0, subreg: 0 }
# CHECK-LABEL: bb.1.sw.bb:
# CHECK: renamable $eax = nofpexcept CVTTSS2SIrm %stack.0,
# CHECK-SAME: debug-instr-number 2 :: (load (s32) from %stack.0)
--- |
; ModuleID = 'lol.ll'
source_filename = "reduced.ll"
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 i32 @foo(i32 %i, float %f) local_unnamed_addr {
if.then:
%call = tail call i32 (i32, ...) undef(i32 %i)
%cond = icmp eq i32 %call, 1
br i1 %cond, label %sw.bb, label %sw.epilog
sw.bb: ; preds = %if.then
%conv = fptosi float %f to i8
call void @llvm.dbg.value(metadata i8 %conv, metadata !6, metadata !DIExpression()), !dbg !17
%tobool.not = icmp eq i8 %conv, 0
br i1 %tobool.not, label %if.end, label %sw.epilog
if.end: ; preds = %sw.bb
tail call void (...) undef()
br label %sw.epilog
sw.epilog: ; preds = %if.then, %if.end, %sw.bb
ret i32 undef
}
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
attributes #0 = { nofree nosync nounwind readnone speculatable willreturn }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3, !4, !5}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "/tmp/test.c", directory: ".")
!2 = !{i32 7, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{i32 7, !"uwtable", i32 1}
!6 = !DILocalVariable(name: "b", scope: !7, file: !8, line: 15, type: !16)
!7 = distinct !DISubprogram(name: "foo", scope: !8, file: !8, line: 8, type: !9, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15)
!8 = !DIFile(filename: "/tmp/test.c", directory: "")
!9 = !DISubroutineType(types: !10)
!10 = !{!11, !11, !12, !13}
!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!12 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
!14 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed)
!15 = !{}
!16 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!17 = !DILocation(line: 0, scope: !7)
...
---
name: foo
tracksRegLiveness: true
registers:
- { id: 0, class: gr32, preferred-register: '' }
- { id: 1, class: fr32, preferred-register: '' }
- { id: 2, class: gr32, preferred-register: '' }
- { id: 3, class: gr8, preferred-register: '' }
- { id: 4, class: gr64, preferred-register: '' }
- { id: 5, class: gr32, preferred-register: '' }
- { id: 6, class: gr32, preferred-register: '' }
- { id: 7, class: gr32, preferred-register: '' }
- { id: 8, class: gr8, preferred-register: '' }
- { id: 9, class: gr32, preferred-register: '' }
- { id: 10, class: gr8, preferred-register: '' }
- { id: 11, class: gr64, preferred-register: '' }
- { id: 12, class: gr32, preferred-register: '' }
liveins:
- { reg: '$edi', virtual-reg: '%0' }
- { reg: '$xmm0', virtual-reg: '%1' }
frameInfo:
hasCalls: true
body: |
bb.0.if.then:
successors: %bb.1(0x40000000), %bb.3(0x40000000)
liveins: $edi, $xmm0
%1:fr32 = COPY killed $xmm0
%0:gr32 = COPY killed $edi
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
%2:gr32 = MOV32r0 implicit-def dead $eflags
%3:gr8 = COPY killed %2.sub_8bit
$edi = COPY killed %0
$al = COPY killed %3
CALL64r undef %4:gr64, csr_64, implicit $rsp, implicit $ssp, implicit killed $edi, implicit killed $al, implicit-def $rsp, implicit-def $ssp, implicit-def $eax
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
%5:gr32 = COPY killed $eax
CMP32ri8 killed %5, 1, implicit-def $eflags
JCC_1 %bb.3, 5, implicit killed $eflags
JMP_1 %bb.1
bb.1.sw.bb:
successors: %bb.2(0x30000000), %bb.3(0x50000000)
%7:gr32 = nofpexcept CVTTSS2SIrr killed %1, implicit $mxcsr, debug-instr-number 1
%8:gr8 = COPY killed %7.sub_8bit
DBG_INSTR_REF 2, 0, !6, !DIExpression(), debug-location !17
TEST8rr killed %8, %8, implicit-def $eflags
JCC_1 %bb.3, 5, implicit killed $eflags
JMP_1 %bb.2
bb.2.if.end:
successors: %bb.3(0x80000000)
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
%9:gr32 = MOV32r0 implicit-def dead $eflags
%10:gr8 = COPY killed %9.sub_8bit
$al = COPY killed %10
CALL64r undef %11:gr64, csr_64, implicit $rsp, implicit $ssp, implicit killed $al, implicit-def $rsp, implicit-def $ssp
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
bb.3.sw.epilog:
RET 0, undef $eax
...

View File

@@ -0,0 +1,82 @@
# RUN: llc %s -march=x86-64 -run-pass=livedebugvalues -experimental-debug-variable-locations -o - 2>&1 | FileCheck %s
#
# Test that memory operands of instructions are interpreted by LiveDebugValues:
# if an instruction reference is substituted to a memory operand, we should be
# able to emit a DBG_VALUE referring to its slot.
#
# In addition, further instructions that write to the same stack slot should
# be recognised as clobbering the value in that slot.
--- |
define i8 @test(i32 %bar) local_unnamed_addr !dbg !7 {
entry:
ret i8 0, !dbg !12
}
declare dso_local void @ext(i64)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5, !6}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!1 = !DIFile(filename: "foo.cpp", directory: ".")
!2 = !DIBasicType(name: "int", size: 8, encoding: DW_ATE_signed)
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 2}
!6 = !{i32 7, !"PIC Level", i32 2}
!7 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: !1, file: !1, line: 6, type: !8, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10)
!8 = !DISubroutineType(types: !9)
!9 = !{!2, !2}
!10 = !{!11}
!11 = !DILocalVariable(name: "baz", scope: !7, file: !1, line: 7, type: !2)
!12 = !DILocation(line: 10, scope: !7)
...
---
name: test
tracksRegLiveness: true
liveins:
- { reg: '$rdi', virtual-reg: '' }
debugValueSubstitutions:
- { srcinst: 2, srcop: 0, dstinst: 3, dstop: 1000000, subreg: 0 }
- { srcinst: 4, srcop: 0, dstinst: 5, dstop: 1000000, subreg: 0 }
stack:
- { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
body: |
bb.0:
liveins: $rdi, $rax
DBG_PHI $rax, 1
MOV64mr $rsp, 1, $noreg, 16, $noreg, $rdi :: (store 8 into %stack.0)
$rax = MOV64ri 0, debug-location !12
INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-instr-number 3, debug-location !DILocation(line: 0, scope: !7) :: (store (s32) into %stack.0)
DBG_INSTR_REF 2, 0, !11, !DIExpression(), debug-location !12
; CHECK: DBG_INSTR_REF 2, 0
; CHECK-NEXT: DBG_VALUE $rsp
;; Test that the old value (from the DBG_PHI) is not tracked anywhere. It
;; should not be considered as being on the stack any more.
DBG_INSTR_REF 1, 0, !11, !DIExpression(), debug-location !12
; CHECK: DBG_INSTR_REF 1, 0
; CHECK-NEXT: DBG_VALUE $noreg
INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-location !12 :: (store (s32) into %stack.0)
;; The above INC32m should be detected as clobbering the stack location,
;; even though it isn't debug labelled.
DBG_INSTR_REF 2, 0, !11, !DIExpression(), debug-location !12
; CHECK: DBG_INSTR_REF 2, 0
; CHECK-NEXT: DBG_VALUE $noreg
;; Store another debug-labelled value to the stack,
INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-instr-number 5, debug-location !DILocation(line: 0, scope: !7) :: (store (s32) into %stack.0)
;; Point the variable at that value.
DBG_INSTR_REF 4, 0, !11, !DIExpression(), debug-location !12
; CHECK: DBG_INSTR_REF 4, 0,
; CHECK-NEXT: DBG_VALUE $rsp
;; Overwrite the stack: LiveDebugValues should explicitly undef the stack
;; location with DBG_VALUE $noreg, as DbgEntityHistoryCalculator doesn't
;; look at the stack.
INC32m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-location !DILocation(line: 0, scope: !7) :: (store (s32) into %stack.0)
; CHECK: INC32m $rsp
; CHECK-NEXT: DBG_VALUE $noreg
$rax = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0)
RETQ $rax, debug-location !12
...