1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

[LiveDebugValues] Tracking copying value between registers

During the execution of long functions or functions that have a lot of
inlined code it could come to the situation where tracked value could be
transferred from one register to another. The transfer is recognized only if
destination register is a callee saved register and if source register is
killed. We do not salvage caller-saved registers since there is a great
chance that killed register would outlive it.

Patch by Nikola Prica.

Differential Revision: https://reviews.llvm.org/D44016

llvm-svn: 336978
This commit is contained in:
Petar Jovanovic 2018-07-13 08:24:26 +00:00
parent 6d90906e48
commit 20374872ff
4 changed files with 702 additions and 49 deletions

View File

@ -40,6 +40,7 @@
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/CodeGen/RegisterScavenging.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
@ -82,6 +83,7 @@ private:
const TargetRegisterInfo *TRI;
const TargetInstrInfo *TII;
const TargetFrameLowering *TFI;
BitVector CalleeSavedRegs;
LexicalScopes LS;
/// Keeps track of lexical scopes associated with a user value's source
@ -179,11 +181,11 @@ private:
using VarLocMap = UniqueVector<VarLoc>;
using VarLocSet = SparseBitVector<>;
using VarLocInMBB = SmallDenseMap<const MachineBasicBlock *, VarLocSet>;
struct SpillDebugPair {
MachineInstr *SpillInst;
struct TransferDebugPair {
MachineInstr *TransferInst;
MachineInstr *DebugInst;
};
using SpillMap = SmallVector<SpillDebugPair, 4>;
using TransferMap = SmallVector<TransferDebugPair, 4>;
/// This holds the working set of currently open ranges. For fast
/// access, this is done both as a set of VarLocIDs, and a map of
@ -236,18 +238,23 @@ private:
bool isSpillInstruction(const MachineInstr &MI, MachineFunction *MF,
unsigned &Reg);
int extractSpillBaseRegAndOffset(const MachineInstr &MI, unsigned &Reg);
void insertTransferDebugPair(MachineInstr &MI, OpenRangesSet &OpenRanges,
TransferMap &Transfers, VarLocMap &VarLocIDs,
unsigned OldVarID, unsigned NewReg = 0);
void transferDebugValue(const MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs);
void transferSpillInst(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs, SpillMap &Spills);
VarLocMap &VarLocIDs, TransferMap &Transfers);
void transferRegisterCopy(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs, TransferMap &Transfers);
void transferRegisterDef(MachineInstr &MI, OpenRangesSet &OpenRanges,
const VarLocMap &VarLocIDs);
bool transferTerminatorInst(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocInMBB &OutLocs, const VarLocMap &VarLocIDs);
bool transfer(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocInMBB &OutLocs, VarLocMap &VarLocIDs, SpillMap &Spills,
bool transferSpills);
bool process(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
TransferMap &Transfers, bool transferChanges);
bool join(MachineBasicBlock &MBB, VarLocInMBB &OutLocs, VarLocInMBB &InLocs,
const VarLocMap &VarLocIDs,
@ -370,6 +377,54 @@ void LiveDebugValues::transferDebugValue(const MachineInstr &MI,
}
}
/// Create new TransferDebugPair and insert it in \p Transfers. The VarLoc
/// with \p OldVarID should be deleted form \p OpenRanges and replaced with
/// new VarLoc. If \p NewReg is different than default zero value then the
/// new location will be register location created by the copy like instruction,
/// otherwise it is variable's location on the stack.
void LiveDebugValues::insertTransferDebugPair(
MachineInstr &MI, OpenRangesSet &OpenRanges, TransferMap &Transfers,
VarLocMap &VarLocIDs, unsigned OldVarID, unsigned NewReg) {
const MachineInstr *DMI = &VarLocIDs[OldVarID].MI;
MachineFunction *MF = MI.getParent()->getParent();
MachineInstr *NewDMI;
if (NewReg) {
// Create a DBG_VALUE instruction to describe the Var in its new
// register location.
NewDMI = BuildMI(*MF, DMI->getDebugLoc(), DMI->getDesc(),
DMI->isIndirectDebugValue(), NewReg,
DMI->getDebugVariable(), DMI->getDebugExpression());
if (DMI->isIndirectDebugValue())
NewDMI->getOperand(1).setImm(DMI->getOperand(1).getImm());
LLVM_DEBUG(dbgs() << "Creating DBG_VALUE inst for register copy: ";
NewDMI->print(dbgs(), false, false, false, TII));
} else {
// Create a DBG_VALUE instruction to describe the Var in its spilled
// location.
unsigned SpillBase;
int SpillOffset = extractSpillBaseRegAndOffset(MI, SpillBase);
auto *SpillExpr = DIExpression::prepend(DMI->getDebugExpression(),
DIExpression::NoDeref, SpillOffset);
NewDMI = BuildMI(*MF, DMI->getDebugLoc(), DMI->getDesc(), true, SpillBase,
DMI->getDebugVariable(), SpillExpr);
LLVM_DEBUG(dbgs() << "Creating DBG_VALUE inst for spill: ";
NewDMI->print(dbgs(), false, false, false, TII));
}
// The newly created DBG_VALUE instruction NewDMI must be inserted after
// MI. Keep track of the pairing.
TransferDebugPair MIP = {&MI, NewDMI};
Transfers.push_back(MIP);
// End all previous ranges of Var.
OpenRanges.erase(VarLocIDs[OldVarID].Var);
// Add the VarLoc to OpenRanges.
VarLoc VL(*NewDMI, LS);
unsigned LocID = VarLocIDs.insert(VL);
OpenRanges.insert(LocID, VL.Var);
}
/// A definition of a register may mark the end of a range.
void LiveDebugValues::transferRegisterDef(MachineInstr &MI,
OpenRangesSet &OpenRanges,
@ -464,14 +519,14 @@ bool LiveDebugValues::isSpillInstruction(const MachineInstr &MI,
/// A spilled register may indicate that we have to end the current range of
/// a variable and create a new one for the spill location.
/// We don't want to insert any instructions in transfer(), so we just create
/// the DBG_VALUE witout inserting it and keep track of it in @Spills.
/// We don't want to insert any instructions in process(), so we just create
/// the DBG_VALUE without inserting it and keep track of it in \p Transfers.
/// It will be inserted into the BB when we're done iterating over the
/// instructions.
void LiveDebugValues::transferSpillInst(MachineInstr &MI,
OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs,
SpillMap &Spills) {
TransferMap &Transfers) {
unsigned Reg;
MachineFunction *MF = MI.getMF();
if (!isSpillInstruction(MI, MF, Reg))
@ -482,33 +537,47 @@ void LiveDebugValues::transferSpillInst(MachineInstr &MI,
if (VarLocIDs[ID].isDescribedByReg() == Reg) {
LLVM_DEBUG(dbgs() << "Spilling Register " << printReg(Reg, TRI) << '('
<< VarLocIDs[ID].Var.getVar()->getName() << ")\n");
insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, ID);
return;
}
}
}
// Create a DBG_VALUE instruction to describe the Var in its spilled
// location, but don't insert it yet to avoid invalidating the
// iterator in our caller.
unsigned SpillBase;
int SpillOffset = extractSpillBaseRegAndOffset(MI, SpillBase);
const MachineInstr *DMI = &VarLocIDs[ID].MI;
auto *SpillExpr = DIExpression::prepend(
DMI->getDebugExpression(), DIExpression::NoDeref, SpillOffset);
MachineInstr *SpDMI =
BuildMI(*MF, DMI->getDebugLoc(), DMI->getDesc(), true, SpillBase,
DMI->getDebugVariable(), SpillExpr);
LLVM_DEBUG(dbgs() << "Creating DBG_VALUE inst for spill: ";
SpDMI->print(dbgs(), false, TII));
/// If \p MI is a register copy instruction, that copies a previously tracked
/// value from one register to another register that is callee saved, we
/// create new DBG_VALUE instruction described with copy destination register.
void LiveDebugValues::transferRegisterCopy(MachineInstr &MI,
OpenRangesSet &OpenRanges,
VarLocMap &VarLocIDs,
TransferMap &Transfers) {
const MachineOperand *SrcRegOp, *DestRegOp;
// The newly created DBG_VALUE instruction SpDMI must be inserted after
// MI. Keep track of the pairing.
SpillDebugPair MIP = {&MI, SpDMI};
Spills.push_back(MIP);
if (!TII->isCopyInstr(MI, SrcRegOp, DestRegOp) || !SrcRegOp->isKill() ||
!DestRegOp->isDef())
return;
// End all previous ranges of Var.
OpenRanges.erase(VarLocIDs[ID].Var);
auto isCalleSavedReg = [&](unsigned Reg) {
for (MCRegAliasIterator RAI(Reg, TRI, true); RAI.isValid(); ++RAI)
if (CalleeSavedRegs.test(*RAI))
return true;
return false;
};
// Add the VarLoc to OpenRanges.
VarLoc VL(*SpDMI, LS);
unsigned SpillLocID = VarLocIDs.insert(VL);
OpenRanges.insert(SpillLocID, VL.Var);
unsigned SrcReg = SrcRegOp->getReg();
unsigned DestReg = DestRegOp->getReg();
// We want to recognize instructions where destination register is callee
// saved register. If register that could be clobbered by the call is
// included, there would be a great chance that it is going to be clobbered
// soon. It is more likely that previous register location, which is callee
// saved, is going to stay unclobbered longer, even if it is killed.
if (!isCalleSavedReg(DestReg))
return;
for (unsigned ID : OpenRanges.getVarLocs()) {
if (VarLocIDs[ID].isDescribedByReg() == SrcReg) {
insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, ID,
DestReg);
return;
}
}
@ -540,14 +609,16 @@ bool LiveDebugValues::transferTerminatorInst(MachineInstr &MI,
}
/// This routine creates OpenRanges and OutLocs.
bool LiveDebugValues::transfer(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
SpillMap &Spills, bool transferSpills) {
bool LiveDebugValues::process(MachineInstr &MI, OpenRangesSet &OpenRanges,
VarLocInMBB &OutLocs, VarLocMap &VarLocIDs,
TransferMap &Transfers, bool transferChanges) {
bool Changed = false;
transferDebugValue(MI, OpenRanges, VarLocIDs);
transferRegisterDef(MI, OpenRanges, VarLocIDs);
if (transferSpills)
transferSpillInst(MI, OpenRanges, VarLocIDs, Spills);
if (transferChanges) {
transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers);
transferSpillInst(MI, OpenRanges, VarLocIDs, Transfers);
}
Changed = transferTerminatorInst(MI, OpenRanges, OutLocs, VarLocIDs);
return Changed;
}
@ -609,7 +680,7 @@ bool LiveDebugValues::join(MachineBasicBlock &MBB, VarLocInMBB &OutLocs,
for (auto ID : Diff) {
// This VarLoc is not found in InLocs i.e. it is not yet inserted. So, a
// new range is started for the var from the mbb's beginning by inserting
// a new DBG_VALUE. transfer() will end this range however appropriate.
// a new DBG_VALUE. process() will end this range however appropriate.
const VarLoc &DiffIt = VarLocIDs[ID];
const MachineInstr *DMI = &DiffIt.MI;
MachineInstr *MI =
@ -639,7 +710,7 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
OpenRangesSet OpenRanges; // Ranges that are open until end of bb.
VarLocInMBB OutLocs; // Ranges that exist beyond bb.
VarLocInMBB InLocs; // Ranges that are incoming after joining.
SpillMap Spills; // DBG_VALUEs associated with spills.
TransferMap Transfers; // DBG_VALUEs associated with spills.
DenseMap<unsigned int, MachineBasicBlock *> OrderToBB;
DenseMap<MachineBasicBlock *, unsigned int> BBToOrder;
@ -650,6 +721,8 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
std::greater<unsigned int>>
Pending;
enum : bool { dontTransferChanges = false, transferChanges = true };
// Initialize every mbb with OutLocs.
// We are not looking at any spill instructions during the initial pass
// over the BBs. The LiveDebugVariables pass has already created DBG_VALUE
@ -657,8 +730,8 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
// within the BB in which the spill occurs.
for (auto &MBB : MF)
for (auto &MI : MBB)
transfer(MI, OpenRanges, OutLocs, VarLocIDs, Spills,
/*transferSpills=*/false);
process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers,
dontTransferChanges);
LLVM_DEBUG(printVarLocInMBB(MF, OutLocs, VarLocIDs,
"OutLocs after initialization", dbgs()));
@ -672,7 +745,7 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
++RPONumber;
}
// This is a standard "union of predecessor outs" dataflow problem.
// To solve it, we perform join() and transfer() using the two worklist method
// To solve it, we perform join() and process() using the two worklist method
// until the ranges converge.
// Ranges have converged when both worklists are empty.
SmallPtrSet<const MachineBasicBlock *, 16> Visited;
@ -694,14 +767,14 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
// examine spill instructions to see whether they spill registers that
// correspond to user variables.
for (auto &MI : *MBB)
OLChanged |= transfer(MI, OpenRanges, OutLocs, VarLocIDs, Spills,
/*transferSpills=*/true);
OLChanged |= process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers,
transferChanges);
// Add any DBG_VALUE instructions necessitated by spills.
for (auto &SP : Spills)
MBB->insertAfter(MachineBasicBlock::iterator(*SP.SpillInst),
SP.DebugInst);
Spills.clear();
for (auto &TR : Transfers)
MBB->insertAfter(MachineBasicBlock::iterator(*TR.TransferInst),
TR.DebugInst);
Transfers.clear();
LLVM_DEBUG(printVarLocInMBB(MF, OutLocs, VarLocIDs,
"OutLocs after propagating", dbgs()));
@ -741,6 +814,8 @@ bool LiveDebugValues::runOnMachineFunction(MachineFunction &MF) {
TRI = MF.getSubtarget().getRegisterInfo();
TII = MF.getSubtarget().getInstrInfo();
TFI = MF.getSubtarget().getFrameLowering();
TFI->determineCalleeSaves(MF, CalleeSavedRegs,
make_unique<RegScavenger>().get());
LS.initialize(MF);
bool Changed = ExtendRanges(MF);

View File

@ -0,0 +1,147 @@
# RUN: llc -run-pass=livedebugvalues %s -o - | FileCheck %s
#
# This test tests tracking variables value transferring from one register to another.
# This example is altered additionally in order to test transferring from one float register
# to another. The altered instructions are labeled below.
#
# CHECK: ![[ARG1:.*]] = !DILocalVariable(name: "arg1"
# CHECK: DBG_VALUE debug-use $r4, debug-use $noreg, ![[ARG1]], !DIExpression(), debug-location
# CHECK: $r5 = MOVr killed $r4, 14, $noreg, $noreg, debug-location
# CHECK-NEXT: DBG_VALUE debug-use $r5, debug-use $noreg, ![[ARG1]], !DIExpression(), debug-location
--- |
; ModuleID = 'live-debug-values-reg-copy.ll'
source_filename = "live-debug-values-reg-copy.c"
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv4t--"
define dso_local arm_aapcscc i32 @foo(i32 %arg1) local_unnamed_addr !dbg !8 {
entry:
call void @llvm.dbg.value(metadata i32 %arg1, metadata !13, metadata !DIExpression()), !dbg !16
%cmp = icmp sgt i32 %arg1, 10, !dbg !16
br i1 %cmp, label %if.end, label %if.else, !dbg !16
if.else: ; preds = %entry
%add5 = add nsw i32 %arg1, 10, !dbg !16
call void @llvm.dbg.value(metadata i32 %add5, metadata !13, metadata !DIExpression()), !dbg !16
%call6 = tail call arm_aapcscc i32 @externFunc2(i32 %add5), !dbg !16
%call8 = tail call arm_aapcscc i32 @externFunc(i32 %add5), !dbg !16
ret i32 %call6, !dbg !16
if.end: ; preds = %entry
%call = tail call arm_aapcscc i32 @externFunc(i32 %arg1), !dbg !16
ret i32 1, !dbg !16
}
declare dso_local arm_aapcscc i32 @externFunc(i32) local_unnamed_addr
declare dso_local arm_aapcscc i32 @externFunc2(i32) local_unnamed_addr
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
; Function Attrs: nounwind
declare void @llvm.stackprotector(i8*, i8**) #1
attributes #0 = { nounwind readnone speculatable }
attributes #1 = { nounwind }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5, !6}
!llvm.ident = !{!7}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "live-debug-values-reg-copy.c", directory: "/")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{i32 1, !"min_enum_size", i32 4}
!7 = !{!"clang version 7.0.0"}
!8 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 4, type: !9, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !12)
!9 = !DISubroutineType(types: !10)
!10 = !{!11, !11}
!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!12 = !{!13}
!13 = !DILocalVariable(name: "arg1", arg: 1, scope: !8, file: !1, line: 4, type: !11)
!16 = !DILocation(line: 4, column: 13, scope: !8)
...
---
name: foo
alignment: 2
exposesReturnsTwice: false
legalized: false
regBankSelected: false
selected: false
tracksRegLiveness: false
registers:
liveins:
- { reg: '$r0', virtual-reg: '' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 16
offsetAdjustment: 0
maxAlignment: 4
adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
savePoint: ''
restorePoint: ''
fixedStack:
stack:
- { id: 0, name: '', type: spill-slot, offset: -4, size: 4, alignment: 4,
stack-id: 0, callee-saved-register: '$lr', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 1, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4,
stack-id: 0, callee-saved-register: '$r11', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 2, name: '', type: spill-slot, offset: -12, size: 4, alignment: 4,
stack-id: 0, callee-saved-register: '$r5', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 3, name: '', type: spill-slot, offset: -16, size: 4, alignment: 4,
stack-id: 0, callee-saved-register: '$r4', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
constants:
body: |
bb.0.entry:
successors: %bb.1(0x40000000), %bb.2(0x40000000)
$sp = frame-setup STMDB_UPD $sp, 14, $noreg, killed $r4, killed $r5, killed $r11, killed $lr
frame-setup CFI_INSTRUCTION def_cfa_offset 16
frame-setup CFI_INSTRUCTION offset $lr, -4
frame-setup CFI_INSTRUCTION offset $r11, -8
frame-setup CFI_INSTRUCTION offset $r5, -12
frame-setup CFI_INSTRUCTION offset $r4, -16
DBG_VALUE debug-use $r0, debug-use $noreg, !13, !DIExpression(), debug-location !16
DBG_VALUE debug-use $r0, debug-use $noreg, !13, !DIExpression(), debug-location !16
CMPri renamable $r0, 10, 14, $noreg, implicit-def $cpsr, debug-location !16
Bcc %bb.2, 13, killed $cpsr, debug-location !16
bb.1.if.end:
BL @externFunc, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $r0, implicit-def $sp, implicit-def dead $r0, debug-location !16
$r0 = MOVi 1, 14, $noreg, $noreg, debug-location !16
$sp = LDMIA_UPD $sp, 14, $noreg, def $r4, def $r5, def $r11, def $lr, debug-location !16
BX_RET 14, $noreg, implicit killed $r0, debug-location !16
bb.2.if.else:
renamable $r4 = ADDri killed renamable $r0, 10, 14, $noreg, $noreg, debug-location !16
DBG_VALUE debug-use $r4, debug-use $noreg, !13, !DIExpression(), debug-location !16
$r0 = MOVr $r4, 14, $noreg, $noreg, debug-location !16
BL @externFunc2, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $r0, implicit-def $sp, implicit-def $r0, debug-location !16
$r5 = MOVr killed $r0, 14, $noreg, $noreg, debug-location !16
$r0 = MOVr $r4, 14, $noreg, $noreg, debug-location !16
BL @externFunc, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $r0, implicit-def $sp, implicit-def dead $r0, debug-location !16
$r0 = MOVr killed $r5, 14, $noreg, $noreg, debug-location !16
; Instruction below is added in order to test moving variable's value from one register to another.
$r5 = MOVr killed $r4, 14, $noreg, $noreg, debug-location !16
$sp = LDMIA_UPD $sp, 14, $noreg, def $r4, def $r5, def $r11, def $lr, debug-location !16
BX_RET 14, $noreg, implicit killed $r0, debug-location !16
...

View File

@ -0,0 +1,241 @@
# RUN: llc -run-pass=livedebugvalues %s -o - | FileCheck %s
#
# This test tests tracking variables value transferring from one register to another.
# This example is altered additionally in order to test transferring from one float register
# to another. The altered instructions are labeled below.
#
# CHECK: ![[ARG1:.*]] = !DILocalVariable(name: "arg1"
# CHECK: ![[ARG2:.*]] = !DILocalVariable(name: "arg2"
# CHECK: DBG_VALUE debug-use $s0_64, debug-use $noreg, ![[ARG2]], !DIExpression(), debug-location
# CHECK: $s1_64 = OR64 killed $s0_64, $zero_64, debug-location
# CHECK-NEXT: DBG_VALUE debug-use $s1_64, debug-use $noreg, ![[ARG2]], !DIExpression(), debug-location
# CHECK: DBG_VALUE debug-use $f24, debug-use $noreg, ![[ARG1]], !DIExpression(), debug-location
# CHECK: $f26 = FMOV_S killed $f24, debug-location
# CHECK-NEXT: DBG_VALUE debug-use $f26, debug-use $noreg, ![[ARG1]], !DIExpression(), debug-location
--- |
; ModuleID = 'live-debug-values-reg-copy.ll'
source_filename = "live-debug-values-reg-copy.c"
target datalayout = "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128"
target triple = "mips64-octeon-linux"
define float @foo(float %arg1, i32 signext %arg2) local_unnamed_addr !dbg !8 {
entry:
call void @llvm.dbg.value(metadata float %arg1, metadata !14, metadata !DIExpression()), !dbg !19
call void @llvm.dbg.value(metadata i32 %arg2, metadata !15, metadata !DIExpression()), !dbg !19
%conv = fpext float %arg1 to double, !dbg !19
%cmp = fcmp ogt double %conv, 1.012310e+01, !dbg !19
br i1 %cmp, label %if.then, label %if.else, !dbg !19
if.then: ; preds = %entry
%call = tail call float @externFunc(float %arg1), !dbg !19
%call5 = tail call i32 @externFunc3(i32 signext %arg2), !dbg !19
%conv6 = sitofp i32 %call5 to float, !dbg !19
%add7 = fadd float %conv6, 0x3FF522D0E0000000, !dbg !19
br label %if.end, !dbg !19
if.else: ; preds = %entry
%add8 = fadd float %arg1, 1.000000e+01, !dbg !19
call void @llvm.dbg.value(metadata float %add8, metadata !14, metadata !DIExpression()), !dbg !19
%call9 = tail call float @externFunc2(float %add8), !dbg !19
%call10 = tail call i32 @externFunc4(i32 signext %arg2), !dbg !19
%conv11 = sitofp i32 %call10 to float, !dbg !19
%add12 = fadd float %call9, %conv11, !dbg !19
%call14 = tail call float @externFunc(float %add8), !dbg !19
br label %if.end
if.end: ; preds = %if.else, %if.then
%local.0 = phi float [ %add7, %if.then ], [ %add12, %if.else ]
ret float %local.0, !dbg !19
}
declare float @externFunc(float) local_unnamed_addr
declare i32 @externFunc3(i32 signext) local_unnamed_addr
declare float @externFunc2(float) local_unnamed_addr
declare i32 @externFunc4(i32 signext) local_unnamed_addr
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
; Function Attrs: nounwind
declare void @llvm.stackprotector(i8*, i8**) #1
attributes #0 = { nounwind readnone speculatable }
attributes #1 = { nounwind }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5, !6}
!llvm.ident = !{!7}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "live-debug-values-reg-copy.c", directory: "/")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{i32 7, !"PIC Level", i32 2}
!7 = !{!"clang version 7.0.0 "}
!8 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 6, type: !9, isLocal: false, isDefinition: true, scopeLine: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !13)
!9 = !DISubroutineType(types: !10)
!10 = !{!11, !11, !12}
!11 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float)
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !{!14, !15}
!14 = !DILocalVariable(name: "arg1", arg: 1, scope: !8, file: !1, line: 6, type: !11)
!15 = !DILocalVariable(name: "arg2", arg: 2, scope: !8, file: !1, line: 6, type: !12)
!19 = !DILocation(line: 6, column: 17, scope: !8)
...
---
name: foo
alignment: 3
exposesReturnsTwice: false
legalized: false
regBankSelected: false
selected: false
tracksRegLiveness: false
registers:
liveins:
- { reg: '$f12', virtual-reg: '' }
- { reg: '$a1_64', virtual-reg: '' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 32
offsetAdjustment: 0
maxAlignment: 8
adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
savePoint: ''
restorePoint: ''
fixedStack:
stack:
- { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8,
stack-id: 0, callee-saved-register: '$d25_64', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
stack-id: 0, callee-saved-register: '$d24_64', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 2, name: '', type: spill-slot, offset: -24, size: 8, alignment: 8,
stack-id: 0, callee-saved-register: '$ra_64', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 3, name: '', type: spill-slot, offset: -32, size: 8, alignment: 8,
stack-id: 0, callee-saved-register: '$s0_64', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
constants:
- id: 0
value: 'double 1.012310e+01'
alignment: 8
isTargetSpecific: false
- id: 1
value: 'float 1.000000e+01'
alignment: 4
isTargetSpecific: false
- id: 2
value: float 0x3FF522D0E0000000
alignment: 4
isTargetSpecific: false
body: |
bb.0.entry:
successors: %bb.1(0x40000000), %bb.2(0x40000000)
$sp_64 = DADDiu $sp_64, -32
CFI_INSTRUCTION def_cfa_offset 32
SDC164 killed $d25_64, $sp_64, 24 :: (store 8 into %stack.0)
SDC164 killed $d24_64, $sp_64, 16 :: (store 8 into %stack.1)
SD killed $ra_64, $sp_64, 8 :: (store 8 into %stack.2)
SD killed $s0_64, $sp_64, 0 :: (store 8 into %stack.3)
CFI_INSTRUCTION offset $d26_64, -8
CFI_INSTRUCTION offset $d25_64, -4
CFI_INSTRUCTION offset $d25_64, -16
CFI_INSTRUCTION offset $d24_64, -12
CFI_INSTRUCTION offset $ra_64, -24
CFI_INSTRUCTION offset $s0_64, -32
DBG_VALUE debug-use $f12, debug-use $noreg, !14, !DIExpression(), debug-location !19
DBG_VALUE debug-use $a1_64, debug-use $noreg, !15, !DIExpression(), debug-location !19
DBG_VALUE debug-use $s0, debug-use $noreg, !15, !DIExpression(), debug-location !19
DBG_VALUE debug-use $s0_64, debug-use $noreg, !15, !DIExpression(), debug-location !19
DBG_VALUE debug-use $f12, debug-use $noreg, !14, !DIExpression(), debug-location !19
renamable $d0_64 = CVT_D64_S renamable $f12, debug-location !19
renamable $at_64 = LUi64 target-flags(mips-highest) %const.0
renamable $at_64 = DADDiu killed renamable $at_64, target-flags(mips-higher) %const.0
renamable $at_64 = DSLL killed renamable $at_64, 16
renamable $at_64 = DADDiu killed renamable $at_64, target-flags(mips-abs-hi) %const.0
renamable $at_64 = DSLL killed renamable $at_64, 16
renamable $d1_64 = LDC164 killed renamable $at_64, target-flags(mips-abs-lo) %const.0, debug-location !19 :: (load 8 from constant-pool)
FCMP_D64 killed renamable $d0_64, killed renamable $d1_64, 7, implicit-def $fcc0, debug-location !19
BC1T killed $fcc0, %bb.2, implicit-def $at, debug-location !19 {
$s0_64 = OR64 $a1_64, $zero_64
}
bb.1.if.then:
successors: %bb.3(0x80000000)
JAL @externFunc, csr_n64, implicit-def dead $ra, implicit $f12, implicit-def $sp, implicit-def dead $f0, debug-location !19 {
NOP debug-location !19
}
JAL @externFunc3, csr_n64, implicit-def dead $ra, implicit $a0_64, implicit-def $sp, implicit-def $v0, debug-location !19 {
renamable $a0_64 = SLL64_32 renamable $s0, implicit $s0_64, debug-location !19
}
$f0 = MTC1 killed $v0, debug-location !19
$f0 = CVT_S_W killed $f0, debug-location !19
; This instruction is inserted additionally in order to test moving from one register to another
$s1_64 = OR64 killed $s0_64, $zero_64, debug-location !19
renamable $at_64 = LUi64 target-flags(mips-highest) %const.2
renamable $at_64 = DADDiu killed renamable $at_64, target-flags(mips-higher) %const.2
renamable $at_64 = DSLL killed renamable $at_64, 16
renamable $at_64 = DADDiu killed renamable $at_64, target-flags(mips-abs-hi) %const.2
renamable $at_64 = DSLL killed renamable $at_64, 16
renamable $f1 = LWC1 killed renamable $at_64, target-flags(mips-abs-lo) %const.2, debug-location !19 :: (load 4 from constant-pool)
J %bb.3, implicit-def dead $at, debug-location !19 {
renamable $f0 = FADD_S killed renamable $f0, killed renamable $f1, debug-location !19
}
bb.2.if.else:
successors: %bb.3(0x80000000)
renamable $at_64 = LUi64 target-flags(mips-highest) %const.1
renamable $at_64 = DADDiu killed renamable $at_64, target-flags(mips-higher) %const.1
renamable $at_64 = DSLL killed renamable $at_64, 16
renamable $at_64 = DADDiu killed renamable $at_64, target-flags(mips-abs-hi) %const.1
renamable $at_64 = DSLL killed renamable $at_64, 16
renamable $f0 = LWC1 killed renamable $at_64, target-flags(mips-abs-lo) %const.1, debug-location !19 :: (load 4 from constant-pool)
renamable $f24 = FADD_S killed renamable $f12, killed renamable $f0, debug-location !19
DBG_VALUE debug-use $f24, debug-use $noreg, !14, !DIExpression(), debug-location !19
JAL @externFunc2, csr_n64, implicit-def dead $ra, implicit $f12, implicit-def $sp, implicit-def $f0, debug-location !19 {
$f12 = FMOV_S $f24, debug-location !19
}
$f25 = FMOV_S $f0, debug-location !19
JAL @externFunc4, csr_n64, implicit-def dead $ra, implicit $a0_64, implicit-def $sp, implicit-def $v0, debug-location !19 {
renamable $a0_64 = SLL64_32 renamable $s0, implicit killed $s0_64, debug-location !19
}
$s0 = OR $v0, $zero, debug-location !19
JAL @externFunc, csr_n64, implicit-def dead $ra, implicit $f12, implicit-def $sp, implicit-def dead $f0, debug-location !19 {
$f12 = FMOV_S $f24, debug-location !19
}
$f0 = MTC1 killed $s0, debug-location !19
$f0 = CVT_S_W killed $f0, debug-location !19
renamable $f0 = FADD_S renamable $f25, killed renamable $f0, debug-location !19
; This instruction is inserted additionally in order to test moving variable's value from one float register to another.
$f26 = FMOV_S killed $f24, debug-location !19
bb.3.if.end:
$s0_64 = LD $sp_64, 0, debug-location !19 :: (load 8 from %stack.3)
$ra_64 = LD $sp_64, 8, debug-location !19 :: (load 8 from %stack.2)
$d24_64 = LDC164 $sp_64, 16, debug-location !19 :: (load 8 from %stack.1)
$d25_64 = LDC164 $sp_64, 24, debug-location !19 :: (load 8 from %stack.0)
PseudoReturn64 undef $ra_64, implicit $f0, debug-location !19 {
$sp_64 = DADDiu $sp_64, 32
}
...

View File

@ -0,0 +1,190 @@
# RUN: llc -run-pass=livedebugvalues %s -o - | FileCheck %s
#
# This test tests tracking variables value transferring from one register to another.
# This example is altered additionally in order to test transferring from one float register
# to another. The altered instructions are labeled below.
#
# CHECK: ![[ARG1:.*]] = !DILocalVariable(name: "arg1"
# CHECK: DBG_VALUE debug-use $ebx, debug-use $noreg, ![[ARG1]], !DIExpression(), debug-location
# CHECK: $r12d = MOV32rr killed $ebx, implicit-def $r12
# CHECK-NEXT: DBG_VALUE debug-use $r12d, debug-use $noreg, ![[ARG1]], !DIExpression(), debug-location
--- |
; ModuleID = 'live-debug-values-reg-copy.ll'
source_filename = "live-debug-values-reg-copy.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define dso_local i32 @foo(i32 %arg1) local_unnamed_addr !dbg !7 {
entry:
%local1 = alloca i32, align 4
call void @llvm.dbg.value(metadata i32 %arg1, metadata !12, metadata !DIExpression()), !dbg !15
%0 = bitcast i32* %local1 to i8*, !dbg !15
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0), !dbg !15
call void @init(i32* nonnull %local1), !dbg !15
%1 = load i32, i32* %local1, align 4, !dbg !15, !tbaa !20
%add = add nsw i32 %1, %arg1, !dbg !15
%call = call i32 @coeficient(i32 %add), !dbg !15
%cmp = icmp sgt i32 %call, 32, !dbg !15
br i1 %cmp, label %if.then, label %if.else, !dbg !15
if.then: ; preds = %entry
%call1 = call i32 @externFunc(i32 %arg1), !dbg !15
%2 = load i32, i32* %local1, align 4, !dbg !15, !tbaa !20
%add2 = add nsw i32 %2, %call1, !dbg !15
br label %if.end, !dbg !15
if.else: ; preds = %entry
%call3 = call i32 @externFunc2(i32 %arg1), !dbg !15
%3 = load i32, i32* %local1, align 4, !dbg !15, !tbaa !20
%add4 = add nsw i32 %3, %call3, !dbg !15
br label %if.end
if.end: ; preds = %if.else, %if.then
%storemerge = phi i32 [ %add4, %if.else ], [ %add2, %if.then ]
%4 = bitcast i32* %local1 to i8*
%mul = shl nsw i32 %arg1, 2, !dbg !15
%add5 = add nsw i32 %storemerge, %mul, !dbg !15
%mul6 = mul nsw i32 %add5, %call, !dbg !15
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %4), !dbg !15
ret i32 %mul6, !dbg !15
}
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #0
declare dso_local void @init(i32*) local_unnamed_addr
declare dso_local i32 @coeficient(i32) local_unnamed_addr
declare dso_local i32 @externFunc(i32) local_unnamed_addr
declare dso_local i32 @externFunc2(i32) local_unnamed_addr
; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #0
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata) #1
; Function Attrs: nounwind
declare void @llvm.stackprotector(i8*, i8**) #2
attributes #0 = { argmemonly nounwind }
attributes #1 = { nounwind readnone speculatable }
attributes #2 = { nounwind }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5}
!llvm.ident = !{!6}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "live-debug-values-reg-copy.c", directory: "/")
!2 = !{}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 4}
!6 = !{!"clang version 7.0.0 "}
!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 6, type: !8, isLocal: false, isDefinition: true, scopeLine: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !11)
!8 = !DISubroutineType(types: !9)
!9 = !{!10, !10}
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!11 = !{!12}
!12 = !DILocalVariable(name: "arg1", arg: 1, scope: !7, file: !1, line: 6, type: !10)
!15 = !DILocation(line: 6, column: 13, scope: !7)
!20 = !{!21, !21, i64 0}
!21 = !{!"int", !22, i64 0}
!22 = !{!"omnipotent char", !23, i64 0}
!23 = !{!"Simple C/C++ TBAA"}
...
---
name: foo
alignment: 4
exposesReturnsTwice: false
legalized: false
regBankSelected: false
selected: false
tracksRegLiveness: true
registers:
liveins:
- { reg: '$edi', virtual-reg: '' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 24
offsetAdjustment: 0
maxAlignment: 4
adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
savePoint: ''
restorePoint: ''
fixedStack:
- { id: 0, type: spill-slot, offset: -24, size: 8, alignment: 8, stack-id: 0,
callee-saved-register: '$rbx', callee-saved-restored: true }
- { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: 0,
callee-saved-register: '$rbp', callee-saved-restored: true }
stack:
- { id: 0, name: local1, type: default, offset: -28, size: 4, alignment: 4,
stack-id: 0, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
constants:
body: |
bb.0.entry:
successors: %bb.1(0x40000000), %bb.2(0x40000000)
liveins: $edi, $rbp, $rbx
frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 16
frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 24
frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 32
CFI_INSTRUCTION offset $rbx, -24
CFI_INSTRUCTION offset $rbp, -16
DBG_VALUE debug-use $edi, debug-use $noreg, !12, !DIExpression(), debug-location !15
$ebx = MOV32rr $edi, implicit-def $rbx
DBG_VALUE debug-use $ebx, debug-use $noreg, !12, !DIExpression(), debug-location !15
renamable $rdi = LEA64r $rsp, 1, $noreg, 4, $noreg
CALL64pcrel32 @init, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, debug-location !15
renamable $edi = MOV32rm $rsp, 1, $noreg, 4, $noreg :: (dereferenceable load 4 from %ir.local1, !tbaa !20)
renamable $edi = ADD32rr killed renamable $edi, renamable $ebx, implicit-def dead $eflags, debug-location !15
CALL64pcrel32 @coeficient, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !15
$ebp = MOV32rr $eax, debug-location !15
$edi = MOV32rr $ebx, debug-location !15
CMP32ri8 renamable $ebp, 33, implicit-def $eflags, debug-location !15
JL_1 %bb.2, implicit killed $eflags, debug-location !15
bb.1.if.then:
successors: %bb.3(0x80000000)
liveins: $ebp, $edi
CALL64pcrel32 @externFunc, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !15
JMP_1 %bb.3
bb.2.if.else:
successors: %bb.3(0x80000000)
liveins: $ebp, $edi
CALL64pcrel32 @externFunc2, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !15
bb.3.if.end:
liveins: $ebp, $ebx, $eax
; Instruction below is added in order to test moving variable's value from one register to another.
$r12d = MOV32rr killed $ebx, implicit-def $r12
renamable $eax = KILL $eax, implicit-def $rax
renamable $eax = ADD32rm renamable $eax, $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, implicit killed $rax, implicit-def $rax :: (dereferenceable load 4 from %ir.local1, !tbaa !20)
renamable $eax = LEA64_32r killed renamable $rax, 4, killed renamable $r12, 0, $noreg, debug-location !15
renamable $eax = IMUL32rr killed renamable $eax, killed renamable $ebp, implicit-def dead $eflags, debug-location !15
$rsp = frame-destroy ADD64ri8 $rsp, 8, implicit-def dead $eflags, debug-location !15
$rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !15
$rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !15
RETQ $eax, debug-location !15
...