/// this DBG_VALUE instruction.
const DIExpression *getDebugExpression() const;
+ /// Return the debug label referenced by
+ /// this DBG_LABEL instruction.
+ const DILabel *getDebugLabel() const;
+
/// Emit an error referring to the source location of this instruction.
/// This should only be used for inline assembly that is somehow
/// impossible to compile. Other errors should have been handled much
bool isPosition() const { return isLabel() || isCFIInstruction(); }
bool isDebugValue() const { return getOpcode() == TargetOpcode::DBG_VALUE; }
+ bool isDebugLabel() const { return getOpcode() == TargetOpcode::DBG_LABEL; }
+ bool isDebugInstr() const { return isDebugValue() || isDebugLabel(); }
/// A DBG_VALUE is indirect iff the first operand is a register and
/// the second operand is an immediate.
case TargetOpcode::EH_LABEL:
case TargetOpcode::GC_LABEL:
case TargetOpcode::DBG_VALUE:
+ case TargetOpcode::DBG_LABEL:
case TargetOpcode::LIFETIME_START:
case TargetOpcode::LIFETIME_END:
return true;
class MCSymbol;
class OptimizationRemarkEmitter;
class SDDbgValue;
+class SDDbgLabel;
class SelectionDAG;
class SelectionDAGTargetInfo;
class TargetLibraryInfo;
BumpPtrAllocator Alloc;
SmallVector<SDDbgValue*, 32> DbgValues;
SmallVector<SDDbgValue*, 32> ByvalParmDbgValues;
+ SmallVector<SDDbgLabel*, 4> DbgLabels;
using DbgValMapType = DenseMap<const SDNode *, SmallVector<SDDbgValue *, 2>>;
DbgValMapType DbgValMap;
DbgValMap[Node].push_back(V);
}
+ void add(SDDbgLabel *L) {
+ DbgLabels.push_back(L);
+ }
+
/// Invalidate all DbgValues attached to the node and remove
/// it from the Node-to-DbgValues map.
void erase(const SDNode *Node);
DbgValMap.clear();
DbgValues.clear();
ByvalParmDbgValues.clear();
+ DbgLabels.clear();
Alloc.Reset();
}
BumpPtrAllocator &getAlloc() { return Alloc; }
bool empty() const {
- return DbgValues.empty() && ByvalParmDbgValues.empty();
+ return DbgValues.empty() && ByvalParmDbgValues.empty() && DbgLabels.empty();
}
ArrayRef<SDDbgValue*> getSDDbgValues(const SDNode *Node) {
}
using DbgIterator = SmallVectorImpl<SDDbgValue*>::iterator;
+ using DbgLabelIterator = SmallVectorImpl<SDDbgLabel*>::iterator;
DbgIterator DbgBegin() { return DbgValues.begin(); }
DbgIterator DbgEnd() { return DbgValues.end(); }
DbgIterator ByvalParmDbgBegin() { return ByvalParmDbgValues.begin(); }
DbgIterator ByvalParmDbgEnd() { return ByvalParmDbgValues.end(); }
+ DbgLabelIterator DbgLabelBegin() { return DbgLabels.begin(); }
+ DbgLabelIterator DbgLabelEnd() { return DbgLabels.end(); }
};
void checkForCycles(const SelectionDAG *DAG, bool force = false);
/// Pool allocation for misc. objects that are created once per SelectionDAG.
BumpPtrAllocator Allocator;
- /// Tracks dbg_value information through SDISel.
+ /// Tracks dbg_value and dbg_label information through SDISel.
SDDbgInfo *DbgInfo;
uint16_t NextPersistentId = 0;
unsigned VReg, bool IsIndirect,
const DebugLoc &DL, unsigned O);
+ /// Creates a SDDbgLabel node.
+ SDDbgLabel *getDbgLabel(DILabel *Label, const DebugLoc &DL, unsigned O);
+
/// Transfer debug values from one node to another, while optionally
/// generating fragment expressions for split-up values. If \p InvalidateDbg
/// is set, debug values are invalidated after they are transferred.
/// value is produced by SD.
void AddDbgValue(SDDbgValue *DB, SDNode *SD, bool isParameter);
+ /// Add a dbg_label SDNode.
+ void AddDbgLabel(SDDbgLabel *DB);
+
/// Get the debug values which reference the given SDNode.
ArrayRef<SDDbgValue*> GetDbgValues(const SDNode* SD) {
return DbgInfo->getSDDbgValues(SD);
return DbgInfo->ByvalParmDbgEnd();
}
+ SDDbgInfo::DbgLabelIterator DbgLabelBegin() {
+ return DbgInfo->DbgLabelBegin();
+ }
+ SDDbgInfo::DbgLabelIterator DbgLabelEnd() {
+ return DbgInfo->DbgLabelEnd();
+ }
+
/// To be invoked on an SDNode that is slated to be erased. This
/// function mirrors \c llvm::salvageDebugInfo.
void salvageDebugInfo(SDNode &N);
/// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic
HANDLE_TARGET_OPCODE(DBG_VALUE)
+/// DBG_LABEL - a mapping of the llvm.dbg.label intrinsic
+HANDLE_TARGET_OPCODE(DBG_LABEL)
+
/// REG_SEQUENCE - This variadic instruction is used to form a register that
/// represents a consecutive sequence of sub-registers. It's used as a
/// register coalescing / allocation aid and must be eliminated before code
let AsmString = "DBG_VALUE";
let hasSideEffects = 0;
}
+def DBG_LABEL : StandardPseudoInstruction {
+ let OutOperandList = (outs);
+ let InOperandList = (ins unknown:$label);
+ let AsmString = "DBG_LABEL";
+ let hasSideEffects = 0;
+}
def REG_SEQUENCE : StandardPseudoInstruction {
let OutOperandList = (outs unknown:$dst);
let InOperandList = (ins unknown:$supersrc, variable_ops);
return true;
}
+/// This method handles the target-independent form of DBG_LABEL, returning
+/// true if it was able to do so. A false return means the target will need
+/// to handle MI in EmitInstruction.
+static bool emitDebugLabelComment(const MachineInstr *MI, AsmPrinter &AP) {
+ if (MI->getNumOperands() != 1)
+ return false;
+
+ SmallString<128> Str;
+ raw_svector_ostream OS(Str);
+ OS << "DEBUG_LABEL: ";
+
+ const DILabel *V = MI->getDebugLabel();
+ if (auto *SP = dyn_cast<DISubprogram>(V->getScope())) {
+ StringRef Name = SP->getName();
+ if (!Name.empty())
+ OS << Name << ":";
+ }
+ OS << V->getName();
+
+ // NOTE: Want this comment at start of line, don't emit with AddComment.
+ AP.OutStreamer->emitRawComment(OS.str());
+ return true;
+}
+
AsmPrinter::CFIMoveType AsmPrinter::needsCFIMoves() const {
if (MAI->getExceptionHandlingType() == ExceptionHandling::DwarfCFI &&
MF->getFunction().needsUnwindTableEntry())
EmitInstruction(&MI);
}
break;
+ case TargetOpcode::DBG_LABEL:
+ if (isVerbose()) {
+ if (!emitDebugLabelComment(&MI, *this))
+ EmitInstruction(&MI);
+ }
+ break;
case TargetOpcode::IMPLICIT_DEF:
if (isVerbose()) emitImplicitDef(&MI);
break;
return -1;
}
+const DILabel *MachineInstr::getDebugLabel() const {
+ assert(isDebugLabel() && "not a DBG_LABEL");
+ return cast<DILabel>(getOperand(0).getMetadata());
+}
+
const DILocalVariable *MachineInstr::getDebugVariable() const {
assert(isDebugValue() && "not a DBG_VALUE");
return cast<DILocalVariable>(getOperand(2).getMetadata());
MO.print(OS, MST, TypeToPrint, /*PrintDef=*/true, IsStandalone,
ShouldPrintRegisterTies, TiedOperandIdx, TRI, IntrinsicInfo);
}
+ } else if (isDebugLabel() && MO.isMetadata()) {
+ // Pretty print DBG_LABEL instructions.
+ auto *DIL = dyn_cast<DILabel>(MO.getMetadata());
+ if (DIL && !DIL->getName().empty())
+ OS << "\"" << DIL->getName() << '\"';
+ else {
+ LLT TypeToPrint = MRI ? getTypeToPrint(i, PrintedTypes, *MRI) : LLT{};
+ unsigned TiedOperandIdx = getTiedOperandIdx(i);
+ MO.print(OS, MST, TypeToPrint, /*PrintDef=*/true, IsStandalone,
+ ShouldPrintRegisterTies, TiedOperandIdx, TRI, IntrinsicInfo);
+ }
} else if (i == AsmDescOp && MO.isImm()) {
// Pretty print the inline asm operand descriptor.
OS << '$' << AsmOpCount++;
case TargetOpcode::EH_LABEL:
case TargetOpcode::GC_LABEL:
case TargetOpcode::DBG_VALUE:
+ case TargetOpcode::DBG_LABEL:
return true;
}
}
return &*MIB;
}
+MachineInstr *
+InstrEmitter::EmitDbgLabel(SDDbgLabel *SD) {
+ MDNode *Label = SD->getLabel();
+ DebugLoc DL = SD->getDebugLoc();
+ assert(cast<DILabel>(Label)->isValidLocationForIntrinsic(DL) &&
+ "Expected inlined-at fields to agree");
+
+ const MCInstrDesc &II = TII->get(TargetOpcode::DBG_LABEL);
+ MachineInstrBuilder MIB = BuildMI(*MF, DL, II);
+ MIB.addMetadata(Label);
+
+ return &*MIB;
+}
+
/// EmitMachineNode - Generate machine code for a target-specific node and
/// needed dependencies.
///
MachineInstr *EmitDbgValue(SDDbgValue *SD,
DenseMap<SDValue, unsigned> &VRBaseMap);
+ /// Generate machine instruction for a dbg_label node.
+ MachineInstr *EmitDbgLabel(SDDbgLabel *SD);
+
/// EmitNode - Generate machine code for a node and needed dependencies.
///
void EmitNode(SDNode *Node, bool IsClone, bool IsCloned,
bool isInvalidated() const { return Invalid; }
};
+/// Holds the information from a dbg_label node through SDISel.
+/// We do not use SDValue here to avoid including its header.
+class SDDbgLabel {
+ MDNode *Label;
+ DebugLoc DL;
+ unsigned Order;
+
+public:
+ SDDbgLabel(MDNode *Label, DebugLoc dl, unsigned O)
+ : Label(Label), DL(std::move(dl)), Order(O) {}
+
+ /// Returns the MDNode pointer for the label.
+ MDNode *getLabel() const { return Label; }
+
+ /// Returns the DebugLoc.
+ DebugLoc getDebugLoc() const { return DL; }
+
+ /// Returns the SDNodeOrder. This is the order of the preceding node in the
+ /// input.
+ unsigned getOrder() const { return Order; }
+};
+
} // end llvm namespace
#endif
MachineBasicBlock *InsertBB = Emitter.getBlock();
MachineBasicBlock::iterator Pos = InsertBB->getFirstTerminator();
InsertBB->insert(Pos, DbgMIs.begin(), DbgMIs.end());
+
+ SDDbgInfo::DbgLabelIterator DLI = DAG->DbgLabelBegin();
+ SDDbgInfo::DbgLabelIterator DLE = DAG->DbgLabelEnd();
+ // Now emit the rest according to source order.
+ LastOrder = 0;
+ for (const auto &InstrOrder : Orders) {
+ unsigned Order = InstrOrder.first;
+ MachineInstr *MI = InstrOrder.second;
+ if (!MI)
+ continue;
+
+ // Insert all SDDbgLabel's whose order(s) are before "Order".
+ for (; DLI != DLE &&
+ (*DLI)->getOrder() >= LastOrder && (*DLI)->getOrder() < Order;
+ ++DLI) {
+ MachineInstr *DbgMI = Emitter.EmitDbgLabel(*DLI);
+ if (DbgMI) {
+ if (!LastOrder)
+ // Insert to start of the BB (after PHIs).
+ BB->insert(BBBegin, DbgMI);
+ else {
+ // Insert at the instruction, which may be in a different
+ // block, if the block was split by a custom inserter.
+ MachineBasicBlock::iterator Pos = MI;
+ MI->getParent()->insert(Pos, DbgMI);
+ }
+ }
+ }
+ if (DLI == DLE)
+ break;
+
+ LastOrder = Order;
+ }
}
InsertPos = Emitter.getInsertPos();
AddDbgValue(Dbg, Dbg->getSDNode(), false);
}
+/// Creates a SDDbgLabel node.
+SDDbgLabel *SelectionDAG::getDbgLabel(DILabel *Label,
+ const DebugLoc &DL, unsigned O) {
+ assert(cast<DILabel>(Label)->isValidLocationForIntrinsic(DL) &&
+ "Expected inlined-at fields to agree");
+ return new (DbgInfo->getAlloc()) SDDbgLabel(Label, DL, O);
+}
+
namespace {
/// RAUWUpdateListener - Helper for ReplaceAllUsesWith - When the node
DbgInfo->add(DB, SD, isParameter);
}
+void SelectionDAG::AddDbgLabel(SDDbgLabel *DB) {
+ DbgInfo->add(DB);
+}
+
SDValue SelectionDAG::makeEquivalentMemoryOrdering(LoadSDNode *OldLoad,
SDValue NewMemOp) {
assert(isa<MemSDNode>(NewMemOp.getNode()) && "Expected a memop node");
}
return nullptr;
}
+ case Intrinsic::dbg_label: {
+ const DbgLabelInst &DI = cast<DbgLabelInst>(I);
+ DILabel *Label = DI.getLabel();
+ assert(Label && "Missing label");
+
+ SDDbgLabel *SDV;
+ SDV = DAG.getDbgLabel(Label, dl, SDNodeOrder);
+ DAG.AddDbgLabel(SDV);
+ return nullptr;
+ }
case Intrinsic::dbg_value: {
const DbgValueInst &DI = cast<DbgValueInst>(I);
assert(DI.getVariable() && "Missing variable");
--- /dev/null
+; Test DBG_LABEL MachineInstr for label debugging.
+; RUN: llc -fast-isel=false -debug-only=isel %s -o /dev/null 2> %t.debug
+; RUN: cat %t.debug | FileCheck %s --check-prefix=CHECKMI
+;
+; CHECKMI: DBG_LABEL "top", debug-location !9
+; CHECKMI: DBG_LABEL "done", debug-location !11
+;
+; RUN: llc -fast-isel=false %s -o - | FileCheck %s --check-prefix=CHECKASM
+;
+; CHECKASM: #DEBUG_LABEL: foo:top
+; CHECKASM: #DEBUG_LABEL: foo:done
+
+source_filename = "debug-label-mi.c"
+
+; Function Attrs: noinline nounwind optnone
+define i32 @foo(i32 signext %a, i32 signext %b) #0 !dbg !4 {
+entry:
+ %a.addr = alloca i32, align 4
+ %b.addr = alloca i32, align 4
+ %sum = alloca i32, align 4
+ store i32 %a, i32* %a.addr, align 4
+ store i32 %b, i32* %b.addr, align 4
+ br label %top
+
+top: ; preds = %entry
+ call void @llvm.dbg.label(metadata !8), !dbg !9
+ %0 = load i32, i32* %a.addr, align 4
+ %1 = load i32, i32* %b.addr, align 4
+ %add = add nsw i32 %0, %1
+ store i32 %add, i32* %sum, align 4
+ br label %done
+
+done: ; preds = %top
+ call void @llvm.dbg.label(metadata !10), !dbg !11
+ %2 = load i32, i32* %sum, align 4
+ ret i32 %2
+}
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.label(metadata)
+
+attributes #0 = { noinline nounwind optnone uwtable }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: false, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "debug-label-mi.c", directory: "./")
+!2 = !{}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: false, unit: !0, retainedNodes: !2)
+!5 = !DISubroutineType(types: !6)
+!6 = !{!7, !7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!8 = !DILabel(scope: !4, name: "top", file: !1, line: 4)
+!9 = !DILocation(line: 4, column: 1, scope: !4)
+!10 = !DILabel(scope: !4, name: "done", file: !1, line: 7)
+!11 = !DILocation(line: 7, column: 1, scope: !4)
--- /dev/null
+; Test DBG_LABEL MachineInstr under optimization.
+; The test case is generated by clang with -O2 is on.
+; RUN: llc -fast-isel=false -debug-only=isel %s -o /dev/null 2> %t.debug
+; RUN: cat %t.debug | FileCheck %s --check-prefix=CHECKMI
+;
+; CHECKMI: DBG_LABEL "end_sum", debug-location !18
+; CHECKMI: DBG_LABEL "end", debug-location !20
+source_filename = "debug-label-opt.c"
+
+define i32 @foo(i32* nocapture readonly %a, i32 %n) local_unnamed_addr !dbg !7 {
+entry:
+ call void @llvm.dbg.value(metadata i32* %a, metadata !13, metadata !DIExpression()), !dbg !6
+ call void @llvm.dbg.value(metadata i32 %n, metadata !14, metadata !DIExpression()), !dbg !6
+ call void @llvm.dbg.value(metadata i32 0, metadata !15, metadata !DIExpression()), !dbg !6
+ switch i32 %n, label %end_sum [
+ i32 2, label %end_sum.sink.split
+ i32 3, label %if.then3
+ ], !dbg !6
+
+if.then3: ; preds = %entry
+ %arrayidx4 = getelementptr inbounds i32, i32* %a, i64 1, !dbg !6
+ br label %end_sum.sink.split, !dbg !6
+
+end_sum.sink.split: ; preds = %entry, %if.then3
+ %a.sink = phi i32* [ %arrayidx4, %if.then3 ], [ %a, %entry ]
+ %.sink = phi i64 [ 2, %if.then3 ], [ 1, %entry ]
+ %0 = load i32, i32* %a.sink, align 4
+ %arrayidx1 = getelementptr inbounds i32, i32* %a, i64 %.sink
+ %1 = load i32, i32* %arrayidx1, align 4
+ %add = add nsw i32 %1, %0
+ br label %end_sum, !dbg !6
+
+end_sum: ; preds = %end_sum.sink.split, %entry
+ %sum.0 = phi i32 [ 0, %entry ], [ %add, %end_sum.sink.split ]
+ call void @llvm.dbg.value(metadata i32 %sum.0, metadata !15, metadata !DIExpression()), !dbg !6
+ call void @llvm.dbg.label(metadata !16), !dbg !18
+ %2 = load i32, i32* %a, align 4, !dbg !6
+ %mul = mul nsw i32 %2, %sum.0, !dbg !19
+ call void @llvm.dbg.value(metadata i32 %mul, metadata !15, metadata !DIExpression()), !dbg !6
+ call void @llvm.dbg.label(metadata !17), !dbg !20
+ ret i32 %mul, !dbg !6
+}
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.label(metadata)
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "debug-label-opt.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 = !DILocation(line: 1, column: 14, scope: !7)
+!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !12)
+!8 = !DISubroutineType(types: !9)
+!9 = !{!10, !11, !10}
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
+!12 = !{!13, !14, !15, !16, !17}
+!13 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 1, type: !11)
+!14 = !DILocalVariable(name: "n", arg: 2, scope: !7, file: !1, line: 1, type: !10)
+!15 = !DILocalVariable(name: "sum", scope: !7, file: !1, line: 3, type: !10)
+!16 = !DILabel(scope: !7, name: "end_sum", file: !1, line: 10)
+!17 = !DILabel(scope: !7, name: "end", file: !1, line: 13)
+!18 = !DILocation(line: 10, column: 1, scope: !7)
+!19 = !DILocation(line: 11, column: 7, scope: !7)
+!20 = !DILocation(line: 13, column: 1, scope: !7)