From: Shiva Chen Date: Wed, 9 May 2018 02:41:08 +0000 (+0000) Subject: [DebugInfo] Convert intrinsic llvm.dbg.label to MachineInstr. X-Git-Tag: llvmorg-7.0.0-rc1~6480 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=cd070cdc94ef5f2da3d3a9724c4fcde778fcd163;p=platform%2Fupstream%2Fllvm.git [DebugInfo] Convert intrinsic llvm.dbg.label to MachineInstr. In order to convert LLVM IR to MachineInstr, we need a new TargetOpcode, DBG_LABEL, to ‘lower’ intrinsic llvm.dbg.label. The patch creates this new TargetOpcode and convert intrinsic llvm.dbg.label to MachineInstr through SelectionDAG. In SelectionDAG, debug information is stored in SDDbgInfo. We create a new data member of SDDbgInfo for labels and use the new data member, SDDbgLabel, to create DBG_LABEL MachineInstr. The new DBG_LABEL MachineInstr uses label metadata from LLVM IR as its parameter. So, the backend could get metadata information of labels from DBG_LABEL MachineInstr. Differential Revision: https://reviews.llvm.org/D45341 Patch by Hsiangkai Wang. llvm-svn: 331842 --- diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h index 5b96c1a..c170a9c 100644 --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -292,6 +292,10 @@ public: /// 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 @@ -831,6 +835,8 @@ public: 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. @@ -907,6 +913,7 @@ public: 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; diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h index 0d809f2..befb011 100644 --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -73,6 +73,7 @@ class MachineConstantPoolValue; class MCSymbol; class OptimizationRemarkEmitter; class SDDbgValue; +class SDDbgLabel; class SelectionDAG; class SelectionDAGTargetInfo; class TargetLibraryInfo; @@ -148,6 +149,7 @@ class SDDbgInfo { BumpPtrAllocator Alloc; SmallVector DbgValues; SmallVector ByvalParmDbgValues; + SmallVector DbgLabels; using DbgValMapType = DenseMap>; DbgValMapType DbgValMap; @@ -164,6 +166,10 @@ public: 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); @@ -172,13 +178,14 @@ public: 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 getSDDbgValues(const SDNode *Node) { @@ -189,11 +196,14 @@ public: } using DbgIterator = SmallVectorImpl::iterator; + using DbgLabelIterator = SmallVectorImpl::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); @@ -255,7 +265,7 @@ class SelectionDAG { /// 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; @@ -1247,6 +1257,9 @@ public: 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. @@ -1328,6 +1341,9 @@ public: /// 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 GetDbgValues(const SDNode* SD) { return DbgInfo->getSDDbgValues(SD); @@ -1349,6 +1365,13 @@ public: 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); diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def index 1606e76..14d6a24 100644 --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -77,6 +77,9 @@ HANDLE_TARGET_OPCODE(SUBREG_TO_REG) /// 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 diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index 743aded..83ead7a 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -997,6 +997,12 @@ def DBG_VALUE : StandardPseudoInstruction { 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); diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 411b11f..89e047a 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -911,6 +911,30 @@ static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) { 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(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()) @@ -1074,6 +1098,12 @@ void AsmPrinter::EmitFunctionBody() { 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; diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp index 92abd84..c0e1a7b 100644 --- a/llvm/lib/CodeGen/MachineInstr.cpp +++ b/llvm/lib/CodeGen/MachineInstr.cpp @@ -613,6 +613,11 @@ int MachineInstr::findInlineAsmFlagIdx(unsigned OpIdx, return -1; } +const DILabel *MachineInstr::getDebugLabel() const { + assert(isDebugLabel() && "not a DBG_LABEL"); + return cast(getOperand(0).getMetadata()); +} + const DILocalVariable *MachineInstr::getDebugVariable() const { assert(isDebugValue() && "not a DBG_VALUE"); return cast(getOperand(2).getMetadata()); @@ -1383,6 +1388,17 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST, 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(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++; diff --git a/llvm/lib/CodeGen/PatchableFunction.cpp b/llvm/lib/CodeGen/PatchableFunction.cpp index 0957705..afb4b0a 100644 --- a/llvm/lib/CodeGen/PatchableFunction.cpp +++ b/llvm/lib/CodeGen/PatchableFunction.cpp @@ -49,6 +49,7 @@ static bool doesNotGeneratecode(const MachineInstr &MI) { case TargetOpcode::EH_LABEL: case TargetOpcode::GC_LABEL: case TargetOpcode::DBG_VALUE: + case TargetOpcode::DBG_LABEL: return true; } } diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp index 0027982..65ee381 100644 --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -753,6 +753,20 @@ InstrEmitter::EmitDbgValue(SDDbgValue *SD, return &*MIB; } +MachineInstr * +InstrEmitter::EmitDbgLabel(SDDbgLabel *SD) { + MDNode *Label = SD->getLabel(); + DebugLoc DL = SD->getDebugLoc(); + assert(cast(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. /// diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h index 8a8a1bb..701b636 100644 --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h @@ -113,6 +113,9 @@ public: MachineInstr *EmitDbgValue(SDDbgValue *SD, DenseMap &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, diff --git a/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h b/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h index 166ff71..703eaa4 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h +++ b/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h @@ -128,6 +128,28 @@ public: 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 diff --git a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp index 0a5793e..430d8fb 100644 --- a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp @@ -911,6 +911,39 @@ EmitSchedule(MachineBasicBlock::iterator &InsertPos) { 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(); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 715bf85..688eb78 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -7420,6 +7420,14 @@ void SelectionDAG::salvageDebugInfo(SDNode &N) { AddDbgValue(Dbg, Dbg->getSDNode(), false); } +/// Creates a SDDbgLabel node. +SDDbgLabel *SelectionDAG::getDbgLabel(DILabel *Label, + const DebugLoc &DL, unsigned O) { + assert(cast(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 @@ -7905,6 +7913,10 @@ void SelectionDAG::AddDbgValue(SDDbgValue *DB, SDNode *SD, bool isParameter) { DbgInfo->add(DB, SD, isParameter); } +void SelectionDAG::AddDbgLabel(SDDbgLabel *DB) { + DbgInfo->add(DB); +} + SDValue SelectionDAG::makeEquivalentMemoryOrdering(LoadSDNode *OldLoad, SDValue NewMemOp) { assert(isa(NewMemOp.getNode()) && "Expected a memop node"); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index fe4cbfc..8998503 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5230,6 +5230,16 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { } return nullptr; } + case Intrinsic::dbg_label: { + const DbgLabelInst &DI = cast(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(I); assert(DI.getVariable() && "Missing variable"); diff --git a/llvm/test/DebugInfo/Generic/debug-label-mi.ll b/llvm/test/DebugInfo/Generic/debug-label-mi.ll new file mode 100644 index 0000000..7a499ba --- /dev/null +++ b/llvm/test/DebugInfo/Generic/debug-label-mi.ll @@ -0,0 +1,58 @@ +; 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) diff --git a/llvm/test/DebugInfo/Generic/debug-label-opt.ll b/llvm/test/DebugInfo/Generic/debug-label-opt.ll new file mode 100644 index 0000000..de08f14 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/debug-label-opt.ll @@ -0,0 +1,73 @@ +; 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)