Index: llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp =================================================================== --- llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp +++ llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp @@ -903,12 +903,11 @@ public: VLocTracker() {} - void defVar(const MachineInstr &MI, Optional ID) { - // XXX skipping overlapping fragments for now. - assert(MI.isDebugValue()); + void defVar(const MachineInstr &MI, const DbgValueProperties &Properties, + Optional ID) { + assert(MI.isDebugValue() || MI.isDebugRef()); DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(), MI.getDebugLoc()->getInlinedAt()); - DbgValueProperties Properties(MI); DbgValue Rec = (ID) ? DbgValue(*ID, Properties, DbgValue::Def) : DbgValue(Properties, DbgValue::Undef); @@ -920,7 +919,7 @@ } void defVar(const MachineInstr &MI, const MachineOperand &MO) { - // XXX skipping overlapping fragments for now. + // Only DBG_VALUEs can define constant-valued variables. assert(MI.isDebugValue()); DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(), MI.getDebugLoc()->getInlinedAt()); @@ -1086,50 +1085,64 @@ } } - /// Handle a DBG_VALUE within a block. Terminate the variables current - /// location, and record the value its DBG_VALUE refers to, so that we can - /// detect location transfers later on. + /// Change a variable value after encountering a DBG_VALUE inside a block. void redefVar(const MachineInstr &MI) { DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(), MI.getDebugLoc()->getInlinedAt()); - const MachineOperand &MO = MI.getOperand(0); + DbgValueProperties Properties(MI); - // Erase any previous location, - auto It = ActiveVLocs.find(Var); - if (It != ActiveVLocs.end()) { - ActiveMLocs[It->second.Loc].erase(Var); - } + const MachineOperand &MO = MI.getOperand(0); - // Insert a new variable location. Ignore non-register locations, we don't - // transfer those, and can't currently describe spill locs independently of - // regs. - // (This is because a spill location is a DBG_VALUE of the stack pointer). + // Ignore non-register locations, we don't transfer those. if (!MO.isReg() || MO.getReg() == 0) { - if (It != ActiveVLocs.end()) + auto It = ActiveVLocs.find(Var); + if (It != ActiveVLocs.end()) { + ActiveMLocs[It->second.Loc].erase(Var); ActiveVLocs.erase(It); + } return; } Register Reg = MO.getReg(); - LocIdx MLoc = MTracker->getRegMLoc(Reg); - DbgValueProperties Properties(MI); + LocIdx NewLoc = MTracker->getRegMLoc(Reg); + redefVar(MI, Properties, NewLoc); + } + + /// Handle a change in variable location within a block. Terminate the + /// variables current location, and record the value it now refers to, so + /// that we can detect location transfers later on. + void redefVar(const MachineInstr &MI, const DbgValueProperties &Properties, + Optional OptNewLoc) { + DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(), + MI.getDebugLoc()->getInlinedAt()); + + // Erase any previous location, + auto It = ActiveVLocs.find(Var); + if (It != ActiveVLocs.end()) + ActiveMLocs[It->second.Loc].erase(Var); + + // If there _is_ no new location, all we had to do was erase. + if (!OptNewLoc) + return; + LocIdx NewLoc = *OptNewLoc; // Check whether our local copy of values-by-location in #VarLocs is out of // date. Wipe old tracking data for the location if it's been clobbered in // the meantime. - if (MTracker->getNumAtPos(MLoc) != VarLocs[MLoc.asU64()]) { - for (auto &P : ActiveMLocs[MLoc.asU64()]) { + if (MTracker->getNumAtPos(NewLoc) != VarLocs[NewLoc.asU64()]) { + for (auto &P : ActiveMLocs[NewLoc]) { ActiveVLocs.erase(P); } - ActiveMLocs[MLoc].clear(); - VarLocs[MLoc.asU64()] = MTracker->getNumAtPos(MLoc); + ActiveMLocs[NewLoc.asU64()].clear(); + VarLocs[NewLoc.asU64()] = MTracker->getNumAtPos(NewLoc); } - ActiveMLocs[MLoc].insert(Var); + ActiveMLocs[NewLoc].insert(Var); if (It == ActiveVLocs.end()) { - ActiveVLocs.insert(std::make_pair(Var, LocAndProperties{MLoc, Properties})); + ActiveVLocs.insert( + std::make_pair(Var, LocAndProperties{NewLoc, Properties})); } else { - It->second.Loc = MLoc; + It->second.Loc = NewLoc; It->second.Properties = Properties; } } @@ -1275,6 +1288,13 @@ DenseMap BBToOrder; DenseMap BBNumToRPO; + /// Pair of MachineInstr, and its 1-based offset into the containing block. + typedef std::pair InstAndNum; + /// Map from debug instruction number to the MachineInstr labelled with that + /// number, and its location within the function. Used to transform + /// instruction numbers in DBG_INSTR_REFs into machine value numbers. + std::map DebugInstrNumToInstr; + // Map of overlapping variable fragments. OverlapMap OverlapFragments; VarToFragments SeenFragments; @@ -1307,6 +1327,10 @@ /// \returns true if MI was recognized and processed. bool transferDebugValue(const MachineInstr &MI); + /// Examines whether \p MI is a DBG_INSTR_REF and notifies trackers. + /// \returns true if MI was recognized and processed. + bool transferDebugInstrRef(MachineInstr &MI); + /// Examines whether \p MI is copy instruction, and notifies trackers. /// \returns true if MI was recognized and processed. bool transferRegisterCopy(MachineInstr &MI); @@ -1501,6 +1525,7 @@ "Expected inlined-at fields to agree"); DebugVariable V(Var, Expr, InlinedAt); + DbgValueProperties Properties(MI); // If there are no instructions in this lexical scope, do no location tracking // at all, this variable shouldn't get a legitimate location range. @@ -1523,9 +1548,9 @@ // Feed defVar the new variable location, or if this is a // DBG_VALUE $noreg, feed defVar None. if (MO.getReg()) - VTracker->defVar(MI, MTracker->readReg(MO.getReg())); + VTracker->defVar(MI, Properties, MTracker->readReg(MO.getReg())); else - VTracker->defVar(MI, None); + VTracker->defVar(MI, Properties, None); } else if (MI.getOperand(0).isImm() || MI.getOperand(0).isFPImm() || MI.getOperand(0).isCImm()) { VTracker->defVar(MI, MI.getOperand(0)); @@ -1539,6 +1564,116 @@ return true; } +bool InstrRefBasedLDV::transferDebugInstrRef(MachineInstr &MI) { + if (!MI.isDebugRef()) + return false; + + // Only handle this instruction when we are building the variable value + // transfer function. + if (!VTracker) + return false; + + unsigned InstNo = MI.getOperand(0).getImm(); + unsigned OpNo = MI.getOperand(1).getImm(); + + const DILocalVariable *Var = MI.getDebugVariable(); + const DIExpression *Expr = MI.getDebugExpression(); + const DILocation *DebugLoc = MI.getDebugLoc(); + const DILocation *InlinedAt = DebugLoc->getInlinedAt(); + assert(Var->isValidLocationForIntrinsic(DebugLoc) && + "Expected inlined-at fields to agree"); + + DebugVariable V(Var, Expr, InlinedAt); + + auto *Scope = LS.findLexicalScope(MI.getDebugLoc().get()); + if (Scope == nullptr) + return true; // Handled by doing nothing. This variable is never in scope. + + const MachineFunction &MF = *MI.getParent()->getParent(); + + // Various optimizations may have happened to the value during codegen, + // recorded in the value substitution table. Apply any substitutions to + // the instruction / operand number in this DBG_INSTR_REF. + auto Sub = MF.DebugValueSubstitutions.find(std::make_pair(InstNo, OpNo)); + while (Sub != MF.DebugValueSubstitutions.end()) { + InstNo = Sub->second.first; + OpNo = Sub->second.second; + Sub = MF.DebugValueSubstitutions.find(std::make_pair(InstNo, OpNo)); + } + + // Default machine value number is -- if no instruction defines + // the corresponding value, it must have been optimized out. + Optional NewID = None; + + // Try to lookup the instruction number, and find the machine value number + // that it defines. + auto InstrIt = DebugInstrNumToInstr.find(InstNo); + if (InstrIt != DebugInstrNumToInstr.end()) { + const MachineInstr &TargetInstr = *InstrIt->second.first; + uint64_t BlockNo = TargetInstr.getParent()->getNumber(); + + // Pick out the designated operand. + assert(OpNo < TargetInstr.getNumOperands()); + const MachineOperand &MO = TargetInstr.getOperand(OpNo); + + // Today, this can only be a register. + assert(MO.isReg() && MO.isDef()); + + unsigned LocID = MTracker->getLocID(MO.getReg(), false); + LocIdx L = MTracker->LocIDToLocIdx[LocID]; + NewID = ValueIDNum(BlockNo, InstrIt->second.second, L); + } + + // We, we have a value number or None. Tell the variable value tracker about + // it. The rest of this LiveDebugValues implementation acts exactly the same + // for DBG_INSTR_REFs as DBG_VALUEs (just, the former can refer to values that + // aren't immediately available). + DbgValueProperties Properties(Expr, false); + VTracker->defVar(MI, Properties, NewID); + + // If we're on the final pass through the function, decompose this INSTR_REF + // into a plain DBG_VALUE. + if (!TTracker) + return true; + + // Pick a location for the machine value number, if such a location exists. + // (This information could be stored in TransferTracker to make it faster). + Optional FoundLoc = None; + for (auto Location : MTracker->locations()) { + LocIdx CurL = Location.Idx; + ValueIDNum ID = MTracker->LocIdxToIDNum[CurL]; + if (NewID && ID == NewID) { + // If this is the first location with that value, pick it. Otherwise, + // consider whether it's a "longer term" location. + if (!FoundLoc) { + FoundLoc = CurL; + continue; + } + + if (MTracker->isSpill(CurL)) + FoundLoc = CurL; // Spills are a longer term location. + else if (!MTracker->isSpill(*FoundLoc) && + !MTracker->isSpill(CurL) && + !isCalleeSaved(*FoundLoc) && + isCalleeSaved(CurL)) + FoundLoc = CurL; // Callee saved regs are longer term than normal. + } + } + + // Tell transfer tracker that the variable value has changed. + TTracker->redefVar(MI, Properties, FoundLoc); + + // Produce a DBG_VALUE representing what this DBG_INSTR_REF meant. + // This DBG_VALUE is potentially a $noreg / undefined location, if + // FoundLoc is None. + // (XXX -- could morph the DBG_INSTR_REF in the future). + MachineInstr *DbgMI = MTracker->emitLoc(FoundLoc, V, Properties); + TTracker->PendingDbgValues.push_back(DbgMI); + TTracker->flushDbgValues(MI.getIterator(), nullptr); + + return true; +} + void InstrRefBasedLDV::transferRegisterDef(MachineInstr &MI) { // Meta Instructions do not affect the debug liveness of any register they // define. @@ -1920,6 +2055,8 @@ // definitions. if (transferDebugValue(MI)) return; + if (transferDebugInstrRef(MI)) + return; if (transferRegisterCopy(MI)) return; if (transferSpillOrRestoreInst(MI)) @@ -1964,6 +2101,20 @@ // Also accumulate fragment map. if (MI.isDebugValue()) accumulateFragmentMap(MI); + + // Create a map from the instruction number (if present) to the + // MachineInstr and its position. + if (MI.peekDebugInstrNum()) { + uint64_t InstrNo = MI.peekDebugInstrNum(); + auto InstrAndPos = std::make_pair(&MI, CurInst); + auto InsertResult = + DebugInstrNumToInstr.insert(std::make_pair(InstrNo, InstrAndPos)); + + // There should never be duplicate instruction numbers. + assert(InsertResult.second); + (void)InsertResult; + } + ++CurInst; } @@ -3123,6 +3274,7 @@ OrderToBB.clear(); BBToOrder.clear(); BBNumToRPO.clear(); + DebugInstrNumToInstr.clear(); return Changed; } Index: llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_instrref_tolocs.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/InstrRef/livedebugvalues_instrref_tolocs.mir @@ -0,0 +1,72 @@ +--- | + ; RUN: llc %s -march=x86-64 -run-pass=livedebugvalues -o - -experimental-debug-variable-locations | FileCheck %s -implicit-check-not=DBG_VALUE + + define i32 @_Z8bb_to_bb() local_unnamed_addr !dbg !12 { + entry: + ret i32 0, !dbg !17 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!7, !8, !9, !10} + !llvm.ident = !{!11} + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3, debugInfoForProfiling: true, nameTableKind: None) + !1 = !DIFile(filename: "main.cpp", directory: "F:\") + !2 = !{} + !3 = !{!4} + !4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression()) + !5 = distinct !DIGlobalVariable(name: "start", scope: !0, file: !1, line: 4, type: !6, isLocal: false, isDefinition: true) + !6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !7 = !{i32 2, !"Dwarf Version", i32 4} + !8 = !{i32 2, !"Debug Info Version", i32 3} + !9 = !{i32 1, !"wchar_size", i32 2} + !10 = !{i32 7, !"PIC Level", i32 2} + !11 = !{!"clang version 10.0.0"} + !12 = distinct !DISubprogram(name: "bb_to_bb", linkageName: "bb_to_bb", scope: !1, file: !1, line: 6, type: !13, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) + !13 = !DISubroutineType(types: !14) + !14 = !{!6, !6} + !15 = !{!16} + !16 = !DILocalVariable(name: "myVar", scope: !12, file: !1, line: 7, type: !6) + !17 = !DILocation(line: 10, scope: !12) + +... +--- +name: _Z8bb_to_bb +debugValueSubstitutions: + - { srcinst: 4, srcop: 0, dstinst: 3, dstop: 0 } +body: | + bb.0.entry: + $rax = MOV64ri 1, debug-instr-number 1, debug-location !17 + ; This debug instruction should identify the value as being in $rax. + DBG_INSTR_REF 1, 0, !16, !DIExpression(), debug-location !17 + ; CHECK: DBG_VALUE $rax, $noreg + + $rbx = COPY killed $rax, debug-location !17 + $rax = MOV64ri 1, debug-location !17 + ; Presently, this COPY isn't followed. Dealing with that is future work. + + DBG_INSTR_REF 2, 0, !16, !DIExpression(), debug-location !17 + ; No instruction is labelled with the number "2". This should produce an + ; empty variable location. + ; CHECK: DBG_VALUE $noreg, $noreg + + $rbx = MOV64ri 1, debug-instr-number 3, debug-location !17 + JMP_1 %bb.1 + + + ; CHECK-LABEL: bb.1: + bb.1: + + DBG_INSTR_REF 3, 0, !16, !DIExpression(), debug-location !17 + ; This refers to a value def'd in a parent block -- but it should be + ; tracked into this block. + ; CHECK: DBG_VALUE $rbx, $noreg + JMP_1 %bb.2 + + ; CHECK-LABEL: bb.2: + bb.2: + ; Just like any other variable location, live-ins should be created for + ; any successor blocks. + ; CHECK: DBG_VALUE $rbx, $noreg + + RETQ $eax, debug-location !17 +...