Index: llvm/lib/Target/ARM/ARMLoadStoreOptimizer.cpp =================================================================== --- llvm/lib/Target/ARM/ARMLoadStoreOptimizer.cpp +++ llvm/lib/Target/ARM/ARMLoadStoreOptimizer.cpp @@ -2173,9 +2173,9 @@ Register &BaseReg, int &Offset, Register &PredReg, ARMCC::CondCodes &Pred, bool &isT2); bool RescheduleOps(MachineBasicBlock *MBB, - SmallVectorImpl &Ops, - unsigned Base, bool isLd, - DenseMap &MI2LocMap); + SmallVectorImpl &Ops, unsigned Base, + bool isLd, DenseMap &MI2LocMap, + DenseMap &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, + DenseMap &RegisterMap) { bool RetVal = false; // Sort by offset (in reverse order). @@ -2476,6 +2476,10 @@ } else { for (unsigned i = 0; i != NumMove; ++i) { MachineInstr *Op = Ops.pop_back_val(); + if (isLd) + // We are moving loads, so we need to record the register that the + // load writes to, for DBG_VAL moving in the future. + RegisterMap[Op->getOperand(0).getReg()] = nullptr; MBB->splice(InsertPos, MBB, Op); } } @@ -2501,6 +2505,7 @@ Base2InstMap Base2StsMap; BaseVec LdBases; BaseVec StBases; + DenseMap RegisterMap; unsigned Loc = 0; MachineBasicBlock::iterator MBBI = MBB->begin(); @@ -2563,7 +2568,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 +2576,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 +2587,228 @@ } } + // Reschedule DBG_VALs to match any loads that were moved. The issue here is + // that there can be a scenario where debug information is lost because of the + // Pre Register Allocation Load Store optimization pass, where a load who's + // result describes the debug infomation for a local variable gets moved below + // the load and that causes the debug information for that load to get lost. + + // 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 result of this pass would move the load for virtual register %2 that is + // used by the DBG_VAL that comes after it, is moved to below that DBG_VAL, + // this results in the debug info for that local variable to be lost after + // this pass. The code below address this issue by moving any DBG_VALs that + // depend on such loads which have been moved to below those loads. + + // After the code below: + // inst_a + // inst_b + // %2 = ld ... + // DBG_VALUE %2, "x", ... + // %3 = ld ... + + // The way this code works is it defines three maps, the RegisterMap, + // LocalVarMap, and InstrMap. + + // RegisterMap: 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_VAL + // MachineInstr pointer that uses that virtual register. + + // LocalVarMap: This map is used to track the relationship between a Debug + // Variable and the DBG_VAL MachineInstr pointer that describes the debug + // information for that Debug Variable. + + // InstrMap: This map is used to track the relationship between a DBG_VAL + // MachineInstr pointer and Register that it uses. + + // The algorithm to solve this issue first uses the RescheduleOps function to + // populate the RegisterMap with the registers that were moved. The keys are + // inserted in the RegisterMap but are not populated with a value. The next + // step is to iterate over every instruction in the basic block. If the + // instruction is a DBG_VAL and it describes a valid debug variable we can + // move forward. If the first operand of the DBG_VAL is a register and that + // register exists in the RegisterMap, we know this is a DBG_VAL that depends + // on a load that was moved, and is a candidate to be moved as well. We can + // populate the RegisterMap and the InstrMap with the DBG_VAL MachineInstr + // pointer. If the DBG_VAL describes debug information for a variable that + // already exists in the LocalVarMap, that means that this DBG_VAL comes after + // a DBG_VAL that already described debug information for the same variable, + // we need to remove the old DBG_VAL as a candidate for moving and replace the + // first operand with a $noreg. If the current MachineInstr is not a DBG_VAL + // we can check if it is one of the loads that was moved by checking if it is + // a load and if it's first operand is a register in the RegisterMap. If that + // is true, we know that the corresponsing DBG_VAL in the RegisterMap has to + // be moved to below the current MachineInstr and that is then done. + + // To illustrate the above algorithm visually let's take this example. + + // Before the Pre Register Allocation Load Store Pass: + // %2 = ld ... + // DBG_VAL %2, A, .... # X + // DBG_VAL 0, A, ... # Y + // %3 = ld ... + // DBG_VAL %3, A, ..., # Z + // %4 = ld ... + + // After Pre Register Allocation Load Store Pass: + // DBG_VAL %2, A, .... # X + // DBG_VAL 0, A, ... # Y + // DBG_VAL %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 LocalVarMap and the InstrMap will be empty. + // LocalVarMap = {}, RegisterMap = {2 -> {}, 3 -> {}}, InstrMap {} + // -> DBG_VAL %2, A, .... # X + // DBG_VAL 0, A, ... # Y + // DBG_VAL %3, A, ..., # Z + // %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // After the first DBG_VAL (denoted with an X) is processed, the LocalVarMap + // and InstrMap will be populated and the RegisterMap entry for %2 will be + // populated as well. + // LocalVarMap = {A -> X}, RegisterMap = {2 -> {X}, 3 -> {}}, InstrMap {X -> + // 2} + // DBG_VAL %2, A, .... # X + // -> DBG_VAL 0, A, ... # Y + // DBG_VAL %3, A, ..., # Z + // %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // After the DBG_VAL Y is processed, the LocalVarMap 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. + // LocalVarMap = {A -> {Y}}, RegisterMap = {2 -> {}, 3 -> {}}, InstrMap = {X + // -> 2} + // DBG_VAL $noreg, A, .... # X + // DBG_VAL 0, A, ... # Y + // -> DBG_VAL %3, A, ..., # Z + // %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // After DBG_VAL Z is processed, the LocalVarMap 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 also updated to have a $noreg as the first operand. + // LocalVarMap = {A -> {Z}}, RegisterMap = {2 -> {}, 3 -> {Z}}, InstrMap = {X + // -> 2, Z -> 3} + // DBG_VAL $noreg, A, .... # X + // DBG_VAL $noreg, A, ... # Y + // DBG_VAL %3, A, ..., # Z + // -> %2 = ld ... + // %3 = ld ... + // %4 = ld ... + + // Nothing happens here since the RegisterMap for %2 contains no value. + // LocalVarMap = {A -> {Z}}, RegisterMap = {2 -> {}, 3 -> {Z}}, InstrMap = {X + // -> 2, Z -> 3} + // DBG_VAL $noreg, A, .... # X + // DBG_VAL $noreg, A, ... # Y + // DBG_VAL %3, A, ..., # Z + // %2 = ld ... + // -> %3 = ld ... + // %4 = ld ... + + // Since the RegisterMap contains Z as a value for %3, the MachineInstr + // pointer Z is moved to come after the load for %3 and the Basic Block + // iterator is moved to after the DBG_VAL Z's new position. + // LocalVarMap = {A -> {Z}}, RegisterMap = {2 -> {}, 3 -> {Z}}, InstrMap = {X + // -> 2, Z -> 3} + // DBG_VAL $noreg, A, .... # X + // DBG_VAL $noreg, A, ... # Y + // %2 = ld ... + // %3 = ld ... + // DBG_VAL %3, A, ..., # Z + // -> %4 = ld ... + + // Nothing happens for %4 and the algorithm exits having processed the entire + // Basic Block. + // LocalVarMap = {A -> {Z}}, RegisterMap = {2 -> {}, 3 -> {Z}}, InstrMap = {X + // -> 2, Z -> 3} + // DBG_VAL $noreg, A, .... # X + // DBG_VAL $noreg, A, ... # Y + // %2 = ld ... + // %3 = ld ... + // DBG_VAL %3, A, ..., # Z + // %4 = ld ... + + MBBI = MBB->begin(); + E = MBB->end(); + DenseMap LocalVarMap; + DenseMap InstrMap; + for (; MBBI != E; ++MBBI) { + MachineInstr &MI = *MBBI; + if (MI.isDebugInstr() && MI.isDebugValueLike()) { + auto Op = MI.getOperand(0); + auto *DbgVar = MI.getDebugVariable(); + // TODO: This should not happen, have to fix the MIR verifier to check for + // such instances and fix them. + if (!DbgVar) + continue; + // If the first operand is a register and it exists in the RegisterMap, we + // know this is a DBG_VAL 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. + if (Op.isReg() && RegisterMap.find(Op.getReg()) != RegisterMap.end()) { + RegisterMap[Op.getReg()] = &MI; + InstrMap[&MI] = Op.getReg(); + } + // Check if DBG_VAL describes a variable which is also described by a + // different DBG_VAL in the LocalVarMap, if so, modify the DBG_VAL in + // LocalVarMap to use a $noreg and remove from LocalVarMap. This is done + // because the old DBG_VAL is no longer relevant to describe the debug + // information for the debug variable and must be dropped as a candidate + // to be moved. + if (LocalVarMap.find(DbgVar) != LocalVarMap.end()) { + auto *Instr = LocalVarMap[DbgVar]; // TODO: Switch to iterator usage + if (InstrMap.find(Instr) != InstrMap.end()) { + RegisterMap[InstrMap[Instr]] = nullptr; + if (Instr->getOperand(0).isReg()) + Instr->getOperand(0).setReg(0); + } + } + LocalVarMap[DbgVar] = &MI; + } else { + // If the first operand of a load matches with a DBG_VAL in RegisterMap, + // then move that DBG_VAL to below the load. + auto Opc = MI.getOpcode(); + if (isLoadSingle(Opc)) { + auto Reg = MI.getOperand(0).getReg(); + if (RegisterMap.find(Reg) == RegisterMap.end() || + RegisterMap[Reg] == nullptr) + continue; + auto *DbgVal = RegisterMap[Reg]; + MachineBasicBlock::iterator InsertPos = MBBI; + InsertPos++; + MBB->splice(InsertPos, MBB, DbgVal); + MBBI++; + } + } + } + return RetVal; } Index: llvm/test/DebugInfo/ARM/move-dbg-values.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/ARM/move-dbg-values.mir @@ -0,0 +1,224 @@ +# 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 !95 +# CHECK-NEXT: DBG_VALUE $noreg, $noreg, !60, !DIExpression(), debug-location !95 +# CHECK-NEXT: DBG_VALUE 0, $noreg, !60, !DIExpression(), debug-location !95 +# CHECK-NEXT: %0:rgpr = t2LDRi12 %5, 0, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir..backtrace_user.ctl_default, align 8) +# CHECK-NEXT: %6:rgpr = t2LDRi12 %5, 4, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy) +# CHECK-NEXT: %2:gpr = t2LDRi12 %5, 8, 14 /* CC::al */, $noreg, debug-location !95 :: (load (s32) from %ir.btc_user_copy_context, align 8) +# CHECK-NEXT: DBG_VALUE %2, $noreg, !60, !DIExpression(), debug-location !95 + +--- | + target triple = "thumbv7k-apple-watchos8.0.0" + %struct.backtrace_control = type {} + %union.anon = 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: + %frame = alloca %union.anon, align 8 + %tobool.not = icmp eq ptr %ctl_in, null, !dbg !96 + %.backtrace_user.ctl_default = select i1 %tobool.not, ptr @backtrace_user.ctl_default, ptr %ctl_in, !dbg !96 + %0 = load i32, ptr %.backtrace_user.ctl_default, !dbg !97 + %btc_addr_offset = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !98 + %1 = load i64, ptr %btc_addr_offset, !dbg !98 + %btc_user_copy = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !99 + %2 = load ptr, ptr %btc_user_copy, !dbg !99 + %tobool6.not = icmp eq ptr %2, null, !dbg !100 + %._backtrace_copyin = select i1 %tobool6.not, ptr @_backtrace_copyin, ptr %2, !dbg !100 + %btc_user_copy_context = getelementptr inbounds %struct.backtrace_control, ptr %.backtrace_user.ctl_default, !dbg !101 + %3 = load ptr, ptr %btc_user_copy_context, !dbg !101 + %4 = trunc i64 %1 to i32 + br label %while.cond, !dbg !105 + while.cond: + %fp.0 = phi i32 [ %0, %entry ], [ %9, %if.end ], !dbg !106 + %frame_index.0 = phi i32 [ 0, %entry ], [ %inc, %if.end ], !dbg !95 + %cmp11 = icmp ne i32 %fp.0, 0, !dbg !107 + %cmp12 = icmp ult i32 %frame_index.0, %max_frames, !dbg !108 + %5 = select i1 %cmp11, i1 %cmp12, i1 false, !dbg !108 + %call = call i32 %._backtrace_copyin() #7, !dbg !109 + %tobool13.not = icmp eq i32 %call, 0, !dbg !110 + br i1 %tobool13.not, label %if.end, label %out, !dbg !112 + if.end: + %6 = icmp eq i32 %0, 0, !dbg !113 + %sunkaddr = getelementptr inbounds i8, ptr %frame, !dbg !115 + %7 = load i32, ptr %sunkaddr, !dbg !115 + %inc = add nuw i32 %frame_index.0, 1, !dbg !117 + %8 = shl i32 %frame_index.0, 2, !dbg !118 + %9 = load i32, ptr %frame, !dbg !119 + %and56 = and i32 %9, 3, !dbg !120 + %cmp57.not = icmp ne i32 %and56, 0, !dbg !122 + %cmp61 = icmp eq i32 %9, %fp.0 + %or.cond99 = select i1 %cmp57.not, i1 true, i1 %cmp61, !dbg !123 + %cmp65 = icmp ult i32 %9, %fp.0 + %or.cond = select i1 %6, i1 %cmp65, i1 false + %or.cond100 = select i1 %or.cond99, i1 true, i1 %or.cond, !dbg !123 + br i1 %or.cond100, label %out, label %while.cond + out: + ret i32 undef, !dbg !125 + } + define internal i32 @_backtrace_copyin() unnamed_addr #3 !dbg !126 {entry:ret i32 undef, !dbg !133} + !llvm.module.flags = !{!86, !87, !88, !89, !90, !91, !92, !93} + !llvm.dbg.cu = !{!32} + !llvm.ident = !{!94} + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(scope: !2, type: !11, isDefinition: true) + !2 = distinct !DISubprogram(scope: !3, type: !4, unit: !32, retainedNodes: !38) + !3 = !DIFile(filename: "backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !4 = !DISubroutineType(types: !5) + !5 = !{!30} + !6 = !DIBasicType(name: "unsigned int", encoding: DW_ATE_unsigned) + !7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 32) + !8 = !DIDerivedType(tag: DW_TAG_typedef, line: 5, baseType: !9) + !9 = !DIBasicType(name: "unsigned long", encoding: DW_ATE_unsigned) + !10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 32) + !11 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !12) + !12 = distinct !DICompositeType(tag: DW_TAG_structure_type, elements: !13) + !13 = !{!14, !15, !26, !27} + !14 = !DIDerivedType(tag: DW_TAG_member, baseType: !8, size: 32) + !15 = !DIDerivedType(tag: DW_TAG_member, baseType: !16, offset: 32) + !16 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !17) + !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 32) + !18 = !DISubroutineType(types: !19) + !19 = !{!20, !23, !25} + !20 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !21) + !21 = !DIBasicType(encoding: DW_ATE_signed) + !22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 32) + !23 = !DIDerivedType(tag: DW_TAG_typedef, name: "user_addr_t", baseType: !24) + !24 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !6) + !25 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !9) + !26 = !DIDerivedType(tag: DW_TAG_member, baseType: !22, offset: 64) + !27 = !DIDerivedType(tag: DW_TAG_member, baseType: !28, offset: 128) + !28 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !29) + !29 = !DIBasicType() + !30 = !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, emissionKind: FullDebug, retainedTypes: !34, globals: !37, sdk: "MacOSX13.0.sdk") + !33 = !DIFile(filename: "/Users/shubham/Development/Delta/backtrace.pp.c", directory: "/Users/shubham/Development/Delta") + !34 = !{!35, !8} + !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 32) + !36 = !DIBasicType(encoding: DW_ATE_signed_char) + !37 = !{} + !38 = !{!39, !40, !41, !42, !43, !44, !45, !46, !47, !49, !50, !51, !52, !53, !54, !55, !56, !57, !58, !59, !60, !61, !78, !80, !81, !84, !85} + !39 = !DILocalVariable(scope: !2, type: !7) + !40 = !DILocalVariable(name: "max_frames", scope: !2, type: !6) + !41 = !DILocalVariable(name: "ctl_in", scope: !2, type: !10) + !42 = !DILocalVariable(scope: !2, type: !30) + !43 = !DILocalVariable(scope: !2, type: !10) + !44 = !DILocalVariable(name: "pc", scope: !2, type: !8) + !45 = !DILocalVariable(name: "next_fp", scope: !2, type: !8) + !46 = !DILocalVariable(name: "fp", scope: !2, type: !8) + !47 = !DILocalVariable(scope: !2, type: !48) + !48 = !DIBasicType(encoding: DW_ATE_boolean) + !49 = !DILocalVariable(scope: !2, type: !28) + !50 = !DILocalVariable(scope: !2, type: !6) + !51 = !DILocalVariable(scope: !2, type: !21) + !52 = !DILocalVariable(scope: !2, type: !25) + !53 = !DILocalVariable(name: "truncated", scope: !2, type: !48) + !54 = !DILocalVariable(name: "user_64", scope: !2, type: !48) + !55 = !DILocalVariable(name: "allow_async", scope: !2, type: !48) + !56 = !DILocalVariable(name: "has_async", scope: !2, type: !48) + !57 = !DILocalVariable(scope: !2, type: !8) + !58 = !DILocalVariable(name: "async_index", scope: !2, type: !6) + !59 = !DILocalVariable(scope: !2, type: !16) + !60 = !DILocalVariable(name: "ctx", scope: !2, type: !22) + !61 = !DILocalVariable(scope: !2, type: !62) + !62 = distinct !DICompositeType(tag: DW_TAG_union_type, elements: !63) + !63 = !{!64, !72} + !64 = !DIDerivedType(tag: DW_TAG_member, baseType: !65, size: 128) + !65 = distinct !DICompositeType(tag: DW_TAG_structure_type, elements: !66) + !66 = !{!67, !71} + !67 = !DIDerivedType(tag: DW_TAG_member, baseType: !68, size: 64) + !68 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !69) + !69 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !70) + !70 = !DIBasicType(encoding: DW_ATE_unsigned) + !71 = !DIDerivedType(tag: DW_TAG_member, baseType: !68, offset: 64) + !72 = !DIDerivedType(tag: DW_TAG_member, baseType: !73, size: 64) + !73 = distinct !DICompositeType(tag: DW_TAG_structure_type, elements: !74) + !74 = !{!75, !77} + !75 = !DIDerivedType(tag: DW_TAG_member, baseType: !76, size: 32) + !76 = !DIDerivedType(tag: DW_TAG_typedef, baseType: !24) + !77 = !DIDerivedType(tag: DW_TAG_member, baseType: !76, offset: 32) + !78 = !DILocalVariable(scope: !79, type: !8) + !79 = distinct !DILexicalBlock(scope: !2, line: 70, column: 46) + !80 = !DILocalVariable(scope: !79, type: !48) + !81 = !DILocalVariable(name: "async_ctx_ptr", scope: !82, type: !23) + !82 = distinct !DILexicalBlock(scope: !83, line: 80, column: 31) + !83 = distinct !DILexicalBlock(scope: !79, line: 80, column: 7) + !84 = !DILocalVariable(scope: !82, type: !23) + !85 = !DILabel(scope: !2, name: "out", file: !3, line: 102) + !86 = !{i32 2, !"SDK Version", [2 x i32] [i32 13, i32 0]} + !87 = !{i32 7, !"Dwarf Version", i32 4} + !88 = !{i32 2, !"Debug Info Version", i32 3} + !89 = !{i32 1, !"wchar_size", i32 4} + !90 = !{i32 1, !"min_enum_size", i32 4} + !91 = !{i32 8, !"PIC Level", i32 2} + !92 = !{i32 7, !"uwtable", i32 2} + !93 = !{i32 7, !"frame-pointer", i32 1} + !94 = !{!"clang version 16.0.0 ()"} + !95 = !DILocation(scope: !2) + !96 = !DILocation(scope: !2) + !97 = !DILocation(scope: !2) + !98 = !DILocation(scope: !2) + !99 = !DILocation(scope: !2) + !100 = !DILocation(scope: !2) + !101 = !DILocation(scope: !2) + !105 = !DILocation(scope: !2) + !106 = !DILocation(scope: !2) + !107 = !DILocation(scope: !2) + !108 = !DILocation(scope: !2) + !109 = !DILocation(scope: !79) + !110 = !DILocation(scope: !79, column: 7) + !112 = !DILocation(scope: !79) + !113 = !DILocation(scope: !2) + !115 = !DILocation(scope: !79) + !117 = !DILocation(scope: !79) + !118 = !DILocation(scope: !79) + !119 = !DILocation(scope: !79) + !120 = !DILocation(scope: !121) + !121 = distinct !DILexicalBlock(scope: !79, line: 94, column: 7) + !122 = !DILocation(scope: !121) + !123 = !DILocation(scope: !79) + !125 = !DILocation(scope: !2) + !126 = distinct !DISubprogram(type: !18, unit: !32, retainedNodes: !127) + !127 = !{} + !133 = !DILocation(scope: !126) +name: backtrace_user +tracksRegLiveness: true +registers: + - {id: 0, class: rgpr, } + - {id: 1, class: gpr, } + - {id: 2, class: gpr, } +stack: + - { id: 0, name: frame, size: 16, } +body: | + bb.0.entry: + liveins: $r0, $r1, $r2 + %10:rgpr = COPY $r2 + %13:rgpr = t2MOV_ga_pcrel target-flags(arm-nonlazy) @backtrace_user.ctl_default, debug-location !96 + t2CMPri %10, 0, 14 /* CC::al */, $noreg, implicit-def $cpsr, debug-location !96 + %15:rgpr = t2MOVCCr %10, killed %13, 0 /* CC::eq */, $cpsr, debug-location !96 + %0:rgpr = t2LDRi12 %15, 0, 14 /* CC::al */, $noreg, debug-location !97 :: (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 !99 :: (load (s32) from %ir.btc_user_copy) + DBG_VALUE 0, $noreg, !60, !DIExpression(), debug-location !95 + %2:gpr = t2LDRi12 %15, 8, 14 /* CC::al */, $noreg, debug-location !101 :: (load (s32) from %ir.btc_user_copy_context, align 8) + DBG_VALUE %2, $noreg, !60, !DIExpression(), debug-location !95 + %20:rgpr = t2MOVi 0, 14 /* CC::al */, $noreg, $noreg + %12:gpr = COPY %20 + bb.1.while.cond: + %4:rgpr = PHI %0, %bb.0, %7, %bb.6, debug-location !106 + %5:rgpr = PHI %12, %bb.0, %6, %bb.6, debug-location !95 + %7:rgpr = t2LDRi12 %stack.0.frame, 0, 14 /* CC::al */, $noreg, debug-location !119 :: (dereferenceable load (s32) from %ir.frame, align 16) + t2CMPrr %7, %4, 14 /* CC::al */, $noreg, implicit-def $cpsr + %28:rgpr = t2MOVCCi %20, 1, 3 /* CC::lo */, $cpsr + bb.6.if.end: + %29:rgpr = t2CLZ %0, 14 /* CC::al */, $noreg + %30:rgpr = t2LSRri %29, 5, 14 /* CC::al */, $noreg, $noreg + %31:rgpr = t2ANDrr %30, %28, 14 /* CC::al */, $noreg, def $cpsr + %32:rgpr = nuw t2ADDri %5, 1, 14 /* CC::al */, $noreg, $noreg + %6:gpr = COPY %32 + t2Bcc %bb.1, 0 /* CC::eq */, $cpsr + bb.4.out: +--- +name: _backtrace_copyin +tracksRegLiveness: true +body: | + bb.0.entry: