Index: llvm/lib/Transforms/Scalar/SROA.cpp =================================================================== --- llvm/lib/Transforms/Scalar/SROA.cpp +++ llvm/lib/Transforms/Scalar/SROA.cpp @@ -117,6 +117,117 @@ cl::Hidden); namespace { +constexpr uint64_t ByteWidth = 8; +static uint64_t bitsToBytes(uint64_t Bits) { + assert(Bits % 8 == 0 && "unexpected bit count"); + return Bits / ByteWidth; +} +static uint64_t bytesToBits(uint64_t Bytes) { + assert(Bytes <= (UINT64_MAX / ByteWidth) && "too many bytes"); + return Bytes * ByteWidth; +} + +/// Find linked dbg.assign and generate a new one with the correct +/// FragmentInfo. Link Inst to the new dbg.assign. If Value is nullptr the +/// value component is copied from the old dbg.assign to the new. +/// \param OldAlloca Alloca for the variable before splitting. +/// \param RelativeOffsetInBytes Offset into \p OldAlloca relative to the +/// offset prior to splitting (change in offset). +/// \param SliceSizeInBytes New number of bytes being written to. +/// \param OldInst Instruction that is being split. +/// \param Inst New instruction performing this part of the +/// split store. +/// \param Dest Store destination. +/// \param Value Stored value. +/// \param DL Datalayout. +static void migrateDebugInfo(AllocaInst *OldAlloca, + uint64_t RelativeOffsetInBytes, + uint64_t SliceSizeInBytes, Instruction *OldInst, + Instruction *Inst, Value *Dest, Value *Value, + const DataLayout &DL) { + uint64_t RelativeOffset = bytesToBits(RelativeOffsetInBytes); + uint64_t SliceSize = bytesToBits(SliceSizeInBytes); + + LLVM_DEBUG(dbgs() << " migrateDebugInfo\n"); + LLVM_DEBUG(dbgs() << " OldAlloca: " << *OldAlloca << "\n"); + LLVM_DEBUG(dbgs() << " RelativeOffset: " << RelativeOffset << "\n"); + LLVM_DEBUG(dbgs() << " SliceSize: " << SliceSize << "\n"); + LLVM_DEBUG(dbgs() << " OldInst: " << *OldInst << "\n"); + LLVM_DEBUG(dbgs() << " Inst: " << *Inst << "\n"); + LLVM_DEBUG(dbgs() << " Dest: " << *Dest << "\n"); + if (Value) + LLVM_DEBUG(dbgs() << " Value: " << *Value << "\n"); + + // The new inst needs a DIAssignID unique metadata tag (if OldInst has + // one). It shouldn't already have one: assert this assumption. + assert(!Inst->getMetadata(LLVMContext::MD_DIAssignID)); + DIAssignID *NewID = nullptr; + auto &Ctx = Inst->getContext(); + DIBuilder DIB(*OldInst->getModule(), /*AllowUnresolved*/ false); + uint64_t AllocaSize = *OldAlloca->getAllocationSizeInBits(DL); + assert(OldAlloca->isStaticAlloca()); + + for (DbgAssignIntrinsic *DbgAssign : at::getAssignmentMarkers(OldInst)) { + LLVM_DEBUG(dbgs() << " existing dbg.assign is: " << *DbgAssign + << "\n"); + auto *Expr = DbgAssign->getExpression(); + + // Check if the dbg.assign already describes a fragment. + auto GetCurrentFragSize = [AllocaSize, DbgAssign, Expr]() -> uint64_t { + if (auto FI = Expr->getFragmentInfo()) + return FI->SizeInBits; + if (auto VarSize = DbgAssign->getVariable()->getSizeInBits()) + return *VarSize; + // The variable type has an unspecified size. This can happen in the + // case of DW_TAG_unspecified_type types, e.g. std::nullptr_t. Because + // there is no fragment and we do not know the size of the variable type, + // we'll guess by looking at the alloca. + return AllocaSize; + }; + uint64_t CurrentFragSize = GetCurrentFragSize(); + bool MakeNewFragment = CurrentFragSize != SliceSize; + assert(MakeNewFragment || RelativeOffset == 0); + + assert(SliceSize <= AllocaSize); + if (MakeNewFragment) { + assert(RelativeOffset + SliceSize <= CurrentFragSize); + auto E = DIExpression::createFragmentExpression(Expr, RelativeOffset, + SliceSize); + assert(E && "Failed to create fragment expr!"); + Expr = *E; + } + + // If we haven't created a DIAssignID ID do that now and attach it to Inst. + if (!NewID) { + NewID = DIAssignID::getDistinct(Ctx); + Inst->setMetadata(LLVMContext::MD_DIAssignID, NewID); + } + + Value = Value ? Value : DbgAssign->getValue(); + auto *NewAssign = DIB.insertDbgAssign( + Inst, Value, DbgAssign->getVariable(), Expr, Dest, + DIExpression::get(Ctx, None), DbgAssign->getDebugLoc()); + + // We could use more precision here at the cost of some additional (code) + // complexity - if the original dbg.assign was adjacent to its store, we + // could position this new dbg.assign adjacent to its store rather than the + // old dbg.assgn. That would result in interleaved dbg.assigns rather than + // what we get now: + // split store !1 + // split store !2 + // dbg.assign !1 + // dbg.assign !2 + // This (current behaviour) results results in debug assignments being + // noted as slightly offset (in code) from the store. In practice this + // should have little effect on the debugging experience due to the fact + // that all the split stores should get the same line number. + NewAssign->moveBefore(DbgAssign); + + NewAssign->setDebugLoc(DbgAssign->getDebugLoc()); + LLVM_DEBUG(dbgs() << "Created new assign intrinsic: " << *NewAssign + << "\n"); + } +} /// A custom IRBuilder inserter which prefixes all names, but only in /// Assert builds. @@ -2285,6 +2396,7 @@ // original alloca. uint64_t NewBeginOffset = 0, NewEndOffset = 0; + uint64_t RelativeOffset = 0; uint64_t SliceSize = 0; bool IsSplittable = false; bool IsSplit = false; @@ -2348,8 +2460,14 @@ NewBeginOffset = std::max(BeginOffset, NewAllocaBeginOffset); NewEndOffset = std::min(EndOffset, NewAllocaEndOffset); + RelativeOffset = NewBeginOffset - BeginOffset; SliceSize = NewEndOffset - NewBeginOffset; - + LLVM_DEBUG(dbgs() << " Begin:(" << BeginOffset << ", " << EndOffset + << ") NewBegin:(" << NewBeginOffset << ", " + << NewEndOffset << ") NewAllocaBegin:(" + << NewAllocaBeginOffset << ", " << NewAllocaEndOffset + << ")\n"); + assert(IsSplit || RelativeOffset == 0); OldUse = I->getUse(); OldPtr = cast(OldUse->get()); @@ -2587,6 +2705,9 @@ bool rewriteVectorizedStoreInst(Value *V, StoreInst &SI, Value *OldOp, AAMDNodes AATags) { + // Capture V for the purpose of debug-info accounting once it's converted + // to a vector store. + Value *OrigV = V; if (V->getType() != VecTy) { unsigned BeginIndex = getIndex(NewBeginOffset); unsigned EndIndex = getIndex(NewEndOffset); @@ -2612,6 +2733,9 @@ Store->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); Pass.DeadInsts.push_back(&SI); + // NOTE: Careful to use OrigV rather than V. + migrateDebugInfo(&OldAI, RelativeOffset, SliceSize, &SI, Store, + Store->getPointerOperand(), OrigV, DL); LLVM_DEBUG(dbgs() << " to: " << *Store << "\n"); return true; } @@ -2634,6 +2758,10 @@ LLVMContext::MD_access_group}); if (AATags) Store->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + + migrateDebugInfo(&OldAI, RelativeOffset, SliceSize, &SI, Store, + Store->getPointerOperand(), Store->getValueOperand(), DL); + Pass.DeadInsts.push_back(&SI); LLVM_DEBUG(dbgs() << " to: " << *Store << "\n"); return true; @@ -2706,6 +2834,10 @@ NewSI->setAtomic(SI.getOrdering(), SI.getSyncScopeID()); if (NewSI->isAtomic()) NewSI->setAlignment(SI.getAlign()); + + migrateDebugInfo(&OldAI, RelativeOffset, SliceSize, &SI, NewSI, + NewSI->getPointerOperand(), NewSI->getValueOperand(), DL); + Pass.DeadInsts.push_back(&SI); deleteIfTriviallyDead(OldOp); @@ -2762,7 +2894,11 @@ assert(NewBeginOffset == BeginOffset); II.setDest(getNewAllocaSlicePtr(IRB, OldPtr->getType())); II.setDestAlignment(getSliceAlign()); - + // In theory we should call migrateDebugInfo here. However, we do not + // emit dbg.assign intrinsics for mem intrinsics storing through non- + // constant geps, or storing a variable number of bytes. + assert(at::getAssignmentMarkers(&II).empty() && + "AT: Unexpected link to non-const GEP"); deleteIfTriviallyDead(OldPtr); return false; } @@ -2795,11 +2931,15 @@ if (!CanContinue) { Type *SizeTy = II.getLength()->getType(); Constant *Size = ConstantInt::get(SizeTy, NewEndOffset - NewBeginOffset); - CallInst *New = IRB.CreateMemSet( + MemIntrinsic *New = cast(IRB.CreateMemSet( getNewAllocaSlicePtr(IRB, OldPtr->getType()), II.getValue(), Size, - MaybeAlign(getSliceAlign()), II.isVolatile()); + MaybeAlign(getSliceAlign()), II.isVolatile())); if (AATags) New->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + + migrateDebugInfo(&OldAI, RelativeOffset, SliceSize, &II, New, + New->getRawDest(), nullptr, DL); + LLVM_DEBUG(dbgs() << " to: " << *New << "\n"); return false; } @@ -2871,6 +3011,10 @@ LLVMContext::MD_access_group}); if (AATags) New->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + + migrateDebugInfo(&OldAI, RelativeOffset, SliceSize, &II, New, + New->getPointerOperand(), V, DL); + LLVM_DEBUG(dbgs() << " to: " << *New << "\n"); return !II.isVolatile(); } @@ -2901,8 +3045,22 @@ if (IsDest) { II.setDest(AdjustedPtr); II.setDestAlignment(SliceAlign); - } - else { + + // Update dbg.assign intrinsics. + if (isa(II.getLength())) { + assert((NewBeginOffset - NewAllocaBeginOffset == 0 || + at::getAssignmentMarkers(&II).empty()) && + "AT: Unkown situation"); + for (auto *DAI : at::getAssignmentMarkers(&II)) + DAI->setAddress(AdjustedPtr); + } else { + // At the moment, we do not emit dbg.assign intrinsics for mem + // intrinsics storing through non- constant geps, or storing a + // variable number of bytes. + assert(at::getAssignmentMarkers(&II).empty() && + "AT: Unexpected link to non-const GEP"); + } + } else { II.setSource(AdjustedPtr); II.setSourceAlignment(SliceAlign); } @@ -2991,6 +3149,9 @@ Size, II.isVolatile()); if (AATags) New->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + + migrateDebugInfo(&OldAI, RelativeOffset, SliceSize, &II, New, DestPtr, + nullptr, DL); LLVM_DEBUG(dbgs() << " to: " << *New << "\n"); return false; } @@ -3069,6 +3230,9 @@ LLVMContext::MD_access_group}); if (AATags) Store->setAAMetadata(AATags.shift(NewBeginOffset - BeginOffset)); + + migrateDebugInfo(&OldAI, RelativeOffset, SliceSize, &II, Store, DstPtr, Src, + DL); LLVM_DEBUG(dbgs() << " to: " << *Store << "\n"); return !II.isVolatile(); } @@ -3406,12 +3570,13 @@ struct StoreOpSplitter : public OpSplitter { StoreOpSplitter(Instruction *InsertionPoint, Value *Ptr, Type *BaseTy, - AAMDNodes AATags, Align BaseAlign, const DataLayout &DL, + AAMDNodes AATags, StoreInst *AggStore, Align BaseAlign, const DataLayout &DL, IRBuilderTy &IRB) : OpSplitter(InsertionPoint, Ptr, BaseTy, BaseAlign, DL, IRB), - AATags(AATags) {} + AATags(AATags), AggStore(AggStore) {} AAMDNodes AATags; + StoreInst *AggStore; /// Emit a leaf store of a single value. This is called at the leaves of the /// recursive emission to actually produce stores. void emitFunc(Type *Ty, Value *&Agg, Align Alignment, const Twine &Name) { @@ -3433,6 +3598,25 @@ GEPOperator::accumulateConstantOffset(BaseTy, GEPIndices, DL, Offset)) Store->setAAMetadata(AATags.shift(Offset.getZExtValue())); + // migrateDebugInfo requires the base Alloca. Walk to it from this gep. + // If we cannot (because there's an intervening non-const or unbounded + // gep) then we wouldn't expect to see dbg.assign intrinsics linked to + // this instruction. + APInt OffsetInBytes(DL.getTypeSizeInBits(Ptr->getType()), false); + Value *Base = InBoundsGEP->stripAndAccumulateInBoundsConstantOffsets( + DL, OffsetInBytes); + if (auto *OldAI = dyn_cast(Base)) { + uint64_t SizeInBits = + DL.getTypeSizeInBits(Store->getValueOperand()->getType()); + migrateDebugInfo(OldAI, OffsetInBytes.getZExtValue(), + bitsToBytes(SizeInBits), AggStore, Store, + Store->getPointerOperand(), Store->getValueOperand(), + DL); + } else { + assert(at::getAssignmentMarkers(Store).empty() && + "AT: unexpected debug.assign linked to store through " + "unbounded GEP"); + } LLVM_DEBUG(dbgs() << " to: " << *Store << "\n"); } }; @@ -3446,7 +3630,7 @@ // We have an aggregate being stored, split it apart. LLVM_DEBUG(dbgs() << " original: " << SI << "\n"); - StoreOpSplitter Splitter(&SI, *U, V->getType(), SI.getAAMetadata(), + StoreOpSplitter Splitter(&SI, *U, V->getType(), SI.getAAMetadata(), &SI, getAdjustedAlignment(&SI, 0), DL, IRB); Splitter.emitSplitOps(V->getType(), V, V->getName() + ".fca"); Visited.erase(&SI); @@ -4061,7 +4245,8 @@ getAdjustedAlignment(SI, PartOffset), /*IsVolatile*/ false); PStore->copyMetadata(*SI, {LLVMContext::MD_mem_parallel_loop_access, - LLVMContext::MD_access_group}); + LLVMContext::MD_access_group, + LLVMContext::MD_DIAssignID}); LLVM_DEBUG(dbgs() << " +" << PartOffset << ":" << *PStore << "\n"); } @@ -4503,6 +4688,8 @@ // Migrate debug information from the old alloca to the new alloca(s) // and the individual partitions. TinyPtrVector DbgDeclares = FindDbgAddrUses(&AI); + for (auto *DbgAssign : at::getAssignmentMarkers(&AI)) + DbgDeclares.push_back(DbgAssign); for (DbgVariableIntrinsic *DbgDeclare : DbgDeclares) { auto *Expr = DbgDeclare->getExpression(); DIBuilder DIB(*AI.getModule(), /*AllowUnresolved*/ false); @@ -4566,8 +4753,23 @@ OldDII->eraseFromParent(); } - DIB.insertDeclare(Fragment.Alloca, DbgDeclare->getVariable(), FragmentExpr, - DbgDeclare->getDebugLoc(), &AI); + if (auto *DbgAssign = dyn_cast(DbgDeclare)) { + // This alloca needs a DIAssignID unique metadata tag. It shouldn't + // alrady have one: assert this assumption. + assert(!Fragment.Alloca->getMetadata(LLVMContext::MD_DIAssignID)); + Fragment.Alloca->setMetadata(LLVMContext::MD_DIAssignID, + DIAssignID::getDistinct(AI.getContext())); + auto *NewAssign = DIB.insertDbgAssign( + Fragment.Alloca, DbgAssign->getValue(), DbgAssign->getVariable(), + FragmentExpr, Fragment.Alloca, DbgAssign->getAddressExpression(), + DbgAssign->getDebugLoc()); + NewAssign->setDebugLoc(DbgAssign->getDebugLoc()); + LLVM_DEBUG(dbgs() << "Created new assign intrinsic: " << *NewAssign + << "\n"); + } else { + DIB.insertDeclare(Fragment.Alloca, DbgDeclare->getVariable(), + FragmentExpr, DbgDeclare->getDebugLoc(), &AI); + } } } return Changed; @@ -4685,6 +4887,11 @@ OldDII->eraseFromParent(); } + for (DbgAssignIntrinsic *DAI : at::getAssignmentMarkers(I)) { + LLVM_DEBUG(dbgs() << " And deleting: " << *DAI << "\n"); + DAI->eraseFromParent(); + } + I->replaceAllUsesWith(UndefValue::get(I->getType())); for (Use &Operand : I->operands()) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/after-inlining.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/after-inlining.ll @@ -0,0 +1,148 @@ +; RUN: opt %s -S -sroa -o - -experimental-assignment-tracking | FileCheck %s + +;; Check that SROA preserves the InlinedAt status of new dbg.assign intriniscs +;; it inserts. + +;; $cat test.c +;; typedef struct { +;; int a; +;; int b[]; +;; } c; +;; int d, e, f; +;; void g(c *h) { +;; if (d) +;; h->a = 1; +;; } +;; void i(c *h) { +;; long j = f = 0; +;; for (; f < h->a; f++) +;; j += h->b[f]; +;; e = j; +;; } +;; void k() { +;; c j; +;; g(&j); +;; i(&j); +;; } +;; void l() { k(); } +;; +;; $ clang test.c -Xclang -fexperimental-assignment-tracking -O2 -g + +; CHECK: call void @llvm.dbg.assign(metadata i1 undef, metadata !{{.+}}, metadata !DIExpression(), metadata !{{.+}}, metadata i32* undef, metadata !DIExpression()), !dbg ![[DBG:[0-9]+]] + +; CHECK-DAG: ![[DBG]] = !DILocation(line: 0, scope: ![[INL_SC:[0-9]+]], inlinedAt: ![[IA:[0-9]+]]) +; CHECK-DAG: ![[IA]] = distinct !DILocation(line: 21, column: 12, scope: ![[SC:[0-9]+]]) +; CHECK-DAG: ![[SC]] = distinct !DISubprogram(name: "l", +; CHECK-DAG: ![[INL_SC]] = distinct !DISubprogram(name: "k" + +%struct.c = type { i32, [0 x i32] } + +@f = dso_local local_unnamed_addr global i32 0, align 4, !dbg !9 +@e = dso_local local_unnamed_addr global i32 0, align 4, !dbg !6 + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #1 +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 + +define dso_local void @l() local_unnamed_addr #4 !dbg !73 { +entry: + %j.i = alloca %struct.c, align 4, !DIAssignID !74 + call void @llvm.dbg.assign(metadata i1 undef, metadata !64, metadata !DIExpression(), metadata !74, metadata %struct.c* %j.i, metadata !DIExpression()) #5, !dbg !75 + %0 = bitcast %struct.c* %j.i to i8*, !dbg !77 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #5, !dbg !77 + %arrayidx.i.i = getelementptr inbounds %struct.c, %struct.c* %j.i, i64 0, i32 1, i64 0, !dbg !78 + %1 = load i32, i32* %arrayidx.i.i, align 4, !dbg !78, !tbaa !31 + store i32 1, i32* @f, align 4, !dbg !80, !tbaa !31 + store i32 %1, i32* @e, align 4, !dbg !81, !tbaa !31 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #5, !dbg !82 + ret void, !dbg !83 +} + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!11, !12, !13} +!llvm.ident = !{!14} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "d", scope: !2, file: !3, line: 5, type: !8, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.c", directory: "/") +!4 = !{} +!5 = !{!0, !6, !9} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "e", scope: !2, file: !3, line: 5, type: !8, isLocal: false, isDefinition: true) +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!9 = !DIGlobalVariableExpression(var: !10, expr: !DIExpression()) +!10 = distinct !DIGlobalVariable(name: "f", scope: !2, file: !3, line: 5, type: !8, isLocal: false, isDefinition: true) +!11 = !{i32 7, !"Dwarf Version", i32 4} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"wchar_size", i32 4} +!14 = !{!"clang version 12.0.0)"} +!15 = distinct !DISubprogram(name: "g", scope: !3, file: !3, line: 6, type: !16, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !27) +!16 = !DISubroutineType(types: !17) +!17 = !{null, !18} +!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64) +!19 = !DIDerivedType(tag: DW_TAG_typedef, name: "c", file: !3, line: 4, baseType: !20) +!20 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, line: 1, size: 32, elements: !21) +!21 = !{!22, !23} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !20, file: !3, line: 2, baseType: !8, size: 32) +!23 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !20, file: !3, line: 3, baseType: !24, offset: 32) +!24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !8, elements: !25) +!25 = !{!26} +!26 = !DISubrange(count: -1) +!27 = !{!28} +!28 = !DILocalVariable(name: "h", arg: 1, scope: !15, file: !3, line: 6, type: !18) +!29 = !DILocation(line: 7, column: 7, scope: !30) +!30 = distinct !DILexicalBlock(scope: !15, file: !3, line: 7, column: 7) +!31 = !{!32, !32, i64 0} +!32 = !{!"int", !33, i64 0} +!33 = !{!"omnipotent char", !34, i64 0} +!34 = !{!"Simple C/C++ TBAA"} +!35 = !DILocation(line: 7, column: 7, scope: !15) +!36 = !DILocation(line: 8, column: 8, scope: !30) +!37 = !DILocation(line: 8, column: 10, scope: !30) +!38 = !DILocation(line: 8, column: 5, scope: !30) +!39 = !DILocation(line: 9, column: 1, scope: !15) +!40 = distinct !DISubprogram(name: "i", scope: !3, file: !3, line: 10, type: !16, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !41) +!41 = !{!42, !43} +!42 = !DILocalVariable(name: "h", arg: 1, scope: !40, file: !3, line: 10, type: !18) +!43 = !DILocalVariable(name: "j", scope: !40, file: !3, line: 11, type: !44) +!44 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!45 = !DILocation(line: 0, scope: !40) +!46 = !DILocation(line: 12, column: 17, scope: !47) +!47 = distinct !DILexicalBlock(scope: !48, file: !3, line: 12, column: 3) +!48 = distinct !DILexicalBlock(scope: !40, file: !3, line: 12, column: 3) +!49 = !DILocation(line: 12, column: 12, scope: !47) +!50 = !DILocation(line: 12, column: 3, scope: !48) +!51 = !DILocation(line: 13, column: 10, scope: !47) +!52 = !DILocation(line: 13, column: 7, scope: !47) +!53 = !DILocation(line: 12, column: 21, scope: !47) +!54 = distinct !{!54, !50, !55, !56} +!55 = !DILocation(line: 13, column: 16, scope: !48) +!56 = !{!"llvm.loop.mustprogress"} +!57 = !DILocation(line: 14, column: 7, scope: !40) +!58 = !DILocation(line: 14, column: 5, scope: !40) +!59 = !DILocation(line: 15, column: 1, scope: !40) +!60 = distinct !DISubprogram(name: "k", scope: !3, file: !3, line: 16, type: !61, scopeLine: 16, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !63) +!61 = !DISubroutineType(types: !62) +!62 = !{null} +!63 = !{!64} +!64 = !DILocalVariable(name: "j", scope: !60, file: !3, line: 17, type: !19) +!65 = distinct !DIAssignID() +!66 = !DILocation(line: 0, scope: !60) +!67 = !DILocation(line: 17, column: 3, scope: !60) +!68 = !DILocation(line: 13, column: 10, scope: !47, inlinedAt: !69) +!69 = distinct !DILocation(line: 19, column: 3, scope: !60) +!70 = !DILocation(line: 0, scope: !40, inlinedAt: !69) +!71 = !DILocation(line: 14, column: 5, scope: !40, inlinedAt: !69) +!72 = !DILocation(line: 20, column: 1, scope: !60) +!73 = distinct !DISubprogram(name: "l", scope: !3, file: !3, line: 21, type: !61, scopeLine: 21, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4) +!74 = distinct !DIAssignID() +!75 = !DILocation(line: 0, scope: !60, inlinedAt: !76) +!76 = distinct !DILocation(line: 21, column: 12, scope: !73) +!77 = !DILocation(line: 17, column: 3, scope: !60, inlinedAt: !76) +!78 = !DILocation(line: 13, column: 10, scope: !47, inlinedAt: !79) +!79 = distinct !DILocation(line: 19, column: 3, scope: !60, inlinedAt: !76) +!80 = !DILocation(line: 0, scope: !40, inlinedAt: !79) +!81 = !DILocation(line: 14, column: 5, scope: !40, inlinedAt: !79) +!82 = !DILocation(line: 20, column: 1, scope: !60, inlinedAt: !76) +!83 = !DILocation(line: 21, column: 17, scope: !73) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/alloca-single-slice.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/alloca-single-slice.ll @@ -0,0 +1,88 @@ +; RUN: opt -sroa -verify -S %s -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg" + +; Check that single sliced allocas retain their assignment tracking debug info. + +;; $ cat test.c +;; struct a { +;; char b[8]; +;; }; +;; int c; +;; void d() { +;; struct a a; +;; memcpy(a.b, 0, c); +;; } +;; $ clang test.c -Xclang -disable-llvm-passes -O2 -g -c -S -emit-llvm -o - \ +;; | opt -passes=declare-to-assign -S -o - + +; CHECK: entry: +; CHECK-NEXT: %a.sroa.0 = alloca i64, align 8, !DIAssignID ![[ID_1:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR:[0-9]+]], metadata !DIExpression(), metadata ![[ID_1]], metadata i64* %a.sroa.0, metadata !DIExpression()), !dbg + +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %a.sroa.0.0.arraydecay.sroa_cast2, i8* align 1 null, i64 %conv, i1 false), !dbg + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +%struct.a = type { [8 x i8] } + +@c = dso_local global i32 0, align 4, !dbg !0 + +define dso_local void @d() !dbg !11 { +entry: + %a = alloca %struct.a, align 1, !DIAssignID !23 + call void @llvm.dbg.assign(metadata i1 undef, metadata !15, metadata !DIExpression(), metadata !23, metadata %struct.a* %a, metadata !DIExpression()), !dbg !24 + %0 = bitcast %struct.a* %a to i8*, !dbg !25 + call void @llvm.lifetime.start.p0i8(i64 8, i8* %0), !dbg !25 + %b = getelementptr inbounds %struct.a, %struct.a* %a, i32 0, i32 0, !dbg !26 + %arraydecay = getelementptr inbounds [8 x i8], [8 x i8]* %b, i64 0, i64 0, !dbg !27 + %1 = load i32, i32* @c, align 4, !dbg !28, !tbaa !29 + %conv = sext i32 %1 to i64, !dbg !28 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %arraydecay, i8* align 1 null, i64 %conv, i1 false), !dbg !27 + %2 = bitcast %struct.a* %a to i8*, !dbg !33 + call void @llvm.lifetime.end.p0i8(i64 8, i8* %2), !dbg !33 + ret void, !dbg !33 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "c", scope: !2, file: !3, line: 4, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 7, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{!"clang version 12.0.0"} +!11 = distinct !DISubprogram(name: "d", scope: !3, file: !3, line: 5, type: !12, scopeLine: 5, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !14) +!12 = !DISubroutineType(types: !13) +!13 = !{null} +!14 = !{!15} +!15 = !DILocalVariable(name: "a", scope: !11, file: !3, line: 6, type: !16) +!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "a", file: !3, line: 1, size: 64, elements: !17) +!17 = !{!18} +!18 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !16, file: !3, line: 2, baseType: !19, size: 64) +!19 = !DICompositeType(tag: DW_TAG_array_type, baseType: !20, size: 64, elements: !21) +!20 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!21 = !{!22} +!22 = !DISubrange(count: 8) +!23 = distinct !DIAssignID() +!24 = !DILocation(line: 0, scope: !11) +!25 = !DILocation(line: 6, column: 3, scope: !11) +!26 = !DILocation(line: 7, column: 12, scope: !11) +!27 = !DILocation(line: 7, column: 3, scope: !11) +!28 = !DILocation(line: 7, column: 18, scope: !11) +!29 = !{!30, !30, i64 0} +!30 = !{!"int", !31, i64 0} +!31 = !{!"omnipotent char", !32, i64 0} +!32 = !{!"Simple C/C++ TBAA"} +!33 = !DILocation(line: 8, column: 1, scope: !11) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/complex.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/complex.ll @@ -0,0 +1,69 @@ +; RUN: opt -sroa -S -o - %s -experimental-assignment-tracking | FileCheck %s +; +;; Based on llvm/test/DebugInfo/ARM/sroa-complex.ll +;; generated from: +;; $ cat test.c +;; void f(_Complex double c) { c = 0; } +;; $ clang test.c -g -O2 -c -Xclang -disable-llvm-passes -S \ +;; -emit-llvm -o - --target="thumbv7-apple-unknown" +;; +;; Commented out some parts of the function that are not relevant to the test. +;; +;; Check that a split store gets dbg.assigns fragments. Ensure that only the +;; value-expression gets fragment info; that the address-expression remains +;; untouched. + +;; dbg.assigns for the split (then promoted) stores. +; CHECK: %c.coerce.fca.0.extract = extractvalue [2 x i64] %c.coerce, 0 +; CHECK: %c.coerce.fca.1.extract = extractvalue [2 x i64] %c.coerce, 1 +; CHECK: call void @llvm.dbg.assign(metadata i64 %c.coerce.fca.0.extract,{{.+}}, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64),{{.+}}, metadata i64* undef, metadata !DIExpression()) +; CHECK: call void @llvm.dbg.assign(metadata i64 %c.coerce.fca.1.extract,{{.+}}, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64),{{.+}}, metadata i64* undef, {{.+}}) + +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "armv7-apple-unknown" + +define dso_local arm_aapcscc void @f([2 x i64] %c.coerce) #0 !dbg !8 { +entry: + %c = alloca { double, double }, align 8, !DIAssignID !14 + call void @llvm.dbg.assign(metadata i1 undef, metadata !13, metadata !DIExpression(), metadata !14, metadata { double, double }* %c, metadata !DIExpression()), !dbg !15 + %0 = bitcast { double, double }* %c to [2 x i64]* + store [2 x i64] %c.coerce, [2 x i64]* %0, align 8, !DIAssignID !16 + call void @llvm.dbg.assign(metadata [2 x i64] %c.coerce, metadata !13, metadata !DIExpression(), metadata !16, metadata [2 x i64]* %0, metadata !DIExpression()), !dbg !15 + ; --- The rest of this function isn't useful for the test --- + ;%c.realp = getelementptr inbounds { double, double }, { double, double }* %c, i32 0, i32 0, !dbg !17 + ;%c.imagp = getelementptr inbounds { double, double }, { double, double }* %c, i32 0, i32 1, !dbg !17 + ;store double 0.000000e+00, double* %c.realp, align 8, !dbg !17, !DIAssignID !18 + ;call void @llvm.dbg.assign(metadata double 0.000000e+00, metadata !13, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !18, metadata double* %c.realp, metadata !DIExpression()), !dbg !15 + ;store double 0.000000e+00, double* %c.imagp, align 8, !dbg !17, !DIAssignID !19 + ;call void @llvm.dbg.assign(metadata double 0.000000e+00, metadata !13, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !19, metadata double* %c.imagp, metadata !DIExpression()), !dbg !15 + ret void, !dbg !20 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", 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 = !{i32 1, !"min_enum_size", i32 4} +!7 = !{!"clang version 12.0.0"} +!8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 2, type: !9, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12) +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11} +!11 = !DIBasicType(name: "complex", size: 128, encoding: DW_ATE_complex_float) +!12 = !{!13} +!13 = !DILocalVariable(name: "c", arg: 1, scope: !8, file: !1, line: 2, type: !11) +!14 = distinct !DIAssignID() +!15 = !DILocation(line: 0, scope: !8) +!16 = distinct !DIAssignID() +!17 = !DILocation(line: 2, column: 31, scope: !8) +!18 = distinct !DIAssignID() +!19 = distinct !DIAssignID() +!20 = !DILocation(line: 2, column: 36, scope: !8) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag-2.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag-2.ll @@ -0,0 +1,269 @@ +; RUN: opt -sroa -S %s -o - -experimental-assignment-tracking | FileCheck %s + +;; $ cat test.cpp +;; class a { +;; public: +;; float b[4]; +;; void c(); +;; }; +;; class B { +;; public: +;; B(a d) : e(d) {} +;; a &f() { return e; } +;; B operator*(const B &)const; +;; int g; +;; a e; +;; }; +;; B B::operator*(const B &)const { return e; } +;; class h { +;; public: +;; B i(); +;; }; +;; void j() { +;; h convexbody, k; +;; B l = k.i(), m = convexbody.i(), n = l * m; +;; a o = n.f(); // Looking at this store, o[0, 128] <- n[32, 160]. +;; o.c(); +;; } +;; Generated by grabbing IR before sroa in: +;; $ clang++ -O2 -g -c test.cpp -Xclang -fexperimental-assignment-tracking + +;; Check that the store 4xfloat split into 2x store 2xfloat has correct debug +;; info when the source (n, 160 bits of int+5*float) is split beforehand (see +;; comment in test.cpp above). Ensure that only the value-expression gets +;; fragment info; that the address-expression remains untouched. + +;; Check nearby instructions to make sure we're looking in the right place. +; CHECK: define dso_local void @_Z1jv() +; CHECK: call void @_ZN1h1iEv(%class.B* nonnull sret(%class.B) align 4 %m, + +; CHECK: store <2 x float> %agg.tmp.sroa.0.0.copyload.i, <2 x float>* %n.sroa.2.4..sroa_cast, align 4,{{.+}}!DIAssignID ![[id1:[0-9]+]] +; CHECK: store <2 x float> %agg.tmp.sroa.2.0.copyload.i, <2 x float>* %n.sroa.4.4..sroa_cast, align 4,{{.+}}!DIAssignID ![[id2:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata <2 x float> %agg.tmp.sroa.0.0.copyload.i, metadata ![[var:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata ![[id1]], metadata <2 x float>* %n.sroa.2.4..sroa_cast, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata <2 x float> %agg.tmp.sroa.2.0.copyload.i, metadata ![[var]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata ![[id2]], metadata <2 x float>* %n.sroa.4.4..sroa_cast, metadata !DIExpression()), !dbg + +; CHECK: ret + +%class.B = type { i32, %class.a } +%class.a = type { [4 x float] } +%class.h = type { i8 } + +$_ZN1BC2E1a = comdat any + +$_ZN1B1fEv = comdat any + +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local void @_ZNK1BmlERKS_(%class.B* noalias nocapture sret(%class.B) align 4 %agg.result, %class.B* nocapture readonly %this, %class.B* nocapture nonnull readnone align 4 dereferenceable(20) %0) local_unnamed_addr #0 align 2 !dbg !7 { +entry: + %agg.tmp.sroa.0.0..sroa_idx = getelementptr inbounds %class.B, %class.B* %this, i64 0, i32 1, !dbg !42 + %agg.tmp.sroa.0.0..sroa_cast = bitcast %class.a* %agg.tmp.sroa.0.0..sroa_idx to <2 x float>*, !dbg !42 + %agg.tmp.sroa.0.0.copyload = load <2 x float>, <2 x float>* %agg.tmp.sroa.0.0..sroa_cast, align 4, !dbg !42, !tbaa.struct !43 + %agg.tmp.sroa.2.0..sroa_idx2 = getelementptr inbounds %class.B, %class.B* %this, i64 0, i32 1, i32 0, i64 2, !dbg !42 + %agg.tmp.sroa.2.0..sroa_cast = bitcast float* %agg.tmp.sroa.2.0..sroa_idx2 to <2 x float>*, !dbg !42 + %agg.tmp.sroa.2.0.copyload = load <2 x float>, <2 x float>* %agg.tmp.sroa.2.0..sroa_cast, align 4, !dbg !42, !tbaa.struct !43 + %d.sroa.0.0..sroa_idx.i = getelementptr inbounds %class.B, %class.B* %agg.result, i64 0, i32 1, !dbg !47 + %d.sroa.0.0..sroa_cast.i = bitcast %class.a* %d.sroa.0.0..sroa_idx.i to <2 x float>*, !dbg !47 + store <2 x float> %agg.tmp.sroa.0.0.copyload, <2 x float>* %d.sroa.0.0..sroa_cast.i, align 4, !dbg !47, !tbaa.struct !43 + %d.sroa.2.0..sroa_idx2.i = getelementptr inbounds %class.B, %class.B* %agg.result, i64 0, i32 1, i32 0, i64 2, !dbg !47 + %d.sroa.2.0..sroa_cast.i = bitcast float* %d.sroa.2.0..sroa_idx2.i to <2 x float>*, !dbg !47 + store <2 x float> %agg.tmp.sroa.2.0.copyload, <2 x float>* %d.sroa.2.0..sroa_cast.i, align 4, !dbg !47, !tbaa.struct !43 + ret void, !dbg !54 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local void @_ZN1BC2E1a(%class.B* %this, <2 x float> %d.coerce0, <2 x float> %d.coerce1) unnamed_addr #2 comdat align 2 !dbg !48 { +entry: + %d.sroa.0.0..sroa_idx = getelementptr inbounds %class.B, %class.B* %this, i64 0, i32 1, !dbg !55 + %d.sroa.0.0..sroa_cast = bitcast %class.a* %d.sroa.0.0..sroa_idx to <2 x float>*, !dbg !55 + store <2 x float> %d.coerce0, <2 x float>* %d.sroa.0.0..sroa_cast, align 4, !dbg !55, !tbaa.struct !43 + %d.sroa.2.0..sroa_idx2 = getelementptr inbounds %class.B, %class.B* %this, i64 0, i32 1, i32 0, i64 2, !dbg !55 + %d.sroa.2.0..sroa_cast = bitcast float* %d.sroa.2.0..sroa_idx2 to <2 x float>*, !dbg !55 + store <2 x float> %d.coerce1, <2 x float>* %d.sroa.2.0..sroa_cast, align 4, !dbg !55, !tbaa.struct !43 + ret void, !dbg !56 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +; Function Attrs: uwtable +define dso_local void @_Z1jv() local_unnamed_addr #4 !dbg !57 { +entry: + %convexbody = alloca %class.h, align 1, !DIAssignID !73 + call void @llvm.dbg.assign(metadata i1 undef, metadata !61, metadata !DIExpression(), metadata !73, metadata %class.h* %convexbody, metadata !DIExpression()), !dbg !74 + %k = alloca %class.h, align 1, !DIAssignID !75 + call void @llvm.dbg.assign(metadata i1 undef, metadata !68, metadata !DIExpression(), metadata !75, metadata %class.h* %k, metadata !DIExpression()), !dbg !74 + %l = alloca %class.B, align 4, !DIAssignID !76 + call void @llvm.dbg.assign(metadata i1 undef, metadata !69, metadata !DIExpression(), metadata !76, metadata %class.B* %l, metadata !DIExpression()), !dbg !74 + %m = alloca %class.B, align 4, !DIAssignID !77 + call void @llvm.dbg.assign(metadata i1 undef, metadata !70, metadata !DIExpression(), metadata !77, metadata %class.B* %m, metadata !DIExpression()), !dbg !74 + %n = alloca %class.B, align 4, !DIAssignID !78 + call void @llvm.dbg.assign(metadata i1 undef, metadata !71, metadata !DIExpression(), metadata !78, metadata %class.B* %n, metadata !DIExpression()), !dbg !74 + %o = alloca %class.a, align 4, !DIAssignID !79 + call void @llvm.dbg.assign(metadata i1 undef, metadata !72, metadata !DIExpression(), metadata !79, metadata %class.a* %o, metadata !DIExpression()), !dbg !74 + %0 = getelementptr inbounds %class.h, %class.h* %convexbody, i64 0, i32 0, !dbg !80 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %0) #7, !dbg !80 + %1 = getelementptr inbounds %class.h, %class.h* %k, i64 0, i32 0, !dbg !80 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %1) #7, !dbg !80 + %2 = bitcast %class.B* %l to i8*, !dbg !81 + call void @llvm.lifetime.start.p0i8(i64 20, i8* nonnull %2) #7, !dbg !81 + call void @_ZN1h1iEv(%class.B* nonnull sret(%class.B) align 4 %l, %class.h* nonnull %k), !dbg !82 + %3 = bitcast %class.B* %m to i8*, !dbg !81 + call void @llvm.lifetime.start.p0i8(i64 20, i8* nonnull %3) #7, !dbg !81 + call void @_ZN1h1iEv(%class.B* nonnull sret(%class.B) align 4 %m, %class.h* nonnull %convexbody), !dbg !83 + %4 = bitcast %class.B* %n to i8*, !dbg !81 + call void @llvm.lifetime.start.p0i8(i64 20, i8* nonnull %4) #7, !dbg !81 + %agg.tmp.sroa.0.0..sroa_idx.i = getelementptr inbounds %class.B, %class.B* %l, i64 0, i32 1, !dbg !84 + %agg.tmp.sroa.0.0..sroa_cast.i = bitcast %class.a* %agg.tmp.sroa.0.0..sroa_idx.i to <2 x float>*, !dbg !84 + %agg.tmp.sroa.0.0.copyload.i = load <2 x float>, <2 x float>* %agg.tmp.sroa.0.0..sroa_cast.i, align 4, !dbg !84, !tbaa.struct !43, !noalias !86 + %agg.tmp.sroa.2.0..sroa_idx2.i = getelementptr inbounds %class.B, %class.B* %l, i64 0, i32 1, i32 0, i64 2, !dbg !84 + %agg.tmp.sroa.2.0..sroa_cast.i = bitcast float* %agg.tmp.sroa.2.0..sroa_idx2.i to <2 x float>*, !dbg !84 + %agg.tmp.sroa.2.0.copyload.i = load <2 x float>, <2 x float>* %agg.tmp.sroa.2.0..sroa_cast.i, align 4, !dbg !84, !tbaa.struct !43, !noalias !86 + %d.sroa.0.0..sroa_idx.i.i = getelementptr inbounds %class.B, %class.B* %n, i64 0, i32 1, !dbg !89 + %d.sroa.0.0..sroa_cast.i.i = bitcast %class.a* %d.sroa.0.0..sroa_idx.i.i to <2 x float>*, !dbg !89 + store <2 x float> %agg.tmp.sroa.0.0.copyload.i, <2 x float>* %d.sroa.0.0..sroa_cast.i.i, align 4, !dbg !89, !tbaa.struct !43, !alias.scope !86 + %d.sroa.2.0..sroa_idx2.i.i = getelementptr inbounds %class.B, %class.B* %n, i64 0, i32 1, i32 0, i64 2, !dbg !89 + %d.sroa.2.0..sroa_cast.i.i = bitcast float* %d.sroa.2.0..sroa_idx2.i.i to <2 x float>*, !dbg !89 + store <2 x float> %agg.tmp.sroa.2.0.copyload.i, <2 x float>* %d.sroa.2.0..sroa_cast.i.i, align 4, !dbg !89, !tbaa.struct !43, !alias.scope !86 + %5 = bitcast %class.a* %o to i8*, !dbg !91 + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %5) #7, !dbg !91 + %e.i = getelementptr inbounds %class.B, %class.B* %n, i64 0, i32 1, !dbg !92 + %6 = bitcast %class.a* %e.i to i8*, !dbg !97 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 4 dereferenceable(16) %5, i8* nonnull align 4 dereferenceable(16) %6, i64 16, i1 false), !dbg !97, !tbaa.struct !43, !DIAssignID !98 + call void @llvm.dbg.assign(metadata i1 undef, metadata !72, metadata !DIExpression(), metadata !98, metadata i8* %5, metadata !DIExpression()), !dbg !74 + call void @_ZN1a1cEv(%class.a* nonnull %o), !dbg !99 + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %5) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 20, i8* nonnull %4) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 20, i8* nonnull %3) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 20, i8* nonnull %2) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %1) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %0) #7, !dbg !100 + ret void, !dbg !100 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +declare dso_local void @_ZN1h1iEv(%class.B* sret(%class.B) align 4, %class.h*) local_unnamed_addr #5 + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local nonnull align 4 dereferenceable(16) %class.a* @_ZN1B1fEv(%class.B* %this) local_unnamed_addr #6 comdat align 2 !dbg !93 { +entry: + %e = getelementptr inbounds %class.B, %class.B* %this, i64 0, i32 1, !dbg !101 + ret %class.a* %e, !dbg !102 +} + +declare dso_local void @_ZN1a1cEv(%class.a*) local_unnamed_addr #5 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +!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: "operator*", linkageName: "_ZNK1BmlERKS_", scope: !8, file: !1, line: 14, type: !33, scopeLine: 14, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !32, retainedNodes: !38) +!8 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "B", file: !1, line: 6, size: 160, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !9, identifier: "_ZTS1B") +!9 = !{!10, !12, !24, !28, !32} +!10 = !DIDerivedType(tag: DW_TAG_member, name: "g", scope: !8, file: !1, line: 11, baseType: !11, size: 32, flags: DIFlagPublic) +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DIDerivedType(tag: DW_TAG_member, name: "e", scope: !8, file: !1, line: 12, baseType: !13, size: 128, offset: 32, flags: DIFlagPublic) +!13 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !1, line: 1, size: 128, flags: DIFlagTypePassByValue, elements: !14, identifier: "_ZTS1a") +!14 = !{!15, !20} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !13, file: !1, line: 3, baseType: !16, size: 128, flags: DIFlagPublic) +!16 = !DICompositeType(tag: DW_TAG_array_type, baseType: !17, size: 128, elements: !18) +!17 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!18 = !{!19} +!19 = !DISubrange(count: 4) +!20 = !DISubprogram(name: "c", linkageName: "_ZN1a1cEv", scope: !13, file: !1, line: 4, type: !21, scopeLine: 4, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!21 = !DISubroutineType(types: !22) +!22 = !{null, !23} +!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!24 = !DISubprogram(name: "B", scope: !8, file: !1, line: 8, type: !25, scopeLine: 8, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!25 = !DISubroutineType(types: !26) +!26 = !{null, !27, !13} +!27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!28 = !DISubprogram(name: "f", linkageName: "_ZN1B1fEv", scope: !8, file: !1, line: 9, type: !29, scopeLine: 9, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!29 = !DISubroutineType(types: !30) +!30 = !{!31, !27} +!31 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !13, size: 64) +!32 = !DISubprogram(name: "operator*", linkageName: "_ZNK1BmlERKS_", scope: !8, file: !1, line: 10, type: !33, scopeLine: 10, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!33 = !DISubroutineType(types: !34) +!34 = !{!8, !35, !37} +!35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!36 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !8) +!37 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !36, size: 64) +!38 = !{!39, !41} +!39 = !DILocalVariable(name: "this", arg: 1, scope: !7, type: !40, flags: DIFlagArtificial | DIFlagObjectPointer) +!40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !36, size: 64) +!41 = !DILocalVariable(arg: 2, scope: !7, file: !1, line: 14, type: !37) +!42 = !DILocation(line: 14, column: 41, scope: !7) +!43 = !{i64 0, i64 16, !44} +!44 = !{!45, !45, i64 0} +!45 = !{!"omnipotent char", !46, i64 0} +!46 = !{!"Simple C++ TBAA"} +!47 = !DILocation(line: 8, column: 12, scope: !48, inlinedAt: !53) +!48 = distinct !DISubprogram(name: "B", linkageName: "_ZN1BC2E1a", scope: !8, file: !1, line: 8, type: !25, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !24, retainedNodes: !49) +!49 = !{!50, !52} +!50 = !DILocalVariable(name: "this", arg: 1, scope: !48, type: !51, flags: DIFlagArtificial | DIFlagObjectPointer) +!51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) +!52 = !DILocalVariable(name: "d", arg: 2, scope: !48, file: !1, line: 8, type: !13) +!53 = distinct !DILocation(line: 14, column: 41, scope: !7) +!54 = !DILocation(line: 14, column: 34, scope: !7) +!55 = !DILocation(line: 8, column: 12, scope: !48) +!56 = !DILocation(line: 8, column: 18, scope: !48) +!57 = distinct !DISubprogram(name: "j", linkageName: "_Z1jv", scope: !1, file: !1, line: 19, type: !58, scopeLine: 19, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !60) +!58 = !DISubroutineType(types: !59) +!59 = !{null} +!60 = !{!61, !68, !69, !70, !71, !72} +!61 = !DILocalVariable(name: "convexbody", scope: !57, file: !1, line: 20, type: !62) +!62 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "h", file: !1, line: 15, size: 8, flags: DIFlagTypePassByValue, elements: !63, identifier: "_ZTS1h") +!63 = !{!64} +!64 = !DISubprogram(name: "i", linkageName: "_ZN1h1iEv", scope: !62, file: !1, line: 17, type: !65, scopeLine: 17, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!65 = !DISubroutineType(types: !66) +!66 = !{!8, !67} +!67 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !62, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!68 = !DILocalVariable(name: "k", scope: !57, file: !1, line: 20, type: !62) +!69 = !DILocalVariable(name: "l", scope: !57, file: !1, line: 21, type: !8) +!70 = !DILocalVariable(name: "m", scope: !57, file: !1, line: 21, type: !8) +!71 = !DILocalVariable(name: "n", scope: !57, file: !1, line: 21, type: !8) +!72 = !DILocalVariable(name: "o", scope: !57, file: !1, line: 22, type: !13) +!73 = distinct !DIAssignID() +!74 = !DILocation(line: 0, scope: !57) +!75 = distinct !DIAssignID() +!76 = distinct !DIAssignID() +!77 = distinct !DIAssignID() +!78 = distinct !DIAssignID() +!79 = distinct !DIAssignID() +!80 = !DILocation(line: 20, column: 3, scope: !57) +!81 = !DILocation(line: 21, column: 3, scope: !57) +!82 = !DILocation(line: 21, column: 11, scope: !57) +!83 = !DILocation(line: 21, column: 31, scope: !57) +!84 = !DILocation(line: 14, column: 41, scope: !7, inlinedAt: !85) +!85 = distinct !DILocation(line: 21, column: 42, scope: !57) +!86 = !{!87} +!87 = distinct !{!87, !88, !"_ZNK1BmlERKS_: %agg.result"} +!88 = distinct !{!88, !"_ZNK1BmlERKS_"} +!89 = !DILocation(line: 8, column: 12, scope: !48, inlinedAt: !90) +!90 = distinct !DILocation(line: 14, column: 41, scope: !7, inlinedAt: !85) +!91 = !DILocation(line: 22, column: 3, scope: !57) +!92 = !DILocation(line: 9, column: 19, scope: !93, inlinedAt: !96) +!93 = distinct !DISubprogram(name: "f", linkageName: "_ZN1B1fEv", scope: !8, file: !1, line: 9, type: !29, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !28, retainedNodes: !94) +!94 = !{!95} +!95 = !DILocalVariable(name: "this", arg: 1, scope: !93, type: !51, flags: DIFlagArtificial | DIFlagObjectPointer) +!96 = distinct !DILocation(line: 22, column: 11, scope: !57) +!97 = !DILocation(line: 22, column: 9, scope: !57) +!98 = distinct !DIAssignID() +!99 = !DILocation(line: 23, column: 5, scope: !57) +!100 = !DILocation(line: 24, column: 1, scope: !57) +!101 = !DILocation(line: 9, column: 19, scope: !93) +!102 = !DILocation(line: 9, column: 12, scope: !93) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag.ll @@ -0,0 +1,117 @@ +; RUN: opt %s -S -sroa -o - -experimental-assignment-tracking | FileCheck %s + +;; $ cat test.cpp +;; class c { +;; float b[4]; +;; }; +;; c fn1(); +;; void d() { +;; c a[3]; +;; a[2] = fn1(); +;; } +;; +;; Generated by grabbing IR before sroa in: +;; $ clang++ -O2 -g -c test.cpp -Xclang -fexperimental-assignment-tracking +;; +;; Check that when the memcpy to fragment(256, 128) is split into two 2xfloat +;; stores, the dbg.assign is split into two with fragment(256, 64) & +;; fragment(320, 64). Ensure that only the value-expression gets fragment info; +;; that the address-expression remains untouched. + +; CHECK: %call = call +; CHECK-NEXT: %0 = extractvalue { <2 x float>, <2 x float> } %call, 0 +; CHECK-NEXT: %1 = extractvalue { <2 x float>, <2 x float> } %call, 1 +; CHECK-NEXT: call void @llvm.dbg.assign(metadata <2 x float> %0, metadata ![[var:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 256, 64),{{.+}},{{.+}}undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata <2 x float> %1, metadata ![[var]], metadata !DIExpression(DW_OP_LLVM_fragment, 320, 64),{{.+}},{{.+}}undef, metadata !DIExpression()), !dbg + +%class.c = type { [4 x float] } + +; Function Attrs: uwtable +define dso_local void @_Z1dv() #0 !dbg !7 { +entry: + %a = alloca [3 x %class.c], align 16, !DIAssignID !22 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !22, metadata [3 x %class.c]* %a, metadata !DIExpression()), !dbg !23 + %ref.tmp = alloca %class.c, align 4 + %0 = bitcast [3 x %class.c]* %a to i8*, !dbg !24 + call void @llvm.lifetime.start.p0i8(i64 48, i8* %0) #4, !dbg !24 + %1 = bitcast %class.c* %ref.tmp to i8*, !dbg !25 + call void @llvm.lifetime.start.p0i8(i64 16, i8* %1) #4, !dbg !25 + %call = call { <2 x float>, <2 x float> } @_Z3fn1v(), !dbg !25 + %coerce.dive = getelementptr inbounds %class.c, %class.c* %ref.tmp, i32 0, i32 0, !dbg !25 + %2 = bitcast [4 x float]* %coerce.dive to { <2 x float>, <2 x float> }*, !dbg !25 + %3 = getelementptr inbounds { <2 x float>, <2 x float> }, { <2 x float>, <2 x float> }* %2, i32 0, i32 0, !dbg !25 + %4 = extractvalue { <2 x float>, <2 x float> } %call, 0, !dbg !25 + store <2 x float> %4, <2 x float>* %3, align 4, !dbg !25 + %5 = getelementptr inbounds { <2 x float>, <2 x float> }, { <2 x float>, <2 x float> }* %2, i32 0, i32 1, !dbg !25 + %6 = extractvalue { <2 x float>, <2 x float> } %call, 1, !dbg !25 + store <2 x float> %6, <2 x float>* %5, align 4, !dbg !25 + %arrayidx = getelementptr inbounds [3 x %class.c], [3 x %class.c]* %a, i64 0, i64 2, !dbg !26 + %7 = bitcast %class.c* %arrayidx to i8*, !dbg !27 + %8 = bitcast %class.c* %ref.tmp to i8*, !dbg !27 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %7, i8* align 4 %8, i64 16, i1 false), !dbg !27, !tbaa.struct !28, !DIAssignID !32 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 256, 128), metadata !32, metadata i8* %7, metadata !DIExpression()), !dbg !23 + %9 = bitcast %class.c* %ref.tmp to i8*, !dbg !26 + call void @llvm.lifetime.end.p0i8(i64 16, i8* %9) #4, !dbg !26 + %10 = bitcast [3 x %class.c]* %a to i8*, !dbg !33 + call void @llvm.lifetime.end.p0i8(i64 48, i8* %10) #4, !dbg !33 + ret void, !dbg !33 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 + +declare !dbg !34 dso_local { <2 x float>, <2 x float> } @_Z3fn1v() #3 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 + +!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: "d", linkageName: "_Z1dv", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "a", scope: !7, file: !1, line: 6, type: !12) +!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 384, elements: !20) +!13 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "c", file: !1, line: 1, size: 128, flags: DIFlagTypePassByValue, elements: !14, identifier: "_ZTS1c") +!14 = !{!15} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !13, file: !1, line: 2, baseType: !16, size: 128) +!16 = !DICompositeType(tag: DW_TAG_array_type, baseType: !17, size: 128, elements: !18) +!17 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!18 = !{!19} +!19 = !DISubrange(count: 4) +!20 = !{!21} +!21 = !DISubrange(count: 3) +!22 = distinct !DIAssignID() +!23 = !DILocation(line: 0, scope: !7) +!24 = !DILocation(line: 6, column: 3, scope: !7) +!25 = !DILocation(line: 7, column: 10, scope: !7) +!26 = !DILocation(line: 7, column: 3, scope: !7) +!27 = !DILocation(line: 7, column: 8, scope: !7) +!28 = !{i64 0, i64 16, !29} +!29 = !{!30, !30, i64 0} +!30 = !{!"omnipotent char", !31, i64 0} +!31 = !{!"Simple C++ TBAA"} +!32 = distinct !DIAssignID() +!33 = !DILocation(line: 8, column: 1, scope: !7) +!34 = !DISubprogram(name: "fn1", linkageName: "_Z3fn1v", scope: !1, file: !1, line: 4, type: !35, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!35 = !DISubroutineType(types: !36) +!36 = !{!13} Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/id.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/id.ll @@ -0,0 +1,180 @@ +; RUN: opt -sroa -S %s -o - -experimental-assignment-tracking | FileCheck %s + +;; Check that multiple dbg.assign intrinsics linked to a store that is getting +;; split (or at least that is touched by SROA, causing a replacement store to +;; be generated) are still both linked to the new store(s). +;; +;; Additionally, check that SROA inserts new dbg.assign intrinsics by the +;; originals. + +;; $ cat test.cpp +;; class a { +;; public: +;; a(int, float &) {} +;; }; +;; float b, d; +;; int c; +;; void f() { +;; float e; +;; if (c) +;; e = b; +;; else +;; e = b / d; +;; a(c, e); +;; } +;; +;; Generated by grabbing IR before sroa in: +;; $ clang++ -O2 -g -c test.cpp -Xclang -fexperimental-assignment-tracking + +; CHECK: if.then: +; CHECK-NEXT: %1 = load float +; CHECK-NEXT: call void @llvm.dbg.assign(metadata float %storemerge, metadata ![[var:[0-9]+]], metadata !DIExpression(), metadata ![[id:[0-9]+]], metadata float* undef, metadata !DIExpression()), !dbg ![[dbg:[0-9]+]] + +; CHECK: if.else: +; CHECK-NEXT: %2 = load float +; CHECK-NEXT: %3 = load float +; CHECK-NEXT: %div = fdiv float +; CHECK: call void @llvm.dbg.assign(metadata float %storemerge, metadata ![[var]], metadata !DIExpression(), metadata ![[id]], metadata float* undef, metadata !DIExpression()), !dbg ![[dbg]] + +%class.a = type { i8 } + +$_ZN1aC2EiRf = comdat any + +@b = dso_local local_unnamed_addr global float 0.000000e+00, align 4, !dbg !0 +@d = dso_local local_unnamed_addr global float 0.000000e+00, align 4, !dbg !6 +@c = dso_local local_unnamed_addr global i32 0, align 4, !dbg !9 + +; Function Attrs: nounwind readonly uwtable +define dso_local void @_Z1fv() local_unnamed_addr #0 !dbg !16 { +entry: + %e = alloca float, align 4, !DIAssignID !21 + call void @llvm.dbg.assign(metadata i1 undef, metadata !20, metadata !DIExpression(), metadata !21, metadata float* %e, metadata !DIExpression()), !dbg !22 + %agg.tmp.ensured = alloca %class.a, align 1 + %0 = bitcast float* %e to i8*, !dbg !23 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #4, !dbg !23 + %1 = load i32, i32* @c, align 4, !dbg !24, !tbaa !26 + %tobool.not = icmp eq i32 %1, 0, !dbg !24 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !30 + +if.then: ; preds = %entry + %2 = load float, float* @b, align 4, !dbg !31, !tbaa !32 + call void @llvm.dbg.assign(metadata float %2, metadata !20, metadata !DIExpression(), metadata !34, metadata float* %e, metadata !DIExpression()), !dbg !22 + br label %if.end, !dbg !35 + +if.else: ; preds = %entry + %3 = load float, float* @b, align 4, !dbg !36, !tbaa !32 + %4 = load float, float* @d, align 4, !dbg !37, !tbaa !32 + %div = fdiv float %3, %4, !dbg !38 + call void @llvm.dbg.assign(metadata float %div, metadata !20, metadata !DIExpression(), metadata !34, metadata float* %e, metadata !DIExpression()), !dbg !22 + br label %if.end + +if.end: ; preds = %if.else, %if.then + %storemerge = phi float [ %div, %if.else ], [ %2, %if.then ], !dbg !39 + store float %storemerge, float* %e, align 4, !dbg !39, !tbaa !32, !DIAssignID !34 + %5 = load i32, i32* @c, align 4, !dbg !40, !tbaa !26 + call void @llvm.dbg.assign(metadata i1 undef, metadata !41, metadata !DIExpression(), metadata !54, metadata %class.a** undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata i1 undef, metadata !51, metadata !DIExpression(), metadata !57, metadata i32* undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata i1 undef, metadata !52, metadata !DIExpression(), metadata !58, metadata float** undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata %class.a* %agg.tmp.ensured, metadata !41, metadata !DIExpression(), metadata !59, metadata %class.a** undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata i32 %5, metadata !51, metadata !DIExpression(), metadata !60, metadata i32* undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata float* %e, metadata !52, metadata !DIExpression(), metadata !61, metadata float** undef, metadata !DIExpression()), !dbg !55 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #4, !dbg !62 + ret void, !dbg !62 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local void @_ZN1aC2EiRf(%class.a* %this, i32 %0, float* nonnull align 4 dereferenceable(4) %1) unnamed_addr #2 comdat align 2 !dbg !42 { +entry: + call void @llvm.dbg.assign(metadata i1 undef, metadata !41, metadata !DIExpression(), metadata !63, metadata %class.a** undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata i1 undef, metadata !51, metadata !DIExpression(), metadata !65, metadata i32* undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata i1 undef, metadata !52, metadata !DIExpression(), metadata !66, metadata float** undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata %class.a* %this, metadata !41, metadata !DIExpression(), metadata !67, metadata %class.a** undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata i32 %0, metadata !51, metadata !DIExpression(), metadata !68, metadata i32* undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata float* %1, metadata !52, metadata !DIExpression(), metadata !69, metadata float** undef, metadata !DIExpression()), !dbg !64 + ret void, !dbg !70 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!12, !13, !14} +!llvm.ident = !{!15} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 5, type: !8, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "reduce.cpp", directory: "") +!4 = !{} +!5 = !{!0, !6, !9} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "d", scope: !2, file: !3, line: 5, type: !8, isLocal: false, isDefinition: true) +!8 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!9 = !DIGlobalVariableExpression(var: !10, expr: !DIExpression()) +!10 = distinct !DIGlobalVariable(name: "c", scope: !2, file: !3, line: 6, type: !11, isLocal: false, isDefinition: true) +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !{i32 7, !"Dwarf Version", i32 4} +!13 = !{i32 2, !"Debug Info Version", i32 3} +!14 = !{i32 1, !"wchar_size", i32 4} +!15 = !{!"clang version 12.0.0"} +!16 = distinct !DISubprogram(name: "f", linkageName: "_Z1fv", scope: !3, file: !3, line: 7, type: !17, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !19) +!17 = !DISubroutineType(types: !18) +!18 = !{null} +!19 = !{!20} +!20 = !DILocalVariable(name: "e", scope: !16, file: !3, line: 8, type: !8) +!21 = distinct !DIAssignID() +!22 = !DILocation(line: 0, scope: !16) +!23 = !DILocation(line: 8, column: 3, scope: !16) +!24 = !DILocation(line: 9, column: 7, scope: !25) +!25 = distinct !DILexicalBlock(scope: !16, file: !3, line: 9, column: 7) +!26 = !{!27, !27, i64 0} +!27 = !{!"int", !28, i64 0} +!28 = !{!"omnipotent char", !29, i64 0} +!29 = !{!"Simple C++ TBAA"} +!30 = !DILocation(line: 9, column: 7, scope: !16) +!31 = !DILocation(line: 10, column: 9, scope: !25) +!32 = !{!33, !33, i64 0} +!33 = !{!"float", !28, i64 0} +!34 = distinct !DIAssignID() +!35 = !DILocation(line: 10, column: 5, scope: !25) +!36 = !DILocation(line: 12, column: 9, scope: !25) +!37 = !DILocation(line: 12, column: 13, scope: !25) +!38 = !DILocation(line: 12, column: 11, scope: !25) +!39 = !DILocation(line: 0, scope: !25) +!40 = !DILocation(line: 13, column: 5, scope: !16) +!41 = !DILocalVariable(name: "this", arg: 1, scope: !42, type: !53, flags: DIFlagArtificial | DIFlagObjectPointer) +!42 = distinct !DISubprogram(name: "a", linkageName: "_ZN1aC2EiRf", scope: !43, file: !3, line: 3, type: !46, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, declaration: !45, retainedNodes: !50) +!43 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !3, line: 1, size: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !44, identifier: "_ZTS1a") +!44 = !{!45} +!45 = !DISubprogram(name: "a", scope: !43, file: !3, line: 3, type: !46, scopeLine: 3, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!46 = !DISubroutineType(types: !47) +!47 = !{null, !48, !11, !49} +!48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!49 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !8, size: 64) +!50 = !{!41, !51, !52} +!51 = !DILocalVariable(arg: 2, scope: !42, file: !3, line: 3, type: !11) +!52 = !DILocalVariable(arg: 3, scope: !42, file: !3, line: 3, type: !49) +!53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) +!54 = distinct !DIAssignID() +!55 = !DILocation(line: 0, scope: !42, inlinedAt: !56) +!56 = distinct !DILocation(line: 13, column: 3, scope: !16) +!57 = distinct !DIAssignID() +!58 = distinct !DIAssignID() +!59 = distinct !DIAssignID() +!60 = distinct !DIAssignID() +!61 = distinct !DIAssignID() +!62 = !DILocation(line: 14, column: 1, scope: !16) +!63 = distinct !DIAssignID() +!64 = !DILocation(line: 0, scope: !42) +!65 = distinct !DIAssignID() +!66 = distinct !DIAssignID() +!67 = distinct !DIAssignID() +!68 = distinct !DIAssignID() +!69 = distinct !DIAssignID() +!70 = !DILocation(line: 3, column: 20, scope: !42) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memcpy.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memcpy.ll @@ -0,0 +1,124 @@ +; RUN: opt -sroa -verify -S %s -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg" + +;; Check that the new slices of an alloca and memcpy intructions get dbg.assign +;; intrinsics with the correct fragment info. +;; +;; Also check that the new dbg.assign intrinsics are inserted after each split +;; store. See llvm/test/DebugInfo/Generic/dbg-assign-sroa-id.ll for the +;; counterpart check. Ensure that only the value-expression gets fragment info; +;; that the address-expression remains untouched. + +;; $ cat test.cpp +;; struct LargeStruct { +;; int A, B, C; +;; int Var; +;; int D, E, F; +;; }; +;; LargeStruct From; +;; int example() { +;; LargeStruct To = From; +;; return To.Var; +;; } +;; $ clang test.cpp -Xclang -fexperimental-assignment-tracking \ +;; -Xclang -disable-llvm-passes -O2 -g -c -S -emit-llvm -o - + +;; Split alloca. +; CHECK: entry: +; CHECK-NEXT: %To.sroa.0 = alloca { i32, i32, i32 }, align 8, !DIAssignID ![[ID_1:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata {{.+}} undef, metadata ![[TO:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 96), metadata ![[ID_1]], metadata { i32, i32, i32 }* %To.sroa.0, metadata !DIExpression()), !dbg + +; CHECK-NEXT: %To.sroa.4 = alloca { i32, i32, i32 }, align 8, !DIAssignID ![[ID_3:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata {{.+}} undef, metadata ![[TO]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 96), metadata ![[ID_3]], metadata { i32, i32, i32 }* %To.sroa.4, metadata !DIExpression()), !dbg + +;; Split memcpy. +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %To.sroa.0.0..sroa_cast1, i8* align 4 bitcast (%struct.LargeStruct* @From to i8*), i64 12, i1 false),{{.*}}!DIAssignID ![[ID_4:[0-9]+]] +;; This slice has been split and is promoted. +; CHECK: %To.sroa.3.0.copyload = load i32, i32* getelementptr inbounds (%struct.LargeStruct, %struct.LargeStruct* @From, i64 0, i32 3), align 4 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %To.sroa.4.0..sroa_cast4, i8* align 4 bitcast (i32* getelementptr inbounds (%struct.LargeStruct, %struct.LargeStruct* @From, i64 0, i32 4) to i8*), i64 12, i1 false){{.*}}!DIAssignID ![[ID_6:[0-9]+]] + +;; Intrinsics for the splits above. +; CHECK-NEXT: call void @llvm.dbg.assign(metadata {{.+}} undef, metadata ![[TO]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 96), metadata ![[ID_4]], metadata i8* %To.sroa.0.0..sroa_cast1, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %To.sroa.3.0.copyload, metadata ![[TO]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !{{.+}}, metadata i32* undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata {{.+}} undef, metadata ![[TO]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 96), metadata ![[ID_6]], metadata i8* %To.sroa.4.0..sroa_cast4, metadata !DIExpression()), !dbg + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +%struct.LargeStruct = type { i32, i32, i32, i32, i32, i32, i32 } + +@From = dso_local global %struct.LargeStruct zeroinitializer, align 4, !dbg !0 + +; Function Attrs: nounwind uwtable mustprogress +define dso_local i32 @_Z7examplev() #0 !dbg !20 { +entry: + %To = alloca %struct.LargeStruct, align 4, !DIAssignID !25 + call void @llvm.dbg.assign(metadata i1 undef, metadata !24, metadata !DIExpression(), metadata !25, metadata %struct.LargeStruct* %To, metadata !DIExpression()), !dbg !26 + %0 = bitcast %struct.LargeStruct* %To to i8*, !dbg !27 + call void @llvm.lifetime.start.p0i8(i64 28, i8* %0) #3, !dbg !27 + %1 = bitcast %struct.LargeStruct* %To to i8*, !dbg !28 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 bitcast (%struct.LargeStruct* @From to i8*), i64 28, i1 false), !dbg !28, !tbaa.struct !29, !DIAssignID !34 + call void @llvm.dbg.assign(metadata i1 undef, metadata !24, metadata !DIExpression(), metadata !34, metadata i8* %1, metadata !DIExpression()), !dbg !28 + %Var = getelementptr inbounds %struct.LargeStruct, %struct.LargeStruct* %To, i32 0, i32 3, !dbg !35 + %2 = load i32, i32* %Var, align 4, !dbg !35, !tbaa !36 + %3 = bitcast %struct.LargeStruct* %To to i8*, !dbg !38 + call void @llvm.lifetime.end.p0i8(i64 28, i8* %3) #3, !dbg !38 + ret i32 %2, !dbg !39 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 + + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!16, !17, !18} +!llvm.ident = !{!19} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "From", scope: !2, file: !3, line: 6, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "sroa-test.cpp", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "LargeStruct", file: !3, line: 1, size: 224, flags: DIFlagTypePassByValue, elements: !7, identifier: "_ZTS11LargeStruct") +!7 = !{!8, !10, !11, !12, !13, !14, !15} +!8 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !6, file: !3, line: 2, baseType: !9, size: 32) +!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!10 = !DIDerivedType(tag: DW_TAG_member, name: "B", scope: !6, file: !3, line: 2, baseType: !9, size: 32, offset: 32) +!11 = !DIDerivedType(tag: DW_TAG_member, name: "C", scope: !6, file: !3, line: 2, baseType: !9, size: 32, offset: 64) +!12 = !DIDerivedType(tag: DW_TAG_member, name: "Var", scope: !6, file: !3, line: 3, baseType: !9, size: 32, offset: 96) +!13 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !6, file: !3, line: 4, baseType: !9, size: 32, offset: 128) +!14 = !DIDerivedType(tag: DW_TAG_member, name: "E", scope: !6, file: !3, line: 4, baseType: !9, size: 32, offset: 160) +!15 = !DIDerivedType(tag: DW_TAG_member, name: "F", scope: !6, file: !3, line: 4, baseType: !9, size: 32, offset: 192) +!16 = !{i32 7, !"Dwarf Version", i32 4} +!17 = !{i32 2, !"Debug Info Version", i32 3} +!18 = !{i32 1, !"wchar_size", i32 4} +!19 = !{!"clang version 12.0.0"} +!20 = distinct !DISubprogram(name: "example", linkageName: "_Z7examplev", scope: !3, file: !3, line: 7, type: !21, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !23) +!21 = !DISubroutineType(types: !22) +!22 = !{!9} +!23 = !{!24} +!24 = !DILocalVariable(name: "To", scope: !20, file: !3, line: 8, type: !6) +!25 = distinct !DIAssignID() +!26 = !DILocation(line: 0, scope: !20) +!27 = !DILocation(line: 8, column: 3, scope: !20) +!28 = !DILocation(line: 8, column: 20, scope: !20) +!29 = !{i64 0, i64 4, !30, i64 4, i64 4, !30, i64 8, i64 4, !30, i64 12, i64 4, !30, i64 16, i64 4, !30, i64 20, i64 4, !30, i64 24, i64 4, !30} +!30 = !{!31, !31, i64 0} +!31 = !{!"int", !32, i64 0} +!32 = !{!"omnipotent char", !33, i64 0} +!33 = !{!"Simple C++ TBAA"} +!34 = distinct !DIAssignID() +!35 = !DILocation(line: 9, column: 13, scope: !20) +!36 = !{!37, !31, i64 12} +!37 = !{!"_ZTS11LargeStruct", !31, i64 0, !31, i64 4, !31, i64 8, !31, i64 12, !31, i64 16, !31, i64 20, !31, i64 24} +!38 = !DILocation(line: 10, column: 1, scope: !20) +!39 = !DILocation(line: 9, column: 3, scope: !20) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/rewrite.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/rewrite.ll @@ -0,0 +1,142 @@ +; RUN: opt -sroa -verify -S %s -experimental-assignment-tracking -o - \ +; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg" + +; Check that the new slices of an alloca and memset intructions get dbg.assign +; intrinsics with the correct fragment info. Ensure that only the +; value-expression gets fragment info; that the address-expression remains +; untouched. + +;; $ cat test.cpp +;; void do_something(); +;; struct LargeStruct { +;; int A, B, C; +;; int Var; +;; int D, E, F; +;; }; +;; int Glob; +;; bool Cond; +;; int example() { +;; LargeStruct S = {0}; +;; S.Var = Glob; +;; return S.Var; +;; } +;; $ clang test.cpp -Xclang -disable-llvm-passes -O2 -g -c -S -emit-llvm -o - + +; CHECK: entry: +; CHECK-NEXT: %S.sroa.0 = alloca { i32, i32, i32 }, align 8, !DIAssignID ![[ID_1:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 96), metadata ![[ID_1]], metadata { i32, i32, i32 }* %S.sroa.0, metadata !DIExpression()), !dbg + +;; The middle slice has been promoted, so the alloca has gone away. + +; CHECK-NEXT: %S.sroa.5 = alloca { i32, i32, i32 }, align 8, !DIAssignID ![[ID_3:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 96), metadata ![[ID_3]], metadata { i32, i32, i32 }* %S.sroa.5, metadata !DIExpression()), !dbg + +;; The memset has been sliced up (middle slice removed). +; CHECK: call void @llvm.memset.p0i8.i64(i8* align 8 %S.sroa.0.0..sroa_cast13, i8 0, i64 12, i1 false), !dbg !{{.+}}, !DIAssignID ![[ID_5:[0-9]+]] +; CHECK: call void @llvm.memset.p0i8.i64(i8* align 8 %S.sroa.5.0..sroa_cast8, i8 0, i64 12, i1 false), !dbg !{{.+}}, !DIAssignID ![[ID_6:[0-9]+]] + +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 96), metadata ![[ID_5]], metadata i8* %S.sroa.0.0..sroa_cast13, metadata !DIExpression()), !dbg +;; Check the middle slice (no memset) gets a correct dbg.assign. +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !{{.+}}, metadata i32* undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 96), metadata ![[ID_6]], metadata i8* %S.sroa.5.0..sroa_cast8, metadata !DIExpression()), !dbg + +;; mem2reg promotes the load/store to the middle slice created by SROA: +; CHECK-NEXT: %0 = load i32, i32* @Glob, align 4, !dbg !{{.+}}, !tbaa +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata ![[ID_4:[0-9]+]], metadata i32* undef, metadata !DIExpression()), !dbg + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +%struct.LargeStruct = type { i32, i32, i32, i32, i32, i32, i32 } + +@Glob = dso_local global i32 0, align 4, !dbg !0 +@Cond = dso_local global i8 0, align 1, !dbg !6 + +; Function Attrs: nounwind uwtable mustprogress +define dso_local i32 @_Z7examplev() #0 !dbg !14 { +entry: + %S = alloca %struct.LargeStruct, align 4, !DIAssignID !28 + call void @llvm.dbg.assign(metadata i1 undef, metadata !18, metadata !DIExpression(), metadata !28, metadata %struct.LargeStruct* %S, metadata !DIExpression()), !dbg !29 + %0 = bitcast %struct.LargeStruct* %S to i8*, !dbg !30 + call void @llvm.lifetime.start.p0i8(i64 28, i8* %0) #4, !dbg !30 + %1 = bitcast %struct.LargeStruct* %S to i8*, !dbg !31 + call void @llvm.memset.p0i8.i64(i8* align 4 %1, i8 0, i64 28, i1 false), !dbg !31, !DIAssignID !32 + call void @llvm.dbg.assign(metadata i8 0, metadata !18, metadata !DIExpression(), metadata !32, metadata i8* %1, metadata !DIExpression()), !dbg !31 + %2 = load i32, i32* @Glob, align 4, !dbg !33, !tbaa !34 + %Var = getelementptr inbounds %struct.LargeStruct, %struct.LargeStruct* %S, i32 0, i32 3, !dbg !38 + store i32 %2, i32* %Var, align 4, !dbg !39, !tbaa !40, !DIAssignID !42 + call void @llvm.dbg.assign(metadata i32 %2, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !42, metadata i32* %Var, metadata !DIExpression()), !dbg !39 + %Var1 = getelementptr inbounds %struct.LargeStruct, %struct.LargeStruct* %S, i32 0, i32 3, !dbg !43 + %3 = load i32, i32* %Var1, align 4, !dbg !43, !tbaa !40 + %4 = bitcast %struct.LargeStruct* %S to i8*, !dbg !44 + call void @llvm.lifetime.end.p0i8(i64 28, i8* %4) #4, !dbg !44 + ret i32 %3, !dbg !45 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +attributes #0 = { nounwind uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nofree nosync nounwind willreturn } +attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } +attributes #3 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #4 = { nounwind } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!10, !11, !12} +!llvm.ident = !{!13} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "Glob", scope: !2, file: !3, line: 7, type: !9, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{} +!5 = !{!0, !6} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "Cond", scope: !2, file: !3, line: 8, type: !8, isLocal: false, isDefinition: true) +!8 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean) +!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!10 = !{i32 7, !"Dwarf Version", i32 4} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{!"clang version 12.0.0"} +!14 = distinct !DISubprogram(name: "example", linkageName: "_Z7examplev", scope: !3, file: !3, line: 9, type: !15, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17) +!15 = !DISubroutineType(types: !16) +!16 = !{!9} +!17 = !{!18} +!18 = !DILocalVariable(name: "S", scope: !14, file: !3, line: 10, type: !19) +!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "LargeStruct", file: !3, line: 2, size: 224, flags: DIFlagTypePassByValue, elements: !20, identifier: "_ZTS11LargeStruct") +!20 = !{!21, !22, !23, !24, !25, !26, !27} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !19, file: !3, line: 3, baseType: !9, size: 32) +!22 = !DIDerivedType(tag: DW_TAG_member, name: "B", scope: !19, file: !3, line: 3, baseType: !9, size: 32, offset: 32) +!23 = !DIDerivedType(tag: DW_TAG_member, name: "C", scope: !19, file: !3, line: 3, baseType: !9, size: 32, offset: 64) +!24 = !DIDerivedType(tag: DW_TAG_member, name: "Var", scope: !19, file: !3, line: 4, baseType: !9, size: 32, offset: 96) +!25 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !19, file: !3, line: 5, baseType: !9, size: 32, offset: 128) +!26 = !DIDerivedType(tag: DW_TAG_member, name: "E", scope: !19, file: !3, line: 5, baseType: !9, size: 32, offset: 160) +!27 = !DIDerivedType(tag: DW_TAG_member, name: "F", scope: !19, file: !3, line: 5, baseType: !9, size: 32, offset: 192) +!28 = distinct !DIAssignID() +!29 = !DILocation(line: 0, scope: !14) +!30 = !DILocation(line: 10, column: 3, scope: !14) +!31 = !DILocation(line: 10, column: 15, scope: !14) +!32 = distinct !DIAssignID() +!33 = !DILocation(line: 11, column: 11, scope: !14) +!34 = !{!35, !35, i64 0} +!35 = !{!"int", !36, i64 0} +!36 = !{!"omnipotent char", !37, i64 0} +!37 = !{!"Simple C++ TBAA"} +!38 = !DILocation(line: 11, column: 5, scope: !14) +!39 = !DILocation(line: 11, column: 9, scope: !14) +!40 = !{!41, !35, i64 12} +!41 = !{!"_ZTS11LargeStruct", !35, i64 0, !35, i64 4, !35, i64 8, !35, i64 12, !35, i64 16, !35, i64 20, !35, i64 24} +!42 = distinct !DIAssignID() +!43 = !DILocation(line: 12, column: 12, scope: !14) +!44 = !DILocation(line: 13, column: 1, scope: !14) +!45 = !DILocation(line: 12, column: 3, scope: !14) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/store.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/store.ll @@ -0,0 +1,166 @@ +; RUN: opt -sroa -verify -S %s -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg" + +; Check that the new slices of an alloca and memset intructions get dbg.assign +; intrinsics with the correct fragment info. Ensure that only the +; value-expression gets fragment info; that the address-expression remains +; untouched. + + +;; $ cat test.cpp +;; void do_something(); +;; struct LargeStruct { +;; int A, B, C; +;; int Var; +;; int D, E, F; +;; }; +;; int Glob; +;; bool Cond; +;; int use(LargeStruct); +;; int example() { +;; LargeStruct S = {0}; +;; S.Var = Glob; +;; use(S); +;; return S.Var; +;; } +;; $ clang test.cpp -Xclang -disable-llvm-passes -O2 -g -c -S -emit-llvm -o - \ +;; | opt -passes=declare-to-assign -S -o - + +; CHECK: entry: +; CHECK-NEXT: %S.sroa.0 = alloca { i32, i32, i32 }, align 8, !DIAssignID ![[ID_1:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 96), metadata ![[ID_1]], metadata { i32, i32, i32 }* %S.sroa.0, metadata !DIExpression()), !dbg + +; CHECK-NEXT: %S.sroa.6 = alloca { i32, i32, i32 }, align 8, !DIAssignID ![[ID_3:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 96), metadata ![[ID_3]], metadata { i32, i32, i32 }* %S.sroa.6, metadata !DIExpression()), !dbg + +;; The memset has been split into [0, 96)[96, 128)[128, 224) bit slices. The +;; memset for the middle slice has been removed. +; CHECK: call void @llvm.memset.p0i8.i64(i8* align 8 %S.sroa.0.0..sroa_cast19, i8 0, i64 12, i1 false), !dbg !{{.+}}, !DIAssignID ![[ID_4:[0-9]+]] +; CHECK-NEXT: %S.sroa.6.0..sroa_cast13 = bitcast { i32, i32, i32 }* %S.sroa.6 to i8*, !dbg +; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 8 %S.sroa.6.0..sroa_cast13, i8 0, i64 12, i1 false), !dbg !{{.+}}, !DIAssignID ![[ID_5:[0-9]+]] + +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 96), metadata ![[ID_4]], metadata i8* %S.sroa.0.0..sroa_cast19, metadata !DIExpression()), !dbg +;; This is the one we care about most in this test: check that a memset->store +;; gets a correct dbg.assign. +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !{{.+}}, metadata i32* undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 96), metadata ![[ID_5]], metadata i8* %S.sroa.6.0..sroa_cast13, metadata !DIExpression()), !dbg + +;; The load from global+store becomes a load. +;; FIXME: In reality it is actually stored again later on. +; CHECK-NEXT: %0 = load i32, i32* @Glob, align 4, !dbg !{{.+}}, !tbaa !{{.+}} +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !{{.+}}, metadata i32* undef, metadata !DIExpression()), !dbg ! + + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +%struct.LargeStruct = type { i32, i32, i32, i32, i32, i32, i32 } + +@Glob = dso_local global i32 0, align 4, !dbg !0 +@Cond = dso_local global i8 0, align 1, !dbg !6 + +; Function Attrs: uwtable mustprogress +define dso_local i32 @_Z7examplev() #0 !dbg !14 { +entry: + %S = alloca %struct.LargeStruct, align 4, !DIAssignID !28 + call void @llvm.dbg.assign(metadata i1 undef, metadata !18, metadata !DIExpression(), metadata !28, metadata %struct.LargeStruct* %S, metadata !DIExpression()), !dbg !29 + %agg.tmp = alloca %struct.LargeStruct, align 8 + %0 = bitcast %struct.LargeStruct* %S to i8*, !dbg !30 + call void @llvm.lifetime.start.p0i8(i64 28, i8* %0) #5, !dbg !30 + %1 = bitcast %struct.LargeStruct* %S to i8*, !dbg !31 + call void @llvm.memset.p0i8.i64(i8* align 4 %1, i8 0, i64 28, i1 false), !dbg !31, !DIAssignID !32 + call void @llvm.dbg.assign(metadata i8 0, metadata !18, metadata !DIExpression(), metadata !32, metadata i8* %1, metadata !DIExpression()), !dbg !31 + %2 = load i32, i32* @Glob, align 4, !dbg !33, !tbaa !34 + %Var = getelementptr inbounds %struct.LargeStruct, %struct.LargeStruct* %S, i32 0, i32 3, !dbg !38 + store i32 %2, i32* %Var, align 4, !dbg !39, !tbaa !40, !DIAssignID !42 + call void @llvm.dbg.assign(metadata i32 %2, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !42, metadata i32* %Var, metadata !DIExpression()), !dbg !39 + %3 = bitcast %struct.LargeStruct* %agg.tmp to i8*, !dbg !43 + %4 = bitcast %struct.LargeStruct* %S to i8*, !dbg !43 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %3, i8* align 4 %4, i64 28, i1 false), !dbg !43, !tbaa.struct !44 + %call = call i32 @_Z3use11LargeStruct(%struct.LargeStruct* byval(%struct.LargeStruct) align 8 %agg.tmp), !dbg !45 + %Var1 = getelementptr inbounds %struct.LargeStruct, %struct.LargeStruct* %S, i32 0, i32 3, !dbg !46 + %5 = load i32, i32* %Var1, align 4, !dbg !46, !tbaa !40 + %6 = bitcast %struct.LargeStruct* %S to i8*, !dbg !47 + call void @llvm.lifetime.end.p0i8(i64 28, i8* %6) #5, !dbg !47 + ret i32 %5, !dbg !48 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #2 + +declare !dbg !49 dso_local i32 @_Z3use11LargeStruct(%struct.LargeStruct* byval(%struct.LargeStruct) align 8) #3 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #4 + +attributes #0 = { uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nofree nosync nounwind willreturn } +attributes #2 = { argmemonly nofree nosync nounwind willreturn writeonly } +attributes #3 = { "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #4 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #5 = { nounwind } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!10, !11, !12} +!llvm.ident = !{!13} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "Glob", scope: !2, file: !3, line: 7, type: !9, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{} +!5 = !{!0, !6} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "Cond", scope: !2, file: !3, line: 8, type: !8, isLocal: false, isDefinition: true) +!8 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean) +!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!10 = !{i32 7, !"Dwarf Version", i32 4} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{!"clang version 12.0.0"} +!14 = distinct !DISubprogram(name: "example", linkageName: "_Z7examplev", scope: !3, file: !3, line: 10, type: !15, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17) +!15 = !DISubroutineType(types: !16) +!16 = !{!9} +!17 = !{!18} +!18 = !DILocalVariable(name: "S", scope: !14, file: !3, line: 11, type: !19) +!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "LargeStruct", file: !3, line: 2, size: 224, flags: DIFlagTypePassByValue, elements: !20, identifier: "_ZTS11LargeStruct") +!20 = !{!21, !22, !23, !24, !25, !26, !27} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !19, file: !3, line: 3, baseType: !9, size: 32) +!22 = !DIDerivedType(tag: DW_TAG_member, name: "B", scope: !19, file: !3, line: 3, baseType: !9, size: 32, offset: 32) +!23 = !DIDerivedType(tag: DW_TAG_member, name: "C", scope: !19, file: !3, line: 3, baseType: !9, size: 32, offset: 64) +!24 = !DIDerivedType(tag: DW_TAG_member, name: "Var", scope: !19, file: !3, line: 4, baseType: !9, size: 32, offset: 96) +!25 = !DIDerivedType(tag: DW_TAG_member, name: "D", scope: !19, file: !3, line: 5, baseType: !9, size: 32, offset: 128) +!26 = !DIDerivedType(tag: DW_TAG_member, name: "E", scope: !19, file: !3, line: 5, baseType: !9, size: 32, offset: 160) +!27 = !DIDerivedType(tag: DW_TAG_member, name: "F", scope: !19, file: !3, line: 5, baseType: !9, size: 32, offset: 192) +!28 = distinct !DIAssignID() +!29 = !DILocation(line: 0, scope: !14) +!30 = !DILocation(line: 11, column: 3, scope: !14) +!31 = !DILocation(line: 11, column: 15, scope: !14) +!32 = distinct !DIAssignID() +!33 = !DILocation(line: 12, column: 11, scope: !14) +!34 = !{!35, !35, i64 0} +!35 = !{!"int", !36, i64 0} +!36 = !{!"omnipotent char", !37, i64 0} +!37 = !{!"Simple C++ TBAA"} +!38 = !DILocation(line: 12, column: 5, scope: !14) +!39 = !DILocation(line: 12, column: 9, scope: !14) +!40 = !{!41, !35, i64 12} +!41 = !{!"_ZTS11LargeStruct", !35, i64 0, !35, i64 4, !35, i64 8, !35, i64 12, !35, i64 16, !35, i64 20, !35, i64 24} +!42 = distinct !DIAssignID() +!43 = !DILocation(line: 13, column: 7, scope: !14) +!44 = !{i64 0, i64 4, !34, i64 4, i64 4, !34, i64 8, i64 4, !34, i64 12, i64 4, !34, i64 16, i64 4, !34, i64 20, i64 4, !34, i64 24, i64 4, !34} +!45 = !DILocation(line: 13, column: 3, scope: !14) +!46 = !DILocation(line: 14, column: 12, scope: !14) +!47 = !DILocation(line: 15, column: 1, scope: !14) +!48 = !DILocation(line: 14, column: 3, scope: !14) +!49 = !DISubprogram(name: "use", linkageName: "_Z3use11LargeStruct", scope: !3, file: !3, line: 9, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !4) +!50 = !DISubroutineType(types: !51) +!51 = !{!9, !19} Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/unspecified-var-size.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/unspecified-var-size.ll @@ -0,0 +1,59 @@ +; RUN: opt -S %s -sroa -o - -experimental-assignment-tracking | FileCheck %s + +;; $ cat test.cpp +;; #include +;; void fun(std::nullptr_t) {} +;; +;; Check that migrateDebugInfo doesn't crash when encountering an alloca for a +;; variable with a type of unspecified size (e.g. DW_TAG_unspecified_type). + +; CHECK: @llvm.dbg.assign(metadata i8* %0,{{.+}}, metadata !DIExpression(),{{.+}}, metadata i8** undef, {{.+}}) +;; There should be no new fragment and the value component should remain as %0. + +define dso_local void @_Z3funDn(i8* %0) #0 !dbg !14 { +entry: + %.addr = alloca i8*, align 8, !DIAssignID !22 + call void @llvm.dbg.assign(metadata i1 undef, metadata !21, metadata !DIExpression(), metadata !22, metadata i8** %.addr, metadata !DIExpression()), !dbg !23 + store i8* %0, i8** %.addr, align 8, !tbaa !24 + store i8* %0, i8** %.addr, align 8, !tbaa !24, !DIAssignID !28 + call void @llvm.dbg.assign(metadata i8* %0, metadata !21, metadata !DIExpression(), metadata !28, metadata i8** %.addr, metadata !DIExpression()), !dbg !23 + ret void, !dbg !29 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #1 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!10, !11, !12} +!llvm.ident = !{!13} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, imports: !3, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{!4} +!4 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !6, file: !9, line: 56) +!5 = !DINamespace(name: "std", scope: null) +!6 = !DIDerivedType(tag: DW_TAG_typedef, name: "max_align_t", file: !7, line: 24, baseType: !8) +!7 = !DIFile(filename: "llvm/coffee-chat/build-rel/lib/clang/12.0.0/include/__stddef_max_align_t.h", directory: "/home/och/dev") +!8 = !DICompositeType(tag: DW_TAG_structure_type, file: !7, line: 19, size: 256, flags: DIFlagFwdDecl, identifier: "_ZTS11max_align_t") +!9 = !DIFile(filename: "/usr/lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/cstddef", directory: "") +!10 = !{i32 7, !"Dwarf Version", i32 4} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{!"clang version 12.0.0"} +!14 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funDn", scope: !1, file: !1, line: 20, type: !15, scopeLine: 20, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !20) +!15 = !DISubroutineType(types: !16) +!16 = !{null, !17} +!17 = !DIDerivedType(tag: DW_TAG_typedef, name: "nullptr_t", scope: !5, file: !18, line: 235, baseType: !19) +!18 = !DIFile(filename: "/usr/lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/x86_64-linux-gnu/c++/7.5.0/bits/c++config.h", directory: "") +!19 = !DIBasicType(tag: DW_TAG_unspecified_type, name: "decltype(nullptr)") +!20 = !{!21} +!21 = !DILocalVariable(arg: 1, scope: !14, file: !1, line: 20, type: !17) +!22 = distinct !DIAssignID() +!23 = !DILocation(line: 0, scope: !14) +!24 = !{!25, !25, i64 0} +!25 = !{!"nullptr_t", !26, i64 0} +!26 = !{!"omnipotent char", !27, i64 0} +!27 = !{!"Simple C++ TBAA"} +!28 = distinct !DIAssignID() +!29 = !DILocation(line: 20, column: 27, scope: !14) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/user-memcpy.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/user-memcpy.ll @@ -0,0 +1,240 @@ +; RUN: opt -sroa -S %s -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg" + +;; Check that the fragments generated in SROA for a split alloca that has a +;; dbg.assign with non-zero-offset fragment already are correct. Ensure that +;; only the value-expression gets fragment info; that the address-expression +;; remains untouched. + +;; $ cat test.cpp +;; #include +;; +;; struct V3i { long x, y, z; }; +;; void fun() { +;; V3i point = {0, 0, 0}; +;; point.z = 5000; +;; V3i other = {10, 9, 8}; +;; std::memcpy(&point.y, &other.x, sizeof(long) * 2); +;; } +;; $ clang++ -c -O2 -g test.cpp -o - -Xclang -disable-llvm-passes -S -emit-llvm \ +;; | opt -passes=declare-to-assign -S -o - + +; CHECK: entry: +;; Allocas have been promoted - the linked dbg.assigns have been removed. + +;; | V3i point = {0, 0, 0}; +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 0, metadata ![[point:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 0, metadata ![[point]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 0, metadata ![[point]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg + +;; point.z = 5000; +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 5000, metadata ![[point]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg + +;; | V3i other = {10, 9, 8}; +;; other is global const: +;; local.other.x = global.other.x +;; local.other.y = global.other.y +;; local.other.z = global.other.z +; CHECK-NEXT: %other.sroa.0.0.copyload = load i64, i64* getelementptr inbounds (%struct.V3i, %struct.V3i* @__const._Z3funv.other, i64 0, i32 0), align 8, !dbg +; CHECK-NEXT: %other.sroa.4.0.copyload = load i64, i64* getelementptr inbounds (%struct.V3i, %struct.V3i* @__const._Z3funv.other, i64 0, i32 1), align 8, !dbg +; CHECK-NEXT: %other.sroa.5.0.copyload = load i64, i64* getelementptr inbounds (%struct.V3i, %struct.V3i* @__const._Z3funv.other, i64 0, i32 2), align 8, !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 %other.sroa.0.0.copyload, metadata ![[other:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 %other.sroa.4.0.copyload, metadata ![[other]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 %other.sroa.5.0.copyload, metadata ![[other]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg + +;; | std::memcpy(&point.y, &other.x, sizeof(long) * 2); +;; other is now 3 scalars: +;; point.y = other.x +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 %other.sroa.0.0.copyload, metadata ![[point]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg +;; +;; point.z = other.y +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 %other.sroa.4.0.copyload, metadata ![[point]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64), metadata !{{.+}}, metadata i64* undef, metadata !DIExpression()), !dbg + +; CHECK: ![[point]] = !DILocalVariable(name: "point", +; CHECK: ![[other]] = !DILocalVariable(name: "other", + +source_filename = "test.cpp" +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" + +%struct.V3i = type { i64, i64, i64 } + +@__const._Z3funv.other = private unnamed_addr constant %struct.V3i { i64 10, i64 9, i64 8 }, align 8 + +; Function Attrs: nounwind uwtable mustprogress +define dso_local void @_Z3funv() !dbg !100 { +entry: + %point = alloca %struct.V3i, align 8, !DIAssignID !112 + call void @llvm.dbg.assign(metadata i1 undef, metadata !104, metadata !DIExpression(), metadata !112, metadata %struct.V3i* %point, metadata !DIExpression()), !dbg !113 + %other = alloca %struct.V3i, align 8, !DIAssignID !114 + call void @llvm.dbg.assign(metadata i1 undef, metadata !111, metadata !DIExpression(), metadata !114, metadata %struct.V3i* %other, metadata !DIExpression()), !dbg !113 + %0 = bitcast %struct.V3i* %point to i8*, !dbg !115 + call void @llvm.lifetime.start.p0i8(i64 24, i8* %0), !dbg !115 + %1 = bitcast %struct.V3i* %point to i8*, !dbg !116 + call void @llvm.memset.p0i8.i64(i8* align 8 %1, i8 0, i64 24, i1 false), !dbg !116, !DIAssignID !117 + call void @llvm.dbg.assign(metadata i8 0, metadata !104, metadata !DIExpression(), metadata !117, metadata i8* %1, metadata !DIExpression()), !dbg !116 + %z = getelementptr inbounds %struct.V3i, %struct.V3i* %point, i32 0, i32 2, !dbg !118 + store i64 5000, i64* %z, align 8, !dbg !119, !tbaa !120, !DIAssignID !125 + call void @llvm.dbg.assign(metadata i64 5000, metadata !104, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64), metadata !125, metadata i64* %z, metadata !DIExpression()), !dbg !119 + %2 = bitcast %struct.V3i* %other to i8*, !dbg !126 + call void @llvm.lifetime.start.p0i8(i64 24, i8* %2), !dbg !126 + %3 = bitcast %struct.V3i* %other to i8*, !dbg !127 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %3, i8* align 8 bitcast (%struct.V3i* @__const._Z3funv.other to i8*), i64 24, i1 false), !dbg !127, !DIAssignID !128 + call void @llvm.dbg.assign(metadata i1 undef, metadata !111, metadata !DIExpression(), metadata !128, metadata i8* %3, metadata !DIExpression()), !dbg !127 + %y = getelementptr inbounds %struct.V3i, %struct.V3i* %point, i32 0, i32 1, !dbg !129 + %4 = bitcast i64* %y to i8*, !dbg !130 + %x = getelementptr inbounds %struct.V3i, %struct.V3i* %other, i32 0, i32 0, !dbg !131 + %5 = bitcast i64* %x to i8*, !dbg !130 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %4, i8* align 8 %5, i64 16, i1 false), !dbg !130, !DIAssignID !132 + call void @llvm.dbg.assign(metadata i1 undef, metadata !104, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 128), metadata !132, metadata i8* %4, metadata !DIExpression()), !dbg !130 + %6 = bitcast %struct.V3i* %other to i8*, !dbg !133 + call void @llvm.lifetime.end.p0i8(i64 24, i8* %6), !dbg !133 + %7 = bitcast %struct.V3i* %point to i8*, !dbg !133 + call void @llvm.lifetime.end.p0i8(i64 24, i8* %7), !dbg !133 + ret void, !dbg !133 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!96, !97, !98} +!llvm.ident = !{!99} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, imports: !3, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{!4, !18, !22, !28, !32, !36, !46, !50, !52, !54, !58, !62, !66, !70, !74, !76, !78, !80, !84, !88, !92, !94} +!4 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !6, file: !17, line: 75) +!5 = !DINamespace(name: "std", scope: null) +!6 = !DISubprogram(name: "memchr", scope: !7, file: !7, line: 90, type: !8, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!7 = !DIFile(filename: "/usr/include/string.h", directory: "") +!8 = !DISubroutineType(types: !9) +!9 = !{!10, !11, !13, !14} +!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!12 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null) +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !DIDerivedType(tag: DW_TAG_typedef, name: "size_t", file: !15, line: 46, baseType: !16) +!15 = !DIFile(filename: "lib/clang/12.0.0/include/stddef.h", directory: "/") +!16 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!17 = !DIFile(filename: "/usr/lib/gcc/x86_64-linux-gnu/7.5.0/../../../../include/c++/7.5.0/cstring", directory: "") +!18 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !19, file: !17, line: 76) +!19 = !DISubprogram(name: "memcmp", scope: !7, file: !7, line: 63, type: !20, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!20 = !DISubroutineType(types: !21) +!21 = !{!13, !11, !11, !14} +!22 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !23, file: !17, line: 77) +!23 = !DISubprogram(name: "memcpy", scope: !7, file: !7, line: 42, type: !24, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!24 = !DISubroutineType(types: !25) +!25 = !{!10, !26, !27, !14} +!26 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !10) +!27 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !11) +!28 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !29, file: !17, line: 78) +!29 = !DISubprogram(name: "memmove", scope: !7, file: !7, line: 46, type: !30, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!30 = !DISubroutineType(types: !31) +!31 = !{!10, !10, !11, !14} +!32 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !33, file: !17, line: 79) +!33 = !DISubprogram(name: "memset", scope: !7, file: !7, line: 60, type: !34, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!34 = !DISubroutineType(types: !35) +!35 = !{!10, !10, !13, !14} +!36 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !37, file: !17, line: 80) +!37 = !DISubprogram(name: "strcat", scope: !7, file: !7, line: 129, type: !38, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!38 = !DISubroutineType(types: !39) +!39 = !{!40, !42, !43} +!40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !41, size: 64) +!41 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!42 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !40) +!43 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !44) +!44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !45, size: 64) +!45 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !41) +!46 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !47, file: !17, line: 81) +!47 = !DISubprogram(name: "strcmp", scope: !7, file: !7, line: 136, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!48 = !DISubroutineType(types: !49) +!49 = !{!13, !44, !44} +!50 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !51, file: !17, line: 82) +!51 = !DISubprogram(name: "strcoll", scope: !7, file: !7, line: 143, type: !48, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!52 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !53, file: !17, line: 83) +!53 = !DISubprogram(name: "strcpy", scope: !7, file: !7, line: 121, type: !38, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!54 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !55, file: !17, line: 84) +!55 = !DISubprogram(name: "strcspn", scope: !7, file: !7, line: 272, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!56 = !DISubroutineType(types: !57) +!57 = !{!14, !44, !44} +!58 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !59, file: !17, line: 85) +!59 = !DISubprogram(name: "strerror", scope: !7, file: !7, line: 396, type: !60, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!60 = !DISubroutineType(types: !61) +!61 = !{!40, !13} +!62 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !63, file: !17, line: 86) +!63 = !DISubprogram(name: "strlen", scope: !7, file: !7, line: 384, type: !64, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!64 = !DISubroutineType(types: !65) +!65 = !{!14, !44} +!66 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !67, file: !17, line: 87) +!67 = !DISubprogram(name: "strncat", scope: !7, file: !7, line: 132, type: !68, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!68 = !DISubroutineType(types: !69) +!69 = !{!40, !42, !43, !14} +!70 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !71, file: !17, line: 88) +!71 = !DISubprogram(name: "strncmp", scope: !7, file: !7, line: 139, type: !72, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!72 = !DISubroutineType(types: !73) +!73 = !{!13, !44, !44, !14} +!74 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !75, file: !17, line: 89) +!75 = !DISubprogram(name: "strncpy", scope: !7, file: !7, line: 124, type: !68, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!76 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !77, file: !17, line: 90) +!77 = !DISubprogram(name: "strspn", scope: !7, file: !7, line: 276, type: !56, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!78 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !79, file: !17, line: 91) +!79 = !DISubprogram(name: "strtok", scope: !7, file: !7, line: 335, type: !38, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!80 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !81, file: !17, line: 92) +!81 = !DISubprogram(name: "strxfrm", scope: !7, file: !7, line: 146, type: !82, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!82 = !DISubroutineType(types: !83) +!83 = !{!14, !42, !43, !14} +!84 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !85, file: !17, line: 93) +!85 = !DISubprogram(name: "strchr", scope: !7, file: !7, line: 225, type: !86, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!86 = !DISubroutineType(types: !87) +!87 = !{!40, !44, !13} +!88 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !89, file: !17, line: 94) +!89 = !DISubprogram(name: "strpbrk", scope: !7, file: !7, line: 302, type: !90, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!90 = !DISubroutineType(types: !91) +!91 = !{!40, !44, !44} +!92 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !93, file: !17, line: 95) +!93 = !DISubprogram(name: "strrchr", scope: !7, file: !7, line: 252, type: !86, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!94 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !5, entity: !95, file: !17, line: 96) +!95 = !DISubprogram(name: "strstr", scope: !7, file: !7, line: 329, type: !90, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!96 = !{i32 7, !"Dwarf Version", i32 4} +!97 = !{i32 2, !"Debug Info Version", i32 3} +!98 = !{i32 1, !"wchar_size", i32 4} +!99 = !{!"clang version 12.0.0"} +!100 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !1, file: !1, line: 4, type: !101, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !103) +!101 = !DISubroutineType(types: !102) +!102 = !{null} +!103 = !{!104, !111} +!104 = !DILocalVariable(name: "point", scope: !100, file: !1, line: 5, type: !105) +!105 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "V3i", file: !1, line: 3, size: 192, flags: DIFlagTypePassByValue, elements: !106, identifier: "_ZTS3V3i") +!106 = !{!107, !109, !110} +!107 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !105, file: !1, line: 3, baseType: !108, size: 64) +!108 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!109 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !105, file: !1, line: 3, baseType: !108, size: 64, offset: 64) +!110 = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: !105, file: !1, line: 3, baseType: !108, size: 64, offset: 128) +!111 = !DILocalVariable(name: "other", scope: !100, file: !1, line: 7, type: !105) +!112 = distinct !DIAssignID() +!113 = !DILocation(line: 0, scope: !100) +!114 = distinct !DIAssignID() +!115 = !DILocation(line: 5, column: 3, scope: !100) +!116 = !DILocation(line: 5, column: 7, scope: !100) +!117 = distinct !DIAssignID() +!118 = !DILocation(line: 6, column: 9, scope: !100) +!119 = !DILocation(line: 6, column: 11, scope: !100) +!120 = !{!121, !122, i64 16} +!121 = !{!"_ZTS3V3i", !122, i64 0, !122, i64 8, !122, i64 16} +!122 = !{!"long", !123, i64 0} +!123 = !{!"omnipotent char", !124, i64 0} +!124 = !{!"Simple C++ TBAA"} +!125 = distinct !DIAssignID() +!126 = !DILocation(line: 7, column: 3, scope: !100) +!127 = !DILocation(line: 7, column: 7, scope: !100) +!128 = distinct !DIAssignID() +!129 = !DILocation(line: 8, column: 22, scope: !100) +!130 = !DILocation(line: 8, column: 3, scope: !100) +!131 = !DILocation(line: 8, column: 32, scope: !100) +!132 = distinct !DIAssignID() +!133 = !DILocation(line: 9, column: 1, scope: !100) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-1.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-1.ll @@ -0,0 +1,110 @@ +; RUN: opt %s -S -sroa -o - -experimental-assignment-tracking | FileCheck %s + +;; It's a shame we can't run SROA without mem2reg; we can't check that the +;; intermediate store got wired up correctly. @OCH Add a flag to do this? +;; Ensure that only the value-expression gets fragment info; that the +;; address-expression remains untouched. + +;; $ cat test.cpp +;; class a { +;; float b[4]; +;; }; +;; class c { +;; a m_fn1() const; +;; void d() const; +;; }; +;; void c::d() const { a e = m_fn1(); } +;; +;; Generated by grabbing IR before sroa in: +;; $ clang++ -O2 -g -c test.cpp -Xclang -fexperimental-assignment-tracking + +; CHECK: %call = call +; CHECK-NEXT: %0 = extractvalue { <2 x float>, <2 x float> } %call, 0 +; CHECK-NEXT: call void @llvm.dbg.assign(metadata <2 x float> %0, metadata ![[var:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata ![[id1:[0-9]+]],{{.+}} undef, metadata !DIExpression()), !dbg +; CHECK-NEXT: %1 = extractvalue { <2 x float>, <2 x float> } %call, 1 +; CHECK-NEXT: call void @llvm.dbg.assign(metadata <2 x float> %1, metadata ![[var]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata ![[id2:[0-9]+]], {{.+}} undef, metadata !DIExpression()), !dbg + +%class.c = type { i8 } +%class.a = type { [4 x float] } + +; Function Attrs: uwtable +define dso_local void @_ZNK1c1dEv(%class.c* %this) #0 align 2 !dbg !7 { +entry: + %this.addr = alloca %class.c*, align 8, !DIAssignID !29 + call void @llvm.dbg.assign(metadata i1 undef, metadata !26, metadata !DIExpression(), metadata !29, metadata %class.c** %this.addr, metadata !DIExpression()), !dbg !30 + %e = alloca %class.a, align 4, !DIAssignID !31 + call void @llvm.dbg.assign(metadata i1 undef, metadata !28, metadata !DIExpression(), metadata !31, metadata %class.a* %e, metadata !DIExpression()), !dbg !30 + store %class.c* %this, %class.c** %this.addr, align 8, !tbaa !32, !DIAssignID !36 + call void @llvm.dbg.assign(metadata %class.c* %this, metadata !26, metadata !DIExpression(), metadata !36, metadata %class.c** %this.addr, metadata !DIExpression()), !dbg !30 + %this1 = load %class.c*, %class.c** %this.addr, align 8 + %0 = bitcast %class.a* %e to i8*, !dbg !37 + call void @llvm.lifetime.start.p0i8(i64 16, i8* %0) #4, !dbg !37 + %call = call { <2 x float>, <2 x float> } @_ZNK1c5m_fn1Ev(%class.c* %this1), !dbg !38 + %coerce.dive = getelementptr inbounds %class.a, %class.a* %e, i32 0, i32 0, !dbg !38 + %1 = bitcast [4 x float]* %coerce.dive to { <2 x float>, <2 x float> }*, !dbg !38 + %2 = getelementptr inbounds { <2 x float>, <2 x float> }, { <2 x float>, <2 x float> }* %1, i32 0, i32 0, !dbg !38 + %3 = extractvalue { <2 x float>, <2 x float> } %call, 0, !dbg !38 + store <2 x float> %3, <2 x float>* %2, align 4, !dbg !38, !DIAssignID !39 + call void @llvm.dbg.assign(metadata <2 x float> %3, metadata !28, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !39, metadata <2 x float>* %2, metadata !DIExpression()), !dbg !30 + %4 = getelementptr inbounds { <2 x float>, <2 x float> }, { <2 x float>, <2 x float> }* %1, i32 0, i32 1, !dbg !38 + %5 = extractvalue { <2 x float>, <2 x float> } %call, 1, !dbg !38 + store <2 x float> %5, <2 x float>* %4, align 4, !dbg !38, !DIAssignID !40 + call void @llvm.dbg.assign(metadata <2 x float> %5, metadata !28, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !40, metadata <2 x float>* %4, metadata !DIExpression()), !dbg !30 + %6 = bitcast %class.a* %e to i8*, !dbg !41 + call void @llvm.lifetime.end.p0i8(i64 16, i8* %6) #4, !dbg !41 + ret void, !dbg !41 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 +declare dso_local { <2 x float>, <2 x float> } @_ZNK1c5m_fn1Ev(%class.c*) #3 +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #1 + + +!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: "test.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: "d", linkageName: "_ZNK1c1dEv", scope: !8, file: !1, line: 8, type: !23, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !22, retainedNodes: !25) +!8 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "c", file: !1, line: 4, size: 8, flags: DIFlagTypePassByValue, elements: !9, identifier: "_ZTS1c") +!9 = !{!10, !22} +!10 = !DISubprogram(name: "m_fn1", linkageName: "_ZNK1c5m_fn1Ev", scope: !8, file: !1, line: 5, type: !11, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!11 = !DISubroutineType(types: !12) +!12 = !{!13, !20} +!13 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !1, line: 1, size: 128, flags: DIFlagTypePassByValue, elements: !14, identifier: "_ZTS1a") +!14 = !{!15} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !13, file: !1, line: 2, baseType: !16, size: 128) +!16 = !DICompositeType(tag: DW_TAG_array_type, baseType: !17, size: 128, elements: !18) +!17 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!18 = !{!19} +!19 = !DISubrange(count: 4) +!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!21 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !8) +!22 = !DISubprogram(name: "d", linkageName: "_ZNK1c1dEv", scope: !8, file: !1, line: 6, type: !23, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!23 = !DISubroutineType(types: !24) +!24 = !{null, !20} +!25 = !{!26, !28} +!26 = !DILocalVariable(name: "this", arg: 1, scope: !7, type: !27, flags: DIFlagArtificial | DIFlagObjectPointer) +!27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !21, size: 64) +!28 = !DILocalVariable(name: "e", scope: !7, file: !1, line: 8, type: !13) +!29 = distinct !DIAssignID() +!30 = !DILocation(line: 0, scope: !7) +!31 = distinct !DIAssignID() +!32 = !{!33, !33, i64 0} +!33 = !{!"any pointer", !34, i64 0} +!34 = !{!"omnipotent char", !35, i64 0} +!35 = !{!"Simple C++ TBAA"} +!36 = distinct !DIAssignID() +!37 = !DILocation(line: 8, column: 21, scope: !7) +!38 = !DILocation(line: 8, column: 27, scope: !7) +!39 = distinct !DIAssignID() +!40 = distinct !DIAssignID() +!41 = !DILocation(line: 8, column: 36, scope: !7) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-2.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-2.ll @@ -0,0 +1,210 @@ +; RUN: opt %s -S -sroa -o - -experimental-assignment-tracking | FileCheck %s + +;; $ cat test.cpp +;; class a { +;; protected: +;; float b[4]; +;; }; +;; float c; +;; class d : a { +;; public: +;; d() { b[3] = c; } +;; void e() {} +;; }; +;; d f(); +;; void g() { +;; d j = f(), h = j, i = h; +;; i.e(); +;; i = d(); +;; } +;; +;; Generated by grabbing IR before sroa in: +;; $ clang++ -O2 -g -c test.cpp -Xclang -fexperimental-assignment-tracking + +;; Check that the dbg.assign value is scalar rather than a vector value because +;; we don't have a way of indexing a vector from within a +;; dbg.assign/DIExpression. Ensure that only the value-expression gets fragment +;; info; that the address-expression remains untouched. + +; CHECK: %i.sroa.4.12.vec.insert = insertelement <2 x float> %i.sroa.4.0.vec.insert, float %2, i32 1, !dbg +;; There's a few dbg intrinsics we're not interested in testing wedged in here. +; CHECK-NEXT: dbg.value +; CHECK-NEXT: dbg.assign +; CHECK-NEXT: dbg.assign +; CHECK-NEXT: call void @llvm.dbg.assign(metadata float %2,{{.+}}, metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32),{{.+}}, metadata <2 x float>* undef, metadata !DIExpression()), !dbg + +%class.d = type { %class.a } +%class.a = type { [4 x float] } + +$_ZN1d1eEv = comdat any + +$_ZN1dC2Ev = comdat any + +@c = dso_local local_unnamed_addr global float 0.000000e+00, align 4, !dbg !0 + +; Function Attrs: uwtable +define dso_local void @_Z1gv() local_unnamed_addr #0 !dbg !11 { +entry: + call void @llvm.dbg.assign(metadata i1 undef, metadata !15, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !32, metadata <2 x float>* undef, metadata !DIExpression()), !dbg !33 + call void @llvm.dbg.assign(metadata i1 undef, metadata !15, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !34, metadata <2 x float>* undef, metadata !DIExpression()), !dbg !33 + call void @llvm.dbg.assign(metadata i1 undef, metadata !30, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !35, metadata <2 x float>* undef, metadata !DIExpression()), !dbg !33 + call void @llvm.dbg.assign(metadata i1 undef, metadata !30, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !36, metadata <2 x float>* undef, metadata !DIExpression()), !dbg !33 + %i = alloca %class.d, align 8, !DIAssignID !37 + call void @llvm.dbg.assign(metadata i1 undef, metadata !31, metadata !DIExpression(), metadata !37, metadata %class.d* %i, metadata !DIExpression()), !dbg !33 + %ref.tmp = alloca %class.d, align 4 + %call = call { <2 x float>, <2 x float> } @_Z1fv(), !dbg !38 + %0 = extractvalue { <2 x float>, <2 x float> } %call, 0, !dbg !38 + call void @llvm.dbg.assign(metadata <2 x float> %0, metadata !15, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !39, metadata <2 x float>* undef, metadata !DIExpression()), !dbg !33 + %1 = extractvalue { <2 x float>, <2 x float> } %call, 1, !dbg !38 + call void @llvm.dbg.assign(metadata <2 x float> %1, metadata !15, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !40, metadata <2 x float>* undef, metadata !DIExpression()), !dbg !33 + call void @llvm.dbg.assign(metadata <2 x float> %0, metadata !30, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !41, metadata <2 x float>* undef, metadata !DIExpression()), !dbg !33 + call void @llvm.dbg.assign(metadata <2 x float> %1, metadata !30, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !42, metadata <2 x float>* undef, metadata !DIExpression()), !dbg !33 + %2 = bitcast %class.d* %i to i8*, !dbg !43 + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %2) #5, !dbg !43 + %h.sroa.0.sroa.0.0.h.sroa.0.0..sroa_cast4.sroa_cast = bitcast %class.d* %i to <2 x float>*, !dbg !44 + store <2 x float> %0, <2 x float>* %h.sroa.0.sroa.0.0.h.sroa.0.0..sroa_cast4.sroa_cast, align 8, !dbg !44, !DIAssignID !45 + call void @llvm.dbg.assign(metadata <2 x float> %0, metadata !31, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !45, metadata <2 x float>* %h.sroa.0.sroa.0.0.h.sroa.0.0..sroa_cast4.sroa_cast, metadata !DIExpression()), !dbg !33 + %h.sroa.0.sroa.4.0.h.sroa.0.0..sroa_cast4.sroa_idx13 = getelementptr inbounds %class.d, %class.d* %i, i64 0, i32 0, i32 0, i64 2, !dbg !44 + %h.sroa.0.sroa.4.0.h.sroa.0.0..sroa_cast4.sroa_cast = bitcast float* %h.sroa.0.sroa.4.0.h.sroa.0.0..sroa_cast4.sroa_idx13 to <2 x float>*, !dbg !44 + store <2 x float> %1, <2 x float>* %h.sroa.0.sroa.4.0.h.sroa.0.0..sroa_cast4.sroa_cast, align 8, !dbg !44, !DIAssignID !46 + call void @llvm.dbg.assign(metadata <2 x float> %1, metadata !31, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64), metadata !46, metadata <2 x float>* %h.sroa.0.sroa.4.0.h.sroa.0.0..sroa_cast4.sroa_cast, metadata !DIExpression()), !dbg !33 + call void @llvm.dbg.assign(metadata i1 undef, metadata !47, metadata !DIExpression(), metadata !51, metadata %class.d** undef, metadata !DIExpression()), !dbg !52 + call void @llvm.dbg.assign(metadata %class.d* %i, metadata !47, metadata !DIExpression(), metadata !54, metadata %class.d** undef, metadata !DIExpression()), !dbg !52 + %3 = bitcast %class.d* %ref.tmp to i8*, !dbg !55 + call void @llvm.lifetime.start.p0i8(i64 16, i8* nonnull %3) #5, !dbg !55 + call void @llvm.dbg.assign(metadata i1 undef, metadata !56, metadata !DIExpression(), metadata !59, metadata %class.d** undef, metadata !DIExpression()), !dbg !60 + call void @llvm.dbg.assign(metadata %class.d* %ref.tmp, metadata !56, metadata !DIExpression(), metadata !62, metadata %class.d** undef, metadata !DIExpression()), !dbg !60 + %4 = load float, float* @c, align 4, !dbg !63, !tbaa !65 + %arrayidx.i = getelementptr inbounds %class.d, %class.d* %ref.tmp, i64 0, i32 0, i32 0, i64 3, !dbg !69 + store float %4, float* %arrayidx.i, align 4, !dbg !70, !tbaa !65 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 8 dereferenceable(16) %2, i8* nonnull align 4 dereferenceable(16) %3, i64 16, i1 false), !dbg !71, !DIAssignID !72 + call void @llvm.dbg.assign(metadata i1 undef, metadata !31, metadata !DIExpression(), metadata !72, metadata i8* %2, metadata !DIExpression()), !dbg !33 + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %3) #5, !dbg !73 + call void @llvm.lifetime.end.p0i8(i64 16, i8* nonnull %2) #5, !dbg !74 + ret void, !dbg !74 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +declare !dbg !75 dso_local { <2 x float>, <2 x float> } @_Z1fv() local_unnamed_addr #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local void @_ZN1d1eEv(%class.d* %this) local_unnamed_addr #3 comdat align 2 !dbg !48 { +entry: + call void @llvm.dbg.assign(metadata i1 undef, metadata !47, metadata !DIExpression(), metadata !78, metadata %class.d** undef, metadata !DIExpression()), !dbg !79 + call void @llvm.dbg.assign(metadata %class.d* %this, metadata !47, metadata !DIExpression(), metadata !80, metadata %class.d** undef, metadata !DIExpression()), !dbg !79 + ret void, !dbg !81 +} + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local void @_ZN1dC2Ev(%class.d* %this) unnamed_addr #3 comdat align 2 !dbg !57 { +entry: + call void @llvm.dbg.assign(metadata i1 undef, metadata !56, metadata !DIExpression(), metadata !82, metadata %class.d** undef, metadata !DIExpression()), !dbg !83 + call void @llvm.dbg.assign(metadata %class.d* %this, metadata !56, metadata !DIExpression(), metadata !84, metadata %class.d** undef, metadata !DIExpression()), !dbg !83 + %0 = load float, float* @c, align 4, !dbg !85, !tbaa !65 + %arrayidx = getelementptr inbounds %class.d, %class.d* %this, i64 0, i32 0, i32 0, i64 3, !dbg !86 + store float %0, float* %arrayidx, align 4, !dbg !87, !tbaa !65 + ret void, !dbg !88 +} + +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #4 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "c", scope: !2, file: !3, line: 5, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!7 = !{i32 7, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{!"clang version 12.0.0"} +!11 = distinct !DISubprogram(name: "g", linkageName: "_Z1gv", scope: !3, file: !3, line: 12, type: !12, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !14) +!12 = !DISubroutineType(types: !13) +!13 = !{null} +!14 = !{!15, !30, !31} +!15 = !DILocalVariable(name: "j", scope: !11, file: !3, line: 13, type: !16) +!16 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "d", file: !3, line: 6, size: 128, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !17, identifier: "_ZTS1d") +!17 = !{!18, !25, !29} +!18 = !DIDerivedType(tag: DW_TAG_inheritance, scope: !16, baseType: !19, extraData: i32 0) +!19 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !3, line: 1, size: 128, flags: DIFlagTypePassByValue, elements: !20, identifier: "_ZTS1a") +!20 = !{!21} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !19, file: !3, line: 3, baseType: !22, size: 128, flags: DIFlagProtected) +!22 = !DICompositeType(tag: DW_TAG_array_type, baseType: !6, size: 128, elements: !23) +!23 = !{!24} +!24 = !DISubrange(count: 4) +!25 = !DISubprogram(name: "d", scope: !16, file: !3, line: 8, type: !26, scopeLine: 8, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!26 = !DISubroutineType(types: !27) +!27 = !{null, !28} +!28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!29 = !DISubprogram(name: "e", linkageName: "_ZN1d1eEv", scope: !16, file: !3, line: 9, type: !26, scopeLine: 9, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!30 = !DILocalVariable(name: "h", scope: !11, file: !3, line: 13, type: !16) +!31 = !DILocalVariable(name: "i", scope: !11, file: !3, line: 13, type: !16) +!32 = distinct !DIAssignID() +!33 = !DILocation(line: 0, scope: !11) +!34 = distinct !DIAssignID() +!35 = distinct !DIAssignID() +!36 = distinct !DIAssignID() +!37 = distinct !DIAssignID() +!38 = !DILocation(line: 13, column: 9, scope: !11) +!39 = distinct !DIAssignID() +!40 = distinct !DIAssignID() +!41 = distinct !DIAssignID() +!42 = distinct !DIAssignID() +!43 = !DILocation(line: 13, column: 3, scope: !11) +!44 = !DILocation(line: 13, column: 25, scope: !11) +!45 = distinct !DIAssignID() +!46 = distinct !DIAssignID() +!47 = !DILocalVariable(name: "this", arg: 1, scope: !48, type: !50, flags: DIFlagArtificial | DIFlagObjectPointer) +!48 = distinct !DISubprogram(name: "e", linkageName: "_ZN1d1eEv", scope: !16, file: !3, line: 9, type: !26, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, declaration: !29, retainedNodes: !49) +!49 = !{!47} +!50 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) +!51 = distinct !DIAssignID() +!52 = !DILocation(line: 0, scope: !48, inlinedAt: !53) +!53 = distinct !DILocation(line: 14, column: 5, scope: !11) +!54 = distinct !DIAssignID() +!55 = !DILocation(line: 15, column: 7, scope: !11) +!56 = !DILocalVariable(name: "this", arg: 1, scope: !57, type: !50, flags: DIFlagArtificial | DIFlagObjectPointer) +!57 = distinct !DISubprogram(name: "d", linkageName: "_ZN1dC2Ev", scope: !16, file: !3, line: 8, type: !26, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, declaration: !25, retainedNodes: !58) +!58 = !{!56} +!59 = distinct !DIAssignID() +!60 = !DILocation(line: 0, scope: !57, inlinedAt: !61) +!61 = distinct !DILocation(line: 15, column: 7, scope: !11) +!62 = distinct !DIAssignID() +!63 = !DILocation(line: 8, column: 16, scope: !64, inlinedAt: !61) +!64 = distinct !DILexicalBlock(scope: !57, file: !3, line: 8, column: 7) +!65 = !{!66, !66, i64 0} +!66 = !{!"float", !67, i64 0} +!67 = !{!"omnipotent char", !68, i64 0} +!68 = !{!"Simple C++ TBAA"} +!69 = !DILocation(line: 8, column: 9, scope: !64, inlinedAt: !61) +!70 = !DILocation(line: 8, column: 14, scope: !64, inlinedAt: !61) +!71 = !DILocation(line: 15, column: 5, scope: !11) +!72 = distinct !DIAssignID() +!73 = !DILocation(line: 15, column: 3, scope: !11) +!74 = !DILocation(line: 16, column: 1, scope: !11) +!75 = !DISubprogram(name: "f", linkageName: "_Z1fv", scope: !3, file: !3, line: 11, type: !76, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !4) +!76 = !DISubroutineType(types: !77) +!77 = !{!16} +!78 = distinct !DIAssignID() +!79 = !DILocation(line: 0, scope: !48) +!80 = distinct !DIAssignID() +!81 = !DILocation(line: 9, column: 13, scope: !48) +!82 = distinct !DIAssignID() +!83 = !DILocation(line: 0, scope: !57) +!84 = distinct !DIAssignID() +!85 = !DILocation(line: 8, column: 16, scope: !64) +!86 = !DILocation(line: 8, column: 9, scope: !64) +!87 = !DILocation(line: 8, column: 14, scope: !64) +!88 = !DILocation(line: 8, column: 19, scope: !57) +