diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -56,6 +56,7 @@ #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" @@ -483,6 +484,45 @@ return true; } +static void shortenAssignment(Instruction *Inst, uint64_t OldOffsetInBits, + uint64_t OldSizeInBits, uint64_t NewSizeInBits, + bool IsOverwriteEnd) { + DIExpression::FragmentInfo DeadFragment; + DeadFragment.SizeInBits = OldSizeInBits - NewSizeInBits; + DeadFragment.OffsetInBits = + OldOffsetInBits + (IsOverwriteEnd ? NewSizeInBits : 0); + + auto CreateDeadFragExpr = [Inst, DeadFragment]() { + // FIXME: This should be using the DIExpression in the Alloca's dbg.assign + // for the variable, since that could also contain a fragment? + return *DIExpression::createFragmentExpression( + DIExpression::get(Inst->getContext(), None), DeadFragment.OffsetInBits, + DeadFragment.SizeInBits); + }; + + // A DIAssignID to use so that the inserted dbg.assign intrinsics do not + // link to any instructions. Created in the loop below (once). + DIAssignID *LinkToNothing = nullptr; + + // Insert an unlinked dbg.assign intrinsic for the dead fragment after each + // overlapping dbg.assign intrinsic. + for (auto *DAI : at::getAssignmentMarkers(Inst)) { + if (auto FragInfo = DAI->getExpression()->getFragmentInfo()) { + if (!DIExpression::fragmentsOverlap(*FragInfo, DeadFragment)) + continue; + } + + // Fragments overlap: insert a new dbg.assign for this dead part. + auto *NewAssign = cast(DAI->clone()); + NewAssign->insertAfter(DAI); + if (!LinkToNothing) + LinkToNothing = DIAssignID::getDistinct(Inst->getContext()); + NewAssign->setAssignId(LinkToNothing); + NewAssign->setExpression(CreateDeadFragExpr()); + NewAssign->setAddress(UndefValue::get(DAI->getAddress()->getType())); + } +} + static bool tryToShorten(Instruction *DeadI, int64_t &DeadStart, uint64_t &DeadSize, int64_t KillingStart, uint64_t KillingSize, bool IsOverwriteEnd) { @@ -574,6 +614,10 @@ DeadIntrinsic->setDest(NewDestGEP); } + // Update attached dbg.assign intrinsics. Assume 8-bit byte. + shortenAssignment(DeadI, DeadStart * 8, DeadSize * 8, NewSize * 8, + IsOverwriteEnd); + // Finally update start and size of dead access. if (!IsOverwriteEnd) DeadStart += ToRemoveSize; diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/dse/dse-after-memcpyopt-merge.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/dse/dse-after-memcpyopt-merge.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/dse/dse-after-memcpyopt-merge.ll @@ -0,0 +1,135 @@ +; RUN: opt %s -S -dse -o - -experimental-assignment-tracking | FileCheck %s + +;; Observed in the wild, but test is created by running memcpyopt on +;; assignment-tracking/memcpyopt/merge-stores.ll then manually inserting +;; two stores that overwrite each end of the memset. +;; +;; A memory intrinsic (or vector instruction) might have multiple dbg.assigns +;; linked to it if it has been created as a result of merging scalar stores, +;; such as in this example. DSE is going to shorten the memset because there's +;; a later store that overwrites part of it. Insert a linked dbg.assign after +;; each overlapping fragment to undef the dead part's memory location without +;; needing to work out how to split the fragments exactly. + +;; Check that there's an unlinked dbg.assign inserted after each overlapping +;; fragment of the shortened store. +;; +; CHECK: call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata ![[ID:[0-9]+]], metadata ptr %arrayidx5.i, metadata !DIExpression()) +; CHECK-NEXT: call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata ![[UniqueID1:[0-9]+]], metadata ptr undef, metadata !DIExpression()) + +; CHECK: call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata ![[ID]], metadata ptr %arrayidx7.i, metadata !DIExpression()) +; CHECK-NEXT: call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata ![[UniqueID2:[0-9]+]], metadata ptr undef, metadata !DIExpression()) + +; CHECK: call void @llvm.memset{{.*}}, !DIAssignID ![[ID]] + +; CHECK-DAG: ![[ID]] = distinct !DIAssignID() +; CHECK-DAG: ![[UniqueID1]] = distinct !DIAssignID() +; CHECK-DAG: ![[UniqueID2]] = distinct !DIAssignID() + +%struct.v = type { [4 x float] } + +$_ZN1vC2Ef = comdat any + +define dso_local void @_Z1fv() local_unnamed_addr !dbg !7 { +entry: + %g = alloca %struct.v, align 4, !DIAssignID !23 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !23, metadata ptr %g, metadata !DIExpression()), !dbg !24 + call void @llvm.lifetime.start.p0i8(i64 16, ptr nonnull %g), !dbg !25 + call void @llvm.dbg.assign(metadata ptr %g, metadata !26, metadata !DIExpression(), metadata !35, metadata ptr undef, metadata !DIExpression()), !dbg !32 + call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !29, metadata !DIExpression(), metadata !36, metadata ptr undef, metadata !DIExpression()), !dbg !32 + %arrayidx.i = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 2, !dbg !37 + call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32), metadata !39, metadata ptr %arrayidx.i, metadata !DIExpression()), !dbg !24 + %arrayidx3.i = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 1, !dbg !40 + call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata !39, metadata ptr %arrayidx3.i, metadata !DIExpression()), !dbg !24 + %arrayidx5.i = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 0, !dbg !41 + call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !39, metadata ptr %arrayidx5.i, metadata !DIExpression()), !dbg !24 + %arrayidx7.i = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 3, !dbg !42 + call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !39, metadata ptr %arrayidx7.i, metadata !DIExpression()), !dbg !24 + %0 = bitcast ptr %arrayidx5.i to ptr, !dbg !43 + call void @llvm.memset.p0i8.i64(ptr align 4 %0, i8 0, i64 16, i1 false), !dbg !44, !DIAssignID !39 + ;; -- Start modification + %arrayidx7 = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 3, !dbg !24 + store float 0.000000e+00, ptr %arrayidx7, align 4, !dbg !24, !DIAssignID !49 + call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !49, metadata ptr %arrayidx7, metadata !DIExpression()), !dbg !24 + %arrayidx = getelementptr inbounds %struct.v, ptr %g, i64 0, i32 0, i64 0, !dbg !24 + store float 0.000000e+00, ptr %arrayidx, align 4, !dbg !24, !DIAssignID !50 + call void @llvm.dbg.assign(metadata float 0.000000e+00, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32), metadata !50, metadata ptr %arrayidx, metadata !DIExpression()), !dbg !24 + ;; -- End modification + call void @_Z3escP1v(ptr nonnull %g), !dbg !43 + call void @llvm.lifetime.end.p0i8(i64 16, ptr nonnull %0), !dbg !45 + ret void, !dbg !45 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) +declare !dbg !64 dso_local void @_Z3escP1v(ptr) local_unnamed_addr +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) +declare void @llvm.memset.p0i8.i64(ptr nocapture writeonly, i8, i64, i1 immarg) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduce.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "f", linkageName: "_Z1fv", scope: !1, file: !1, line: 12, type: !8, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "g", scope: !7, file: !1, line: 13, type: !12) +!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "v", file: !1, line: 1, size: 128, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !13, identifier: "_ZTS1v") +!13 = !{!14, !19} +!14 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !12, file: !1, line: 2, baseType: !15, size: 128) +!15 = !DICompositeType(tag: DW_TAG_array_type, baseType: !16, size: 128, elements: !17) +!16 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!17 = !{!18} +!18 = !DISubrange(count: 4) +!19 = !DISubprogram(name: "v", scope: !12, file: !1, line: 4, type: !20, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!20 = !DISubroutineType(types: !21) +!21 = !{null, !22, !16} +!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!23 = distinct !DIAssignID() +!24 = !DILocation(line: 0, scope: !7) +!25 = !DILocation(line: 13, column: 3, scope: !7) +!26 = !DILocalVariable(name: "this", arg: 1, scope: !27, type: !30, flags: DIFlagArtificial | DIFlagObjectPointer) +!27 = distinct !DISubprogram(name: "v", linkageName: "_ZN1vC2Ef", scope: !12, file: !1, line: 4, type: !20, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !19, retainedNodes: !28) +!28 = !{!26, !29} +!29 = !DILocalVariable(name: "d", arg: 2, scope: !27, file: !1, line: 4, type: !16) +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!31 = distinct !DIAssignID() +!32 = !DILocation(line: 0, scope: !27, inlinedAt: !33) +!33 = distinct !DILocation(line: 13, column: 5, scope: !7) +!34 = distinct !DIAssignID() +!35 = distinct !DIAssignID() +!36 = distinct !DIAssignID() +!37 = !DILocation(line: 5, column: 19, scope: !38, inlinedAt: !33) +!38 = distinct !DILexicalBlock(scope: !27, file: !1, line: 4, column: 14) +!39 = distinct !DIAssignID() +!40 = !DILocation(line: 5, column: 12, scope: !38, inlinedAt: !33) +!41 = !DILocation(line: 5, column: 5, scope: !38, inlinedAt: !33) +!42 = !DILocation(line: 6, column: 5, scope: !38, inlinedAt: !33) +!43 = !DILocation(line: 14, column: 3, scope: !7) +!44 = !DILocation(line: 5, column: 17, scope: !38, inlinedAt: !33) +!45 = !DILocation(line: 15, column: 1, scope: !7) +!46 = distinct !DIAssignID() +!47 = !DILocation(line: 0, scope: !27) +!48 = distinct !DIAssignID() +!49 = distinct !DIAssignID() +!50 = distinct !DIAssignID() +!51 = !DILocation(line: 5, column: 19, scope: !38) +!52 = !DILocation(line: 5, column: 24, scope: !38) +!57 = !DILocation(line: 5, column: 12, scope: !38) +!58 = !DILocation(line: 5, column: 17, scope: !38) +!59 = !DILocation(line: 5, column: 5, scope: !38) +!60 = !DILocation(line: 5, column: 10, scope: !38) +!61 = !DILocation(line: 6, column: 5, scope: !38) +!62 = !DILocation(line: 6, column: 10, scope: !38) +!63 = !DILocation(line: 7, column: 3, scope: !27) +!64 = !DISubprogram(name: "esc", linkageName: "_Z3escP1v", scope: !1, file: !1, line: 10, type: !65, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!65 = !DISubroutineType(types: !66) +!66 = !{null, !30} diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/dse/shorten.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/dse/shorten.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/dse/shorten.ll @@ -0,0 +1,128 @@ +; RUN: opt %s -S -dse -o - -experimental-assignment-tracking | FileCheck %s + +;; $ cat test.cpp +;; void esc(int*); +;; void shortenEnd() { +;; int local[20]; +;; __builtin_memset(local, 0, 6 * 4); +;; __builtin_memset(local + 4, 8, 10 * 4); +;; esc(local); +;; } +;; void shortenStart() { +;; int local2[10]; +;; __builtin_memset(local2, 0, 10 * 4); +;; __builtin_memset(local2, 8, 4 * 4); +;; esc(local2); +;; } +;; IR grabbed before dse in: +;; clang++ -O2 -g -Xclang -fexperimental-assignment-tracking + +;; DeadStoreElimination will shorten the first store in shortenEnd from [0, +;; 192) bits to [0, 128) bits. Check that we get an unlinked dbg.assign covering +;; the deleted bits [128, 192) (offset=128 size=64). It will shorten also the +;; first store in shortenStart from [0, 320) bits to [128, 320). Check that we +;; get an unlinked dbg.assign covering the deleted bits [0, 128) (offset=0 +;; size=128). + +; CHECK: @_Z10shortenEndv +; CHECK: call void @llvm.memset{{.*}}, !DIAssignID ![[ID:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 192), metadata ![[ID:[0-9]+]], metadata ptr %local, metadata !DIExpression()) +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64), metadata ![[UniqueID1:[0-9]+]], metadata ptr undef, metadata !DIExpression()) + +; CHECK: @_Z12shortenStartv +; CHECK: call void @llvm.memset{{.*}}, !DIAssignID ![[ID2:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR2:[0-9]+]], metadata !DIExpression(), metadata ![[ID2]], metadata ptr %local2, metadata !DIExpression()) +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR2]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 128), metadata ![[UniqueID2:[0-9]+]], metadata ptr undef, metadata !DIExpression()) + +; CHECK-DAG: ![[ID]] = distinct !DIAssignID() +; CHECK-DAG: ![[UniqueID1]] = distinct !DIAssignID() +; CHECK-DAG: ![[UniqueID2]] = distinct !DIAssignID() + +define dso_local void @_Z10shortenEndv() local_unnamed_addr #0 !dbg !7 { +entry: + %local = alloca [20 x i32], align 16, !DIAssignID !16 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !16, metadata ptr %local, metadata !DIExpression()), !dbg !17 + call void @llvm.lifetime.start.p0i8(i64 80, ptr nonnull %local) #5, !dbg !18 + %arraydecay = getelementptr inbounds [20 x i32], ptr %local, i64 0, i64 0, !dbg !19 + call void @llvm.memset.p0i8.i64(ptr noundef nonnull align 16 dereferenceable(24) %local, i8 0, i64 24, i1 false), !dbg !19, !DIAssignID !20 + call void @llvm.dbg.assign(metadata i8 0, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 192), metadata !20, metadata ptr %local, metadata !DIExpression()), !dbg !17 + %add.ptr = getelementptr inbounds [20 x i32], ptr %local, i64 0, i64 4, !dbg !21 + call void @llvm.memset.p0i8.i64(ptr noundef nonnull align 16 dereferenceable(40) %add.ptr, i8 8, i64 40, i1 false), !dbg !22, !DIAssignID !23 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 320), metadata !23, metadata ptr %add.ptr, metadata !DIExpression()), !dbg !17 + call void @_Z3escPi(ptr noundef nonnull %arraydecay), !dbg !24 + call void @llvm.lifetime.end.p0i8(i64 80, ptr nonnull %local) #5, !dbg !25 + ret void, !dbg !25 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) +declare void @llvm.memset.p0i8.i64(ptr nocapture writeonly, i8, i64, i1 immarg) +declare !dbg !26 dso_local void @_Z3escPi(ptr noundef) local_unnamed_addr +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture) + +define dso_local void @_Z12shortenStartv() local_unnamed_addr #0 !dbg !31 { +entry: + %local2 = alloca [10 x i32], align 16, !DIAssignID !37 + call void @llvm.dbg.assign(metadata i1 undef, metadata !33, metadata !DIExpression(), metadata !37, metadata ptr %local2, metadata !DIExpression()), !dbg !38 + call void @llvm.lifetime.start.p0i8(i64 40, ptr nonnull %local2) #5, !dbg !39 + %arraydecay = getelementptr inbounds [10 x i32], ptr %local2, i64 0, i64 0, !dbg !40 + call void @llvm.memset.p0i8.i64(ptr noundef nonnull align 16 dereferenceable(40) %local2, i8 0, i64 40, i1 false), !dbg !40, !DIAssignID !41 + call void @llvm.dbg.assign(metadata i8 0, metadata !33, metadata !DIExpression(), metadata !41, metadata ptr %local2, metadata !DIExpression()), !dbg !38 + call void @llvm.memset.p0i8.i64(ptr noundef nonnull align 16 dereferenceable(16) %local2, i8 8, i64 16, i1 false), !dbg !42, !DIAssignID !43 + call void @llvm.dbg.assign(metadata i1 undef, metadata !33, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 128), metadata !43, metadata ptr %local2, metadata !DIExpression()), !dbg !38 + call void @_Z3escPi(ptr noundef nonnull %arraydecay), !dbg !44 + call void @llvm.lifetime.end.p0i8(i64 40, ptr nonnull %local2) #5, !dbg !45 + ret void, !dbg !45 +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{!"clang version 14.0.0"} +!7 = distinct !DISubprogram(name: "shortenEnd", linkageName: "_Z10shortenEndv", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 3, type: !12) +!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 640, elements: !14) +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !{!15} +!15 = !DISubrange(count: 20) +!16 = distinct !DIAssignID() +!17 = !DILocation(line: 0, scope: !7) +!18 = !DILocation(line: 3, column: 3, scope: !7) +!19 = !DILocation(line: 4, column: 3, scope: !7) +!20 = distinct !DIAssignID() +!21 = !DILocation(line: 5, column: 26, scope: !7) +!22 = !DILocation(line: 5, column: 3, scope: !7) +!23 = distinct !DIAssignID() +!24 = !DILocation(line: 6, column: 3, scope: !7) +!25 = !DILocation(line: 7, column: 1, scope: !7) +!26 = !DISubprogram(name: "esc", linkageName: "_Z3escPi", scope: !1, file: !1, line: 1, type: !27, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !30) +!27 = !DISubroutineType(types: !28) +!28 = !{null, !29} +!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) +!30 = !{} +!31 = distinct !DISubprogram(name: "shortenStart", linkageName: "_Z12shortenStartv", scope: !1, file: !1, line: 8, type: !8, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !32) +!32 = !{!33} +!33 = !DILocalVariable(name: "local2", scope: !31, file: !1, line: 9, type: !34) +!34 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 320, elements: !35) +!35 = !{!36} +!36 = !DISubrange(count: 10) +!37 = distinct !DIAssignID() +!38 = !DILocation(line: 0, scope: !31) +!39 = !DILocation(line: 9, column: 3, scope: !31) +!40 = !DILocation(line: 10, column: 3, scope: !31) +!41 = distinct !DIAssignID() +!42 = !DILocation(line: 11, column: 3, scope: !31) +!43 = distinct !DIAssignID() +!44 = !DILocation(line: 12, column: 3, scope: !31) +!45 = !DILocation(line: 13, column: 1, scope: !31)