diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp --- a/llvm/lib/Transforms/Scalar/SROA.cpp +++ b/llvm/lib/Transforms/Scalar/SROA.cpp @@ -119,6 +119,122 @@ static cl::opt SROAStrictInbounds("sroa-strict-inbounds", cl::init(false), 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) { + auto MarkerRange = at::getAssignmentMarkers(OldInst); + // Nothing to do if OldInst has no linked dbg.assign intrinsics. + if (MarkerRange.empty()) + return; + + 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 : MarkerRange) { + 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, std::nullopt), 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. @@ -2435,6 +2551,7 @@ // original alloca. uint64_t NewBeginOffset = 0, NewEndOffset = 0; + uint64_t RelativeOffset = 0; uint64_t SliceSize = 0; bool IsSplittable = false; bool IsSplit = false; @@ -2508,8 +2625,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()); @@ -2749,6 +2872,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); @@ -2774,6 +2900,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; } @@ -2796,6 +2925,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; @@ -2871,6 +3004,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); @@ -2926,7 +3063,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; } @@ -2959,11 +3100,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; } @@ -3036,6 +3181,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(); } @@ -3053,7 +3202,6 @@ (!IsDest && II.getRawSource() == OldPtr)); Align SliceAlign = getSliceAlign(); - // For unsplit intrinsics, we simply modify the source and destination // pointers in place. This isn't just an optimization, it is a matter of // correctness. With unsplit intrinsics we may be dealing with transfers @@ -3064,10 +3212,16 @@ if (!IsSplittable) { Value *AdjustedPtr = getNewAllocaSlicePtr(IRB, OldPtr->getType()); if (IsDest) { + // Update the address component of linked dbg.assigns. + for (auto *DAI : at::getAssignmentMarkers(&II)) { + if (any_of(DAI->location_ops(), + [&](Value *V) { return V == II.getDest(); }) || + DAI->getAddress() == II.getDest()) + DAI->replaceVariableLocationOp(II.getDest(), AdjustedPtr); + } II.setDest(AdjustedPtr); II.setDestAlignment(SliceAlign); - } - else { + } else { II.setSource(AdjustedPtr); II.setSourceAlignment(SliceAlign); } @@ -3156,6 +3310,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; } @@ -3242,6 +3399,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(); } @@ -3579,12 +3739,13 @@ struct StoreOpSplitter : public OpSplitter { StoreOpSplitter(Instruction *InsertionPoint, Value *Ptr, Type *BaseTy, - AAMDNodes AATags, Align BaseAlign, const DataLayout &DL, - IRBuilderTy &IRB) + 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) { @@ -3606,6 +3767,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"); } }; @@ -3619,7 +3799,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); @@ -4220,7 +4400,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"); } @@ -4693,6 +4874,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); @@ -4712,9 +4895,10 @@ if (ExprFragment) { uint64_t AbsEnd = ExprFragment->OffsetInBits + ExprFragment->SizeInBits; - if (Start >= AbsEnd) + if (Start >= AbsEnd) { // No need to describe a SROAed padding. continue; + } Size = std::min(Size, AbsEnd - Start); } // The new, smaller fragment is stenciled out from the old fragment. @@ -4756,8 +4940,23 @@ OldDII->eraseFromParent(); } - DIB.insertDeclare(Fragment.Alloca, DbgDeclare->getVariable(), FragmentExpr, - DbgDeclare->getDebugLoc(), &AI); + if (auto *DbgAssign = dyn_cast(DbgDeclare)) { + if (!Fragment.Alloca->hasMetadata(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; @@ -4883,6 +5082,7 @@ OldDII->eraseFromParent(); } + at::deleteAssignmentMarkers(I); I->replaceAllUsesWith(UndefValue::get(I->getType())); for (Use &Operand : I->operands()) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/after-inlining.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/after-inlining.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/after-inlining.ll @@ -0,0 +1,144 @@ +; RUN: opt %s -S -passes=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 ptr 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, ptr nocapture) #2 +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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 ptr %j.i, metadata !DIExpression()) #5, !dbg !75 + %0 = bitcast ptr %j.i to ptr, !dbg !77 + call void @llvm.lifetime.start.p0i8(i64 4, ptr nonnull %0) #5, !dbg !77 + %arrayidx.i.i = getelementptr inbounds %struct.c, ptr %j.i, i64 0, i32 1, i64 0, !dbg !78 + %1 = load i32, ptr %arrayidx.i.i, align 4, !dbg !78 + store i32 1, ptr @f, align 4, !dbg !80 + store i32 %1, ptr @e, align 4, !dbg !81 + call void @llvm.lifetime.end.p0i8(i64 4, ptr 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) +!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) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/alloca-single-slice.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/alloca-single-slice.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/alloca-single-slice.ll @@ -0,0 +1,82 @@ +; RUN: opt -passes=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 ptr %a.sroa.0, 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.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 ptr %a, metadata !DIExpression()), !dbg !24 + %0 = bitcast ptr %a to ptr, !dbg !25 + call void @llvm.lifetime.start.p0i8(i64 8, ptr %0), !dbg !25 + %b = getelementptr inbounds %struct.a, ptr %a, i32 0, i32 0, !dbg !26 + %arraydecay = getelementptr inbounds [8 x i8], ptr %b, i64 0, i64 0, !dbg !27 + %1 = load i32, ptr @c, align 4, !dbg !28 + %conv = sext i32 %1 to i64, !dbg !28 + call void @llvm.memcpy.p0i8.p0i8.i64(ptr align 1 %arraydecay, ptr align 1 null, i64 %conv, i1 false), !dbg !27 + %2 = bitcast ptr %a to ptr, !dbg !33 + call void @llvm.lifetime.end.p0i8(i64 8, ptr %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) +!33 = !DILocation(line: 8, column: 1, scope: !11) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/complex.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/complex.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/complex.ll @@ -0,0 +1,69 @@ +; RUN: opt -passes=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 ptr 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 ptr 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 ptr %c, metadata !DIExpression()), !dbg !15 + %0 = bitcast ptr %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 }, ptr %c, i32 0, i32 0, !dbg !17 + ;%c.imagp = getelementptr inbounds { double, double }, ptr %c, i32 0, i32 1, !dbg !17 + ;store double 0.000000e+00, ptr %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 ptr %c.realp, metadata !DIExpression()), !dbg !15 + ;store double 0.000000e+00, ptr %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 ptr %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) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag-2.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag-2.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag-2.ll @@ -0,0 +1,260 @@ +; RUN: opt -passes=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(ptr nonnull sret(%class.B) align 4 %m, + +; CHECK: store <2 x float> %agg.tmp.sroa.0.0.copyload.i, ptr %4, align 4,{{.+}}!DIAssignID ![[id1:[0-9]+]] +; CHECK: store <2 x float> %agg.tmp.sroa.2.0.copyload.i, ptr %n.sroa.4.4..sroa_idx, 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 ptr %4, 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 ptr %n.sroa.4.4..sroa_idx, 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_(ptr noalias nocapture sret(%class.B) align 4 %agg.result, ptr nocapture readonly %this, ptr 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, ptr %this, i64 0, i32 1, !dbg !42 + %agg.tmp.sroa.0.0..sroa_cast = bitcast ptr %agg.tmp.sroa.0.0..sroa_idx to ptr, !dbg !42 + %agg.tmp.sroa.0.0.copyload = load <2 x float>, ptr %agg.tmp.sroa.0.0..sroa_cast, align 4, !dbg !42 + %agg.tmp.sroa.2.0..sroa_idx2 = getelementptr inbounds %class.B, ptr %this, i64 0, i32 1, i32 0, i64 2, !dbg !42 + %agg.tmp.sroa.2.0..sroa_cast = bitcast ptr %agg.tmp.sroa.2.0..sroa_idx2 to ptr, !dbg !42 + %agg.tmp.sroa.2.0.copyload = load <2 x float>, ptr %agg.tmp.sroa.2.0..sroa_cast, align 4, !dbg !42 + %d.sroa.0.0..sroa_idx.i = getelementptr inbounds %class.B, ptr %agg.result, i64 0, i32 1, !dbg !47 + %d.sroa.0.0..sroa_cast.i = bitcast ptr %d.sroa.0.0..sroa_idx.i to ptr, !dbg !47 + store <2 x float> %agg.tmp.sroa.0.0.copyload, ptr %d.sroa.0.0..sroa_cast.i, align 4, !dbg !47 + %d.sroa.2.0..sroa_idx2.i = getelementptr inbounds %class.B, ptr %agg.result, i64 0, i32 1, i32 0, i64 2, !dbg !47 + %d.sroa.2.0..sroa_cast.i = bitcast ptr %d.sroa.2.0..sroa_idx2.i to ptr, !dbg !47 + store <2 x float> %agg.tmp.sroa.2.0.copyload, ptr %d.sroa.2.0..sroa_cast.i, align 4, !dbg !47 + ret void, !dbg !54 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local void @_ZN1BC2E1a(ptr %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, ptr %this, i64 0, i32 1, !dbg !55 + %d.sroa.0.0..sroa_cast = bitcast ptr %d.sroa.0.0..sroa_idx to ptr, !dbg !55 + store <2 x float> %d.coerce0, ptr %d.sroa.0.0..sroa_cast, align 4, !dbg !55 + %d.sroa.2.0..sroa_idx2 = getelementptr inbounds %class.B, ptr %this, i64 0, i32 1, i32 0, i64 2, !dbg !55 + %d.sroa.2.0..sroa_cast = bitcast ptr %d.sroa.2.0..sroa_idx2 to ptr, !dbg !55 + store <2 x float> %d.coerce1, ptr %d.sroa.2.0..sroa_cast, align 4, !dbg !55 + 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 ptr %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 ptr %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 ptr %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 ptr %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 ptr %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 ptr %o, metadata !DIExpression()), !dbg !74 + %0 = getelementptr inbounds %class.h, ptr %convexbody, i64 0, i32 0, !dbg !80 + call void @llvm.lifetime.start.p0i8(i64 1, ptr nonnull %0) #7, !dbg !80 + %1 = getelementptr inbounds %class.h, ptr %k, i64 0, i32 0, !dbg !80 + call void @llvm.lifetime.start.p0i8(i64 1, ptr nonnull %1) #7, !dbg !80 + %2 = bitcast ptr %l to ptr, !dbg !81 + call void @llvm.lifetime.start.p0i8(i64 20, ptr nonnull %2) #7, !dbg !81 + call void @_ZN1h1iEv(ptr nonnull sret(%class.B) align 4 %l, ptr nonnull %k), !dbg !82 + %3 = bitcast ptr %m to ptr, !dbg !81 + call void @llvm.lifetime.start.p0i8(i64 20, ptr nonnull %3) #7, !dbg !81 + call void @_ZN1h1iEv(ptr nonnull sret(%class.B) align 4 %m, ptr nonnull %convexbody), !dbg !83 + %4 = bitcast ptr %n to ptr, !dbg !81 + call void @llvm.lifetime.start.p0i8(i64 20, ptr nonnull %4) #7, !dbg !81 + %agg.tmp.sroa.0.0..sroa_idx.i = getelementptr inbounds %class.B, ptr %l, i64 0, i32 1, !dbg !84 + %agg.tmp.sroa.0.0..sroa_cast.i = bitcast ptr %agg.tmp.sroa.0.0..sroa_idx.i to ptr, !dbg !84 + %agg.tmp.sroa.0.0.copyload.i = load <2 x float>, ptr %agg.tmp.sroa.0.0..sroa_cast.i, align 4, !dbg !84 + %agg.tmp.sroa.2.0..sroa_idx2.i = getelementptr inbounds %class.B, ptr %l, i64 0, i32 1, i32 0, i64 2, !dbg !84 + %agg.tmp.sroa.2.0..sroa_cast.i = bitcast ptr %agg.tmp.sroa.2.0..sroa_idx2.i to ptr, !dbg !84 + %agg.tmp.sroa.2.0.copyload.i = load <2 x float>, ptr %agg.tmp.sroa.2.0..sroa_cast.i, align 4, !dbg !84 + %d.sroa.0.0..sroa_idx.i.i = getelementptr inbounds %class.B, ptr %n, i64 0, i32 1, !dbg !89 + %d.sroa.0.0..sroa_cast.i.i = bitcast ptr %d.sroa.0.0..sroa_idx.i.i to ptr, !dbg !89 + store <2 x float> %agg.tmp.sroa.0.0.copyload.i, ptr %d.sroa.0.0..sroa_cast.i.i, align 4, !dbg !89 + %d.sroa.2.0..sroa_idx2.i.i = getelementptr inbounds %class.B, ptr %n, i64 0, i32 1, i32 0, i64 2, !dbg !89 + %d.sroa.2.0..sroa_cast.i.i = bitcast ptr %d.sroa.2.0..sroa_idx2.i.i to ptr, !dbg !89 + store <2 x float> %agg.tmp.sroa.2.0.copyload.i, ptr %d.sroa.2.0..sroa_cast.i.i, align 4, !dbg !89 + %5 = bitcast ptr %o to ptr, !dbg !91 + call void @llvm.lifetime.start.p0i8(i64 16, ptr nonnull %5) #7, !dbg !91 + %e.i = getelementptr inbounds %class.B, ptr %n, i64 0, i32 1, !dbg !92 + %6 = bitcast ptr %e.i to ptr, !dbg !97 + call void @llvm.memcpy.p0i8.p0i8.i64(ptr nonnull align 4 dereferenceable(16) %5, ptr nonnull align 4 dereferenceable(16) %6, i64 16, i1 false), !dbg !97, !DIAssignID !98 + call void @llvm.dbg.assign(metadata i1 undef, metadata !72, metadata !DIExpression(), metadata !98, metadata ptr %5, metadata !DIExpression()), !dbg !74 + call void @_ZN1a1cEv(ptr nonnull %o), !dbg !99 + call void @llvm.lifetime.end.p0i8(i64 16, ptr nonnull %5) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 20, ptr nonnull %4) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 20, ptr nonnull %3) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 20, ptr nonnull %2) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 1, ptr nonnull %1) #7, !dbg !100 + call void @llvm.lifetime.end.p0i8(i64 1, ptr nonnull %0) #7, !dbg !100 + ret void, !dbg !100 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) #1 +declare dso_local void @_ZN1h1iEv(ptr sret(%class.B) align 4, ptr) local_unnamed_addr #5 + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local nonnull align 4 dereferenceable(16) ptr @_ZN1B1fEv(ptr %this) local_unnamed_addr #6 comdat align 2 !dbg !93 { +entry: + %e = getelementptr inbounds %class.B, ptr %this, i64 0, i32 1, !dbg !101 + ret ptr %e, !dbg !102 +} + +declare dso_local void @_ZN1a1cEv(ptr) local_unnamed_addr #5 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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) +!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) +!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) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag.ll @@ -0,0 +1,113 @@ +; RUN: opt %s -S -passes=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 ptr %a, metadata !DIExpression()), !dbg !23 + %ref.tmp = alloca %class.c, align 4 + %0 = bitcast ptr %a to ptr, !dbg !24 + call void @llvm.lifetime.start.p0i8(i64 48, ptr %0) #4, !dbg !24 + %1 = bitcast ptr %ref.tmp to ptr, !dbg !25 + call void @llvm.lifetime.start.p0i8(i64 16, ptr %1) #4, !dbg !25 + %call = call { <2 x float>, <2 x float> } @_Z3fn1v(), !dbg !25 + %coerce.dive = getelementptr inbounds %class.c, ptr %ref.tmp, i32 0, i32 0, !dbg !25 + %2 = bitcast ptr %coerce.dive to ptr, !dbg !25 + %3 = getelementptr inbounds { <2 x float>, <2 x float> }, ptr %2, i32 0, i32 0, !dbg !25 + %4 = extractvalue { <2 x float>, <2 x float> } %call, 0, !dbg !25 + store <2 x float> %4, ptr %3, align 4, !dbg !25 + %5 = getelementptr inbounds { <2 x float>, <2 x float> }, ptr %2, i32 0, i32 1, !dbg !25 + %6 = extractvalue { <2 x float>, <2 x float> } %call, 1, !dbg !25 + store <2 x float> %6, ptr %5, align 4, !dbg !25 + %arrayidx = getelementptr inbounds [3 x %class.c], ptr %a, i64 0, i64 2, !dbg !26 + %7 = bitcast ptr %arrayidx to ptr, !dbg !27 + %8 = bitcast ptr %ref.tmp to ptr, !dbg !27 + call void @llvm.memcpy.p0i8.p0i8.i64(ptr align 16 %7, ptr align 4 %8, i64 16, i1 false), !dbg !27, !DIAssignID !32 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 256, 128), metadata !32, metadata ptr %7, metadata !DIExpression()), !dbg !23 + %9 = bitcast ptr %ref.tmp to ptr, !dbg !26 + call void @llvm.lifetime.end.p0i8(i64 16, ptr %9) #4, !dbg !26 + %10 = bitcast ptr %a to ptr, !dbg !33 + call void @llvm.lifetime.end.p0i8(i64 48, ptr %10) #4, !dbg !33 + ret void, !dbg !33 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr 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(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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) +!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} diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/id.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/id.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/id.ll @@ -0,0 +1,174 @@ +; RUN: opt -passes=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 ptr 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 ptr 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 ptr %e, metadata !DIExpression()), !dbg !22 + %agg.tmp.ensured = alloca %class.a, align 1 + %0 = bitcast ptr %e to ptr, !dbg !23 + call void @llvm.lifetime.start.p0i8(i64 4, ptr nonnull %0) #4, !dbg !23 + %1 = load i32, ptr @c, align 4, !dbg !24 + %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, ptr @b, align 4, !dbg !31 + call void @llvm.dbg.assign(metadata float %2, metadata !20, metadata !DIExpression(), metadata !34, metadata ptr %e, metadata !DIExpression()), !dbg !22 + br label %if.end, !dbg !35 + +if.else: ; preds = %entry + %3 = load float, ptr @b, align 4, !dbg !36 + %4 = load float, ptr @d, align 4, !dbg !37 + %div = fdiv float %3, %4, !dbg !38 + call void @llvm.dbg.assign(metadata float %div, metadata !20, metadata !DIExpression(), metadata !34, metadata ptr %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, ptr %e, align 4, !dbg !39, !DIAssignID !34 + %5 = load i32, ptr @c, align 4, !dbg !40 + call void @llvm.dbg.assign(metadata i1 undef, metadata !41, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata i1 undef, metadata !51, metadata !DIExpression(), metadata !57, metadata ptr undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata i1 undef, metadata !52, metadata !DIExpression(), metadata !58, metadata ptr undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata ptr %agg.tmp.ensured, metadata !41, metadata !DIExpression(), metadata !59, metadata ptr undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata i32 %5, metadata !51, metadata !DIExpression(), metadata !60, metadata ptr undef, metadata !DIExpression()), !dbg !55 + call void @llvm.dbg.assign(metadata ptr %e, metadata !52, metadata !DIExpression(), metadata !61, metadata ptr undef, metadata !DIExpression()), !dbg !55 + call void @llvm.lifetime.end.p0i8(i64 4, ptr nonnull %0) #4, !dbg !62 + ret void, !dbg !62 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) #1 + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local void @_ZN1aC2EiRf(ptr %this, i32 %0, ptr 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 ptr undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata i1 undef, metadata !51, metadata !DIExpression(), metadata !65, metadata ptr undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata i1 undef, metadata !52, metadata !DIExpression(), metadata !66, metadata ptr undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata ptr %this, metadata !41, metadata !DIExpression(), metadata !67, metadata ptr undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata i32 %0, metadata !51, metadata !DIExpression(), metadata !68, metadata ptr undef, metadata !DIExpression()), !dbg !64 + call void @llvm.dbg.assign(metadata ptr %1, metadata !52, metadata !DIExpression(), metadata !69, metadata ptr undef, metadata !DIExpression()), !dbg !64 + ret void, !dbg !70 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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) +!30 = !DILocation(line: 9, column: 7, scope: !16) +!31 = !DILocation(line: 10, column: 9, scope: !25) +!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) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memcpy.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memcpy.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memcpy.ll @@ -0,0 +1,117 @@ +; RUN: opt -passes=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 ptr %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 ptr %To.sroa.4, metadata !DIExpression()), !dbg + +;; Split memcpy. +; CHECK: call void @llvm.memcpy{{.*}}(ptr align 8 %To.sroa.0, ptr align 4 @From, 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, ptr getelementptr inbounds (i8, ptr @From, i64 12) +; CHECK: call void @llvm.memcpy{{.*}}(ptr align 8 %To.sroa.4, ptr align 4 getelementptr inbounds (i8, ptr @From, i64 16), 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 ptr %To.sroa.0, 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 ptr 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 ptr %To.sroa.4, 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 ptr %To, metadata !DIExpression()), !dbg !26 + %0 = bitcast ptr %To to ptr, !dbg !27 + call void @llvm.lifetime.start.p0i8(i64 28, ptr %0) #3, !dbg !27 + %1 = bitcast ptr %To to ptr, !dbg !28 + call void @llvm.memcpy.p0i8.p0i8.i64(ptr align 4 %1, ptr align 4 bitcast (ptr @From to ptr), i64 28, i1 false), !dbg !28, !DIAssignID !34 + call void @llvm.dbg.assign(metadata i1 undef, metadata !24, metadata !DIExpression(), metadata !34, metadata ptr %1, metadata !DIExpression()), !dbg !28 + %Var = getelementptr inbounds %struct.LargeStruct, ptr %To, i32 0, i32 3, !dbg !35 + %2 = load i32, ptr %Var, align 4, !dbg !35 + %3 = bitcast ptr %To to ptr, !dbg !38 + call void @llvm.lifetime.end.p0i8(i64 28, ptr %3) #3, !dbg !38 + ret i32 %2, !dbg !39 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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) +!34 = distinct !DIAssignID() +!35 = !DILocation(line: 9, column: 13, scope: !20) +!38 = !DILocation(line: 10, column: 1, scope: !20) +!39 = !DILocation(line: 9, column: 3, scope: !20) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memmove-to-from-same-alloca.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memmove-to-from-same-alloca.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memmove-to-from-same-alloca.ll @@ -0,0 +1,163 @@ +; RUN: opt %s -passes=sroa -o - -S -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; Generated from this C++ source: +;; __attribute__((nodebug)) struct Blob {int P[6];} Glob; +;; __attribute__((nodebug)) int Cond; +;; __attribute__((nodebug)) Blob *C; +;; __attribute__((nodebug)) void call(int); +;; +;; void f() { +;; int A[16]; +;; __attribute__ ((nodebug)) int B[16]; +;; // A[0:6) <- Glob +;; __builtin_memmove(&A[0], &Glob, sizeof(Blob)); +;; call(0); +;; // B[8:14) <- Glob +;; __builtin_memmove(&B[8], &Glob, sizeof(Blob)); +;; call(A[0]); +;; // A[8:14) <- A[0:6) +;; __builtin_memmove(&A[8], &A[0], sizeof(Blob)); +;; call(A[8]); +;; if (Cond) +;; // C <- A[8:14) +;; __builtin_memmove(C, &A[8], sizeof(Blob)); +;; else +;; // C <- B[8:14) +;; __builtin_memmove(C, &B[8], sizeof(Blob)); +;; } +;; +;; using: +;; clang test.cpp -emit-llvm -S -g -O2 -Xclang -disable-llvm-passes -o - \ +;; | opt -passes=declare-to-assign -o test.ll - -S + +;; We're interested in variable A and the second memmove with A as a dest (the +;; third memmove in the source). SROA is going to chop up A so that the only +;; Alloca'd slice remaining is what were originally elements 1 through 5 +;; inclusive (element 0 is promoted). Incidentally, the memmove later becomes a +;; memcpy. Check that the dbg.assign address and fragment are correct and +;; ensure the DIAssignID still links it to the memmove(/memcpy). + +; CHECK: %A.sroa.0.sroa.5 = alloca [5 x i32] +; CHECK: llvm.memcpy{{.*}}(ptr align 4 %A.sroa.0.sroa.5, ptr align 4 getelementptr inbounds (i8, ptr @Glob, i64 4), i64 20, i1 false){{.*}}!DIAssignID ![[ID:[0-9]+]] +;; Here's the dbg.assign for element 0 - it's not important for the test. +; CHECK-NEXT: llvm.dbg.assign({{.*}}!DIExpression(DW_OP_LLVM_fragment, 0, 32){{.*}}) +;; This is the dbg.assign we care about: +; CHECK-NEXT: llvm.dbg.assign(metadata i1 undef, metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 32, 160), metadata ![[ID]], metadata ptr %A.sroa.0.sroa.5, metadata !DIExpression()) + +; CHECK: ![[VAR]] = !DILocalVariable(name: "A" + +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.Blob = type { [6 x i32] } + +@Glob = dso_local global %struct.Blob zeroinitializer, align 4 +@Cond = dso_local global i32 0, align 4 +@C = dso_local global ptr null, align 8 + +; Function Attrs: mustprogress uwtable +define dso_local void @_Z1fv() #0 !dbg !9 { +entry: + %A = alloca [16 x i32], align 16, !DIAssignID !18 + call void @llvm.dbg.assign(metadata i1 undef, metadata !13, metadata !DIExpression(), metadata !18, metadata ptr %A, metadata !DIExpression()), !dbg !19 + %B = alloca [16 x i32], align 16 + call void @llvm.lifetime.start.p0(i64 64, ptr %A) #5, !dbg !20 + call void @llvm.lifetime.start.p0(i64 64, ptr %B) #5, !dbg !21 + %arrayidx = getelementptr inbounds [16 x i32], ptr %A, i64 0, i64 0, !dbg !22 + call void @llvm.memmove.p0.p0.i64(ptr align 16 %arrayidx, ptr align 4 @Glob, i64 24, i1 false), !dbg !23, !DIAssignID !24 + call void @llvm.dbg.assign(metadata i1 undef, metadata !13, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 192), metadata !24, metadata ptr %arrayidx, metadata !DIExpression()), !dbg !19 + call void @_Z4calli(i32 noundef 0), !dbg !25 + %arrayidx1 = getelementptr inbounds [16 x i32], ptr %B, i64 0, i64 8, !dbg !26 + call void @llvm.memmove.p0.p0.i64(ptr align 16 %arrayidx1, ptr align 4 @Glob, i64 24, i1 false), !dbg !27 + %arrayidx2 = getelementptr inbounds [16 x i32], ptr %A, i64 0, i64 0, !dbg !28 + %0 = load i32, ptr %arrayidx2, align 16, !dbg !28 + call void @_Z4calli(i32 noundef %0), !dbg !33 + %arrayidx3 = getelementptr inbounds [16 x i32], ptr %A, i64 0, i64 8, !dbg !34 + %arrayidx4 = getelementptr inbounds [16 x i32], ptr %A, i64 0, i64 0, !dbg !35 + call void @llvm.memmove.p0.p0.i64(ptr align 16 %arrayidx3, ptr align 16 %arrayidx4, i64 24, i1 false), !dbg !36, !DIAssignID !37 + call void @llvm.dbg.assign(metadata i1 undef, metadata !13, metadata !DIExpression(DW_OP_LLVM_fragment, 256, 192), metadata !37, metadata ptr %arrayidx3, metadata !DIExpression()), !dbg !19 + %arrayidx5 = getelementptr inbounds [16 x i32], ptr %A, i64 0, i64 8, !dbg !38 + %1 = load i32, ptr %arrayidx5, align 16, !dbg !38 + call void @_Z4calli(i32 noundef %1), !dbg !39 + %2 = load i32, ptr @Cond, align 4, !dbg !40 + %tobool = icmp ne i32 %2, 0, !dbg !40 + br i1 %tobool, label %if.then, label %if.else, !dbg !42 + +if.then: ; preds = %entry + %3 = load ptr, ptr @C, align 8, !dbg !43 + %arrayidx6 = getelementptr inbounds [16 x i32], ptr %A, i64 0, i64 8, !dbg !46 + call void @llvm.memmove.p0.p0.i64(ptr align 4 %3, ptr align 16 %arrayidx6, i64 24, i1 false), !dbg !47 + br label %if.end, !dbg !47 + +if.else: ; preds = %entry + %4 = load ptr, ptr @C, align 8, !dbg !48 + %arrayidx7 = getelementptr inbounds [16 x i32], ptr %B, i64 0, i64 8, !dbg !49 + call void @llvm.memmove.p0.p0.i64(ptr align 4 %4, ptr align 16 %arrayidx7, i64 24, i1 false), !dbg !50 + br label %if.end + +if.end: ; preds = %if.else, %if.then + call void @llvm.lifetime.end.p0(i64 64, ptr %B) #5, !dbg !51 + call void @llvm.lifetime.end.p0(i64 64, ptr %A) #5, !dbg !51 + ret void, !dbg !51 +} + +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1 +declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 +declare void @llvm.memmove.p0.p0.i64(ptr nocapture writeonly, ptr nocapture readonly, i64, i1 immarg) #3 +declare void @_Z4calli(i32 noundef) #4 +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #2 + + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 16.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{!"clang version 16.0.0"} +!9 = distinct !DISubprogram(name: "f", linkageName: "_Z1fv", scope: !1, file: !1, line: 6, type: !10, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12) +!10 = !DISubroutineType(types: !11) +!11 = !{null} +!12 = !{!13} +!13 = !DILocalVariable(name: "A", scope: !9, file: !1, line: 7, type: !14) +!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !15, size: 512, elements: !16) +!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!16 = !{!17} +!17 = !DISubrange(count: 16) +!18 = distinct !DIAssignID() +!19 = !DILocation(line: 0, scope: !9) +!20 = !DILocation(line: 7, column: 3, scope: !9) +!21 = !DILocation(line: 8, column: 3, scope: !9) +!22 = !DILocation(line: 10, column: 22, scope: !9) +!23 = !DILocation(line: 10, column: 3, scope: !9) +!24 = distinct !DIAssignID() +!25 = !DILocation(line: 11, column: 3, scope: !9) +!26 = !DILocation(line: 13, column: 22, scope: !9) +!27 = !DILocation(line: 13, column: 3, scope: !9) +!28 = !DILocation(line: 14, column: 8, scope: !9) +!33 = !DILocation(line: 14, column: 3, scope: !9) +!34 = !DILocation(line: 16, column: 22, scope: !9) +!35 = !DILocation(line: 16, column: 29, scope: !9) +!36 = !DILocation(line: 16, column: 3, scope: !9) +!37 = distinct !DIAssignID() +!38 = !DILocation(line: 17, column: 8, scope: !9) +!39 = !DILocation(line: 17, column: 3, scope: !9) +!40 = !DILocation(line: 18, column: 7, scope: !41) +!41 = distinct !DILexicalBlock(scope: !9, file: !1, line: 18, column: 7) +!42 = !DILocation(line: 18, column: 7, scope: !9) +!43 = !DILocation(line: 20, column: 23, scope: !41) +!46 = !DILocation(line: 20, column: 27, scope: !41) +!47 = !DILocation(line: 20, column: 5, scope: !41) +!48 = !DILocation(line: 23, column: 23, scope: !41) +!49 = !DILocation(line: 23, column: 27, scope: !41) +!50 = !DILocation(line: 23, column: 5, scope: !41) +!51 = !DILocation(line: 24, column: 1, scope: !9) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/rewrite.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/rewrite.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/rewrite.ll @@ -0,0 +1,130 @@ +; RUN: opt -passes=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 ptr %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 ptr %S.sroa.5, metadata !DIExpression()), !dbg + +;; The memset has been sliced up (middle slice removed). +; CHECK: call void @llvm.memset{{.*}}(ptr align 8 %S.sroa.0, i8 0, i64 12, i1 false), !dbg !{{.+}}, !DIAssignID ![[ID_5:[0-9]+]] +; CHECK: call void @llvm.memset{{.*}}(ptr align 8 %S.sroa.5, 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 ptr %S.sroa.0, 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 ptr 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 ptr %S.sroa.5, metadata !DIExpression()), !dbg + +;; mem2reg promotes the load/store to the middle slice created by SROA: +; CHECK-NEXT: %0 = load i32, ptr @Glob, align 4, !dbg !{{.+}} +; 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 ptr 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 ptr %S, metadata !DIExpression()), !dbg !29 + %0 = bitcast ptr %S to ptr, !dbg !30 + call void @llvm.lifetime.start.p0i8(i64 28, ptr %0) #4, !dbg !30 + %1 = bitcast ptr %S to ptr, !dbg !31 + call void @llvm.memset.p0i8.i64(ptr 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 ptr %1, metadata !DIExpression()), !dbg !31 + %2 = load i32, ptr @Glob, align 4, !dbg !33 + %Var = getelementptr inbounds %struct.LargeStruct, ptr %S, i32 0, i32 3, !dbg !38 + store i32 %2, ptr %Var, align 4, !dbg !39, !DIAssignID !42 + call void @llvm.dbg.assign(metadata i32 %2, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !42, metadata ptr %Var, metadata !DIExpression()), !dbg !39 + %Var1 = getelementptr inbounds %struct.LargeStruct, ptr %S, i32 0, i32 3, !dbg !43 + %3 = load i32, ptr %Var1, align 4, !dbg !43 + %4 = bitcast ptr %S to ptr, !dbg !44 + call void @llvm.lifetime.end.p0i8(i64 28, ptr %4) #4, !dbg !44 + ret i32 %3, !dbg !45 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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 = !{!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) +!38 = !DILocation(line: 11, column: 5, scope: !14) +!39 = !DILocation(line: 11, column: 9, scope: !14) +!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) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/store.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/store.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/store.ll @@ -0,0 +1,150 @@ +; RUN: opt -passes=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 ptr %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 ptr %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{{.*}}(ptr align 8 %S.sroa.0, i8 0, i64 12, i1 false), !dbg !{{.+}}, !DIAssignID ![[ID_4:[0-9]+]] +; CHECK-NEXT: call void @llvm.memset{{.*}}(ptr align 8 %S.sroa.6, 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 ptr %S.sroa.0, 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 ptr 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 ptr %S.sroa.6, 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, ptr @Glob, align 4, !dbg !{{.+}} +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %0, metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !{{.+}}, metadata ptr 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 ptr %S, metadata !DIExpression()), !dbg !29 + %agg.tmp = alloca %struct.LargeStruct, align 8 + %0 = bitcast ptr %S to ptr, !dbg !30 + call void @llvm.lifetime.start.p0i8(i64 28, ptr %0) #5, !dbg !30 + %1 = bitcast ptr %S to ptr, !dbg !31 + call void @llvm.memset.p0i8.i64(ptr 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 ptr %1, metadata !DIExpression()), !dbg !31 + %2 = load i32, ptr @Glob, align 4, !dbg !33 + %Var = getelementptr inbounds %struct.LargeStruct, ptr %S, i32 0, i32 3, !dbg !38 + store i32 %2, ptr %Var, align 4, !dbg !39, !DIAssignID !42 + call void @llvm.dbg.assign(metadata i32 %2, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 96, 32), metadata !42, metadata ptr %Var, metadata !DIExpression()), !dbg !39 + %3 = bitcast ptr %agg.tmp to ptr, !dbg !43 + %4 = bitcast ptr %S to ptr, !dbg !43 + call void @llvm.memcpy.p0i8.p0i8.i64(ptr align 4 %3, ptr align 4 %4, i64 28, i1 false), !dbg !43 + %call = call i32 @_Z3use11LargeStruct(ptr byval(%struct.LargeStruct) align 8 %agg.tmp), !dbg !45 + %Var1 = getelementptr inbounds %struct.LargeStruct, ptr %S, i32 0, i32 3, !dbg !46 + %5 = load i32, ptr %Var1, align 4, !dbg !46 + %6 = bitcast ptr %S to ptr, !dbg !47 + call void @llvm.lifetime.end.p0i8(i64 28, ptr %6) #5, !dbg !47 + ret i32 %5, !dbg !48 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #2 + +declare !dbg !49 dso_local i32 @_Z3use11LargeStruct(ptr byval(%struct.LargeStruct) align 8) #3 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr nocapture) #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #4 + +!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) +!38 = !DILocation(line: 12, column: 5, scope: !14) +!39 = !DILocation(line: 12, column: 9, scope: !14) +!42 = distinct !DIAssignID() +!43 = !DILocation(line: 13, column: 7, scope: !14) +!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} diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/unspecified-var-size.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/unspecified-var-size.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/unspecified-var-size.ll @@ -0,0 +1,54 @@ +; RUN: opt -S %s -passes=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 ptr %0,{{.+}}, metadata !DIExpression(),{{.+}}, metadata ptr undef, {{.+}}) +;; There should be no new fragment and the value component should remain as %0. + +define dso_local void @_Z3funDn(ptr %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 ptr %.addr, metadata !DIExpression()), !dbg !23 + store ptr %0, ptr %.addr, align 8, !DIAssignID !28 + call void @llvm.dbg.assign(metadata ptr %0, metadata !21, metadata !DIExpression(), metadata !28, metadata ptr %.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: "clang/12.0.0/include/__stddef_max_align_t.h", directory: "/") +!8 = !DICompositeType(tag: DW_TAG_structure_type, file: !7, line: 19, size: 256, flags: DIFlagFwdDecl, identifier: "_ZTS11max_align_t") +!9 = !DIFile(filename: "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: "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) +!28 = distinct !DIAssignID() +!29 = !DILocation(line: 20, column: 27, scope: !14) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/user-memcpy.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/user-memcpy.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/user-memcpy.ll @@ -0,0 +1,235 @@ +; RUN: opt -passes=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 ptr 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 ptr 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 ptr 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 ptr 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, ptr @__const._Z3funv.other +; CHECK-NEXT: %other.sroa.4.0.copyload = load i64, ptr getelementptr inbounds (i8, ptr @__const._Z3funv.other, i64 8) +; CHECK-NEXT: %other.sroa.5.0.copyload = load i64, ptr getelementptr inbounds (i8, ptr @__const._Z3funv.other, i64 16) +; 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 ptr 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 ptr 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 ptr 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 ptr 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 ptr 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 ptr %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 ptr %other, metadata !DIExpression()), !dbg !113 + %0 = bitcast ptr %point to ptr, !dbg !115 + call void @llvm.lifetime.start.p0i8(i64 24, ptr %0), !dbg !115 + %1 = bitcast ptr %point to ptr, !dbg !116 + call void @llvm.memset.p0i8.i64(ptr 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 ptr %1, metadata !DIExpression()), !dbg !116 + %z = getelementptr inbounds %struct.V3i, ptr %point, i32 0, i32 2, !dbg !118 + store i64 5000, ptr %z, align 8, !dbg !119, !DIAssignID !125 + call void @llvm.dbg.assign(metadata i64 5000, metadata !104, metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64), metadata !125, metadata ptr %z, metadata !DIExpression()), !dbg !119 + %2 = bitcast ptr %other to ptr, !dbg !126 + call void @llvm.lifetime.start.p0i8(i64 24, ptr %2), !dbg !126 + %3 = bitcast ptr %other to ptr, !dbg !127 + call void @llvm.memcpy.p0i8.p0i8.i64(ptr align 8 %3, ptr align 8 bitcast (ptr @__const._Z3funv.other to ptr), i64 24, i1 false), !dbg !127, !DIAssignID !128 + call void @llvm.dbg.assign(metadata i1 undef, metadata !111, metadata !DIExpression(), metadata !128, metadata ptr %3, metadata !DIExpression()), !dbg !127 + %y = getelementptr inbounds %struct.V3i, ptr %point, i32 0, i32 1, !dbg !129 + %4 = bitcast ptr %y to ptr, !dbg !130 + %x = getelementptr inbounds %struct.V3i, ptr %other, i32 0, i32 0, !dbg !131 + %5 = bitcast ptr %x to ptr, !dbg !130 + call void @llvm.memcpy.p0i8.p0i8.i64(ptr align 8 %4, ptr 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 ptr %4, metadata !DIExpression()), !dbg !130 + %6 = bitcast ptr %other to ptr, !dbg !133 + call void @llvm.lifetime.end.p0i8(i64 24, ptr %6), !dbg !133 + %7 = bitcast ptr %point to ptr, !dbg !133 + call void @llvm.lifetime.end.p0i8(i64 24, ptr %7), !dbg !133 + ret void, !dbg !133 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr nocapture) +declare void @llvm.memset.p0i8.i64(ptr nocapture writeonly, i8, i64, i1 immarg) +declare void @llvm.memcpy.p0i8.p0i8.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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) +!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) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-1.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-1.ll @@ -0,0 +1,104 @@ +; RUN: opt %s -S -passes=sroa -o - -experimental-assignment-tracking | FileCheck %s + +;; 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(ptr %this) #0 align 2 !dbg !7 { +entry: + %this.addr = alloca ptr, align 8, !DIAssignID !29 + call void @llvm.dbg.assign(metadata i1 undef, metadata !26, metadata !DIExpression(), metadata !29, metadata ptr %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 ptr %e, metadata !DIExpression()), !dbg !30 + store ptr %this, ptr %this.addr, align 8, !DIAssignID !36 + call void @llvm.dbg.assign(metadata ptr %this, metadata !26, metadata !DIExpression(), metadata !36, metadata ptr %this.addr, metadata !DIExpression()), !dbg !30 + %this1 = load ptr, ptr %this.addr, align 8 + %0 = bitcast ptr %e to ptr, !dbg !37 + call void @llvm.lifetime.start.p0i8(i64 16, ptr %0) #4, !dbg !37 + %call = call { <2 x float>, <2 x float> } @_ZNK1c5m_fn1Ev(ptr %this1), !dbg !38 + %coerce.dive = getelementptr inbounds %class.a, ptr %e, i32 0, i32 0, !dbg !38 + %1 = bitcast ptr %coerce.dive to ptr, !dbg !38 + %2 = getelementptr inbounds { <2 x float>, <2 x float> }, ptr %1, i32 0, i32 0, !dbg !38 + %3 = extractvalue { <2 x float>, <2 x float> } %call, 0, !dbg !38 + store <2 x float> %3, ptr %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 ptr %2, metadata !DIExpression()), !dbg !30 + %4 = getelementptr inbounds { <2 x float>, <2 x float> }, ptr %1, i32 0, i32 1, !dbg !38 + %5 = extractvalue { <2 x float>, <2 x float> } %call, 1, !dbg !38 + store <2 x float> %5, ptr %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 ptr %4, metadata !DIExpression()), !dbg !30 + %6 = bitcast ptr %e to ptr, !dbg !41 + call void @llvm.lifetime.end.p0i8(i64 16, ptr %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, ptr nocapture) #2 +declare dso_local { <2 x float>, <2 x float> } @_ZNK1c5m_fn1Ev(ptr) #3 +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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() +!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) diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-2.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-2.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-2.ll @@ -0,0 +1,206 @@ +; RUN: opt %s -S -passes=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 ptr 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 ptr 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 ptr 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 ptr 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 ptr 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 ptr %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 ptr 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 ptr 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 ptr 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 ptr undef, metadata !DIExpression()), !dbg !33 + %2 = bitcast ptr %i to ptr, !dbg !43 + call void @llvm.lifetime.start.p0i8(i64 16, ptr nonnull %2) #5, !dbg !43 + %h.sroa.0.sroa.0.0.h.sroa.0.0..sroa_cast4.sroa_cast = bitcast ptr %i to ptr, !dbg !44 + store <2 x float> %0, ptr %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 ptr %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, ptr %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 ptr %h.sroa.0.sroa.4.0.h.sroa.0.0..sroa_cast4.sroa_idx13 to ptr, !dbg !44 + store <2 x float> %1, ptr %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 ptr %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 ptr undef, metadata !DIExpression()), !dbg !52 + call void @llvm.dbg.assign(metadata ptr %i, metadata !47, metadata !DIExpression(), metadata !54, metadata ptr undef, metadata !DIExpression()), !dbg !52 + %3 = bitcast ptr %ref.tmp to ptr, !dbg !55 + call void @llvm.lifetime.start.p0i8(i64 16, ptr nonnull %3) #5, !dbg !55 + call void @llvm.dbg.assign(metadata i1 undef, metadata !56, metadata !DIExpression(), metadata !59, metadata ptr undef, metadata !DIExpression()), !dbg !60 + call void @llvm.dbg.assign(metadata ptr %ref.tmp, metadata !56, metadata !DIExpression(), metadata !62, metadata ptr undef, metadata !DIExpression()), !dbg !60 + %4 = load float, ptr @c, align 4, !dbg !63 + %arrayidx.i = getelementptr inbounds %class.d, ptr %ref.tmp, i64 0, i32 0, i32 0, i64 3, !dbg !69 + store float %4, ptr %arrayidx.i, align 4, !dbg !70 + call void @llvm.memcpy.p0i8.p0i8.i64(ptr nonnull align 8 dereferenceable(16) %2, ptr 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 ptr %2, metadata !DIExpression()), !dbg !33 + call void @llvm.lifetime.end.p0i8(i64 16, ptr nonnull %3) #5, !dbg !73 + call void @llvm.lifetime.end.p0i8(i64 16, ptr nonnull %2) #5, !dbg !74 + ret void, !dbg !74 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, ptr 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(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local void @_ZN1d1eEv(ptr %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 ptr undef, metadata !DIExpression()), !dbg !79 + call void @llvm.dbg.assign(metadata ptr %this, metadata !47, metadata !DIExpression(), metadata !80, metadata ptr undef, metadata !DIExpression()), !dbg !79 + ret void, !dbg !81 +} + +; Function Attrs: nounwind uwtable +define linkonce_odr dso_local void @_ZN1dC2Ev(ptr %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 ptr undef, metadata !DIExpression()), !dbg !83 + call void @llvm.dbg.assign(metadata ptr %this, metadata !56, metadata !DIExpression(), metadata !84, metadata ptr undef, metadata !DIExpression()), !dbg !83 + %0 = load float, ptr @c, align 4, !dbg !85 + %arrayidx = getelementptr inbounds %class.d, ptr %this, i64 0, i32 0, i32 0, i64 3, !dbg !86 + store float %0, ptr %arrayidx, align 4, !dbg !87 + ret void, !dbg !88 +} + +declare void @llvm.lifetime.end.p0i8(i64 immarg, ptr 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) +!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) +