Index: llvm/lib/CodeGen/MachineSink.cpp =================================================================== --- llvm/lib/CodeGen/MachineSink.cpp +++ llvm/lib/CodeGen/MachineSink.cpp @@ -15,7 +15,9 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" @@ -127,6 +129,26 @@ /// current block. DenseSet SeenDbgVars; + /// A set of DBG_VALUEs, indexed by their DebugVariable. Refers to the + /// original / non-sunk DBG_VALUE, which should be cloned to the final + /// sunk location. + using SinkingVarSet = SetVector>; + + /// Record of a vreg definition that is being sunk to a different block. + /// Stores the DBG_VALUEs that referred to this vreg def, which should be + /// re-created after MachineSinking completes. + struct SunkVRegDef { + /// Set of DBG_VALUEs to recreate. + SinkingVarSet InstsToSink; + /// The defining instruction, which provides the location to place + /// recreated DBG_VALUEs. + MachineInstr *MI; + }; + + /// If a vreg definition is sunk, map its vreg number to a SunkVRegDef + /// that tracks debugging information. + MapVector SunkVRegDefs; + public: static char ID; // Pass identification @@ -184,6 +206,11 @@ /// to the copy source. void SalvageUnsunkDebugUsersOfCopy(MachineInstr &, MachineBasicBlock *TargetBlock); + + /// Re-insert any DBG_VALUE instructions that had their operands sunk + /// in this function. + void reinsertDbgValuesForSunkVRegDefs(MachineFunction &MF); + bool AllUsesDominatedByBlock(unsigned Reg, MachineBasicBlock *MBB, MachineBasicBlock *DefMBB, bool &BreakPHIEdge, bool &LocalUse) const; @@ -316,6 +343,27 @@ return true; } +void MachineSinking::reinsertDbgValuesForSunkVRegDefs(MachineFunction &MF) { + // Re-insert any DBG_VALUEs sunk. + for (auto &Iterator : SunkVRegDefs) { + SunkVRegDef Def = Iterator.second; + unsigned VReg = Iterator.first; + + MachineBasicBlock::iterator DestLoc = Def.MI->getIterator(); + MachineBasicBlock *MBB = Def.MI->getParent(); + + // Place DBG_VALUEs immediately after the sunk instruction. + ++DestLoc; + + // Collect the DBG_VALUEs being sunk and put them in a deterministic order. + for (auto &SunkLoc : Def.InstsToSink) { + MachineInstr *NewDbgMI = MF.CloneMachineInstr(SunkLoc.second); + NewDbgMI->getOperand(0).setReg(VReg); + MBB->insert(DestLoc, NewDbgMI); + } + } +} + bool MachineSinking::runOnMachineFunction(MachineFunction &MF) { if (skipFunction(MF.getFunction())) return false; @@ -366,6 +414,9 @@ MRI->clearKillFlags(I); RegsToClearKillFlags.clear(); + reinsertDbgValuesForSunkVRegDefs(MF); + SunkVRegDefs.clear(); + return EverMadeChange; } @@ -983,25 +1034,34 @@ ++InsertPos; // Collect debug users of any vreg that this inst defines. - SmallVector DbgUsersToSink; for (auto &MO : MI.operands()) { if (!MO.isReg() || !MO.isDef() || !MO.getReg().isVirtual()) continue; + + // If no DBG_VALUE uses this reg, skip further analysis. if (!SeenDbgUsers.count(MO.getReg())) continue; - // Sink any users that don't pass any other DBG_VALUEs for this variable. + SunkVRegDef &SunkDef = SunkVRegDefs[MO.getReg()]; + SunkDef.MI = &MI; + + // Record that these DBG_VALUEs refer to this sinking vreg, so that they + // can be re-specified after sinking completes. Try copy-propagating + // the original location too. auto &Users = SeenDbgUsers[MO.getReg()]; for (auto &User : Users) { MachineInstr *DbgMI = User.getPointer(); - if (User.getInt()) { - // This DBG_VALUE would re-order assignments. If we can't copy-propagate - // it, it can't be recovered. Set it undef. - if (!attemptDebugCopyProp(MI, *DbgMI)) - DbgMI->getOperand(0).setReg(0); - } else { - DbgUsersToSink.push_back(DbgMI); - } + DebugVariable Var(DbgMI->getDebugVariable(), DbgMI->getDebugExpression(), + DbgMI->getDebugLoc()->getInlinedAt()); + // If we can't copy-propagate the original DBG_VALUE, mark it undef, as + // its operand will not be available. + if (!attemptDebugCopyProp(MI, *DbgMI)) + DbgMI->getOperand(0).setReg(0); + + // Debug users that don't pass any other DBG_VALUEs for this variable + // can be sunk. + if (!User.getInt()) + SunkDef.InstsToSink.insert({Var, DbgMI}); } } @@ -1011,6 +1071,7 @@ if (MI.getMF()->getFunction().getSubprogram() && MI.isCopy()) SalvageUnsunkDebugUsersOfCopy(MI, SuccToSinkTo); + SmallVector DbgUsersToSink; // Deliberately empty performSink(MI, *SuccToSinkTo, InsertPos, DbgUsersToSink); // Conservatively, clear any kill flags, since it's possible that they are no Index: llvm/test/DebugInfo/MIR/X86/machinesink.mir =================================================================== --- llvm/test/DebugInfo/MIR/X86/machinesink.mir +++ llvm/test/DebugInfo/MIR/X86/machinesink.mir @@ -9,6 +9,9 @@ # Test two checks DBG_VALUEs do not sink past a DBG_VALUE in the same block # that redefines the variable location, while three checks that copy-propagation # happens in the same scenario. +# +# Test four: if we sink a DBG_VALUE via an intermediate block, no spurious +# DBG_VALUE $noreg's should appear along the way. --- | target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @@ -46,6 +49,22 @@ ret void } + define void @test4(i32* nocapture readonly %p) local_unnamed_addr !dbg !301 { + ; Stripped + entry: + br label %block1 + block1: + br label %block2 + block2: + br label %exit + combined: + br label %branch1 + branch1: + br label %exit + exit: + ret void + } + ; Function Attrs: nounwind readnone declare void @llvm.dbg.value(metadata, i64, metadata, metadata) @@ -81,10 +100,15 @@ !202 = !{!203} !203 = !DILocalVariable(name: "r", arg: 1, scope: !201, file: !2, line: 2, type: !12) !204 = !DILocation(line: 1, column: 1, scope: !201) + !301 = distinct !DISubprogram(name: "test2", scope: !2, file: !2, line: 2, type: !10, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !1, retainedNodes: !302) + !302 = !{!303} + !303 = !DILocalVariable(name: "s", arg: 1, scope: !301, file: !2, line: 2, type: !12) + !304 = !DILocation(line: 1, column: 1, scope: !301) ; CHECK: [[VARNUM:![0-9]+]] = !DILocalVariable(name: "p", ; CHECK: [[VAR2NUM:![0-9]+]] = !DILocalVariable(name: "q", ; CHECK: [[VAR3NUM:![0-9]+]] = !DILocalVariable(name: "r", + ; CHECK: [[VAR4NUM:![0-9]+]] = !DILocalVariable(name: "s", ... --- @@ -235,3 +259,85 @@ $rax = MOV64rr %2 RET 0 ... +--- +name: test4 +tracksRegLiveness: true +liveins: + - { reg: '$rdi', virtual-reg: '%2' } + - { reg: '$rsi', virtual-reg: '%2' } +body: | + bb.0.entry: + successors: %bb.1.block1, %bb.2.block2 + liveins: $rdi, $esi + + ; The DBG_VALUE here should sink through several blocks, but not leave any + ; additional DBG_VALUE $noregs on its way. + ; CHECK-LABEL: bb.0.entry: + ; CHECK: [[TEST4VREG:%[0-9]+]]:gr64 = COPY $rdi + ; CHECK-NEXT: CMP32ri $esi, 0 + ; CHECK-NEXT: DBG_VALUE $noreg, $noreg, [[VAR4NUM]] + ; CHECK-NEXT: JCC_1 %bb.1, 4 + ; CHECK-NEXT: JMP_1 + + %2:gr64 = COPY $rdi + %5:gr64 = ADD64ri8 %2, 1, implicit-def dead $eflags + CMP32ri $esi, 0, implicit-def $eflags + DBG_VALUE %5, $noreg, !303, !17, debug-location !304 + JCC_1 %bb.1.block1, 4, implicit $eflags + JMP_1 %bb.2.block2 + + bb.1.block1: + successors: %bb.3.combined + liveins: $esi + + ; CHECK-LABEL: bb.1.block1 + ; CHECK-NOT: DBG_VALUE + ; CHECK: JMP_1 %bb.3 + + JMP_1 %bb.3.combined + + bb.2.block2: + successors: %bb.3.combined + liveins: $esi + + ; CHECK-LABEL: bb.2.block2 + ; CHECK-NOT: DBG_VALUE + ; CHECK: JMP_1 %bb.3 + + JMP_1 %bb.3.combined + + bb.3.combined: + successors: %bb.4.branch1, %bb.5.exit + liveins: $esi + + ; CHECK-LABEL: bb.3.combined + ; CHECK-NOT: DBG_VALUE + ; CHECK: JCC_1 %bb.4, 4, implicit $eflags + ; CHECK-NEXT: JMP_1 %bb.5 + + CMP32ri $esi, 1, implicit-def $eflags + JCC_1 %bb.4.branch1, 4, implicit $eflags + JMP_1 %bb.5.exit + + bb.4.branch1: + successors: %bb.5.exit + + ; This block should receive the sunk copy and DBG_VALUE. + ; CHECK-LABEL: bb.4.branch1: + ; CHECK-NOT: DBG_VALUE + ; CHECK: [[TEST4VREG2:%[0-9]+]]:gr64 = ADD64ri8 [[TEST4VREG]], 1 + ; CHECK-NEXT: DBG_VALUE [[TEST4VREG2]], $noreg, [[VAR4NUM]] + ; CHECK-NEXT: ADD64ri8 + ; CHECK: JMP_1 %bb.5 + + %1:gr64 = ADD64ri8 %5, 4, implicit-def dead $eflags + JMP_1 %bb.5.exit + + bb.5.exit: + ; CHECK-LABEL: bb.5.exit + ; CHECK-NOT: DBG_VALUE + ; CHECK: RET 0 + + $rax = MOV64rr %2 + RET 0 +...