diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py --- a/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/TestBasicEntryValuesX86_64.py @@ -6,8 +6,7 @@ supported_platforms.extend(lldbplatformutil.getDarwinOSTriples()) lldbinline.MakeInlineTest(__file__, globals(), - [decorators.skipIf(bugnumber="llvm.org/pr44059"), - decorators.skipUnlessPlatform(supported_platforms), + [decorators.skipUnlessPlatform(supported_platforms), decorators.skipIf(compiler="clang", compiler_version=['<', '10.0']), decorators.skipUnlessArch('x86_64'), decorators.skipUnlessHasCallSiteInfo, diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp --- a/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp +++ b/lldb/packages/Python/lldbsuite/test/functionalities/param_entry_vals/basic_entry_values_x86_64/main.cpp @@ -18,6 +18,14 @@ /* Clobbers */ : "rsi" \ ); +// Destroy %rbx in the current frame. +#define DESTROY_RBX \ + asm volatile ("xorq %%rbx, %%rbx" \ + /* Outputs */ : \ + /* Inputs */ : \ + /* Clobbers */ : "rbx" \ + ); + struct S1 { int field1 = 123; int *field2 = &field1; @@ -30,10 +38,17 @@ // Destroy 'x' in the current frame. DESTROY_RSI; - //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=FUNC1-DESC") - // FUNC1-DESC: name = "x", type = "int", location = DW_OP_entry_value(DW_OP_reg4 RSI) + // NOTE: Currently, we do not generate DW_OP_entry_value for the 'x', + // since it gets copied into a register that is not callee saved, + // and we can not guarantee that its value has not changed. ++sink; + + // Destroy 'sink' in the current frame. + DESTROY_RBX; + + //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=FUNC1-DESC") + // FUNC1-DESC: name = "sink", type = "int &", location = DW_OP_entry_value(DW_OP_reg5 RDI) } __attribute__((noinline)) @@ -43,10 +58,16 @@ // Destroy 'x' in the current frame. DESTROY_RSI; - //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC2-EXPR") - // FUNC2-EXPR: (int) ${{.*}} = 123 + //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC2-EXPR-FAIL", expect_cmd_failure=True) + // FUNC2-EXPR-FAIL: couldn't get the value of variable x: variable not available ++sink; + + // Destroy 'sink' in the current frame. + DESTROY_RBX; + + //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC2-EXPR") + // FUNC2-EXPR: ${{.*}} = 2 } __attribute__((noinline)) @@ -69,10 +90,16 @@ // Destroy 'x' in the current frame. DESTROY_RSI; - //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR", expect_cmd_failure=True) - // FUNC4-EXPR: couldn't get the value of variable x: Could not evaluate DW_OP_entry_value. + //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC4-EXPR-FAIL", expect_cmd_failure=True) + // FUNC4-EXPR-FAIL: couldn't get the value of variable x: variable not available ++sink; + + // Destroy 'sink' in the current frame. + DESTROY_RBX; + + //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC4-EXPR", expect_cmd_failure=True) + // FUNC4-EXPR: couldn't get the value of variable sink: Could not evaluate DW_OP_entry_value. } __attribute__((noinline)) @@ -98,10 +125,16 @@ // Destroy 'x' in the current frame. DESTROY_RSI; - //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC7-EXPR") - // FUNC7-EXPR: (int) ${{.*}} = 123 + //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC7-EXPR-FAIL", expect_cmd_failure=True) + // FUNC7-EXPR-FAIL: couldn't get the value of variable x: variable not available ++sink; + + // Destroy 'sink' in the current frame. + DESTROY_RBX; + + //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC7-EXPR") + // FUNC7-EXPR: ${{.*}} = 4 } __attribute__((always_inline)) @@ -129,10 +162,16 @@ // Destroy 'x' in the current frame. DESTROY_RSI; - //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC11-EXPR") - // FUNC11-EXPR: (int) ${{.*}} = 123 + //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC11-EXPR-FAIL", expect_cmd_failure=True) + // FUNC11-EXPR-FAIL: couldn't get the value of variable x: variable not available ++sink; + + // Destroy 'sink' in the current frame. + DESTROY_RBX; + + //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC11-EXPR") + // FUNC11-EXPR: ${{.*}} = 5 } __attribute__((noinline)) @@ -150,10 +189,16 @@ // Destroy 'x' in the current frame. DESTROY_RSI; - //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC13-EXPR") - // FUNC13-EXPR: (int) ${{.*}} = 123 + //% self.filecheck("expr x", "main.cpp", "-check-prefix=FUNC13-EXPR-FAIL", expect_cmd_failure=True) + // FUNC13-EXPR-FAIL: couldn't get the value of variable x: variable not available - ++sink; + use(sink); + + // Destroy 'sink' in the current frame. + DESTROY_RBX; + + //% self.filecheck("expr sink", "main.cpp", "-check-prefix=FUNC13-EXPR") + // FUNC13-EXPR: ${{.*}} = 5 } __attribute__((noinline, disable_tail_calls)) diff --git a/llvm/lib/CodeGen/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues.cpp --- a/llvm/lib/CodeGen/LiveDebugValues.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues.cpp @@ -226,7 +226,9 @@ RegisterKind, SpillLocKind, ImmediateKind, - EntryValueKind + EntryValueKind, + EntryValueBackupKind, + EntryValueCopyBackupKind } Kind = InvalidKind; /// The value location. Stored separately to avoid repeatedly @@ -248,7 +250,7 @@ assert(MI.isDebugValue() && "not a DBG_VALUE"); assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE"); if (int RegNo = isDbgValueDescribedByReg(MI)) { - Kind = MI.isDebugEntryValue() ? EntryValueKind : RegisterKind; + Kind = RegisterKind; Loc.RegNo = RegNo; } else if (MI.getOperand(0).isImm()) { Kind = ImmediateKind; @@ -260,17 +262,50 @@ Kind = ImmediateKind; Loc.CImm = MI.getOperand(0).getCImm(); } - assert((Kind != ImmediateKind || !MI.isDebugEntryValue()) && - "entry values must be register locations"); + + // We create the debug entry values from the factory functions rather than + // from this ctor. + assert(Kind != EntryValueKind && !isEntryBackupLoc()); } /// Take the variable and machine-location in DBG_VALUE MI, and build an /// entry location using the given expression. static VarLoc CreateEntryLoc(const MachineInstr &MI, LexicalScopes &LS, - const DIExpression *EntryExpr) { + const DIExpression *EntryExpr, unsigned Reg) { VarLoc VL(MI, LS); + assert(VL.Kind == RegisterKind); VL.Kind = EntryValueKind; VL.Expr = EntryExpr; + VL.Loc.RegNo = Reg; + return VL; + } + + /// Take the variable and machine-location from the DBG_VALUE (from the + /// function entry), and build an entry value backup location. The backup + /// location will turn into the normal location if the backup is valid at + /// the time of the primary location clobbering. + static VarLoc CreateEntryBackupLoc(const MachineInstr &MI, + LexicalScopes &LS, + const DIExpression *EntryExpr) { + VarLoc VL(MI, LS); + assert(VL.Kind == RegisterKind); + VL.Kind = EntryValueBackupKind; + VL.Expr = EntryExpr; + return VL; + } + + /// Take the variable and machine-location from the DBG_VALUE (from the + /// function entry), and build a copy of an entry value backup location by + /// setting the register location to NewReg. + static VarLoc CreateEntryCopyBackupLoc(const MachineInstr &MI, + LexicalScopes &LS, + const DIExpression *EntryExpr, + unsigned NewReg) { + VarLoc VL(MI, LS); + assert(VL.Kind == RegisterKind); + VL.Kind = EntryValueCopyBackupKind; + VL.Expr = EntryExpr; + VL.Loc.RegNo = NewReg; return VL; } @@ -309,8 +344,11 @@ switch (Kind) { case EntryValueKind: // An entry value is a register location -- but with an updated - // expression. - return BuildMI(MF, DbgLoc, IID, Indirect, Loc.RegNo, Var, Expr); + // expression. The register location of such DBG_VALUE is always the one + // from the entry DBG_VALUE, it does not matter if the entry value was + // copied in to another register due to some optimizations. + return BuildMI(MF, DbgLoc, IID, Indirect, MI.getOperand(0).getReg(), + Var, Expr); case RegisterKind: // Register locations are like the source DBG_VALUE, but with the // register number from this VarLoc. @@ -329,8 +367,11 @@ MachineOperand MO = MI.getOperand(0); return BuildMI(MF, DbgLoc, IID, Indirect, MO, Var, DIExpr); } + case EntryValueBackupKind: + case EntryValueCopyBackupKind: case InvalidKind: - llvm_unreachable("Tried to produce DBG_VALUE for invalid VarLoc"); + llvm_unreachable( + "Tried to produce DBG_VALUE for invalid or backup VarLoc"); } llvm_unreachable("Unrecognized LiveDebugValues.VarLoc.Kind enum"); } @@ -338,6 +379,27 @@ /// Is the Loc field a constant or constant object? bool isConstant() const { return Kind == ImmediateKind; } + /// Check if the Loc field is an entry backup location. + bool isEntryBackupLoc() const { + return Kind == EntryValueBackupKind || Kind == EntryValueCopyBackupKind; + } + + /// If this variable is described by a register holding the entry value, + /// return it, otherwise return 0. + unsigned getEntryValueBackupReg() const { + if (Kind == EntryValueBackupKind) + return Loc.RegNo; + return 0; + } + + /// If this variable is described by a register holding the copy of the + /// entry value, return it, otherwise return 0. + unsigned getEntryValueCopyBackupReg() const { + if (Kind == EntryValueCopyBackupKind) + return Loc.RegNo; + return 0; + } + /// If this variable is described by a register, return it, /// otherwise return 0. unsigned isDescribedByReg() const { @@ -357,6 +419,8 @@ switch (Kind) { case RegisterKind: case EntryValueKind: + case EntryValueBackupKind: + case EntryValueCopyBackupKind: dbgs() << printReg(Loc.RegNo, TRI); break; case SpillLocKind: @@ -374,7 +438,12 @@ if (Var.getInlinedAt()) dbgs() << "!" << Var.getInlinedAt()->getMetadataID() << ")\n"; else - dbgs() << "(null))\n"; + dbgs() << "(null))"; + + if (isEntryBackupLoc()) + dbgs() << " (backup loc)\n"; + else + dbgs() << "\n"; } #endif @@ -390,7 +459,6 @@ } }; - using DebugParamMap = SmallDenseMap; using VarLocMap = UniqueVector; using VarLocSet = SparseBitVector<>; using VarLocInMBB = SmallDenseMap; @@ -416,10 +484,18 @@ /// 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. + /// previous open ranges for the same variable. In addition, we keep + /// two different maps (Vars/EntryValuesBackupVars), so erase/insert + /// methods act differently depending on whether a VarLoc is primary + /// location or backup one. In the case the VarLoc is backup location + /// we will erase/insert from the EntryValuesBackupVars map, otherwise + /// we perform the operation on the Vars. class OpenRangesSet { VarLocSet VarLocs; + // Map the DebugVariable to recent primary location ID. SmallDenseMap Vars; + // Map the DebugVariable to recent backup location ID. + SmallDenseMap EntryValuesBackupVars; OverlapMap &OverlappingFragments; public: @@ -427,40 +503,38 @@ const VarLocSet &getVarLocs() const { return VarLocs; } - /// Terminate all open ranges for Var by removing it from the set. - void erase(DebugVariable Var); + /// Terminate all open ranges for VL.Var by removing it from the set. + void erase(const VarLoc &VL); /// Terminate all open ranges listed in \c KillSet by removing /// them from the set. - void erase(const VarLocSet &KillSet, const VarLocMap &VarLocIDs) { - VarLocs.intersectWithComplement(KillSet); - for (unsigned ID : KillSet) - Vars.erase(VarLocIDs[ID].Var); - } + void erase(const VarLocSet &KillSet, const VarLocMap &VarLocIDs); /// Insert a new range into the set. - void insert(unsigned VarLocID, DebugVariable Var) { - VarLocs.set(VarLocID); - Vars.insert({Var, VarLocID}); - } + void insert(unsigned VarLocID, const VarLoc &VL); /// Insert a set of ranges. void insertFromLocSet(const VarLocSet &ToLoad, const VarLocMap &Map) { for (unsigned Id : ToLoad) { - const VarLoc &Var = Map[Id]; - insert(Id, Var.Var); + const VarLoc &VarL = Map[Id]; + insert(Id, VarL); } } + llvm::Optional getEntryValueBackup(DebugVariable Var); + /// Empty the set. void clear() { VarLocs.clear(); Vars.clear(); + EntryValuesBackupVars.clear(); } /// Return whether the set is empty or not. bool empty() const { - assert(Vars.empty() == VarLocs.empty() && "open ranges are inconsistent"); + assert(Vars.empty() == EntryValuesBackupVars.empty() && + Vars.empty() == VarLocs.empty() && + "open ranges are inconsistent"); return VarLocs.empty(); } }; @@ -502,21 +576,23 @@ VarLocMap &VarLocIDs); void transferSpillOrRestoreInst(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers); + bool removeEntryValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, const VarLoc &EntryVL); void emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers, - DebugParamMap &DebugEntryVals, SparseBitVector<> &KillSet); + void recordEntryValue(const MachineInstr &MI, + const DefinedRegsSet &DefinedRegs, + OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs); void transferRegisterCopy(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers); void transferRegisterDef(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers, - DebugParamMap &DebugEntryVals); + VarLocMap &VarLocIDs, TransferMap &Transfers); bool transferTerminator(MachineBasicBlock *MBB, OpenRangesSet &OpenRanges, VarLocInMBB &OutLocs, const VarLocMap &VarLocIDs); void process(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers, - DebugParamMap &DebugEntryVals); + VarLocMap &VarLocIDs, TransferMap &Transfers); void accumulateFragmentMap(MachineInstr &MI, VarToFragments &SeenFragments, OverlapMap &OLapMap); @@ -619,18 +695,24 @@ } /// Erase a variable from the set of open ranges, and additionally erase any -/// fragments that may overlap it. -void LiveDebugValues::OpenRangesSet::erase(DebugVariable Var) { +/// fragments that may overlap it. If the VarLoc is a buckup location, erase +/// the variable from the EntryValuesBackupVars set, indicating we should stop +/// tracking its backup entry location. Otherwise, if the VarLoc is primary +/// location, erase the variable from the Vars set. +void LiveDebugValues::OpenRangesSet::erase(const VarLoc &VL) { // Erasure helper. - auto DoErase = [this](DebugVariable VarToErase) { - auto It = Vars.find(VarToErase); - if (It != Vars.end()) { + auto DoErase = [VL, this](DebugVariable VarToErase) { + auto *EraseFrom = VL.isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; + auto It = EraseFrom->find(VarToErase); + if (It != EraseFrom->end()) { unsigned ID = It->second; VarLocs.reset(ID); - Vars.erase(It); + EraseFrom->erase(It); } }; + DebugVariable Var = VL.Var; + // Erase the variable/fragment that ends here. DoErase(Var); @@ -651,6 +733,34 @@ } } +void LiveDebugValues::OpenRangesSet::erase(const VarLocSet &KillSet, + const VarLocMap &VarLocIDs) { + VarLocs.intersectWithComplement(KillSet); + for (unsigned ID : KillSet) { + const VarLoc *VL = &VarLocIDs[ID]; + auto *EraseFrom = VL->isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; + EraseFrom->erase(VL->Var); + } +} + +void LiveDebugValues::OpenRangesSet::insert(unsigned VarLocID, + const VarLoc &VL) { + auto *InsertInto = VL.isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; + VarLocs.set(VarLocID); + InsertInto->insert({VL.Var, VarLocID}); +} + +/// Return the Loc ID of an entry value backup location, if it exists for the +/// variable. +llvm::Optional +LiveDebugValues::OpenRangesSet::getEntryValueBackup(DebugVariable Var) { + auto It = EntryValuesBackupVars.find(Var); + if (It != EntryValuesBackupVars.end()) + return It->second; + + return llvm::None; +} + //===----------------------------------------------------------------------===// // Debug Range Extension Implementation //===----------------------------------------------------------------------===// @@ -693,6 +803,62 @@ return {Reg, Offset}; } +/// Try to salvage the debug entry value if we encounter a new debug value +/// describing the same parameter, otherwise stop tracking the value. Return +/// true if we should stop tracking the entry value, otherwise return false. +bool LiveDebugValues::removeEntryValue(const MachineInstr &MI, + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, + const VarLoc &EntryVL) { + // Skip the DBG_VALUE which is the debug entry value itself. + if (MI.isIdenticalTo(EntryVL.MI)) + return false; + + // If the parameter's location is not register location, we can not track + // the entry value any more. In addition, if the debug expression from the + // DBG_VALUE is not empty, we can assume the parameter's value has changed + // indicating that we should stop tracking its entry value as well. + if (!MI.getOperand(0).isReg() || + MI.getDebugExpression()->getNumElements() != 0) + return true; + + // If the DBG_VALUE comes from a copy instruction that copies the entry value, + // it means the parameter's value has not changed and we should be able to use + // its entry value. + bool TrySalvageEntryValue = false; + Register Reg = MI.getOperand(0).getReg(); + auto I = std::next(MI.getReverseIterator()); + const MachineOperand *SrcRegOp, *DestRegOp; + if (I != MI.getParent()->rend()) { + // TODO: Try to keep tracking of an entry value if we encounter a propagated + // DBG_VALUE describing the copy of the entry value. (Propagated entry value + // does not indicate the parameter modification.) + auto DestSrc = TII->isCopyInstr(*I); + if (!DestSrc) + return true; + + SrcRegOp = DestSrc->Source; + DestRegOp = DestSrc->Destination; + if (Reg != DestRegOp->getReg()) + return true; + TrySalvageEntryValue = true; + } + + if (TrySalvageEntryValue) { + for (unsigned ID : OpenRanges.getVarLocs()) { + const VarLoc &VL = VarLocIDs[ID]; + if (!VL.isEntryBackupLoc()) + continue; + + if (VL.getEntryValueCopyBackupReg() == Reg && + VL.MI.getOperand(0).getReg() == SrcRegOp->getReg()) + return false; + } + } + + return true; +} + /// End all previous ranges related to @MI and start a new range from @MI /// if it is a DBG_VALUE instr. void LiveDebugValues::transferDebugValue(const MachineInstr &MI, @@ -707,18 +873,33 @@ assert(Var->isValidLocationForIntrinsic(DebugLoc) && "Expected inlined-at fields to agree"); - // End all previous ranges of Var. DebugVariable V(Var, Expr, InlinedAt); - OpenRanges.erase(V); - // Add the VarLoc to OpenRanges from this DBG_VALUE. + // Check if this DBG_VALUE indicates a parameter's value changing. + // If that is the case, we should stop tracking its entry value. + auto EntryValBackupID = OpenRanges.getEntryValueBackup(V); + if (Var->isParameter() && EntryValBackupID) { + const VarLoc &EntryVL = VarLocIDs[*EntryValBackupID]; + if (removeEntryValue(MI, OpenRanges, VarLocIDs, EntryVL)) { + LLVM_DEBUG(dbgs() << "Deleting a DBG entry value because of: "; + MI.print(dbgs(), /*IsStandalone*/ false, + /*SkipOpers*/ false, /*SkipDebugLoc*/ false, + /*AddNewLine*/ true, TII)); + OpenRanges.erase(EntryVL); + } + } + unsigned ID; if (isDbgValueDescribedByReg(MI) || MI.getOperand(0).isImm() || MI.getOperand(0).isFPImm() || MI.getOperand(0).isCImm()) { // Use normal VarLoc constructor for registers and immediates. VarLoc VL(MI, LS); + // End all previous ranges of VL.Var. + OpenRanges.erase(VL); + ID = VarLocIDs.insert(VL); - OpenRanges.insert(ID, VL.Var); + // Add the VarLoc to OpenRanges from this DBG_VALUE. + OpenRanges.insert(ID, VL); } else if (MI.hasOneMemOperand()) { llvm_unreachable("DBG_VALUE with mem operand encountered after regalloc?"); } else { @@ -728,32 +909,30 @@ } } +/// Turn the entry value backup locations into primary locations. void LiveDebugValues::emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers, - DebugParamMap &DebugEntryVals, SparseBitVector<> &KillSet) { for (unsigned ID : KillSet) { if (!VarLocIDs[ID].Var.getVar()->isParameter()) continue; - const MachineInstr *CurrDebugInstr = &VarLocIDs[ID].MI; + auto DebugVar = VarLocIDs[ID].Var; + auto EntryValBackupID = OpenRanges.getEntryValueBackup(DebugVar); - // If parameter's DBG_VALUE is not in the map that means we can't - // generate parameter's entry value. - if (!DebugEntryVals.count(CurrDebugInstr->getDebugVariable())) + // If the parameter has the entry value backup, it means we should + // be able to use its entry value. + if (!EntryValBackupID) continue; - auto ParamDebugInstr = DebugEntryVals[CurrDebugInstr->getDebugVariable()]; - DIExpression *NewExpr = DIExpression::prepend( - ParamDebugInstr->getDebugExpression(), DIExpression::EntryValue); - - VarLoc EntryLoc = VarLoc::CreateEntryLoc(*ParamDebugInstr, LS, NewExpr); - - unsigned EntryValLocID = VarLocIDs.insert(EntryLoc); - Transfers.push_back({&MI, EntryValLocID}); - OpenRanges.insert(EntryValLocID, EntryLoc.Var); + const VarLoc &EntryVL = VarLocIDs[*EntryValBackupID]; + VarLoc EntryLoc = + VarLoc::CreateEntryLoc(EntryVL.MI, LS, EntryVL.Expr, EntryVL.Loc.RegNo); + unsigned EntryValueID = VarLocIDs.insert(EntryLoc); + Transfers.push_back({&MI, EntryValueID}); + OpenRanges.insert(EntryValueID, EntryLoc); } } @@ -774,17 +953,17 @@ // Close this variable's previous location range. DebugVariable V(*DebugInstr); - OpenRanges.erase(V); + OpenRanges.erase(VL); // Record the new location as an open range, and a postponed transfer // inserting a DBG_VALUE for this location. - OpenRanges.insert(LocId, VL.Var); + OpenRanges.insert(LocId, VL); TransferDebugPair MIP = {&MI, LocId}; Transfers.push_back(MIP); }; - // End all previous ranges of Var. - OpenRanges.erase(VarLocIDs[OldVarID].Var); + // End all previous ranges of VL.Var. + OpenRanges.erase(VarLocIDs[OldVarID]); switch (Kind) { case TransferKind::TransferCopy: { assert(NewReg && @@ -832,7 +1011,7 @@ /// A definition of a register may mark the end of a range. void LiveDebugValues::transferRegisterDef( MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, - TransferMap &Transfers, DebugParamMap &DebugEntryVals) { + TransferMap &Transfers) { MachineFunction *MF = MI.getMF(); const TargetLowering *TLI = MF->getSubtarget().getTargetLowering(); unsigned SP = TLI->getStackPointerRegisterToSaveRestore(); @@ -866,8 +1045,7 @@ if (auto *TPC = getAnalysisIfAvailable()) { auto &TM = TPC->getTM(); if (TM.Options.EnableDebugEntryValues) - emitEntryValues(MI, OpenRanges, VarLocIDs, Transfers, DebugEntryVals, - KillSet); + emitEntryValues(MI, OpenRanges, VarLocIDs, Transfers, KillSet); } } @@ -1026,14 +1204,14 @@ OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers) { - auto DestSrc = TII->isCopyInstr(MI); if (!DestSrc) return; const MachineOperand *DestRegOp = DestSrc->Destination; const MachineOperand *SrcRegOp = DestSrc->Source; - if (!SrcRegOp->isKill() || !DestRegOp->isDef()) + + if (!DestRegOp->isDef()) return; auto isCalleeSavedReg = [&](unsigned Reg) { @@ -1054,6 +1232,30 @@ if (!isCalleeSavedReg(DestReg)) return; + // Remember an entry value movement. If we encounter a new debug value of + // a parameter describing only a moving of the value around, rather then + // modifying it, we are still able to use the entry value if needed. + if (isRegOtherThanSPAndFP(*DestRegOp, MI, TRI)) { + for (unsigned ID : OpenRanges.getVarLocs()) { + if (VarLocIDs[ID].getEntryValueBackupReg() == SrcReg) { + LLVM_DEBUG(dbgs() << "Copy of the entry value: "; MI.dump();); + VarLoc EntryValLocCopyBackup = VarLoc::CreateEntryCopyBackupLoc( + VarLocIDs[ID].MI, LS, VarLocIDs[ID].Expr, DestReg); + + // Stop tracking the original entry value. + OpenRanges.erase(VarLocIDs[ID]); + + // Start tracking the entry value copy. + unsigned EntryValCopyLocID = VarLocIDs.insert(EntryValLocCopyBackup); + OpenRanges.insert(EntryValCopyLocID, EntryValLocCopyBackup); + break; + } + } + } + + if (!SrcRegOp->isKill()) + return; + for (unsigned ID : OpenRanges.getVarLocs()) { if (VarLocIDs[ID].isDescribedByReg() == SrcReg) { insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, ID, @@ -1148,11 +1350,9 @@ /// This routine creates OpenRanges. void LiveDebugValues::process(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers, - DebugParamMap &DebugEntryVals) { + VarLocMap &VarLocIDs, TransferMap &Transfers) { transferDebugValue(MI, OpenRanges, VarLocIDs); - transferRegisterDef(MI, OpenRanges, VarLocIDs, Transfers, - DebugEntryVals); + transferRegisterDef(MI, OpenRanges, VarLocIDs, Transfers); transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers); transferSpillOrRestoreInst(MI, OpenRanges, VarLocIDs, Transfers); } @@ -1273,6 +1473,8 @@ // The ID location is live-in to MBB -- work out what kind of machine // location it is and create a DBG_VALUE. const VarLoc &DiffIt = VarLocIDs[ID]; + if (DiffIt.isEntryBackupLoc()) + continue; MachineInstr *MI = DiffIt.BuildDbgValue(*MBB.getParent()); MBB.insert(MBB.instr_begin(), MI); @@ -1284,8 +1486,7 @@ bool LiveDebugValues::isEntryValueCandidate( const MachineInstr &MI, const DefinedRegsSet &DefinedRegs) const { - if (!MI.isDebugValue()) - return false; + assert(MI.isDebugValue() && "This must be DBG_VALUE."); // TODO: Add support for local variables that are expressed in terms of // parameters entry values. @@ -1332,6 +1533,34 @@ Regs.insert(*AI); } +/// This routine records the entry values of function parameters. The values +/// could be used as backup values. If we loose the track of some unmodified +/// parameters, the backup values will be used as a primary locations. +void LiveDebugValues::recordEntryValue(const MachineInstr &MI, + const DefinedRegsSet &DefinedRegs, + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs) { + if (auto *TPC = getAnalysisIfAvailable()) { + auto &TM = TPC->getTM(); + if (!TM.Options.EnableDebugEntryValues) + return; + } + + if (!isEntryValueCandidate(MI, DefinedRegs) || + OpenRanges.getEntryValueBackup(DebugVariable(MI))) + return; + + LLVM_DEBUG(dbgs() << "Creating the backup entry location: "; MI.dump();); + + // Create the entry value and use it as a backup location until it is + // valid. It is valid until a parameter is not changed. + DIExpression *NewExpr = + DIExpression::prepend(MI.getDebugExpression(), DIExpression::EntryValue); + VarLoc EntryValLocAsBackup = VarLoc::CreateEntryBackupLoc(MI, LS, NewExpr); + unsigned EntryValLocID = VarLocIDs.insert(EntryValLocAsBackup); + OpenRanges.insert(EntryValLocID, EntryValLocAsBackup); +} + /// Calculate the liveness information for the given machine function and /// extend ranges across basic blocks. bool LiveDebugValues::ExtendRanges(MachineFunction &MF) { @@ -1368,23 +1597,17 @@ std::greater> Pending; - // Working set of currently collected debug variables mapped to DBG_VALUEs - // representing candidates for production of debug entry values. - DebugParamMap DebugEntryVals; - // Set of register defines that are seen when traversing the entry block // looking for debug entry value candidates. DefinedRegsSet DefinedRegs; // Only in the case of entry MBB collect DBG_VALUEs representing // function parameters in order to generate debug entry values for them. - MachineBasicBlock &First_MBB = *(MF.begin()); for (auto &MI : First_MBB) { collectRegDefs(MI, DefinedRegs, TRI); - if (isEntryValueCandidate(MI, DefinedRegs) && - !DebugEntryVals.count(MI.getDebugVariable())) - DebugEntryVals[MI.getDebugVariable()] = &MI; + if (MI.isDebugValue()) + recordEntryValue(MI, DefinedRegs, OpenRanges, VarLocIDs); } // Initialize per-block structures and scan for fragment overlaps. @@ -1443,7 +1666,7 @@ // First load any pending inlocs. OpenRanges.insertFromLocSet(PendingInLocs[MBB], VarLocIDs); for (auto &MI : *MBB) - process(MI, OpenRanges, VarLocIDs, Transfers, DebugEntryVals); + process(MI, OpenRanges, VarLocIDs, Transfers); OLChanged |= transferTerminator(MBB, OpenRanges, OutLocs, VarLocIDs); LLVM_DEBUG(printVarLocInMBB(MF, OutLocs, VarLocIDs, diff --git a/llvm/test/DebugInfo/MIR/ARM/dbgcall-site-propagated-value.mir b/llvm/test/DebugInfo/MIR/ARM/dbgcall-site-propagated-value.mir --- a/llvm/test/DebugInfo/MIR/ARM/dbgcall-site-propagated-value.mir +++ b/llvm/test/DebugInfo/MIR/ARM/dbgcall-site-propagated-value.mir @@ -129,6 +129,19 @@ --- name: callee tracksRegLiveness: true +stack: + - { id: 0, name: '', type: spill-slot, offset: -4, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: -8, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '$r11', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 2, name: '', type: spill-slot, offset: -12, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '$r10', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 3, name: '', type: spill-slot, offset: -16, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '$r4', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } body: | bb.0: successors: %bb.2(0x30000000), %bb.1(0x50000000) diff --git a/llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir b/llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir @@ -0,0 +1,121 @@ +# RUN: llc -debug-entry-values -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s +# +#extern void fn1 (int, int, int); +# +#__attribute__((noinline)) +#int +#fn2 (int a, int b, int c) { +# int q = 2 + a; +# +# fn1 (5, 6, q); +# +# b = b + 7; +# if (b < 17) +# return 1; +# else +# return 0; +#} +# +# CHECK: ![[ARG_A:.*]] = !DILocalVariable(name: "a" +# CHECK: ![[ARG_B:.*]] = !DILocalVariable(name: "b" +# CHECK: ![[ARG_C:.*]] = !DILocalVariable(name: "c" +# CHECK: DBG_VALUE $edi, $noreg, ![[ARG_A]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: DBG_VALUE $edx, $noreg, ![[ARG_C]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: DBG_VALUE $edi, $noreg, ![[ARG_A]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK-NOT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# +--- | + ; ModuleID = 'test.c' + source_filename = "test.c" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: noinline nounwind uwtable + define dso_local i32 @fn2(i32 %a, i32 %b, i32 %c) local_unnamed_addr !dbg !12 { + entry: + call void @llvm.dbg.value(metadata i32 %a, metadata !16, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %b, metadata !17, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %c, metadata !18, metadata !DIExpression()), !dbg !20 + %add = add nsw i32 %a, 2, !dbg !21 + call void @llvm.dbg.value(metadata i32 %add, metadata !19, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 5, i32 6, i32 %add), !dbg !22 + call void @llvm.dbg.value(metadata i32 %b, metadata !17, metadata !DIExpression(DW_OP_plus_uconst, 7, DW_OP_stack_value)), !dbg !20 + %cmp = icmp slt i32 %b, 10, !dbg !23 + %. = zext i1 %cmp to i32, !dbg !25 + ret i32 %., !dbg !26 + } + + declare !dbg !4 dso_local void @fn1(i32, i32, i32) local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!8, !9, !10} + !llvm.ident = !{!11} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "test.c", directory: "/dir") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "fn1", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{null, !7, !7, !7} + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 2, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{!"clang version 10.0.0"} + !12 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 5, type: !13, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) + !13 = !DISubroutineType(types: !14) + !14 = !{!7, !7, !7, !7} + !15 = !{!16, !17, !18, !19} + !16 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !1, line: 5, type: !7) + !17 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !1, line: 5, type: !7) + !18 = !DILocalVariable(name: "c", arg: 3, scope: !12, file: !1, line: 5, type: !7) + !19 = !DILocalVariable(name: "q", scope: !12, file: !1, line: 7, type: !7) + !20 = !DILocation(line: 0, scope: !12) + !21 = !DILocation(line: 7, column: 15, scope: !12) + !22 = !DILocation(line: 9, column: 5, scope: !12) + !23 = !DILocation(line: 12, column: 11, scope: !24) + !24 = distinct !DILexicalBlock(scope: !12, file: !1, line: 12, column: 9) + !25 = !DILocation(line: 0, scope: !24) + !26 = !DILocation(line: 16, column: 1, scope: !12) + +... +--- +name: fn2 +alignment: 16 +callSites: + - { bb: 0, offset: 14, fwdArgRegs: + - { arg: 0, reg: '$edi' } + - { arg: 1, reg: '$esi' } + - { arg: 2, reg: '$edx' } } +body: | + bb.0.entry: + liveins: $edi, $esi, $rbx + + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + DBG_VALUE $esi, $noreg, !17, !DIExpression(), debug-location !20 + DBG_VALUE $edx, $noreg, !18, !DIExpression(), debug-location !20 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + CFI_INSTRUCTION offset $rbx, -16 + $ebx = MOV32rr $esi + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + renamable $edi = KILL $edi, implicit-def $rdi + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + renamable $edx = LEA64_32r killed renamable $rdi, 1, $noreg, 2, $noreg, debug-location !21 + DBG_VALUE $edx, $noreg, !19, !DIExpression(), debug-location !20 + $edi = MOV32ri 5, debug-location !22 + $esi = MOV32ri 6, debug-location !22 + CALL64pcrel32 @fn1, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $edx, implicit-def $rsp, implicit-def $ssp, debug-location !22 + DBG_VALUE $ebx, $noreg, !17, !DIExpression(DW_OP_plus_uconst, 7, DW_OP_stack_value), debug-location !20 + renamable $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, debug-location !23 + CMP32ri8 killed renamable $ebx, 10, implicit-def $eflags, debug-location !23 + renamable $al = SETCCr 12, implicit killed $eflags, implicit killed $eax, implicit-def $eax, debug-location !23 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !26 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !26 + RETQ $eax, debug-location !26 + +... diff --git a/llvm/test/DebugInfo/MIR/X86/entry-values-diamond-bbs.mir b/llvm/test/DebugInfo/MIR/X86/entry-values-diamond-bbs.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/entry-values-diamond-bbs.mir @@ -0,0 +1,179 @@ +# RUN: llc -debug-entry-values -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s +# +# The test case was artificially adjusted, in order to make proper diamond basic +# block structure relevant to the debug entry values propagation. +# +# CHECK: ![[ARG_B:.*]] = !DILocalVariable(name: "b" +# CHECK: bb.0.entry +# CHECK: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression() +# CHECK: bb.1.if.then +# CHECK: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression() +# CHECK: $ebx = MOV32rr $esi +# CHECK-NEXT: DBG_VALUE $ebx, $noreg, ![[ARG_B]], !DIExpression() +# CHECK-NEXT: $esi = MOV32ri 5 +# CHECK-NEXT: $ebx = MOV32ri 1 +# CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: bb.2.if.else +# CHECK: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression() +# CHECK: $ebx = MOV32rr $esi +# CHECK-NEXT: DBG_VALUE $ebx, $noreg, ![[ARG_B]], !DIExpression() +# CHECK-NEXT: $esi = MOV32ri 1 +# CHECK-NEXT: $ebx = MOV32ri 2 +# CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: bb.3.if.end +# CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# +--- | + ; ModuleID = 'test.c' + source_filename = "test.c" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: noinline nounwind uwtable + define dso_local i32 @fn2(i32 %a, i32 %b, i32 %c) local_unnamed_addr !dbg !12 { + entry: + call void @llvm.dbg.value(metadata i32 %a, metadata !16, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %b, metadata !17, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %c, metadata !18, metadata !DIExpression()), !dbg !20 + %add = add nsw i32 %a, 2, !dbg !21 + call void @llvm.dbg.value(metadata i32 %add, metadata !19, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 5, i32 6, i32 %add) #3, !dbg !22 + %cmp = icmp slt i32 %b, 17, !dbg !23 + br i1 %cmp, label %if.then, label %if.else, !dbg !25 + + if.then: ; preds = %entry + %add1 = add nsw i32 %b, 7, !dbg !26 + call void @llvm.dbg.value(metadata i32 %add1, metadata !17, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 5, i32 %add1, i32 %c) #3, !dbg !28 + br label %if.end, !dbg !29 + + if.else: ; preds = %entry + %add2 = add nuw nsw i32 %b, 1, !dbg !30 + call void @llvm.dbg.value(metadata i32 %add2, metadata !17, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 1, i32 %add2, i32 %c) #3, !dbg !32 + br label %if.end + + if.end: ; preds = %if.else, %if.then + %b.addr.0 = phi i32 [ %add1, %if.then ], [ %add2, %if.else ], !dbg !33 + call void @llvm.dbg.value(metadata i32 %b.addr.0, metadata !17, metadata !DIExpression()), !dbg !20 + ret i32 %b.addr.0, !dbg !34 + } + + declare !dbg !4 dso_local void @fn1(i32, i32, i32) local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!8, !9, !10} + !llvm.ident = !{!11} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "test.c", directory: "/dir") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "fn1", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{null, !7, !7, !7} + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 2, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{!"clang version 10.0.0"} + !12 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 5, type: !13, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) + !13 = !DISubroutineType(types: !14) + !14 = !{!7, !7, !7, !7} + !15 = !{!16, !17, !18, !19} + !16 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !1, line: 5, type: !7) + !17 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !1, line: 5, type: !7) + !18 = !DILocalVariable(name: "c", arg: 3, scope: !12, file: !1, line: 5, type: !7) + !19 = !DILocalVariable(name: "q", scope: !12, file: !1, line: 7, type: !7) + !20 = !DILocation(line: 0, scope: !12) + !21 = !DILocation(line: 7, column: 15, scope: !12) + !22 = !DILocation(line: 9, column: 5, scope: !12) + !23 = !DILocation(line: 11, column: 11, scope: !24) + !24 = distinct !DILexicalBlock(scope: !12, file: !1, line: 11, column: 9) + !25 = !DILocation(line: 11, column: 9, scope: !12) + !26 = !DILocation(line: 12, column: 13, scope: !27) + !27 = distinct !DILexicalBlock(scope: !24, file: !1, line: 11, column: 17) + !28 = !DILocation(line: 13, column: 8, scope: !27) + !29 = !DILocation(line: 14, column: 5, scope: !27) + !30 = !DILocation(line: 15, column: 13, scope: !31) + !31 = distinct !DILexicalBlock(scope: !24, file: !1, line: 14, column: 12) + !32 = !DILocation(line: 16, column: 7, scope: !31) + !33 = !DILocation(line: 0, scope: !24) + !34 = !DILocation(line: 19, column: 5, scope: !12) + +... +--- +name: fn2 +alignment: 16 +fixedStack: + - { id: 0, type: spill-slot, offset: -24, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$rbx', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbp', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } +body: | + bb.0.entry: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $edi, $edx, $esi, $rbp, $rbx + + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + DBG_VALUE $esi, $noreg, !17, !DIExpression(), debug-location !20 + DBG_VALUE $edx, $noreg, !18, !DIExpression(), debug-location !20 + frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 24 + frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 32 + CFI_INSTRUCTION offset $rbx, -24 + CFI_INSTRUCTION offset $rbp, -16 + $ebp = MOV32rr $edx + DBG_VALUE $ebp, $noreg, !18, !DIExpression(), debug-location !20 + renamable $edi = KILL $edi, implicit-def $rdi + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + renamable $edx = LEA64_32r killed renamable $rdi, 1, $noreg, 2, $noreg, debug-location !21 + DBG_VALUE $edx, $noreg, !19, !DIExpression(), debug-location !20 + $edi = MOV32ri 5, debug-location !22 + CMP32ri8 renamable $ebp, 16, implicit-def $eflags, debug-location !23 + JCC_1 %bb.2, 15, implicit killed $eflags, debug-location !25 + + bb.1.if.then: + successors: %bb.3(0x80000000) + liveins: $ebp, $ebx, $esi + + $ebx = MOV32rr $esi + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $esi = MOV32ri 5, debug-location !28 + $ebx = MOV32ri 1 + JMP_1 %bb.3 + + bb.2.if.else: + successors: %bb.3(0x80000000) + liveins: $ebp, $ebx, $esi + + $ebx = MOV32rr $esi + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $esi = MOV32ri 1, debug-location !32 + $ebx = MOV32ri 2 + + bb.3.if.end: + liveins: $ebx, $edi, $ebp + + $esi = MOV32rr $ebx, debug-location !33 + $edx = MOV32rr killed $ebp, debug-location !33 + CALL64pcrel32 @fn1, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $edx, implicit-def $rsp, implicit-def $ssp, debug-location !33 + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $eax = MOV32rr killed $ebx, debug-location !34 + $rsp = frame-destroy ADD64ri8 $rsp, 8, implicit-def dead $eflags, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 24, debug-location !34 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 16, debug-location !34 + $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !34 + RETQ killed $eax, debug-location !34 + +... diff --git a/llvm/test/DebugInfo/MIR/X86/kill-entry-value-after-diamond-bbs.mir b/llvm/test/DebugInfo/MIR/X86/kill-entry-value-after-diamond-bbs.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/kill-entry-value-after-diamond-bbs.mir @@ -0,0 +1,180 @@ +# RUN: llc -debug-entry-values -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s +# +# The test case was artificially adjusted, in order to make proper diamond basic +# block structure relevant to the debug entry values clobbering. +# +# CHECK: ![[ARG_B:.*]] = !DILocalVariable(name: "b" +# CHECK: bb.0.entry +# CHECK: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression() +# CHECK: bb.1.if.then +# CHECK: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression() +# CHECK: $ebx = MOV32rr $esi +# CHECK-NEXT: DBG_VALUE $ebx, $noreg, ![[ARG_B]], !DIExpression() +# CHECK-NEXT: $esi = MOV32ri 5 +# CHECK-NEXT: $ebx = MOV32ri 1 +# CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: bb.2.if.else +# CHECK: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression() +# CHECK: $ebp = MOV32rr $esi +# CHECK: DBG_VALUE $ebp, $noreg, ![[ARG_B]], !DIExpression() +# CHECK-NEXT: $esi = MOV32ri 1 +# CHECK-NEXT: $ebp = MOV32ri 2 +# CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: bb.3.if.end +# CHECK-NOT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# +--- | + ; ModuleID = 'test.c' + source_filename = "test.c" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: noinline nounwind uwtable + define dso_local i32 @fn2(i32 %a, i32 %b, i32 %c) local_unnamed_addr !dbg !12 { + entry: + call void @llvm.dbg.value(metadata i32 %a, metadata !16, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %b, metadata !17, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %c, metadata !18, metadata !DIExpression()), !dbg !20 + %add = add nsw i32 %a, 2, !dbg !21 + call void @llvm.dbg.value(metadata i32 %add, metadata !19, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 5, i32 6, i32 %add), !dbg !22 + %cmp = icmp slt i32 %b, 17, !dbg !23 + br i1 %cmp, label %if.then, label %if.else, !dbg !25 + + if.then: ; preds = %entry + %add1 = add nsw i32 %b, 7, !dbg !26 + call void @llvm.dbg.value(metadata i32 %add1, metadata !17, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 5, i32 %add1, i32 %c), !dbg !28 + br label %if.end, !dbg !29 + + if.else: ; preds = %entry + %add2 = add nuw nsw i32 %b, 1, !dbg !30 + call void @llvm.dbg.value(metadata i32 %add2, metadata !17, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 1, i32 %add2, i32 %c), !dbg !32 + br label %if.end + + if.end: ; preds = %if.else, %if.then + %b.addr.0 = phi i32 [ %add1, %if.then ], [ %add2, %if.else ], !dbg !33 + call void @llvm.dbg.value(metadata i32 %b.addr.0, metadata !17, metadata !DIExpression()), !dbg !20 + ret i32 %b.addr.0, !dbg !34 + } + + declare !dbg !4 dso_local void @fn1(i32, i32, i32) local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!8, !9, !10} + !llvm.ident = !{!11} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "test.c", directory: "/dir") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "fn1", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{null, !7, !7, !7} + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 2, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{!"clang version 10.0.0"} + !12 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 5, type: !13, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) + !13 = !DISubroutineType(types: !14) + !14 = !{!7, !7, !7, !7} + !15 = !{!16, !17, !18, !19} + !16 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !1, line: 5, type: !7) + !17 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !1, line: 5, type: !7) + !18 = !DILocalVariable(name: "c", arg: 3, scope: !12, file: !1, line: 5, type: !7) + !19 = !DILocalVariable(name: "q", scope: !12, file: !1, line: 7, type: !7) + !20 = !DILocation(line: 0, scope: !12) + !21 = !DILocation(line: 7, column: 15, scope: !12) + !22 = !DILocation(line: 9, column: 5, scope: !12) + !23 = !DILocation(line: 11, column: 11, scope: !24) + !24 = distinct !DILexicalBlock(scope: !12, file: !1, line: 11, column: 9) + !25 = !DILocation(line: 11, column: 9, scope: !12) + !26 = !DILocation(line: 12, column: 13, scope: !27) + !27 = distinct !DILexicalBlock(scope: !24, file: !1, line: 11, column: 17) + !28 = !DILocation(line: 13, column: 8, scope: !27) + !29 = !DILocation(line: 14, column: 5, scope: !27) + !30 = !DILocation(line: 15, column: 13, scope: !31) + !31 = distinct !DILexicalBlock(scope: !24, file: !1, line: 14, column: 12) + !32 = !DILocation(line: 16, column: 7, scope: !31) + !33 = !DILocation(line: 0, scope: !24) + !34 = !DILocation(line: 19, column: 5, scope: !12) + +... +--- +name: fn2 +alignment: 16 +fixedStack: + - { id: 0, type: spill-slot, offset: -24, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$rbx', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbp', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } +body: | + bb.0.entry: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $edi, $edx, $esi, $rbp, $rbx + + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + DBG_VALUE $esi, $noreg, !17, !DIExpression(), debug-location !20 + DBG_VALUE $edx, $noreg, !18, !DIExpression(), debug-location !20 + frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 24 + frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 32 + CFI_INSTRUCTION offset $rbx, -24 + CFI_INSTRUCTION offset $rbp, -16 + $ebp = MOV32rr $edx + DBG_VALUE $ebp, $noreg, !18, !DIExpression(), debug-location !20 + renamable $edi = KILL $edi, implicit-def $rdi + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + renamable $edx = LEA64_32r killed renamable $rdi, 1, $noreg, 2, $noreg, debug-location !21 + DBG_VALUE $edx, $noreg, !19, !DIExpression(), debug-location !20 + $edi = MOV32ri 5, debug-location !22 + CMP32ri8 renamable $ebp, 16, implicit-def $eflags, debug-location !23 + JCC_1 %bb.2, 15, implicit killed $eflags, debug-location !25 + + bb.1.if.then: + successors: %bb.3(0x80000000) + liveins: $ebp, $ebx, $esi + + $ebx = MOV32rr $esi + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $esi = MOV32ri 5, debug-location !28 + $ebx = MOV32ri 1 + JMP_1 %bb.3 + + bb.2.if.else: + successors: %bb.3(0x80000000) + liveins: $ebp, $ebx, $esi + + $ebp = MOV32rr $esi + DBG_VALUE $ebp, $noreg, !17, !DIExpression(), debug-location !20 + $esi = MOV32ri 1, debug-location !32 + $ebp = MOV32ri 2 + + bb.3.if.end: + liveins: $ebx, $edi, $ebp + + $esi = MOV32rr $ebx, debug-location !33 + $edx = MOV32rr killed $ebp, debug-location !33 + CALL64pcrel32 @fn1, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $edx, implicit-def $rsp, implicit-def $ssp, debug-location !33 + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $eax = MOV32rr killed $ebx, debug-location !34 + $rsp = frame-destroy ADD64ri8 $rsp, 8, implicit-def dead $eflags, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 24, debug-location !34 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 16, debug-location !34 + $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !34 + RETQ killed $eax, debug-location !34 + +... + diff --git a/llvm/test/DebugInfo/MIR/X86/propagate-entry-value-cross-bbs.mir b/llvm/test/DebugInfo/MIR/X86/propagate-entry-value-cross-bbs.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/propagate-entry-value-cross-bbs.mir @@ -0,0 +1,184 @@ +# RUN: llc -debug-entry-values -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s +# +#extern void fn1 (int, int, int); +#__attribute__((noinline)) +#int +#fn2 (int a, int b, int c) { +# int q = 2 + a; +# fn1 (5, 6, q); +# if (b < 17) { +# b = b + 7; +# fn1 (5, b, q); +# } else { +# b = b + 1; +# fn1 (1, b, q); +# } +# return b; +#} +# CHECK: ![[ARG_C:.*]] = !DILocalVariable(name: "c" +# CHECK: bb.0.entry: +# CHECK: DBG_VALUE $edx, $noreg, ![[ARG_C]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: bb.1.if.then: +# CHECK: DBG_VALUE $edx, $noreg, ![[ARG_C]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: bb.2.if.else: +# CHECK: DBG_VALUE $edx, $noreg, ![[ARG_C]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK: bb.3.if.end: +# CHECK: DBG_VALUE $edx, $noreg, ![[ARG_C]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# +--- | + ; ModuleID = 'test.c' + source_filename = "test.c" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: noinline nounwind uwtable + define dso_local i32 @fn2(i32 %a, i32 %b, i32 %c) local_unnamed_addr !dbg !12 { + entry: + call void @llvm.dbg.value(metadata i32 %a, metadata !16, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %b, metadata !17, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %c, metadata !18, metadata !DIExpression()), !dbg !20 + %add = add nsw i32 %a, 2, !dbg !21 + call void @llvm.dbg.value(metadata i32 %add, metadata !19, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 5, i32 6, i32 %add), !dbg !22 + %cmp = icmp slt i32 %b, 17, !dbg !23 + br i1 %cmp, label %if.then, label %if.else, !dbg !25 + + if.then: ; preds = %entry + %add1 = add nsw i32 %b, 7, !dbg !26 + call void @llvm.dbg.value(metadata i32 %add1, metadata !17, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 5, i32 %add1, i32 %add), !dbg !28 + br label %if.end, !dbg !29 + + if.else: ; preds = %entry + %add2 = add nuw nsw i32 %b, 1, !dbg !30 + call void @llvm.dbg.value(metadata i32 %add2, metadata !17, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 1, i32 %add2, i32 %add), !dbg !32 + br label %if.end + + if.end: ; preds = %if.else, %if.then + %b.addr.0 = phi i32 [ %add1, %if.then ], [ %add2, %if.else ], !dbg !33 + call void @llvm.dbg.value(metadata i32 %b.addr.0, metadata !17, metadata !DIExpression()), !dbg !20 + ret i32 %b.addr.0, !dbg !34 + } + + declare !dbg !4 dso_local void @fn1(i32, i32, i32) local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!8, !9, !10} + !llvm.ident = !{!11} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "test.c", directory: "/") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "fn1", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{null, !7, !7, !7} + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 2, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{!"clang version 10.0.0"} + !12 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 5, type: !13, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) + !13 = !DISubroutineType(types: !14) + !14 = !{!7, !7, !7, !7} + !15 = !{!16, !17, !18, !19} + !16 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !1, line: 5, type: !7) + !17 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !1, line: 5, type: !7) + !18 = !DILocalVariable(name: "c", arg: 3, scope: !12, file: !1, line: 5, type: !7) + !19 = !DILocalVariable(name: "q", scope: !12, file: !1, line: 7, type: !7) + !20 = !DILocation(line: 0, scope: !12) + !21 = !DILocation(line: 7, column: 15, scope: !12) + !22 = !DILocation(line: 9, column: 5, scope: !12) + !23 = !DILocation(line: 11, column: 11, scope: !24) + !24 = distinct !DILexicalBlock(scope: !12, file: !1, line: 11, column: 9) + !25 = !DILocation(line: 11, column: 9, scope: !12) + !26 = !DILocation(line: 12, column: 13, scope: !27) + !27 = distinct !DILexicalBlock(scope: !24, file: !1, line: 11, column: 17) + !28 = !DILocation(line: 13, column: 8, scope: !27) + !29 = !DILocation(line: 14, column: 5, scope: !27) + !30 = !DILocation(line: 15, column: 13, scope: !31) + !31 = distinct !DILexicalBlock(scope: !24, file: !1, line: 14, column: 12) + !32 = !DILocation(line: 16, column: 7, scope: !31) + !33 = !DILocation(line: 0, scope: !24) + !34 = !DILocation(line: 19, column: 5, scope: !12) + +... +--- +name: fn2 +alignment: 16 +callSites: + - { bb: 0, offset: 20, fwdArgRegs: + - { arg: 0, reg: '$edi' } + - { arg: 1, reg: '$esi' } + - { arg: 2, reg: '$edx' } } + - { bb: 3, offset: 2, fwdArgRegs: + - { arg: 0, reg: '$edi' } + - { arg: 1, reg: '$esi' } + - { arg: 2, reg: '$edx' } } +body: | + bb.0.entry: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $edi, $esi, $rbp, $rbx + + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + DBG_VALUE $esi, $noreg, !17, !DIExpression(), debug-location !20 + DBG_VALUE $edx, $noreg, !18, !DIExpression(), debug-location !20 + frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 24 + frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 32 + CFI_INSTRUCTION offset $rbx, -24 + CFI_INSTRUCTION offset $rbp, -16 + $ebx = MOV32rr $esi + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $ebp = MOV32rr $edi + DBG_VALUE $ebp, $noreg, !16, !DIExpression(), debug-location !20 + renamable $ebp = nsw ADD32ri8 killed renamable $ebp, 2, implicit-def dead $eflags, debug-location !21 + DBG_VALUE $ebp, $noreg, !19, !DIExpression(), debug-location !20 + $edi = MOV32ri 5, debug-location !22 + $esi = MOV32ri 6, debug-location !22 + $edx = MOV32rr $ebp, debug-location !22 + CALL64pcrel32 @fn1, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit killed $edx, implicit-def $rsp, implicit-def $ssp, debug-location !22 + CMP32ri8 renamable $ebx, 16, implicit-def $eflags, debug-location !23 + JCC_1 %bb.2, 15, implicit killed $eflags, debug-location !25 + + bb.1.if.then: + successors: %bb.3(0x80000000) + liveins: $ebp, $ebx + + renamable $ebx = nsw ADD32ri8 killed renamable $ebx, 7, implicit-def dead $eflags, debug-location !26 + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $edi = MOV32ri 5, debug-location !28 + JMP_1 %bb.3 + + bb.2.if.else: + successors: %bb.3(0x80000000) + liveins: $ebp, $ebx + + renamable $ebx = nuw nsw ADD32ri8 killed renamable $ebx, 1, implicit-def dead $eflags, debug-location !30 + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $edi = MOV32ri 1, debug-location !32 + + bb.3.if.end: + liveins: $ebx, $edi, $ebp + + $esi = MOV32rr $ebx, debug-location !33 + $edx = MOV32rr killed $ebp, debug-location !33 + CALL64pcrel32 @fn1, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit killed $edx, implicit-def $rsp, implicit-def $ssp, debug-location !33 + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + $eax = MOV32rr killed $ebx, debug-location !34 + $rsp = frame-destroy ADD64ri8 $rsp, 8, implicit-def dead $eflags, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 24, debug-location !34 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 16, debug-location !34 + $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !34 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !34 + RETQ killed $eax, debug-location !34 + +...