Index: include/llvm/IR/DebugInfoMetadata.h =================================================================== --- include/llvm/IR/DebugInfoMetadata.h +++ include/llvm/IR/DebugInfoMetadata.h @@ -2523,17 +2523,14 @@ createFragmentExpression(const DIExpression *Expr, unsigned OffsetInBits, unsigned SizeInBits); - /// Determine the relative position of the fragments described by this - /// DIExpression and \p Other. + /// Determine the relative position of the fragments passed in. /// Returns -1 if this is entirely before Other, 0 if this and Other overlap, /// 1 if this is entirely after Other. - int fragmentCmp(const DIExpression *Other) const { - auto Fragment1 = *getFragmentInfo(); - auto Fragment2 = *Other->getFragmentInfo(); - unsigned l1 = Fragment1.OffsetInBits; - unsigned l2 = Fragment2.OffsetInBits; - unsigned r1 = l1 + Fragment1.SizeInBits; - unsigned r2 = l2 + Fragment2.SizeInBits; + static int fragmentCmp(const FragmentInfo &A, const FragmentInfo &B) { + unsigned l1 = A.OffsetInBits; + unsigned l2 = B.OffsetInBits; + unsigned r1 = l1 + A.SizeInBits; + unsigned r2 = l2 + B.SizeInBits; if (r1 <= l2) return -1; else if (r2 <= l1) @@ -2542,6 +2539,14 @@ return 0; } + /// Determine the relative position of the fragments described by this + /// DIExpression and \p Other. Calls static fragmentCmp implementation. + int fragmentCmp(const DIExpression *Other) const { + auto Fragment1 = *getFragmentInfo(); + auto Fragment2 = *Other->getFragmentInfo(); + return fragmentCmp(Fragment1, Fragment2); + } + /// Check if fragments overlap between this DIExpression and \p Other. bool fragmentsOverlap(const DIExpression *Other) const { if (!isFragment() || !Other->isFragment()) @@ -2550,6 +2555,33 @@ } }; +inline bool operator==(const DIExpression::FragmentInfo &A, + const struct DIExpression::FragmentInfo &B) { + return std::tie(A.SizeInBits, A.OffsetInBits) == + std::tie(B.SizeInBits, B.OffsetInBits); +} + +inline bool operator<(const struct DIExpression::FragmentInfo &A, + const struct DIExpression::FragmentInfo &B) { + return std::tie(A.SizeInBits, A.OffsetInBits) < + std::tie(B.SizeInBits, B.OffsetInBits); +} + +template <> struct DenseMapInfo { + using FragInfo = struct DIExpression::FragmentInfo; + static const uint64_t MaxVal = std::numeric_limits::max(); + + static inline FragInfo getEmptyKey() { return {MaxVal, MaxVal}; } + + static inline FragInfo getTombstoneKey() { return {MaxVal - 1, MaxVal - 1}; } + + static unsigned getHashValue(const FragInfo &Frag) { + return hash_value(std::make_pair(Frag.SizeInBits, Frag.OffsetInBits)); + } + + static bool isEqual(const FragInfo &A, const FragInfo &B) { return A == B; } +}; + /// Global variables. /// /// TODO: Remove DisplayName. It's always equal to Name. Index: lib/CodeGen/LiveDebugValues.cpp =================================================================== --- lib/CodeGen/LiveDebugValues.cpp +++ lib/CodeGen/LiveDebugValues.cpp @@ -20,6 +20,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SparseBitVector.h" #include "llvm/ADT/Statistic.h" @@ -57,6 +58,7 @@ #include #include #include +#include #include #include @@ -107,24 +109,47 @@ } }; - /// Based on std::pair so it can be used as an index into a DenseMap. - using DebugVariableBase = - std::pair; - /// A potentially inlined instance of a variable. - struct DebugVariable : public DebugVariableBase { - DebugVariable(const DILocalVariable *Var, const DILocation *InlinedAt) - : DebugVariableBase(Var, InlinedAt) {} - - const DILocalVariable *getVar() const { return this->first; } - const DILocation *getInlinedAt() const { return this->second; } - - bool operator<(const DebugVariable &DV) const { - if (getVar() == DV.getVar()) - return getInlinedAt() < DV.getInlinedAt(); - return getVar() < DV.getVar(); + using FragInfo = DIExpression::FragmentInfo; + using OptFragInfo = Optional; + + /// Storage for identifying a potentially inlined instance of a variable, + /// or a fragment thereof. + class DebugVariable { + const DILocalVariable *Variable; + OptFragInfo Fragment; + const DILocation *InlinedAt; + + public: + DebugVariable(const DILocalVariable *Var, OptFragInfo &&FragInfo, + const DILocation *InlinedAt) + : Variable(Var), Fragment(FragInfo), InlinedAt(InlinedAt) {} + + DebugVariable(const DILocalVariable *Var, const DIExpression *DIExpr, + const DILocation *InlinedAt) + : DebugVariable(Var, DIExpr->getFragmentInfo(), InlinedAt) {} + + DebugVariable(const MachineInstr &MI) + : DebugVariable(MI.getDebugVariable(), + MI.getDebugExpression()->getFragmentInfo(), + MI.getDebugLoc()->getInlinedAt()) {} + + const DILocalVariable *getVar() const { return Variable; } + const OptFragInfo &getFrag() const { return Fragment; } + const DILocation *getInlinedAt() const { return InlinedAt; } + + bool operator==(const DebugVariable &Other) const { + return std::tie(Variable, Fragment, InlinedAt) == + std::tie(Other.Variable, Other.Fragment, Other.InlinedAt); + } + + bool operator<(const DebugVariable &Other) const { + return std::tie(Variable, Fragment, InlinedAt) < + std::tie(Other.Variable, Other.Fragment, Other.InlinedAt); } }; + friend struct llvm::DenseMapInfo; + /// A pair of debug variable and value location. struct VarLoc { // The location at which a spilled variable resides. It consists of a @@ -155,8 +180,7 @@ } Loc; VarLoc(const MachineInstr &MI, LexicalScopes &LS) - : Var(MI.getDebugVariable(), MI.getDebugLoc()->getInlinedAt()), MI(MI), - UVS(MI.getDebugLoc(), LS) { + : Var(MI), MI(MI), UVS(MI.getDebugLoc(), LS) { static_assert((sizeof(Loc) == sizeof(uint64_t)), "hash does not cover all members of Loc"); assert(MI.isDebugValue() && "not a DBG_VALUE"); @@ -170,8 +194,7 @@ /// The constructor for spill locations. VarLoc(const MachineInstr &MI, unsigned SpillBase, int SpillOffset, LexicalScopes &LS) - : Var(MI.getDebugVariable(), MI.getDebugLoc()->getInlinedAt()), MI(MI), - UVS(MI.getDebugLoc(), LS) { + : Var(MI), MI(MI), UVS(MI.getDebugLoc(), LS) { assert(MI.isDebugValue() && "not a DBG_VALUE"); assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE"); Kind = SpillLocKind; @@ -215,26 +238,34 @@ }; using TransferMap = SmallVector; + // Types for recording sets of variable fragments that overlap. For a given + // local variable, we record all other fragments of that variable that could + // overlap it, to reduce search time. + using FragOfVar = + std::pair; + using OverlapMap = + DenseMap>; + + // Helper while building OverlapMap, a map of all fragments seen for a given + // DILocalVariable. + using VarToFrags = DenseMap>; + /// 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 /// DebugVariable to recent VarLocID. Note that a DBG_VALUE ends all /// previous open ranges for the same variable. class OpenRangesSet { VarLocSet VarLocs; - SmallDenseMap Vars; + SmallDenseMap Vars; + OverlapMap &OverlappingFrags; public: + OpenRangesSet(OverlapMap &_OLapMap) : OverlappingFrags(_OLapMap) {} + const VarLocSet &getVarLocs() const { return VarLocs; } /// Terminate all open ranges for Var by removing it from the set. - void erase(DebugVariable Var) { - auto It = Vars.find(Var); - if (It != Vars.end()) { - unsigned ID = It->second; - VarLocs.reset(ID); - Vars.erase(It); - } - } + void erase(DebugVariable Var); /// Terminate all open ranges listed in \c KillSet by removing /// them from the set. @@ -245,7 +276,7 @@ } /// Insert a new range into the set. - void insert(unsigned VarLocID, DebugVariableBase Var) { + void insert(unsigned VarLocID, DebugVariable Var) { VarLocs.set(VarLocID); Vars.insert({Var, VarLocID}); } @@ -293,6 +324,9 @@ VarLocInMBB &OutLocs, VarLocMap &VarLocIDs, TransferMap &Transfers, bool transferChanges); + void accumulateFragMap(MachineInstr &MI, VarToFrags &SeenFragments, + OverlapMap &OLapMap); + bool join(MachineBasicBlock &MBB, VarLocInMBB &OutLocs, VarLocInMBB &InLocs, const VarLocMap &VarLocIDs, SmallPtrSet &Visited, @@ -326,6 +360,35 @@ } // end anonymous namespace +namespace llvm { + +template <> struct DenseMapInfo { + using DV = LiveDebugValues::DebugVariable; + using OptFragInfo = LiveDebugValues::OptFragInfo; + using FragInfo = LiveDebugValues::FragInfo; + + // Empty key: no key should be generated that has no DILocalVariable. + static inline DV getEmptyKey() { return DV(nullptr, OptFragInfo(), nullptr); } + + // Difference in tombstone is that the Optional is meaningful + static inline DV getTombstoneKey() { + return DV(nullptr, OptFragInfo({0, 0}), nullptr); + } + + static unsigned getHashValue(const DV &D) { + unsigned HV = 0; + const OptFragInfo &Frag = D.getFrag(); + if (Frag) + HV = DenseMapInfo::getHashValue(*Frag); + + return hash_combine(D.getVar(), HV, D.getInlinedAt()); + } + + static bool isEqual(const DV &A, const DV &B) { return A == B; } +}; + +}; // namespace llvm + //===----------------------------------------------------------------------===// // Implementation //===----------------------------------------------------------------------===// @@ -349,6 +412,39 @@ MachineFunctionPass::getAnalysisUsage(AU); } +/// Erase a variable from the set of open ranges, and additionally erase any +/// fragments that may overlap it. +void LiveDebugValues::OpenRangesSet::erase(DebugVariable Var) { + // Erasure helper. + auto DoErase = [this](DebugVariable &VarToErase) { + auto It = Vars.find(VarToErase); + if (It != Vars.end()) { + unsigned ID = It->second; + VarLocs.reset(ID); + Vars.erase(It); + } + }; + + // Erase the given variable: no further analysis requried if it has no + // fragment. + DoErase(Var); + if (!Var.getFrag()) + return; + + // There may be fragments that overlap the designated fragment. Look them up + // in the pre-computed overlap map, and erase them too. + auto MapIt = + OverlappingFrags.find(std::make_pair(Var.getVar(), *Var.getFrag())); + if (MapIt != OverlappingFrags.end()) { + for (auto Fragment : MapIt->second) { + LiveDebugValues::DebugVariable NewFrag( + Var.getVar(), LiveDebugValues::OptFragInfo(Fragment), + Var.getInlinedAt()); + DoErase(NewFrag); + } + } +} + //===----------------------------------------------------------------------===// // Debug Range Extension Implementation //===----------------------------------------------------------------------===// @@ -399,13 +495,14 @@ if (!MI.isDebugValue()) return; 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"); // End all previous ranges of Var. - DebugVariable V(Var, InlinedAt); + DebugVariable V(Var, Expr, InlinedAt); OpenRanges.erase(V); // Add the VarLoc to OpenRanges from this DBG_VALUE. @@ -435,8 +532,7 @@ unsigned LocId = VarLocIDs.insert(VL); // Close this variable's previous location range. - DebugVariable V(DebugInstr->getDebugVariable(), - DebugInstr->getDebugLoc()->getInlinedAt()); + DebugVariable V(*DebugInstr); OpenRanges.erase(V); OpenRanges.insert(LocId, VL.Var); @@ -728,6 +824,69 @@ return Changed; } +/// Accumulate a mapping between each DILocalVariable fragment and other +/// fragments of that DILocalVariable which overlap. This reduces work during +/// the data-flow stage from "Find any overlapping fragments" to "Check if the +/// known-to-overlap fragments are present". +/// \param MI A previously unprocessed DEBUG_VALUE instruction to analyze for +/// fragment usage. +/// \param SeenFragments Map from DILocalVariable to all fragments of that +/// Variable which are known to exist. +/// \param OverlappingFrags The overlap map being constructed, from one +/// Var/Fragment pair to a vector of fragments known to overlap. +void LiveDebugValues::accumulateFragMap(MachineInstr &MI, + VarToFrags &SeenFragments, + OverlapMap &OverlappingFrags) { + DebugVariable MIVar(MI); + + // If there is no fragment, then no other use of the variable has a fragment + // either. We can ignore the variable. + if (!MIVar.getFrag()) + return; + + // If this is the first sighting of this variable, then we are guaranteed + // there are currently no overlapping fragments either. Initialize the set + // of seen fragments, record no overlaps for the current one, and return. + auto SeenIt = SeenFragments.find(MIVar.getVar()); + if (SeenIt == SeenFragments.end()) { + SmallSet OneFrag; + OneFrag.insert(*MIVar.getFrag()); + SeenFragments.insert({MIVar.getVar(), OneFrag}); + + OverlappingFrags.insert( + {std::make_pair(MIVar.getVar(), *MIVar.getFrag()), {}}); + return; + } + + // If this particular Variable/Fragment pair already exists in the overlap + // map, it has already been accounted for. + auto IsInOLapMap = OverlappingFrags.insert( + {std::make_pair(MIVar.getVar(), *MIVar.getFrag()), {}}); + if (!IsInOLapMap.second) + return; + + auto OverlapIt = IsInOLapMap.first; + + // Otherwise, examine all other seen fragments for this variable. Record any + // pair of overlapping fragments. + const FragInfo &ThisFrag = *MIVar.getFrag(); + for (auto &AFrag : SeenIt->second) { + // Does this previously seen fragment overlap? + if (DIExpression::fragmentCmp(ThisFrag, AFrag) == 0) { + // Yes: Mark the current fragment as being overlapped. + OverlapIt->second.push_back(AFrag); + // Mark the previously seen fragment as being overlapped by the current + // one. + auto Overlapped = + OverlappingFrags.find(std::make_pair(MIVar.getVar(), AFrag)); + assert(Overlapped != OverlappingFrags.end()); + Overlapped->second.push_back(ThisFrag); + } + } + + SeenIt->second.insert(ThisFrag); +} + /// This routine creates OpenRanges and OutLocs. bool LiveDebugValues::process(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocInMBB &OutLocs, VarLocMap &VarLocIDs, @@ -850,11 +1009,15 @@ bool OLChanged = false; bool MBBJoined = false; - VarLocMap VarLocIDs; // Map VarLoc<>unique ID for use in bitvectors. - 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. - TransferMap Transfers; // DBG_VALUEs associated with spills. + VarLocMap VarLocIDs; // Map VarLoc<>unique ID for use in bitvectors. + OverlapMap OverlappingFrags; // Map of overlapping variable fragments + OpenRangesSet OpenRanges(OverlappingFrags); + // Ranges that are open until end of bb. + VarLocInMBB OutLocs; // Ranges that exist beyond bb. + VarLocInMBB InLocs; // Ranges that are incoming after joining. + TransferMap Transfers; // DBG_VALUEs associated with spills. + + VarToFrags SeenFragments; // Blocks which are artificial, i.e. blocks which exclusively contain // instructions without locations, or with line 0 locations. @@ -876,10 +1039,14 @@ // over the BBs. The LiveDebugVariables pass has already created DBG_VALUE // instructions for spills of registers that are known to be user variables // within the BB in which the spill occurs. - for (auto &MBB : MF) - for (auto &MI : MBB) + for (auto &MBB : MF) { + for (auto &MI : MBB) { process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers, dontTransferChanges); + if (MI.isDebugValue()) + accumulateFragMap(MI, SeenFragments, OverlappingFrags); + } + } auto hasNonArtificialLocation = [](const MachineInstr &MI) -> bool { if (const DebugLoc &DL = MI.getDebugLoc()) Index: test/DebugInfo/ARM/partial-subreg.ll =================================================================== --- test/DebugInfo/ARM/partial-subreg.ll +++ test/DebugInfo/ARM/partial-subreg.ll @@ -10,30 +10,6 @@ ; CHECK: DW_TAG_formal_parameter ; CHECK-NEXT: DW_AT_location [DW_FORM_sec_offset] ({{.*}} ; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_regx D16, DW_OP_piece 0x8, DW_OP_regx D17, DW_OP_piece 0x4 -; CHECK-NEXT: [0x{{.*}}, 0x{{.*}}): DW_OP_regx D16, DW_OP_piece 0x8, DW_OP_regx D17, DW_OP_piece 0x4 - -; FIXME: The second location list entry should not be emitted. -; -; The input to LiveDebugValues is: -; -; bb.0.entry: -; [...] -; Bcc %bb.2, 1, killed $cpsr, debug-location !10; simd.swift:5900:12 -; bb.1: -; [...] -; DBG_VALUE $q8, $noreg, !"self", !DIExpression(DW_OP_LLVM_fragment, 0, 96) -; B %bb.3 -; bb.2.select.false: -; [...] -; DBG_VALUE $q8, $noreg, !"self", !DIExpression(DW_OP_LLVM_fragment, 0, 96) -; bb.3.select.end: -; [...] -; -; The two DBG_VALUEs in the blocks describe different fragments of the -; variable. However, LiveDebugValues is not aware of fragments, so it will -; incorrectly insert a copy of the first DBG_VALUE in bb.3.select.end, since -; the debug values in its predecessor blocks are described by the same -; register. source_filename = "simd.ll" target datalayout = "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32" Index: test/DebugInfo/MIR/X86/live-debug-values-fragments.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/live-debug-values-fragments.mir @@ -0,0 +1,172 @@ +# RUN: llc %s -o - -run-pass=livedebugvalues | FileCheck %s +# +# The first func tests that, for two independent variable fragments defined in +# blocks 1 and 2, _both_ their locations are propagated into the exit block. +# LiveDebugValues previously ignored fragments and only propagated the last +# variable location seen. +# +# The second func tests that overlapping variable fragments are handled +# correctly -- that the redefinition of a particular fragment also clobbers +# any overlaps. The dbg value of $cx in block one should not be propagated to +# block two, because an overlapping dbg value inst has been encountered. +# +# CHECK-LABEL: foo +# CHECK-LABEL: bb.3.bb3: +# CHECK: DBG_VALUE $ebx, $noreg, !{{[0-9]+}}, +# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 32, 32) +# CHECK-NEXT: DBG_VALUE $eax, $noreg, !{{[0-9]+}}, +# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 32) +# CHECK-NEXT: XOR32rr +# CHECK-NEXT: RETQ +# +# CHECK-LABEL: bar +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE $cx, $noreg, !{{[0-9]+}}, +# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 16) + +# CHECK-LABEL: bb.1.bb1: +# CHECK: DBG_VALUE $cx, $noreg, !{{[0-9]+}}, +# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 16) +# CHECK-NEXT: MOV32rr +# CHECK-NEXT: DBG_VALUE $ax, $noreg, !{{[0-9]+}}, +# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 8, 16) +# CHECK-NEXT: JMP_1 + +# CHECK-LABEL: bb.2.bb2: +# CHECK-NOT: DBG_VALUE +# CHECK: DBG_VALUE $ax, $noreg, !{{[0-9]+}}, +# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 8, 16) +# CHECK-NEXT: MOV32rr +# CHECK-NEXT: ADD32ri8 +# CHECK-NEXT: DBG_VALUE $ebx, $noreg, !{{[0-9]+}}, +# CHECK-SAME !DIExpression(DW_OP_LLVM_fragment, 32, 32) +# CHECK-NEXT: JMP_1 + +# CHECK-LABEL: bb.3.bb3: +# CHECK: DBG_VALUE $ebx, $noreg, !{{[0-9]+}}, +# CHECK-SAME !DIExpression(DW_OP_LLVM_fragment, 32, 32) +# CHECK-NEXT: DBG_VALUE $ax, $noreg, !{{[0-9]+}}, +# CHECK-SAME: !DIExpression(DW_OP_LLVM_fragment, 8, 16) +# CHECK-NEXT: XOR32rr +# CHECK-NEXT: RETQ + +--- | + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + define i32 @foo(i32* %bees, i32* %output) !dbg !4 { + entry: + br label %bb1 + bb1: + br label %bb2 + bb2: + br label %bb3 + bb3: + ret i32 0 + } + + define i32 @bar(i32* %bees, i32* %output) !dbg !40 { + entry: + br label %bb1 + bb1: + br label %bb2 + bb2: + br label %bb3 + bb3: + ret i32 0 + } + + !llvm.module.flags = !{!0, !100} + !llvm.dbg.cu = !{!1} + + !100 = !{i32 2, !"Dwarf Version", i32 4} + !0 = !{i32 2, !"Debug Info Version", i32 3} + !1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, producer: "beards", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) + !2 = !DIFile(filename: "bees.cpp", directory: ".") + !3 = !DILocalVariable(name: "flannel", scope: !4, file: !2, line: 1, type: !16) + !4 = distinct !DISubprogram(name: "nope", scope: !2, file: !2, line: 1, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !13, type: !14, isDefinition: true) + !8 = !DILocation(line: 4, scope: !4) + !13 = !{!3} + !14 = !DISubroutineType(types: !15) + !15 = !{!16} + !16 = !DIBasicType(name: "looong", size: 64, align: 64, encoding: DW_ATE_signed) + !40 = distinct !DISubprogram(name: "toast", scope: !2, file: !2, line: 1, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !53, type: !14, isDefinition: true) + !43 = !DILocalVariable(name: "charm", scope: !40, file: !2, line: 1, type: !16) + !48 = !DILocation(line: 4, scope: !40) + !53 = !{!43} + +... +--- +name: foo +tracksRegLiveness: true +registers: [] +liveins: + - { reg: '$rdi', virtual-reg: '' } +body: | + bb.0.entry: + successors: %bb.1 + liveins: $rdi + + $ecx = XOR32rr undef $ecx, undef $ecx, implicit-def $eflags + JMP_1 %bb.1 + + bb.1.bb1 (align 4): + successors: %bb.2 + liveins: $ecx, $rdi + + $eax = MOV32rr killed $ecx, implicit-def $rax + DBG_VALUE $eax, $noreg, !3, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !8 + JMP_1 %bb.2 + + bb.2.bb2: + successors: %bb.3 + liveins: $eax + + $ebx = MOV32rr $eax + $ebx = ADD32ri8 $ebx, 3, implicit-def dead $eflags, implicit killed $rbx, implicit-def $rbx + DBG_VALUE $ebx, $noreg, !3, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !8 + JMP_1 %bb.3 + + bb.3.bb3: + liveins: $eax, $ebx + $eax = XOR32rr killed $eax, killed $ebx, implicit-def $eflags + RETQ $eax, debug-location !8 + +... +--- +name: bar +tracksRegLiveness: true +registers: [] +liveins: + - { reg: '$rdi', virtual-reg: '' } +body: | + bb.0.entry: + successors: %bb.1 + liveins: $rdi + + $ecx = XOR32rr undef $ecx, undef $ecx, implicit-def $eflags + DBG_VALUE $cx, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 0, 16), debug-location !48 + JMP_1 %bb.1 + + bb.1.bb1: + successors: %bb.2 + liveins: $ecx, $rdi + + $eax = MOV32rr killed $ecx, implicit-def $rax + DBG_VALUE $ax, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 8, 16), debug-location !48 + JMP_1 %bb.2 + + bb.2.bb2: + successors: %bb.3 + liveins: $eax + + $ebx = MOV32rr $eax + $ebx = ADD32ri8 $ebx, 3, implicit-def dead $eflags, implicit killed $rbx, implicit-def $rbx + DBG_VALUE $ebx, $noreg, !43, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !48 + JMP_1 %bb.3 + + bb.3.bb3: + liveins: $eax, $ebx + $eax = XOR32rr killed $eax, killed $ebx, implicit-def $eflags + RETQ $eax, debug-location !48 + +...