mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-02-01 05:01:59 +01:00
Consider regmasks when computing register-based DBG_VALUE live ranges
Now register parameters that aren't saved to the stack or CSRs are considered dead after the first call. Previously the debugger would show whatever was in the register. Fixes PR26589 Reviewers: aprantl Differential Revision: http://reviews.llvm.org/D17211 llvm-svn: 264429
This commit is contained in:
parent
8945842662
commit
bc7d490b15
@ -15,7 +15,9 @@
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Target/TargetLowering.h"
|
||||
#include "llvm/Target/TargetRegisterInfo.h"
|
||||
#include "llvm/Target/TargetSubtargetInfo.h"
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
using namespace llvm;
|
||||
@ -122,26 +124,6 @@ static void clobberRegisterUses(RegDescribedVarsMap &RegVars, unsigned RegNo,
|
||||
clobberRegisterUses(RegVars, I, HistMap, ClobberingInstr);
|
||||
}
|
||||
|
||||
// \brief Collect all registers clobbered by @MI and apply the functor
|
||||
// @Func to their RegNo.
|
||||
// @Func should be a functor with a void(unsigned) signature. We're
|
||||
// not using std::function here for performance reasons. It has a
|
||||
// small but measurable impact. By using a functor instead of a
|
||||
// std::set& here, we can avoid the overhead of constructing
|
||||
// temporaries in calculateDbgValueHistory, which has a significant
|
||||
// performance impact.
|
||||
template<typename Callable>
|
||||
static void applyToClobberedRegisters(const MachineInstr &MI,
|
||||
const TargetRegisterInfo *TRI,
|
||||
Callable Func) {
|
||||
for (const MachineOperand &MO : MI.operands()) {
|
||||
if (!MO.isReg() || !MO.isDef() || !MO.getReg())
|
||||
continue;
|
||||
for (MCRegAliasIterator AI(MO.getReg(), TRI, true); AI.isValid(); ++AI)
|
||||
Func(*AI);
|
||||
}
|
||||
}
|
||||
|
||||
// \brief Returns the first instruction in @MBB which corresponds to
|
||||
// the function epilogue, or nullptr if @MBB doesn't contain an epilogue.
|
||||
static const MachineInstr *getFirstEpilogueInst(const MachineBasicBlock &MBB) {
|
||||
@ -173,10 +155,23 @@ static void collectChangingRegs(const MachineFunction *MF,
|
||||
auto FirstEpilogueInst = getFirstEpilogueInst(MBB);
|
||||
|
||||
for (const auto &MI : MBB) {
|
||||
// Avoid looking at prologue or epilogue instructions.
|
||||
if (&MI == FirstEpilogueInst)
|
||||
break;
|
||||
if (!MI.getFlag(MachineInstr::FrameSetup))
|
||||
applyToClobberedRegisters(MI, TRI, [&](unsigned r) { Regs.set(r); });
|
||||
if (MI.getFlag(MachineInstr::FrameSetup))
|
||||
continue;
|
||||
|
||||
// Look for register defs and register masks. Register masks are
|
||||
// typically on calls and they clobber everything not in the mask.
|
||||
for (const MachineOperand &MO : MI.operands()) {
|
||||
if (MO.isReg() && MO.isDef() && MO.getReg()) {
|
||||
for (MCRegAliasIterator AI(MO.getReg(), TRI, true); AI.isValid();
|
||||
++AI)
|
||||
Regs.set(*AI);
|
||||
} else if (MO.isRegMask()) {
|
||||
Regs.setBitsNotInMask(MO.getRegMask());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,16 +182,35 @@ void llvm::calculateDbgValueHistory(const MachineFunction *MF,
|
||||
BitVector ChangingRegs(TRI->getNumRegs());
|
||||
collectChangingRegs(MF, TRI, ChangingRegs);
|
||||
|
||||
const TargetLowering *TLI = MF->getSubtarget().getTargetLowering();
|
||||
unsigned SP = TLI->getStackPointerRegisterToSaveRestore();
|
||||
RegDescribedVarsMap RegVars;
|
||||
for (const auto &MBB : *MF) {
|
||||
for (const auto &MI : MBB) {
|
||||
if (!MI.isDebugValue()) {
|
||||
// Not a DBG_VALUE instruction. It may clobber registers which describe
|
||||
// some variables.
|
||||
applyToClobberedRegisters(MI, TRI, [&](unsigned RegNo) {
|
||||
if (ChangingRegs.test(RegNo))
|
||||
clobberRegisterUses(RegVars, RegNo, Result, MI);
|
||||
});
|
||||
for (const MachineOperand &MO : MI.operands()) {
|
||||
if (MO.isReg() && MO.isDef() && MO.getReg()) {
|
||||
// If this is a register def operand, it may end a debug value
|
||||
// range.
|
||||
for (MCRegAliasIterator AI(MO.getReg(), TRI, true); AI.isValid();
|
||||
++AI)
|
||||
if (ChangingRegs.test(*AI))
|
||||
clobberRegisterUses(RegVars, *AI, Result, MI);
|
||||
} else if (MO.isRegMask()) {
|
||||
// If this is a register mask operand, clobber all debug values in
|
||||
// non-CSRs.
|
||||
for (int I = ChangingRegs.find_first(); I != -1;
|
||||
I = ChangingRegs.find_next(I)) {
|
||||
// Don't consider SP to be clobbered by register masks.
|
||||
if (unsigned(I) != SP && TRI->isPhysicalRegister(I) &&
|
||||
MO.clobbersPhysReg(I)) {
|
||||
clobberRegisterUses(RegVars, I, Result, MI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Target/TargetInstrInfo.h"
|
||||
#include "llvm/Target/TargetLowering.h"
|
||||
#include "llvm/Target/TargetRegisterInfo.h"
|
||||
#include "llvm/Target/TargetSubtargetInfo.h"
|
||||
#include <queue>
|
||||
@ -208,18 +209,33 @@ void LiveDebugValues::transferDebugValue(MachineInstr &MI,
|
||||
/// A definition of a register may mark the end of a range.
|
||||
void LiveDebugValues::transferRegisterDef(MachineInstr &MI,
|
||||
VarLocList &OpenRanges) {
|
||||
MachineFunction *MF = MI.getParent()->getParent();
|
||||
const TargetLowering *TLI = MF->getSubtarget().getTargetLowering();
|
||||
unsigned SP = TLI->getStackPointerRegisterToSaveRestore();
|
||||
for (const MachineOperand &MO : MI.operands()) {
|
||||
if (!(MO.isReg() && MO.isDef() && MO.getReg() &&
|
||||
TRI->isPhysicalRegister(MO.getReg())))
|
||||
continue;
|
||||
// Remove ranges of all aliased registers.
|
||||
for (MCRegAliasIterator RAI(MO.getReg(), TRI, true); RAI.isValid(); ++RAI)
|
||||
if (MO.isReg() && MO.isDef() && MO.getReg() &&
|
||||
TRI->isPhysicalRegister(MO.getReg())) {
|
||||
// Remove ranges of all aliased registers.
|
||||
for (MCRegAliasIterator RAI(MO.getReg(), TRI, true); RAI.isValid(); ++RAI)
|
||||
OpenRanges.erase(std::remove_if(OpenRanges.begin(), OpenRanges.end(),
|
||||
[&](const VarLoc &V) {
|
||||
return (*RAI ==
|
||||
isDescribedByReg(*V.MI));
|
||||
}),
|
||||
OpenRanges.end());
|
||||
} else if (MO.isRegMask()) {
|
||||
// Remove ranges of all clobbered registers. Register masks don't usually
|
||||
// list SP as preserved. While the debug info may be off for an
|
||||
// instruction or two around callee-cleanup calls, transferring the
|
||||
// DEBUG_VALUE across the call is still a better user experience.
|
||||
OpenRanges.erase(std::remove_if(OpenRanges.begin(), OpenRanges.end(),
|
||||
[&](const VarLoc &V) {
|
||||
return (*RAI ==
|
||||
isDescribedByReg(*V.MI));
|
||||
unsigned Reg = isDescribedByReg(*V.MI);
|
||||
return Reg && Reg != SP &&
|
||||
MO.clobbersPhysReg(Reg);
|
||||
}),
|
||||
OpenRanges.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,7 @@
|
||||
# DBG_VALUE for variable "n" is extended into BB#5 from its predecessors BB#3
|
||||
# and BB#4.
|
||||
# CHECK: bb.5.if.end.7:
|
||||
# CHECK: DBG_VALUE debug-use %rsi, debug-use _, !13, !20, debug-location !22
|
||||
# CHECK-NEXT: DBG_VALUE debug-use %ebx, debug-use _, !14, !20, debug-location !33
|
||||
# CHECK: DBG_VALUE debug-use %ebx, debug-use _, !14, !20, debug-location !33
|
||||
|
||||
|
||||
--- |
|
||||
|
@ -16,9 +16,9 @@
|
||||
; Test that we only emit register-indirect locations for the array array.
|
||||
; rdar://problem/14874886
|
||||
;
|
||||
; CHECK: ##DEBUG_VALUE: main:array <- [%R{{.*}}+0]
|
||||
; CHECK: ##DEBUG_VALUE: main:array <- [%R{{.*}}+0]
|
||||
; CHECK: ##DEBUG_VALUE: main:array <- [%R{{.*}}+0]
|
||||
; FIXME: If we described this location as RSP-relative instead of RDI-relative
|
||||
; the live range would be larger.
|
||||
; CHECK: ##DEBUG_VALUE: main:array <- [%RDI+0]
|
||||
; CHECK-NOT: ##DEBUG_VALUE: main:array <- %R{{.*}}
|
||||
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-macosx10.9.0"
|
||||
|
@ -14,7 +14,7 @@
|
||||
; CHECK: DBG_VALUE 23, 0, ![[X]],
|
||||
; CHECK: DBG_VALUE debug-use %rdi, debug-use _, ![[X]]
|
||||
; CHECK: bb.1.if.then:
|
||||
; CHECK: DBG_VALUE debug-use %rdi, debug-use _, ![[X]],
|
||||
; CHECK: DBG_VALUE 43, 0, ![[X]],
|
||||
; CHECK: bb.2.if.end:
|
||||
; CHECK-NOT: DBG_VALUE 23, 0, ![[X]],
|
||||
; CHECK: RETQ %eax
|
||||
|
117
test/DebugInfo/X86/dbg-value-regmask-clobber.ll
Normal file
117
test/DebugInfo/X86/dbg-value-regmask-clobber.ll
Normal file
@ -0,0 +1,117 @@
|
||||
; RUN: llc < %s | FileCheck %s --check-prefix=ASM
|
||||
; RUN: llc < %s -filetype=obj | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF
|
||||
|
||||
; Values in registers should be clobbered by calls, which use a regmask instead
|
||||
; of individual register def operands.
|
||||
|
||||
; ASM: main: # @main
|
||||
; ASM: #DEBUG_VALUE: main:argc <- %ECX
|
||||
; ASM: movl $1, x(%rip)
|
||||
; ASM: callq clobber
|
||||
; ASM-NEXT: [[argc_range_end:.Ltmp[0-9]+]]:
|
||||
; Previously LiveDebugValues would claim argc was still in ECX after the call.
|
||||
; ASM-NOT: #DEBUG_VALUE: main:argc
|
||||
|
||||
; argc is the first debug location.
|
||||
; ASM: .Ldebug_loc1:
|
||||
; ASM-NEXT: .quad .Lfunc_begin0-.Lfunc_begin0
|
||||
; ASM-NEXT: .quad [[argc_range_end]]-.Lfunc_begin0
|
||||
; ASM-NEXT: .short 3 # Loc expr size
|
||||
; ASM-NEXT: .byte 82 # super-register DW_OP_reg2
|
||||
; ASM-NEXT: .byte 147 # DW_OP_piece
|
||||
; ASM-NEXT: .byte 4 # 4
|
||||
|
||||
; argc is the first formal parameter.
|
||||
; DWARF: .debug_info contents:
|
||||
; DWARF: DW_TAG_formal_parameter
|
||||
; DWARF-NEXT: DW_AT_location [DW_FORM_sec_offset] ([[argc_loc_offset:0x.*]])
|
||||
; DWARF-NEXT: DW_AT_name [DW_FORM_strp] {{.*}} "argc"
|
||||
|
||||
; DWARF: .debug_loc contents:
|
||||
; DWARF: [[argc_loc_offset]]: Beginning address offset: 0x0000000000000000
|
||||
; DWARF-NEXT: Ending address offset: 0x0000000000000013
|
||||
; DWARF-NEXT: Location description: 52 93 04
|
||||
|
||||
; ModuleID = 't.cpp'
|
||||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc18.0.0"
|
||||
|
||||
@x = common global i32 0, align 4
|
||||
|
||||
; Function Attrs: nounwind uwtable
|
||||
define i32 @main(i32 %argc, i8** nocapture readnone %argv) #0 !dbg !4 {
|
||||
entry:
|
||||
tail call void @llvm.dbg.value(metadata i8** %argv, i64 0, metadata !12, metadata !21), !dbg !22
|
||||
tail call void @llvm.dbg.value(metadata i32 %argc, i64 0, metadata !13, metadata !21), !dbg !23
|
||||
store volatile i32 1, i32* @x, align 4, !dbg !24, !tbaa !25
|
||||
tail call void @clobber() #3, !dbg !29
|
||||
store volatile i32 2, i32* @x, align 4, !dbg !30, !tbaa !25
|
||||
%0 = load volatile i32, i32* @x, align 4, !dbg !31, !tbaa !25
|
||||
%tobool = icmp eq i32 %0, 0, !dbg !31
|
||||
br i1 %tobool, label %if.else, label %if.then, !dbg !33
|
||||
|
||||
if.then: ; preds = %entry
|
||||
store volatile i32 3, i32* @x, align 4, !dbg !34, !tbaa !25
|
||||
br label %if.end, !dbg !36
|
||||
|
||||
if.else: ; preds = %entry
|
||||
store volatile i32 4, i32* @x, align 4, !dbg !37, !tbaa !25
|
||||
br label %if.end
|
||||
|
||||
if.end: ; preds = %if.else, %if.then
|
||||
ret i32 0, !dbg !39
|
||||
}
|
||||
|
||||
declare void @clobber()
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #2
|
||||
|
||||
attributes #0 = { nounwind uwtable }
|
||||
attributes #2 = { nounwind readnone }
|
||||
attributes #3 = { nounwind }
|
||||
|
||||
!llvm.dbg.cu = !{!0}
|
||||
!llvm.module.flags = !{!17, !18, !19}
|
||||
!llvm.ident = !{!20}
|
||||
|
||||
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.9.0 (trunk 260617) (llvm/trunk 260619)", isOptimized: true, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3, globals: !14)
|
||||
!1 = !DIFile(filename: "t.cpp", directory: "D:\5Csrc\5Cllvm\5Cbuild")
|
||||
!2 = !{}
|
||||
!3 = !{!4}
|
||||
!4 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 4, type: !5, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: true, variables: !11)
|
||||
!5 = !DISubroutineType(types: !6)
|
||||
!6 = !{!7, !7, !8}
|
||||
!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
|
||||
!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64, align: 64)
|
||||
!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64, align: 64)
|
||||
!10 = !DIBasicType(name: "char", size: 8, align: 8, encoding: DW_ATE_signed_char)
|
||||
!11 = !{!12, !13}
|
||||
!12 = !DILocalVariable(name: "argv", arg: 2, scope: !4, file: !1, line: 4, type: !8)
|
||||
!13 = !DILocalVariable(name: "argc", arg: 1, scope: !4, file: !1, line: 4, type: !7)
|
||||
!14 = !{!15}
|
||||
!15 = !DIGlobalVariable(name: "x", scope: !0, file: !1, line: 1, type: !16, isLocal: false, isDefinition: true, variable: i32* @x)
|
||||
!16 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7)
|
||||
!17 = !{i32 2, !"Dwarf Version", i32 4}
|
||||
!18 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
!19 = !{i32 1, !"PIC Level", i32 2}
|
||||
!20 = !{!"clang version 3.9.0 (trunk 260617) (llvm/trunk 260619)"}
|
||||
!21 = !DIExpression()
|
||||
!22 = !DILocation(line: 4, column: 27, scope: !4)
|
||||
!23 = !DILocation(line: 4, column: 14, scope: !4)
|
||||
!24 = !DILocation(line: 5, column: 5, scope: !4)
|
||||
!25 = !{!26, !26, i64 0}
|
||||
!26 = !{!"int", !27, i64 0}
|
||||
!27 = !{!"omnipotent char", !28, i64 0}
|
||||
!28 = !{!"Simple C/C++ TBAA"}
|
||||
!29 = !DILocation(line: 6, column: 3, scope: !4)
|
||||
!30 = !DILocation(line: 7, column: 5, scope: !4)
|
||||
!31 = !DILocation(line: 8, column: 7, scope: !32)
|
||||
!32 = distinct !DILexicalBlock(scope: !4, file: !1, line: 8, column: 7)
|
||||
!33 = !DILocation(line: 8, column: 7, scope: !4)
|
||||
!34 = !DILocation(line: 9, column: 7, scope: !35)
|
||||
!35 = distinct !DILexicalBlock(scope: !32, file: !1, line: 8, column: 10)
|
||||
!36 = !DILocation(line: 10, column: 3, scope: !35)
|
||||
!37 = !DILocation(line: 11, column: 7, scope: !38)
|
||||
!38 = distinct !DILexicalBlock(scope: !32, file: !1, line: 10, column: 10)
|
||||
!39 = !DILocation(line: 13, column: 1, scope: !4)
|
@ -15,8 +15,12 @@
|
||||
; CHECK: #DEBUG_VALUE: bar:y <- [%RDI+0]
|
||||
; CHECK: movq %rdi, [[OFFSET:[0-9]+]](%rsp)
|
||||
; CHECK-NEXT: [[START_LABEL:.Ltmp[0-9]+]]
|
||||
; CHECK-NEXT: #DEBUG_VALUE: bar:y <- [complex expression]
|
||||
; This location should be valid until the end of the function.
|
||||
|
||||
; CHECK: movq %rbp, %rsp
|
||||
; CHECK-NEXT: [[END_LABEL:.Ltmp[0-9]+]]:
|
||||
|
||||
; CHECK: .Ldebug_loc{{[0-9]+}}:
|
||||
; We expect two location ranges for the variable.
|
||||
|
||||
@ -27,7 +31,7 @@
|
||||
|
||||
; Then it's addressed via %rsp:
|
||||
; CHECK: .quad [[START_LABEL]]-.Lfunc_begin0
|
||||
; CHECK-NEXT: .Lfunc_end0-.Lfunc_begin0
|
||||
; CHECK-NEXT: .quad [[END_LABEL]]-.Lfunc_begin0
|
||||
; CHECK: DW_OP_breg7
|
||||
; CHECK-NEXT: [[OFFSET]]
|
||||
; CHECK: DW_OP_deref
|
||||
|
@ -31,7 +31,9 @@
|
||||
; and BB#4.
|
||||
; CHECK: .LBB0_5:
|
||||
; CHECK-NEXT: #DEBUG_VALUE: main:n <- %EBX
|
||||
; CHECK-NEXT: #DEBUG_VALUE: main:argv <- %RSI
|
||||
; Other register values have been clobbered.
|
||||
; CHECK-NOT: #DEBUG_VALUE:
|
||||
; CHECK: movl %ecx, m(%rip)
|
||||
|
||||
; ModuleID = 'LiveDebugValues.c'
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
|
@ -14,7 +14,7 @@
|
||||
; Location of "range" string is spilled from %rdx to stack and is
|
||||
; addressed via %rbp.
|
||||
; CHECK: movq %rdx, {{[-0-9]+}}(%rbp)
|
||||
; CHECK-NEXT: [[START_LABEL:.Ltmp[0-9]+]]
|
||||
; CHECK-NEXT: [[START_LABEL:.Ltmp[0-9]+]]:
|
||||
; This location should be valid until the end of the function.
|
||||
|
||||
; Verify that we have proper range in debug_loc section:
|
||||
|
Loading…
x
Reference in New Issue
Block a user