diff --git a/llvm/lib/Target/ARM/ARMLoadStoreOptimizer.cpp b/llvm/lib/Target/ARM/ARMLoadStoreOptimizer.cpp --- a/llvm/lib/Target/ARM/ARMLoadStoreOptimizer.cpp +++ b/llvm/lib/Target/ARM/ARMLoadStoreOptimizer.cpp @@ -2172,10 +2172,10 @@ unsigned &NewOpc, Register &EvenReg, Register &OddReg, Register &BaseReg, int &Offset, Register &PredReg, ARMCC::CondCodes &Pred, bool &isT2); - bool RescheduleOps(MachineBasicBlock *MBB, - SmallVectorImpl &Ops, - unsigned Base, bool isLd, - DenseMap &MI2LocMap); + bool RescheduleOps( + MachineBasicBlock *MBB, SmallVectorImpl &Ops, + unsigned Base, bool isLd, DenseMap &MI2LocMap, + SmallDenseMap, 8> &RegisterMap); bool RescheduleLoadStoreInstrs(MachineBasicBlock *MBB); bool DistributeIncrements(); bool DistributeIncrements(Register Base); @@ -2324,10 +2324,10 @@ return true; } -bool ARMPreAllocLoadStoreOpt::RescheduleOps(MachineBasicBlock *MBB, - SmallVectorImpl &Ops, - unsigned Base, bool isLd, - DenseMap &MI2LocMap) { +bool ARMPreAllocLoadStoreOpt::RescheduleOps( + MachineBasicBlock *MBB, SmallVectorImpl &Ops, unsigned Base, + bool isLd, DenseMap &MI2LocMap, + SmallDenseMap, 8> &RegisterMap) { bool RetVal = false; // Sort by offset (in reverse order). @@ -2476,6 +2476,12 @@ } else { for (unsigned i = 0; i != NumMove; ++i) { MachineInstr *Op = Ops.pop_back_val(); + if (isLd) { + // Populate RegisterMap with all Registers defined by loads. + Register Reg = Op->getOperand(0).getReg(); + RegisterMap[Reg]; + } + MBB->splice(InsertPos, MBB, Op); } } @@ -2489,6 +2495,44 @@ return RetVal; } +static void forEachDbgRegOperand(MachineInstr *MI, + std::function Fn) { + if (MI->isNonListDebugValue()) { + auto &Op = MI->getOperand(0); + if (Op.isReg()) + Fn(Op); + } else { + for (unsigned I = 2; I < MI->getNumOperands(); I++) { + auto &Op = MI->getOperand(I); + if (Op.isReg()) + Fn(Op); + } + } +} + +// Update the RegisterMap with the instruction that was moved because a +// DBG_VALUE_LIST may need to be moved again. +static void updateRegisterMapForDbgValueListAfterMove( + SmallDenseMap, 8> &RegisterMap, + MachineInstr *DbgValueListInstr, MachineInstr *InstrToReplace) { + + forEachDbgRegOperand(DbgValueListInstr, [&](MachineOperand &Op) { + auto RegIt = RegisterMap.find(Op.getReg()); + if (RegIt == RegisterMap.end()) + return; + auto &InstrVec = RegIt->getSecond(); + for (unsigned I = 0; I < InstrVec.size(); I++) + if (InstrVec[I] == InstrToReplace) + InstrVec[I] = DbgValueListInstr; + }); +} + +static DebugVariable createDebugVariableFromMachineInstr(MachineInstr *MI) { + auto DbgVar = DebugVariable(MI->getDebugVariable(), MI->getDebugExpression(), + MI->getDebugLoc()->getInlinedAt()); + return DbgVar; +} + bool ARMPreAllocLoadStoreOpt::RescheduleLoadStoreInstrs(MachineBasicBlock *MBB) { bool RetVal = false; @@ -2501,6 +2545,10 @@ Base2InstMap Base2StsMap; BaseVec LdBases; BaseVec StBases; + // This map is used to track the relationship between the virtual + // register that is the result of a load that is moved and the DBG_VALUE + // MachineInstr pointer that uses that virtual register. + SmallDenseMap, 8> RegisterMap; unsigned Loc = 0; MachineBasicBlock::iterator MBBI = MBB->begin(); @@ -2563,7 +2611,7 @@ unsigned Base = LdBases[i]; SmallVectorImpl &Lds = Base2LdsMap[Base]; if (Lds.size() > 1) - RetVal |= RescheduleOps(MBB, Lds, Base, true, MI2LocMap); + RetVal |= RescheduleOps(MBB, Lds, Base, true, MI2LocMap, RegisterMap); } // Re-schedule stores. @@ -2571,7 +2619,7 @@ unsigned Base = StBases[i]; SmallVectorImpl &Sts = Base2StsMap[Base]; if (Sts.size() > 1) - RetVal |= RescheduleOps(MBB, Sts, Base, false, MI2LocMap); + RetVal |= RescheduleOps(MBB, Sts, Base, false, MI2LocMap, RegisterMap); } if (MBBI != E) { @@ -2582,6 +2630,257 @@ } } + // Reschedule DBG_VALUEs to match any loads that were moved. When a load is + // sunk beyond a DBG_VALUE that is referring to it, the DBG_VALUE becomes a + // use-before-def, resulting in a loss of debug info. + + // Example: + // Before the Pre Register Allocation Load Store Pass + // inst_a + // %2 = ld ... + // inst_b + // DBG_VALUE %2, "x", ... + // %3 = ld ... + + // After the Pass: + // inst_a + // inst_b + // DBG_VALUE %2, "x", ... + // %2 = ld ... + // %3 = ld ... + + // The code below addresses this by moving the DBG_VALUE to the position + // immediately after the load. + + // Example: + // After the code below: + // inst_a + // inst_b + // %2 = ld ... + // DBG_VALUE %2, "x", ... + // %3 = ld ... + + // The algorithm works in two phases: First RescheduleOps() populates the + // RegisterMap with registers that were moved as keys, there is no value + // inserted. In the next phase, every MachineInstr in a basic block is + // iterated over. If it is a valid DBG_VALUE or DBG_VALUE_LIST and it uses one + // or more registers in the RegisterMap, the RegisterMap and InstrMap are + // populated with the MachineInstr. If the DBG_VALUE or DBG_VALUE_LIST + // describes debug information for a variable that already exists in the + // DbgValueSinkCandidates, the MachineInstr in the DbgValueSinkCandidates must + // be set to undef. If the current MachineInstr is a load that was moved, + // undef the corresponding DBG_VALUE or DBG_VALUE_LIST and clone it to below + // the load. + + // To illustrate the above algorithm visually let's take this example. + + // Before the Pre Register Allocation Load Store Pass: + // %2 = ld ... + // DBG_VALUE %2, A, .... # X + // DBG_VALUE 0, A, ... # Y + // %3 = ld ... + // DBG_VALUE %3, A, ..., # Z + // %4 = ld ... + + // After Pre Register Allocation Load Store Pass: + // DBG_VALUE %2, A, .... # X + // DBG_VALUE 0, A, ... # Y + // DBG_VALUE %3, A, ..., # Z + // %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // The algorithm below does the following: + + // In the beginning, the RegisterMap will have been populated with the virtual + // registers %2, and %3, the DbgValueSinkCandidates and the InstrMap will be + // empty. DbgValueSinkCandidates = {}, RegisterMap = {2 -> {}, 3 -> {}}, + // InstrMap {} + // -> DBG_VALUE %2, A, .... # X + // DBG_VALUE 0, A, ... # Y + // DBG_VALUE %3, A, ..., # Z + // %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // After the first DBG_VALUE (denoted with an X) is processed, the + // DbgValueSinkCandidates and InstrMap will be populated and the RegisterMap + // entry for %2 will be populated as well. DbgValueSinkCandidates = {A -> X}, + // RegisterMap = {2 -> {X}, 3 -> {}}, InstrMap {X -> 2} + // DBG_VALUE %2, A, .... # X + // -> DBG_VALUE 0, A, ... # Y + // DBG_VALUE %3, A, ..., # Z + // %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // After the DBG_VALUE Y is processed, the DbgValueSinkCandidates is updated + // to now hold Y for A and the RegisterMap is also updated to remove X from + // %2, this is because both X and Y describe the same debug variable A. X is + // also updated to have a $noreg as the first operand. + // DbgValueSinkCandidates = {A -> {Y}}, RegisterMap = {2 -> {}, 3 -> {}}, + // InstrMap = {X-> 2} + // DBG_VALUE $noreg, A, .... # X + // DBG_VALUE 0, A, ... # Y + // -> DBG_VALUE %3, A, ..., # Z + // %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // After DBG_VALUE Z is processed, the DbgValueSinkCandidates is updated to + // hold Z fr A, the RegisterMap is updated to hold Z for %3, and the InstrMap + // is updated to have Z mapped to %3. This is again because Z describes the + // debug variable A, Y is not updated to have $noreg as first operand because + // its first operand is an immediate, not a register. + // DbgValueSinkCandidates = {A -> {Z}}, RegisterMap = {2 -> {}, 3 -> {Z}}, + // InstrMap = {X -> 2, Z -> 3} + // DBG_VALUE $noreg, A, .... # X + // DBG_VALUE 0, A, ... # Y + // DBG_VALUE %3, A, ..., # Z + // -> %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // Nothing happens here since the RegisterMap for %2 contains no value. + // DbgValueSinkCandidates = {A -> {Z}}, RegisterMap = {2 -> {}, 3 -> {Z}}, + // InstrMap = {X -> 2, Z -> 3} + // DBG_VALUE $noreg, A, .... # X + // DBG_VALUE 0, A, ... # Y + // DBG_VALUE %3, A, ..., # Z + // %2 = ld ... + // -> %3 = ld ... + // %4 = ld ... + + // Since the RegisterMap contains Z as a value for %3, the MachineInstr + // pointer Z is copied to come after the load for %3 and the old Z's first + // operand is changed to $noreg the Basic Block iterator is moved to after the + // DBG_VALUE Z's new position. + // DbgValueSinkCandidates = {A -> {Z}}, RegisterMap = {2 -> {}, 3 -> {Z}}, + // InstrMap = {X -> 2, Z -> 3} + // DBG_VALUE $noreg, A, .... # X + // DBG_VALUE 0, A, ... # Y + // DBG_VALUE $noreg, A, ..., # Old Z + // %2 = ld ... + // %3 = ld ... + // DBG_VALUE %3, A, ..., # Z + // -> %4 = ld ... + + // Nothing happens for %4 and the algorithm exits having processed the entire + // Basic Block. + // DbgValueSinkCandidates = {A -> {Z}}, RegisterMap = {2 -> {}, 3 -> {Z}}, + // InstrMap = {X -> 2, Z -> 3} + // DBG_VALUE $noreg, A, .... # X + // DBG_VALUE 0, A, ... # Y + // DBG_VALUE $noreg, A, ..., # Old Z + // %2 = ld ... + // %3 = ld ... + // DBG_VALUE %3, A, ..., # Z + // %4 = ld ... + + // This map is used to track the relationship between + // a Debug Variable and the DBG_VALUE MachineInstr pointer that describes the + // debug information for that Debug Variable. + SmallDenseMap DbgValueSinkCandidates; + // This map is used to track the relationship between a DBG_VALUE or + // DBG_VALUE_LIST MachineInstr pointer and Registers that it uses. + SmallDenseMap, 8> InstrMap; + for (MBBI = MBB->begin(), E = MBB->end(); MBBI != E; ++MBBI) { + MachineInstr &MI = *MBBI; + + auto PopulateRegisterAndInstrMapForDebugInstr = [&](Register Reg) { + auto RegIt = RegisterMap.find(Reg); + if (RegIt == RegisterMap.end()) + return; + auto &InstrVec = RegIt->getSecond(); + InstrVec.push_back(&MI); + InstrMap[&MI].push_back(Reg); + }; + + if (MI.isDebugValue()) { + auto *DILocalVar = MI.getDebugVariable(); + // TODO: This should not happen, have to fix the MIR verifier to check for + // such instances and fix them. + if (!DILocalVar) + continue; + auto DbgVar = createDebugVariableFromMachineInstr(&MI); + // If the first operand is a register and it exists in the RegisterMap, we + // know this is a DBG_VALUE that uses the result of a load that was moved, + // and is therefore a candidate to also be moved, add it to the + // RegisterMap and InstrMap. + forEachDbgRegOperand(&MI, [&](MachineOperand &Op) { + PopulateRegisterAndInstrMapForDebugInstr(Op.getReg()); + }); + + // If the current DBG_VALUE describes the same variable as one of the + // in-flight DBG_VALUEs, remove the candidate from the list and set it to + // undef. Moving one DBG_VALUE past another would result in the variable's + // value going back in time when stepping through the block in the + // debugger. + auto InstrIt = DbgValueSinkCandidates.find(DbgVar); + if (InstrIt != DbgValueSinkCandidates.end()) { + auto *Instr = InstrIt->getSecond(); + auto RegIt = InstrMap.find(Instr); + if (RegIt != InstrMap.end()) { + const auto &RegVec = RegIt->getSecond(); + // For every Register in the RegVec, remove the MachineInstr in the + // RegisterMap that describes the DbgVar. + for (auto &Reg : RegVec) { + auto RegIt = RegisterMap.find(Reg); + if (RegIt == RegisterMap.end()) + continue; + auto &InstrVec = RegIt->getSecond(); + auto IsDbgVar = [&](MachineInstr *I) -> bool { + auto Var = createDebugVariableFromMachineInstr(I); + return Var == DbgVar; + }; + + InstrVec.erase( + std::remove_if(InstrVec.begin(), InstrVec.end(), IsDbgVar), + InstrVec.end()); + } + forEachDbgRegOperand(Instr, + [&](MachineOperand &Op) { Op.setReg(0); }); + } + } + DbgValueSinkCandidates[DbgVar] = &MI; + } else { + // If the first operand of a load matches with a DBG_VALUE in RegisterMap, + // then move that DBG_VALUE to below the load. + auto Opc = MI.getOpcode(); + if (!isLoadSingle(Opc)) + continue; + auto Reg = MI.getOperand(0).getReg(); + auto RegIt = RegisterMap.find(Reg); + if (RegIt == RegisterMap.end()) + continue; + auto &DbgInstrVec = RegIt->getSecond(); + if (!DbgInstrVec.size()) + continue; + for (auto *DbgInstr : DbgInstrVec) { + MachineBasicBlock::iterator InsertPos = std::next(MBBI); + auto *ClonedMI = MI.getMF()->CloneMachineInstr(DbgInstr); + MBB->insert(InsertPos, ClonedMI); + MBBI++; + // Erase the entry into the DbgValueSinkCandidates for the DBG_VALUE + // that was moved. + auto DbgVar = createDebugVariableFromMachineInstr(DbgInstr); + auto DbgIt = DbgValueSinkCandidates.find(DbgVar); + // If the instruction is a DBG_VALUE_LIST, it may have already been + // erased from the DbgValueSinkCandidates. Only erase if it exists in + // the DbgValueSinkCandidates. + if (DbgIt != DbgValueSinkCandidates.end()) + DbgValueSinkCandidates.erase(DbgIt); + // Zero out original dbg instr + forEachDbgRegOperand(DbgInstr, + [&](MachineOperand &Op) { Op.setReg(0); }); + // Update RegisterMap with ClonedMI because it might have to be moved + // again. + if (DbgInstr->isDebugValueList()) + updateRegisterMapForDbgValueListAfterMove(RegisterMap, ClonedMI, + DbgInstr); + } + } + } return RetVal; } diff --git a/llvm/test/DebugInfo/ARM/move-dbg-value-after-value-list.mir b/llvm/test/DebugInfo/ARM/move-dbg-value-after-value-list.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/ARM/move-dbg-value-after-value-list.mir @@ -0,0 +1,66 @@ +# RUN: llc %s -start-after=arm-mve-vpt-opts -stop-after=arm-prera-ldst-opt -o - | FileCheck %s +# CHECK: %5:rgpr = t2MOVCCr %3, killed %4, 0 /* CC::eq */, $cpsr, debug-location !14 +# CHECK-NEXT: DBG_VALUE 0, $noreg, !10, !DIExpression(), debug-location !14 +# CHECK-NEXT: DBG_VALUE 1, $noreg, !10, !DIExpression(), debug-location !14 +# CHECK-NEXT: DBG_VALUE_LIST !10, !DIExpression(), $noreg, $noreg, debug-location !14 +# CHECK-NEXT: DBG_VALUE $noreg, $noreg, !10, !DIExpression(), debug-location !14 +# CHECK-NEXT: %0:rgpr = t2LDRi12 %5, 0, 14 /* CC::al */, $noreg, debug-location !14 :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) +# CHECK-NEXT: %6:rgpr = t2LDRi12 %5, 4, 14 /* CC::al */, $noreg, debug-location !14 :: (load (s32) from %ir.btc_user_copy) +# CHECK-NEXT: %2:gpr = t2LDRi12 %5, 8, 14 /* CC::al */, $noreg, debug-location !14 :: (load (s32) from %ir.btc_user_copy_context, align 8) +# CHECK-NEXT: DBG_VALUE %2, $noreg, !10, !DIExpression(), debug-location !14 +# CHECK-NEXT: %7:rgpr = t2ANDrr %6, %0, 14 /* CC::al */, $noreg, def $cpsr +# CHECK-NEXT: %8:rgpr = t2ANDrr %6, %0, 14 /* CC::al */, $noreg, def $cpsr +# CHECK-NEXT: %9:rgpr = t2ANDrr %6, %0, 14 /* CC::al */, $noreg, def $cpsr +# CHECK-NEXT: %10:rgpr = t2ANDrr %6, %0, 14 /* CC::al */, $noreg, def $cpsr + +# This test specifically checks if a DBG_VALUE_LIST that is succeeded by a DBG_VALUE can be properly undefed and the DBG_VALUE is the one that is moved. Assuming both instructions describe the same local variable. + +--- | + target triple = "thumbv7k-apple-watchos8.0.0" + %struct.backtrace_control = type {} + @backtrace_user.ctl_default = internal unnamed_addr constant %struct.backtrace_control zeroinitializer, !dbg !0 + define i32 @backtrace_user(ptr nocapture noundef writeonly %bt, i32 noundef %max_frames, ptr noundef readonly %ctl_in, ptr nocapture noundef readnone %info_out) local_unnamed_addr #0 !dbg !2 {entry: + %tobool.not = icmp eq ptr %ctl_in, null, !dbg !95 + %.backtrace_user.ctl_default = select i1 %tobool.not, ptr @backtrace_user.ctl_default, ptr %ctl_in, !dbg !95 + %btc_user_copy = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !95 + %btc_user_copy_context = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !95 + ret i32 undef, !dbg !95 + } + !llvm.module.flags = !{!88, !93} + !llvm.dbg.cu = !{!32} + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(type: !11, isDefinition: true) + !2 = distinct !DISubprogram(unit: !32, retainedNodes: !38) + !3 = !DIFile(filename: "backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !11 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !12) + !12 = distinct !DICompositeType(tag: DW_TAG_member, offset: 32) + !32 = distinct !DICompileUnit(language: DW_LANG_C11, file: !33, sdk: "MacOSX13.0.sdk") + !33 = !DIFile(filename: "/Users/shubham/Development/Delta/backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !38 = !{!60, !85} + !60 = !DILocalVariable(scope: !2, type: !62) + !62 = distinct !DICompositeType(tag: DW_TAG_union_type, size: 128) + !85 = !DILabel(scope: !2, name: "out", file: !3, line: 102) + !88 = !{i32 2, !"Debug Info Version", i32 3} + !93 = !{i32 7, !"frame-pointer", i32 1} + !95 = !DILocation(line: 5, column: 10, scope: !2) +name: backtrace_user +registers: + - {id: 0, class: rgpr, } + - {id: 1, class: gpr, } + - {id: 2, class: gpr, } +body: | + bb.0.entry: + %10:rgpr = COPY $r2 + %13:rgpr = t2MOV_ga_pcrel target-flags(arm-nonlazy) @backtrace_user.ctl_default, debug-location !95 + %15:rgpr = t2MOVCCr %10, killed %13, 0 /* CC::eq */, $cpsr, debug-location !95 + DBG_VALUE 0, $noreg, !60, !DIExpression(), debug-location !95 + %0:rgpr = t2LDRi12 %15, 0, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) + DBG_VALUE 1, $noreg, !60, !DIExpression(), debug-location !95 + %16:rgpr = t2LDRi12 %15, 4, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy) + DBG_VALUE_LIST !60, !DIExpression(), %16, %0, debug-location !95 + %2:gpr = t2LDRi12 %15, 8, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy_context, align 8) + DBG_VALUE %2, $noreg, !60, !DIExpression(), debug-location !95 + %31:rgpr = t2ANDrr %16, %0, 14 /* CC::al */, $noreg, def $cpsr + %32:rgpr = t2ANDrr %16, %0, 14 /* CC::al */, $noreg, def $cpsr + %33:rgpr = t2ANDrr %16, %0, 14 /* CC::al */, $noreg, def $cpsr + %34:rgpr = t2ANDrr %16, %0, 14 /* CC::al */, $noreg, def $cpsr diff --git a/llvm/test/DebugInfo/ARM/move-dbg-value-lists.mir b/llvm/test/DebugInfo/ARM/move-dbg-value-lists.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/ARM/move-dbg-value-lists.mir @@ -0,0 +1,58 @@ +# RUN: llc %s -start-after=arm-mve-vpt-opts -stop-after=arm-prera-ldst-opt -o - | FileCheck %s +# CHECK: %5:rgpr = t2MOVCCr %3, killed %4, 0 /* CC::eq */, $cpsr, debug-location !14 +# CHECK-NEXT: DBG_VALUE 0, $noreg, !10, !DIExpression(), debug-location !14 +# CHECK-NEXT: DBG_VALUE 1, $noreg, !10, !DIExpression(), debug-location !14 +# CHECK-NEXT: DBG_VALUE_LIST !10, !DIExpression(), $noreg, $noreg, debug-location !14 +# CHECK-NEXT: %0:rgpr = t2LDRi12 %5, 0, 14 /* CC::al */, $noreg, debug-location !14 :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) +# CHECK-NEXT: DBG_VALUE_LIST !10, !DIExpression(), $noreg, $noreg, debug-location !14 +# CHECK-NEXT: %6:rgpr = t2LDRi12 %5, 4, 14 /* CC::al */, $noreg, debug-location !14 :: (load (s32) from %ir.btc_user_copy) +# CHECK-NEXT: DBG_VALUE_LIST !10, !DIExpression(), %6, %0, debug-location !14 +# CHECK-NEXT: %2:gpr = t2LDRi12 %5, 8, 14 /* CC::al */, $noreg, debug-location !14 :: (load (s32) from %ir.btc_user_copy_context, align 8) +# CHECK-NEXT: %7:rgpr = t2ANDrr %6, %0, 14 /* CC::al */, $noreg, def $cpsr +# CHECK-NEXT: DBG_VALUE %2, $noreg, !10, !DIExpression(), debug-location !14 +--- | + target triple = "thumbv7k-apple-watchos8.0.0" + %struct.backtrace_control = type {} + @backtrace_user.ctl_default = internal unnamed_addr constant %struct.backtrace_control zeroinitializer, !dbg !0 + define i32 @backtrace_user(ptr nocapture noundef writeonly %bt, i32 noundef %max_frames, ptr noundef readonly %ctl_in, ptr nocapture noundef readnone %info_out) local_unnamed_addr #0 !dbg !2 {entry: + %tobool.not = icmp eq ptr %ctl_in, null, !dbg !95 + %.backtrace_user.ctl_default = select i1 %tobool.not, ptr @backtrace_user.ctl_default, ptr %ctl_in, !dbg !95 + %btc_user_copy = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !95 + %btc_user_copy_context = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !95 + ret i32 undef, !dbg !95 + } + !llvm.module.flags = !{!88, !93} + !llvm.dbg.cu = !{!32} + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(type: !11, isDefinition: true) + !2 = distinct !DISubprogram(unit: !32, retainedNodes: !38) + !3 = !DIFile(filename: "backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !11 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !12) + !12 = distinct !DICompositeType(tag: DW_TAG_member, offset: 32) + !32 = distinct !DICompileUnit(language: DW_LANG_C11, file: !33, sdk: "MacOSX13.0.sdk") + !33 = !DIFile(filename: "/Users/shubham/Development/Delta/backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !38 = !{!60, !85} + !60 = !DILocalVariable(scope: !2, type: !62) + !62 = distinct !DICompositeType(tag: DW_TAG_union_type, size: 64) + !85 = !DILabel(scope: !2, name: "out", file: !3, line: 102) + !88 = !{i32 2, !"Debug Info Version", i32 3} + !93 = !{i32 7, !"frame-pointer", i32 1} + !95 = !DILocation(line: 5, column: 10, scope: !2) +name: backtrace_user +registers: + - {id: 0, class: rgpr, } + - {id: 1, class: gpr, } + - {id: 2, class: gpr, } +body: | + bb.0.entry: + %10:rgpr = COPY $r2 + %13:rgpr = t2MOV_ga_pcrel target-flags(arm-nonlazy) @backtrace_user.ctl_default, debug-location !95 + %15:rgpr = t2MOVCCr %10, killed %13, 0 /* CC::eq */, $cpsr, debug-location !95 + DBG_VALUE 0, $noreg, !60, !DIExpression(), debug-location !95 + %0:rgpr = t2LDRi12 %15, 0, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) + DBG_VALUE 1, $noreg, !60, !DIExpression(), debug-location !95 + %16:rgpr = t2LDRi12 %15, 4, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy) + DBG_VALUE_LIST !60, !DIExpression(), %16, %0, debug-location !95 + %2:gpr = t2LDRi12 %15, 8, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy_context, align 8) + %31:rgpr = t2ANDrr %16, %0, 14 /* CC::al */, $noreg, def $cpsr + DBG_VALUE %2, $noreg, !60, !DIExpression(), debug-location !95 diff --git a/llvm/test/DebugInfo/ARM/move-dbg-value-same-reg.mir b/llvm/test/DebugInfo/ARM/move-dbg-value-same-reg.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/ARM/move-dbg-value-same-reg.mir @@ -0,0 +1,64 @@ +# RUN: llc %s -start-after=arm-mve-vpt-opts -stop-after=arm-prera-ldst-opt -o - | FileCheck %s +# CHECK: %5:rgpr = t2MOVCCr %3, killed %4, 0 /* CC::eq */, $cpsr, debug-location !{{[0-9]+}} +# CHECK-NEXT: DBG_VALUE 0, $noreg, ![[DBG:[0-9]+]], !DIExpression(), debug-location !{{[0-9]+}} +# CHECK-NEXT: DBG_VALUE $noreg, $noreg, ![[DBG]], !DIExpression(), debug-location !{{[0-9]+}} +# CHECK-NEXT: DBG_VALUE $noreg, $noreg, !{{[0-9]+}}, !DIExpression(), debug-location !{{[0-9]+}} +# CHECK-NEXT: %0:rgpr = t2LDRi12 %5, 0, 14 /* CC::al */, $noreg, debug-location !{{[0-9]+}} :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) +# CHECK-NEXT: DBG_VALUE %0, $noreg, ![[DBG]], !DIExpression(), debug-location !{{[0-9]+}} +# CHECK-NEXT: DBG_VALUE %0, $noreg, !{{[0-9]+}}, !DIExpression(), debug-location !{{[0-9]+}} +# CHECK-NEXT: %6:rgpr = t2LDRi12 %5, 4, 14 /* CC::al */, $noreg, debug-location !{{[0-9]+}} :: (load (s32) from %ir.btc_user_copy) +# CHECK-NEXT: %2:gpr = t2LDRi12 %5, 8, 14 /* CC::al */, $noreg, debug-location !{{[0-9]+}} :: (load (s32) from %ir.btc_user_copy_context, align 8) +# CHECK-NEXT: %7:rgpr = t2ANDrr %6, %0, 14 /* CC::al */, $noreg, def $cpsr +# CHECK-NEXT: DBG_VALUE %2, $noreg, ![[DBG]], !DIExpression(), debug-location !{{[0-9]+}} + +# This test checks the case where two DBG_VALUEs share the same virtual register but describe different local variables. + +--- | + target triple = "thumbv7k-apple-watchos8.0.0" + %struct.backtrace_control = type {} + @backtrace_user.ctl_default = internal unnamed_addr constant %struct.backtrace_control zeroinitializer, !dbg !0 + define i32 @backtrace_user(ptr nocapture noundef writeonly %bt, i32 noundef %max_frames, ptr noundef readonly %ctl_in, ptr nocapture noundef readnone %info_out) local_unnamed_addr #0 !dbg !2 {entry: + %tobool.not = icmp eq ptr %ctl_in, null, !dbg !95 + %.backtrace_user.ctl_default = select i1 %tobool.not, ptr @backtrace_user.ctl_default, ptr %ctl_in, !dbg !95 + %btc_user_copy = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !95 + %btc_user_copy_context = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !95 + ret i32 undef, !dbg !95 + } + !llvm.module.flags = !{!88, !93} + !llvm.dbg.cu = !{!32} + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(type: !11, isDefinition: true) + !2 = distinct !DISubprogram(unit: !32, retainedNodes: !38) + !3 = !DIFile(filename: "backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !11 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !12) + !12 = distinct !DICompositeType(tag: DW_TAG_member, offset: 32) + !16 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !21) + !21 = !DIBasicType() + !32 = distinct !DICompileUnit(language: DW_LANG_C11, file: !33, sdk: "MacOSX13.0.sdk") + !33 = !DIFile(filename: "/Users/shubham/Development/Delta/backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !38 = !{!59, !60, !85} + !59 = !DILocalVariable(scope: !2, type: !16) + !60 = !DILocalVariable(scope: !2, type: !62) + !62 = distinct !DICompositeType(tag: DW_TAG_union_type, size: 128) + !85 = !DILabel(scope: !2, name: "out", file: !3, line: 102) + !88 = !{i32 2, !"Debug Info Version", i32 3} + !93 = !{i32 7, !"frame-pointer", i32 1} + !95 = !DILocation(line: 5, column: 10, scope: !2) +name: backtrace_user +registers: + - {id: 0, class: rgpr, } + - {id: 1, class: gpr, } + - {id: 2, class: gpr, } +body: | + bb.0.entry: + %10:rgpr = COPY $r2 + %13:rgpr = t2MOV_ga_pcrel target-flags(arm-nonlazy) @backtrace_user.ctl_default, debug-location !95 + %15:rgpr = t2MOVCCr %10, killed %13, 0 /* CC::eq */, $cpsr, debug-location !95 + DBG_VALUE 0, $noreg, !60, !DIExpression(), debug-location !95 + %0:rgpr = t2LDRi12 %15, 0, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) + DBG_VALUE %0, $noreg, !60, !DIExpression(), debug-location !95 + DBG_VALUE %0, $noreg, !59, !DIExpression(), debug-location !95 + %16:rgpr = t2LDRi12 %15, 4, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy) + %2:gpr = t2LDRi12 %15, 8, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy_context, align 8) + %31:rgpr = t2ANDrr %16, %0, 14 /* CC::al */, $noreg, def $cpsr + DBG_VALUE %2, $noreg, !60, !DIExpression(), debug-location !95 diff --git a/llvm/test/DebugInfo/ARM/move-dbg-values-imm-test.mir b/llvm/test/DebugInfo/ARM/move-dbg-values-imm-test.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/ARM/move-dbg-values-imm-test.mir @@ -0,0 +1,130 @@ +# RUN: llc -O2 %s -start-after=arm-mve-vpt-opts -stop-after=arm-prera-ldst-opt -o - | FileCheck %s +# CHECK: DBG_VALUE 13, $noreg, !11, !DIExpression(), debug-location !13 + + +# This test checks to make sure that a DBG_VALUE that uses an immediate doesn't get zero-ed out by a DBG_VALUE that comes later in the basic block, and describes the same variable. + +--- | + ; ModuleID = '/Users/shubham/Development/llvm-project/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll' + source_filename = "/Users/shubham/Development/llvm-project/llvm/test/DebugInfo/Generic/incorrect-variable-debugloc1.ll" + target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + target triple = "armv8-unknown-linux" + + ; Function Attrs: uwtable + define i32 @main() #0 !dbg !6 { + entry: + %c = alloca i32, align 4 + tail call void @llvm.dbg.value(metadata i32 13, metadata !11, metadata !DIExpression()), !dbg !13 + store volatile i32 13, ptr %c, align 4, !dbg !14 + %call = tail call i32 @_Z4funcv(), !dbg !15 + tail call void @llvm.dbg.value(metadata i32 %call, metadata !11, metadata !DIExpression()), !dbg !13 + store volatile i32 %call, ptr %c, align 4, !dbg !15 + tail call void @llvm.dbg.value(metadata ptr %c, metadata !11, metadata !DIExpression(DW_OP_deref)), !dbg !13 + %c.0.c.0. = load volatile i32, ptr %c, align 4, !dbg !16 + ret i32 %c.0.c.0., !dbg !16 + } + + declare i32 @_Z4funcv() #1 + + ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) + declare void @llvm.dbg.value(metadata, metadata, metadata) #2 + + attributes #0 = { uwtable "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #1 = { "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4} + !llvm.ident = !{!5} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.6.0 (trunk 223522)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, globals: !2, imports: !2) + !1 = !DIFile(filename: "test.cpp", directory: "/home/kromanova/ngh/ToT_latest/llvm/test/DebugInfo") + !2 = !{} + !3 = !{i32 2, !"Dwarf Version", i32 2} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{!"clang version 3.6.0 (trunk 223522)"} + !6 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 3, type: !7, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) + !7 = !DISubroutineType(types: !8) + !8 = !{!9} + !9 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) + !10 = !{!11} + !11 = !DILocalVariable(name: "c", scope: !6, file: !1, line: 5, type: !12) + !12 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !9) + !13 = !DILocation(line: 5, column: 16, scope: !6) + !14 = !DILocation(line: 5, column: 3, scope: !6) + !15 = !DILocation(line: 6, column: 7, scope: !6) + !16 = !DILocation(line: 7, column: 3, scope: !6) + +... +--- +name: main +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: gpr, preferred-register: '' } + - { id: 1, class: gpr, preferred-register: '' } + - { id: 2, class: gpr, preferred-register: '' } +liveins: [] +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 4 + adjustsStack: true + hasCalls: true + stackProtector: '' + functionContext: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + localFrameSize: 4 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: + - { id: 0, name: c, type: default, offset: 0, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + local-offset: -4, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.0.entry: + DBG_VALUE 13, $noreg, !11, !DIExpression(), debug-location !13 + %0:gpr = MOVi 13, 14 /* CC::al */, $noreg, $noreg + STRi12 killed %0, %stack.0.c, 0, 14 /* CC::al */, $noreg, debug-location !14 :: (volatile store (s32) into %ir.c) + ADJCALLSTACKDOWN 0, 0, 14 /* CC::al */, $noreg, implicit-def dead $sp, implicit $sp, debug-location !15 + BL @_Z4funcv, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp, implicit-def $r0, debug-location !15 + ADJCALLSTACKUP 0, -1, 14 /* CC::al */, $noreg, implicit-def dead $sp, implicit $sp, debug-location !15 + %1:gpr = COPY $r0, debug-location !15 + DBG_VALUE %1, $noreg, !11, !DIExpression(), debug-location !13 + STRi12 %1, %stack.0.c, 0, 14 /* CC::al */, $noreg, debug-location !15 :: (volatile store (s32) into %ir.c) + DBG_VALUE %stack.0.c, $noreg, !11, !DIExpression(DW_OP_deref), debug-location !13 + %2:gpr = LDRi12 %stack.0.c, 0, 14 /* CC::al */, $noreg, debug-location !16 :: (volatile dereferenceable load (s32) from %ir.c) + $r0 = COPY %2, debug-location !16 + BX_RET 14 /* CC::al */, $noreg, implicit $r0, debug-location !16 + +... diff --git a/llvm/test/DebugInfo/ARM/move-dbg-values.mir b/llvm/test/DebugInfo/ARM/move-dbg-values.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/ARM/move-dbg-values.mir @@ -0,0 +1,60 @@ +# RUN: llc %s -start-after=arm-mve-vpt-opts -stop-after=arm-prera-ldst-opt -o - | FileCheck %s +# CHECK: %5:rgpr = t2MOVCCr %3, killed %4, 0 /* CC::eq */, $cpsr, debug-location !15 +# CHECK-NEXT: DBG_VALUE 0, $noreg, !10, !DIExpression(), debug-location !15 +# CHECK-NEXT: DBG_VALUE $noreg, $noreg, !10, !DIExpression(), debug-location !15 +# CHECK-NEXT: DBG_VALUE $noreg, $noreg, !10, !DIExpression(), debug-location !15 +# CHECK-NEXT: %0:rgpr = t2LDRi12 %5, 0, 14 /* CC::al */, $noreg, debug-location !15 :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) +# CHECK-NEXT: %6:rgpr = t2LDRi12 %5, 4, 14 /* CC::al */, $noreg, debug-location !15 :: (load (s32) from %ir.btc_user_copy) +# CHECK-NEXT: DBG_VALUE %6, $noreg, !10, !DIExpression(), debug-location !15 +# CHECK-NEXT: %2:gpr = t2LDRi12 %5, 8, 14 /* CC::al */, $noreg, debug-location !15 :: (load (s32) from %ir.btc_user_copy_context, align 8) +# CHECK-NEXT: %7:rgpr = t2ANDrr %6, %0, 14 /* CC::al */, $noreg, def $cpsr +# CHECK-NEXT: DBG_VALUE %2, $noreg, !10, !DIExpression(), debug-location !15 +--- | + target triple = "thumbv7k-apple-watchos8.0.0" + %struct.backtrace_control = type {} + @backtrace_user.ctl_default = internal unnamed_addr constant %struct.backtrace_control zeroinitializer, !dbg !0 + define i32 @backtrace_user(ptr nocapture noundef writeonly %bt, i32 noundef %max_frames, ptr noundef readonly %ctl_in, ptr nocapture noundef readnone %info_out) local_unnamed_addr #0 !dbg !2 {entry: + %tobool.not = icmp eq ptr %ctl_in, null, !dbg !95 + %.backtrace_user.ctl_default = select i1 %tobool.not, ptr @backtrace_user.ctl_default, ptr %ctl_in, !dbg !95 + %btc_user_copy = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !95 + %btc_user_copy_context = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !95 + ret i32 undef, !dbg !95 + } + !llvm.module.flags = !{!88, + !93} + !llvm.dbg.cu = !{!32} + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(type: !11, isDefinition: true) + !2 = distinct !DISubprogram(unit: !32, retainedNodes: !38) + !3 = !DIFile(filename: "backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !8 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !11, size: 32) + !11 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !12) + !12 = distinct !DICompositeType(tag: DW_TAG_structure_type, size: 32) + !22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 32) + !31 = !DICompositeType(tag: DW_TAG_structure_type, flags: DIFlagFwdDecl) + !32 = distinct !DICompileUnit(language: DW_LANG_C11, file: !33, sdk: "MacOSX13.0.sdk") + !33 = !DIFile(filename: "/Users/shubham/Development/Delta/backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !38 = !{!60, !85} + !60 = !DILocalVariable(scope: !2, type: !22) + !85 = !DILabel(scope: !2, name: "out", file: !3, line: 102) + !88 = !{i32 2, !"Debug Info Version", i32 3} + !93 = !{i32 7, !"frame-pointer", i32 1} + !95 = !DILocation(line: 5, column: 10, scope: !2) +name: backtrace_user +registers: + - {id: 0, class: rgpr, } + - {id: 1, class: gpr, } + - {id: 2, class: gpr, } +body: | + bb.0.entry: + %10:rgpr = COPY $r2 + %13:rgpr = t2MOV_ga_pcrel target-flags(arm-nonlazy) @backtrace_user.ctl_default, debug-location !95 + %15:rgpr = t2MOVCCr %10, killed %13, 0 /* CC::eq */, $cpsr, debug-location !95 + DBG_VALUE 0, $noreg, !60, !DIExpression(), debug-location !95 + %0:rgpr = t2LDRi12 %15, 0, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) + DBG_VALUE %0, $noreg, !60, !DIExpression(), debug-location !95 + %16:rgpr = t2LDRi12 %15, 4, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy) + DBG_VALUE %16, $noreg, !60, !DIExpression(), debug-location !95 + %2:gpr = t2LDRi12 %15, 8, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy_context, align 8) + %31:rgpr = t2ANDrr %16, %0, 14 /* CC::al */, $noreg, def $cpsr + DBG_VALUE %2, $noreg, !60, !DIExpression(), debug-location !95