Index: lib/CodeGen/LiveDebugValues.cpp =================================================================== --- lib/CodeGen/LiveDebugValues.cpp +++ lib/CodeGen/LiveDebugValues.cpp @@ -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 @@ 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 @@ using VarLocMap = UniqueVector; using VarLocSet = SparseBitVector<>; using VarLocInMBB = SmallDenseMap; - struct SpillDebugPair { - MachineInstr *SpillInst; + struct TransferDebugPair { + MachineInstr *TransferInst; MachineInstr *DebugInst; }; - using SpillMap = SmallVector; + using TransferMap = SmallVector; /// 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 @@ 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 @@ } } +/// 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 @@ /// 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 @@ 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)); - - // 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); - - // End all previous ranges of Var. - OpenRanges.erase(VarLocIDs[ID].Var); - - // Add the VarLoc to OpenRanges. - VarLoc VL(*SpDMI, LS); - unsigned SpillLocID = VarLocIDs.insert(VL); - OpenRanges.insert(SpillLocID, VL.Var); +/// 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; + + if (!TII->isCopyInstr(MI, SrcRegOp, DestRegOp) || !SrcRegOp->isKill() || + !DestRegOp->isDef()) + return; + + auto isCalleSavedReg = [&](unsigned Reg) { + for (MCRegAliasIterator RAI(Reg, TRI, true); RAI.isValid(); ++RAI) + if (CalleeSavedRegs.test(*RAI)) + return true; + return false; + }; + + 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 @@ } /// 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 @@ 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 @@ 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 OrderToBB; DenseMap BBToOrder; @@ -650,6 +721,8 @@ std::greater> 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 @@ // 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 @@ ++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 Visited; @@ -694,14 +767,14 @@ // 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 @@ TRI = MF.getSubtarget().getRegisterInfo(); TII = MF.getSubtarget().getInstrInfo(); TFI = MF.getSubtarget().getFrameLowering(); + TFI->determineCalleeSaves(MF, CalleeSavedRegs, + make_unique().get()); LS.initialize(MF); bool Changed = ExtendRanges(MF); Index: test/DebugInfo/MIR/ARM/live-debug-values-reg-copy.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/ARM/live-debug-values-reg-copy.mir @@ -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 + +... Index: test/DebugInfo/MIR/Mips/live-debug-values-reg-copy.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/Mips/live-debug-values-reg-copy.mir @@ -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 + } + +... Index: test/DebugInfo/MIR/X86/live-debug-values-reg-copy.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/live-debug-values-reg-copy.mir @@ -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 + +...