Index: llvm/lib/Transforms/Scalar/SROA.cpp =================================================================== --- llvm/lib/Transforms/Scalar/SROA.cpp +++ llvm/lib/Transforms/Scalar/SROA.cpp @@ -113,6 +113,117 @@ cl::Hidden); namespace { +constexpr uint64_t ByteWidth = 8; +static uint64_t bitsToBytes(uint64_t Bits) { + assert(Bits % 8 == 0 && "unexpected bit count"); + return Bits / ByteWidth; +} +static uint64_t bytesToBits(uint64_t Bytes) { + assert(Bytes <= (UINT64_MAX / ByteWidth) && "too many bytes"); + return Bytes * ByteWidth; +} + +/// Find linked dbg.assign and generate a new one with the correct +/// FragmentInfo. Link Inst to the new dbg.assign. If Value is nullptr the +/// value component is copied from the old dbg.assign to the new. +/// \param OldAlloca Alloca for the variable before splitting. +/// \param RelativeOffsetInBytes Offset into \p OldAlloca relative to the +/// offset prior to splitting (change in offset). +/// \param SliceSizeInBytes New number of bytes being written to. +/// \param OldInst Instruction that is being split. +/// \param Inst New instruction performing this part of the +/// split store. +/// \param Dest Store destination. +/// \param Value Stored value. +/// \param DL Datalayout. +static void migrateDebugInfo(AllocaInst *OldAlloca, + uint64_t RelativeOffsetInBytes, + uint64_t SliceSizeInBytes, Instruction *OldInst, + Instruction *Inst, Value *Dest, Value *Value, + const DataLayout &DL) { + uint64_t RelativeOffset = bytesToBits(RelativeOffsetInBytes); + uint64_t SliceSize = bytesToBits(SliceSizeInBytes); + + LLVM_DEBUG(dbgs() << " migrateDebugInfo\n"); + LLVM_DEBUG(dbgs() << " OldAlloca: " << *OldAlloca << "\n"); + LLVM_DEBUG(dbgs() << " RelativeOffset: " << RelativeOffset << "\n"); + LLVM_DEBUG(dbgs() << " SliceSize: " << SliceSize << "\n"); + LLVM_DEBUG(dbgs() << " OldInst: " << *OldInst << "\n"); + LLVM_DEBUG(dbgs() << " Inst: " << *Inst << "\n"); + LLVM_DEBUG(dbgs() << " Dest: " << *Dest << "\n"); + if (Value) + LLVM_DEBUG(dbgs() << " Value: " << *Value << "\n"); + + // The new inst needs a DIAssignID unique metadata tag (if OldInst has + // one). It shouldn't already have one: assert this assumption. + assert(!Inst->getMetadata(LLVMContext::MD_DIAssignID)); + DIAssignID *NewID = nullptr; + auto &Ctx = Inst->getContext(); + DIBuilder DIB(*OldInst->getModule(), /*AllowUnresolved*/ false); + uint64_t AllocaSize = *OldAlloca->getAllocationSizeInBits(DL); + assert(OldAlloca->isStaticAlloca()); + + for (DbgAssignIntrinsic *DbgAssign : at::getAssignmentMarkers(OldInst)) { + LLVM_DEBUG(dbgs() << " existing dbg.assign is: " << *DbgAssign + << "\n"); + auto *Expr = DbgAssign->getExpression(); + + // Check if the dbg.assign already describes a fragment. + auto GetCurrentFragSize = [AllocaSize, DbgAssign, Expr]() -> uint64_t { + if (auto FI = Expr->getFragmentInfo()) + return FI->SizeInBits; + if (auto VarSize = DbgAssign->getVariable()->getSizeInBits()) + return *VarSize; + // The variable type has an unspecified size. This can happen in the + // case of DW_TAG_unspecified_type types, e.g. std::nullptr_t. Because + // there is no fragment and we do not know the size of the variable type, + // we'll guess by looking at the alloca. + return AllocaSize; + }; + uint64_t CurrentFragSize = GetCurrentFragSize(); + bool MakeNewFragment = CurrentFragSize != SliceSize; + assert(MakeNewFragment || RelativeOffset == 0); + + assert(SliceSize <= AllocaSize); + if (MakeNewFragment) { + assert(RelativeOffset + SliceSize <= CurrentFragSize); + auto E = DIExpression::createFragmentExpression(Expr, RelativeOffset, + SliceSize); + assert(E && "Failed to create fragment expr!"); + Expr = *E; + } + + // If we haven't created a DIAssignID ID do that now and attach it to Inst. + if (!NewID) { + NewID = DIAssignID::getDistinct(Ctx); + Inst->setMetadata(LLVMContext::MD_DIAssignID, NewID); + } + + Value = Value ? Value : DbgAssign->getValue(); + auto *NewAssign = DIB.insertDbgAssign( + Inst, Value, DbgAssign->getVariable(), Expr, Dest, + DIExpression::get(Ctx, None), DbgAssign->getDebugLoc()); + + // We could use more precision here at the cost of some additional (code) + // complexity - if the original dbg.assign was adjacent to its store, we + // could position this new dbg.assign adjacent to its store rather than the + // old dbg.assgn. That would result in interleaved dbg.assigns rather than + // what we get now: + // split store !1 + // split store !2 + // dbg.assign !1 + // dbg.assign !2 + // This (current behaviour) results results in debug assignments being + // noted as slightly offset (in code) from the store. In practice this + // should have little effect on the debugging experience due to the fact + // that all the split stores should get the same line number. + NewAssign->moveBefore(DbgAssign); + + NewAssign->setDebugLoc(DbgAssign->getDebugLoc()); + LLVM_DEBUG(dbgs() << "Created new assign intrinsic: " << *NewAssign + << "\n"); + } +} /// A custom IRBuilder inserter which prefixes all names, but only in /// Assert builds. @@ -2321,6 +2432,7 @@ // original alloca. uint64_t NewBeginOffset = 0, NewEndOffset = 0; + uint64_t RelativeOffset = 0; uint64_t SliceSize = 0; bool IsSplittable = false; bool IsSplit = false; @@ -2384,8 +2496,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()); @@ -2623,6 +2741,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); @@ -2648,6 +2769,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; } @@ -2670,6 +2794,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; @@ -2742,6 +2870,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); @@ -2797,7 +2929,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; } @@ -2830,11 +2966,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; } @@ -2906,6 +3046,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(); } @@ -2923,7 +3067,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 @@ -2934,10 +3077,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); } @@ -3026,6 +3175,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; } @@ -3104,6 +3256,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(); } @@ -3441,12 +3596,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) { @@ -3468,6 +3624,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"); } }; @@ -3481,7 +3656,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); @@ -4082,7 +4257,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"); } @@ -4543,6 +4719,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); @@ -4562,9 +4740,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. @@ -4606,8 +4785,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; @@ -4725,6 +4919,11 @@ OldDII->eraseFromParent(); } + for (DbgAssignIntrinsic *DAI : at::getAssignmentMarkers(I)) { + LLVM_DEBUG(dbgs() << " And deleting: " << *DAI << "\n"); + DAI->eraseFromParent(); + } + I->replaceAllUsesWith(UndefValue::get(I->getType())); for (Use &Operand : I->operands()) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/after-inlining.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/after-inlining.ll @@ -0,0 +1,148 @@ +; RUN: opt %s -S -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, !tbaa !31 + store i32 1, ptr @f, align 4, !dbg !80, !tbaa !31 + store i32 %1, ptr @e, align 4, !dbg !81, !tbaa !31 + 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) +!31 = !{!32, !32, i64 0} +!32 = !{!"int", !33, i64 0} +!33 = !{!"omnipotent char", !34, i64 0} +!34 = !{!"Simple C/C++ TBAA"} +!35 = !DILocation(line: 7, column: 7, scope: !15) +!36 = !DILocation(line: 8, column: 8, scope: !30) +!37 = !DILocation(line: 8, column: 10, scope: !30) +!38 = !DILocation(line: 8, column: 5, scope: !30) +!39 = !DILocation(line: 9, column: 1, scope: !15) +!40 = distinct !DISubprogram(name: "i", scope: !3, file: !3, line: 10, type: !16, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !41) +!41 = !{!42, !43} +!42 = !DILocalVariable(name: "h", arg: 1, scope: !40, file: !3, line: 10, type: !18) +!43 = !DILocalVariable(name: "j", scope: !40, file: !3, line: 11, type: !44) +!44 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!45 = !DILocation(line: 0, scope: !40) +!46 = !DILocation(line: 12, column: 17, scope: !47) +!47 = distinct !DILexicalBlock(scope: !48, file: !3, line: 12, column: 3) +!48 = distinct !DILexicalBlock(scope: !40, file: !3, line: 12, column: 3) +!49 = !DILocation(line: 12, column: 12, scope: !47) +!50 = !DILocation(line: 12, column: 3, scope: !48) +!51 = !DILocation(line: 13, column: 10, scope: !47) +!52 = !DILocation(line: 13, column: 7, scope: !47) +!53 = !DILocation(line: 12, column: 21, scope: !47) +!54 = distinct !{!54, !50, !55, !56} +!55 = !DILocation(line: 13, column: 16, scope: !48) +!56 = !{!"llvm.loop.mustprogress"} +!57 = !DILocation(line: 14, column: 7, scope: !40) +!58 = !DILocation(line: 14, column: 5, scope: !40) +!59 = !DILocation(line: 15, column: 1, scope: !40) +!60 = distinct !DISubprogram(name: "k", scope: !3, file: !3, line: 16, type: !61, scopeLine: 16, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !63) +!61 = !DISubroutineType(types: !62) +!62 = !{null} +!63 = !{!64} +!64 = !DILocalVariable(name: "j", scope: !60, file: !3, line: 17, type: !19) +!65 = distinct !DIAssignID() +!66 = !DILocation(line: 0, scope: !60) +!67 = !DILocation(line: 17, column: 3, scope: !60) +!68 = !DILocation(line: 13, column: 10, scope: !47, inlinedAt: !69) +!69 = distinct !DILocation(line: 19, column: 3, scope: !60) +!70 = !DILocation(line: 0, scope: !40, inlinedAt: !69) +!71 = !DILocation(line: 14, column: 5, scope: !40, inlinedAt: !69) +!72 = !DILocation(line: 20, column: 1, scope: !60) +!73 = distinct !DISubprogram(name: "l", scope: !3, file: !3, line: 21, type: !61, scopeLine: 21, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4) +!74 = distinct !DIAssignID() +!75 = !DILocation(line: 0, scope: !60, inlinedAt: !76) +!76 = distinct !DILocation(line: 21, column: 12, scope: !73) +!77 = !DILocation(line: 17, column: 3, scope: !60, inlinedAt: !76) +!78 = !DILocation(line: 13, column: 10, scope: !47, inlinedAt: !79) +!79 = distinct !DILocation(line: 19, column: 3, scope: !60, inlinedAt: !76) +!80 = !DILocation(line: 0, scope: !40, inlinedAt: !79) +!81 = !DILocation(line: 14, column: 5, scope: !40, inlinedAt: !79) +!82 = !DILocation(line: 20, column: 1, scope: !60, inlinedAt: !76) +!83 = !DILocation(line: 21, column: 17, scope: !73) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/alloca-single-slice.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/alloca-single-slice.ll @@ -0,0 +1,86 @@ +; 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, !tbaa !29 + %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) +!29 = !{!30, !30, i64 0} +!30 = !{!"int", !31, i64 0} +!31 = !{!"omnipotent char", !32, i64 0} +!32 = !{!"Simple C/C++ TBAA"} +!33 = !DILocation(line: 8, column: 1, scope: !11) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/complex.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/complex.ll @@ -0,0 +1,69 @@ +; RUN: opt -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) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag-2.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag-2.ll @@ -0,0 +1,269 @@ +; RUN: opt -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, !tbaa.struct !43 + %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, !tbaa.struct !43 + %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, !tbaa.struct !43 + %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, !tbaa.struct !43 + 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, !tbaa.struct !43 + %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, !tbaa.struct !43 + ret void, !dbg !56 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +; Function Attrs: uwtable +define dso_local void @_Z1jv() local_unnamed_addr #4 !dbg !57 { +entry: + %convexbody = alloca %class.h, align 1, !DIAssignID !73 + call void @llvm.dbg.assign(metadata i1 undef, metadata !61, metadata !DIExpression(), metadata !73, metadata 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, !tbaa.struct !43, !noalias !86 + %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, !tbaa.struct !43, !noalias !86 + %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, !tbaa.struct !43, !alias.scope !86 + %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, !tbaa.struct !43, !alias.scope !86 + %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, !tbaa.struct !43, !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 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +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) +!43 = !{i64 0, i64 16, !44} +!44 = !{!45, !45, i64 0} +!45 = !{!"omnipotent char", !46, i64 0} +!46 = !{!"Simple C++ TBAA"} +!47 = !DILocation(line: 8, column: 12, scope: !48, inlinedAt: !53) +!48 = distinct !DISubprogram(name: "B", linkageName: "_ZN1BC2E1a", scope: !8, file: !1, line: 8, type: !25, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !24, retainedNodes: !49) +!49 = !{!50, !52} +!50 = !DILocalVariable(name: "this", arg: 1, scope: !48, type: !51, flags: DIFlagArtificial | DIFlagObjectPointer) +!51 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) +!52 = !DILocalVariable(name: "d", arg: 2, scope: !48, file: !1, line: 8, type: !13) +!53 = distinct !DILocation(line: 14, column: 41, scope: !7) +!54 = !DILocation(line: 14, column: 34, scope: !7) +!55 = !DILocation(line: 8, column: 12, scope: !48) +!56 = !DILocation(line: 8, column: 18, scope: !48) +!57 = distinct !DISubprogram(name: "j", linkageName: "_Z1jv", scope: !1, file: !1, line: 19, type: !58, scopeLine: 19, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !60) +!58 = !DISubroutineType(types: !59) +!59 = !{null} +!60 = !{!61, !68, !69, !70, !71, !72} +!61 = !DILocalVariable(name: "convexbody", scope: !57, file: !1, line: 20, type: !62) +!62 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "h", file: !1, line: 15, size: 8, flags: DIFlagTypePassByValue, elements: !63, identifier: "_ZTS1h") +!63 = !{!64} +!64 = !DISubprogram(name: "i", linkageName: "_ZN1h1iEv", scope: !62, file: !1, line: 17, type: !65, scopeLine: 17, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!65 = !DISubroutineType(types: !66) +!66 = !{!8, !67} +!67 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !62, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!68 = !DILocalVariable(name: "k", scope: !57, file: !1, line: 20, type: !62) +!69 = !DILocalVariable(name: "l", scope: !57, file: !1, line: 21, type: !8) +!70 = !DILocalVariable(name: "m", scope: !57, file: !1, line: 21, type: !8) +!71 = !DILocalVariable(name: "n", scope: !57, file: !1, line: 21, type: !8) +!72 = !DILocalVariable(name: "o", scope: !57, file: !1, line: 22, type: !13) +!73 = distinct !DIAssignID() +!74 = !DILocation(line: 0, scope: !57) +!75 = distinct !DIAssignID() +!76 = distinct !DIAssignID() +!77 = distinct !DIAssignID() +!78 = distinct !DIAssignID() +!79 = distinct !DIAssignID() +!80 = !DILocation(line: 20, column: 3, scope: !57) +!81 = !DILocation(line: 21, column: 3, scope: !57) +!82 = !DILocation(line: 21, column: 11, scope: !57) +!83 = !DILocation(line: 21, column: 31, scope: !57) +!84 = !DILocation(line: 14, column: 41, scope: !7, inlinedAt: !85) +!85 = distinct !DILocation(line: 21, column: 42, scope: !57) +!86 = !{!87} +!87 = distinct !{!87, !88, !"_ZNK1BmlERKS_: %agg.result"} +!88 = distinct !{!88, !"_ZNK1BmlERKS_"} +!89 = !DILocation(line: 8, column: 12, scope: !48, inlinedAt: !90) +!90 = distinct !DILocation(line: 14, column: 41, scope: !7, inlinedAt: !85) +!91 = !DILocation(line: 22, column: 3, scope: !57) +!92 = !DILocation(line: 9, column: 19, scope: !93, inlinedAt: !96) +!93 = distinct !DISubprogram(name: "f", linkageName: "_ZN1B1fEv", scope: !8, file: !1, line: 9, type: !29, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !28, retainedNodes: !94) +!94 = !{!95} +!95 = !DILocalVariable(name: "this", arg: 1, scope: !93, type: !51, flags: DIFlagArtificial | DIFlagObjectPointer) +!96 = distinct !DILocation(line: 22, column: 11, scope: !57) +!97 = !DILocation(line: 22, column: 9, scope: !57) +!98 = distinct !DIAssignID() +!99 = !DILocation(line: 23, column: 5, scope: !57) +!100 = !DILocation(line: 24, column: 1, scope: !57) +!101 = !DILocation(line: 9, column: 19, scope: !93) +!102 = !DILocation(line: 9, column: 12, scope: !93) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/frag.ll @@ -0,0 +1,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} Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/id.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/id.ll @@ -0,0 +1,180 @@ +; RUN: opt -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, !tbaa !26 + %tobool.not = icmp eq i32 %1, 0, !dbg !24 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !30 + +if.then: ; preds = %entry + %2 = load float, ptr @b, align 4, !dbg !31, !tbaa !32 + 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, !tbaa !32 + %4 = load float, ptr @d, align 4, !dbg !37, !tbaa !32 + %div = fdiv float %3, %4, !dbg !38 + call void @llvm.dbg.assign(metadata float %div, metadata !20, metadata !DIExpression(), metadata !34, metadata 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, !tbaa !32, !DIAssignID !34 + %5 = load i32, ptr @c, align 4, !dbg !40, !tbaa !26 + 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) +!26 = !{!27, !27, i64 0} +!27 = !{!"int", !28, i64 0} +!28 = !{!"omnipotent char", !29, i64 0} +!29 = !{!"Simple C++ TBAA"} +!30 = !DILocation(line: 9, column: 7, scope: !16) +!31 = !DILocation(line: 10, column: 9, scope: !25) +!32 = !{!33, !33, i64 0} +!33 = !{!"float", !28, i64 0} +!34 = distinct !DIAssignID() +!35 = !DILocation(line: 10, column: 5, scope: !25) +!36 = !DILocation(line: 12, column: 9, scope: !25) +!37 = !DILocation(line: 12, column: 13, scope: !25) +!38 = !DILocation(line: 12, column: 11, scope: !25) +!39 = !DILocation(line: 0, scope: !25) +!40 = !DILocation(line: 13, column: 5, scope: !16) +!41 = !DILocalVariable(name: "this", arg: 1, scope: !42, type: !53, flags: DIFlagArtificial | DIFlagObjectPointer) +!42 = distinct !DISubprogram(name: "a", linkageName: "_ZN1aC2EiRf", scope: !43, file: !3, line: 3, type: !46, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, declaration: !45, retainedNodes: !50) +!43 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !3, line: 1, size: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !44, identifier: "_ZTS1a") +!44 = !{!45} +!45 = !DISubprogram(name: "a", scope: !43, file: !3, line: 3, type: !46, scopeLine: 3, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!46 = !DISubroutineType(types: !47) +!47 = !{null, !48, !11, !49} +!48 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!49 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !8, size: 64) +!50 = !{!41, !51, !52} +!51 = !DILocalVariable(arg: 2, scope: !42, file: !3, line: 3, type: !11) +!52 = !DILocalVariable(arg: 3, scope: !42, file: !3, line: 3, type: !49) +!53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !43, size: 64) +!54 = distinct !DIAssignID() +!55 = !DILocation(line: 0, scope: !42, inlinedAt: !56) +!56 = distinct !DILocation(line: 13, column: 3, scope: !16) +!57 = distinct !DIAssignID() +!58 = distinct !DIAssignID() +!59 = distinct !DIAssignID() +!60 = distinct !DIAssignID() +!61 = distinct !DIAssignID() +!62 = !DILocation(line: 14, column: 1, scope: !16) +!63 = distinct !DIAssignID() +!64 = !DILocation(line: 0, scope: !42) +!65 = distinct !DIAssignID() +!66 = distinct !DIAssignID() +!67 = distinct !DIAssignID() +!68 = distinct !DIAssignID() +!69 = distinct !DIAssignID() +!70 = !DILocation(line: 3, column: 20, scope: !42) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memcpy.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memcpy.ll @@ -0,0 +1,124 @@ +; RUN: opt -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, !tbaa.struct !29, !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, !tbaa !36 + %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) +!29 = !{i64 0, i64 4, !30, i64 4, i64 4, !30, i64 8, i64 4, !30, i64 12, i64 4, !30, i64 16, i64 4, !30, i64 20, i64 4, !30, i64 24, i64 4, !30} +!30 = !{!31, !31, i64 0} +!31 = !{!"int", !32, i64 0} +!32 = !{!"omnipotent char", !33, i64 0} +!33 = !{!"Simple C++ TBAA"} +!34 = distinct !DIAssignID() +!35 = !DILocation(line: 9, column: 13, scope: !20) +!36 = !{!37, !31, i64 12} +!37 = !{!"_ZTS11LargeStruct", !31, i64 0, !31, i64 4, !31, i64 8, !31, i64 12, !31, i64 16, !31, i64 20, !31, i64 24} +!38 = !DILocation(line: 10, column: 1, scope: !20) +!39 = !DILocation(line: 9, column: 3, scope: !20) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/memmove-to-from-same-alloca.ll =================================================================== --- /dev/null +++ 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) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/rewrite.ll =================================================================== --- /dev/null +++ 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) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/store.ll =================================================================== --- /dev/null +++ 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} Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/tmp.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/tmp.ll @@ -0,0 +1,99 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=sroa -S | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-n8:16:32:64" + +%S1 = type { i64, [42 x float] } + +declare void @llvm.memcpy.p0.p1.i32(ptr nocapture, ptr addrspace(1) nocapture, i32, i1) nounwind +declare void @llvm.memcpy.p0.p0.i32(ptr nocapture, ptr nocapture, i32, i1) nounwind +declare void @llvm.memset.p0.i32(ptr nocapture, i8, i32, i1) nounwind +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) +declare void @ext() +declare void @esc(ptr) +;@g2 = dso_local global <4 x i32> zeroinitializer, align 16, !dbg !45 +;@g = dso_local global ptr zeroinitializer, align 16 + +define i32 @test5(<16 x i32> %x, <16 x i32> %y, ptr %z) !dbg !10 { +; CHECK-LABEL: @test5( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[Z_TMP1:%.*]] = getelementptr inbounds <16 x i32>, ptr [[Z:%.*]], i64 0, i64 2 +; CHECK-NEXT: [[A_SROA_0_8_VEC_EXTRACT3:%.*]] = extractelement <16 x i32> [[Y:%.*]], i32 2 +; CHECK-NEXT: store i32 [[A_SROA_0_8_VEC_EXTRACT3]], ptr [[Z_TMP1]], align 1 +; CHECK-NEXT: [[A_SROA_0_8_VEC_EXTRACT:%.*]] = extractelement <16 x i32> [[Y]], i32 2 +; CHECK-NEXT: [[A_SROA_4_12_VEC_EXTRACT:%.*]] = extractelement <16 x i32> [[Y]], i32 3 +; CHECK-NEXT: [[A_SROA_4_0_VEC_EXTRACT:%.*]] = extractelement <16 x i32> [[Y]], i32 0 +; CHECK-NEXT: [[TMP4:%.*]] = add i32 [[A_SROA_0_8_VEC_EXTRACT]], [[A_SROA_4_12_VEC_EXTRACT]] +; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[A_SROA_4_0_VEC_EXTRACT]], [[TMP4]] +; CHECK-NEXT: ret i32 [[TMP5]] +; +; The same as the above, but with reversed source and destination for the +; element memcpy, and a self copy. +entry: + %a = alloca [2 x <16 x i32>], !DIAssignID !14 + call void @llvm.dbg.assign(metadata i1 undef, metadata !28, metadata !DIExpression(), metadata !14, metadata ptr %a, metadata !DIExpression()), !dbg !17 + + store <16 x i32> %x, ptr %a + %a.y = getelementptr inbounds [2 x <16 x i32>], ptr %a, i64 0, i64 1 + store <16 x i32> %y, ptr %a.y + + call void @ext() + + ;call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %a.y, i32 16, i1 false), !DIAssignID !19 + ;call void @llvm.dbg.assign(metadata i1 undef, metadata !28, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 128), metadata !19, metadata ptr %a, metadata !DIExpression()), !dbg !17 + + call void @llvm.memcpy.p0.p0.i32(ptr %a.y, ptr %a, i32 16, i1 false), !DIAssignID !19 + call void @llvm.dbg.assign(metadata i1 undef, metadata !28, metadata !DIExpression(DW_OP_LLVM_fragment, 512, 512), metadata !19, metadata ptr %a, metadata !DIExpression()), !dbg !17 + + %a.tmp1 = getelementptr inbounds [2 x <16 x i32>], ptr %a, i64 0, i64 1, i64 2 + ;%z.tmp1 = getelementptr inbounds <16 x i32>, ptr %z, i64 0, i64 2 + ;call void @llvm.memcpy.p0.p0.i32(ptr %z.tmp1, ptr %a.tmp1, i32 4, i1 false) + %tmp1 = load i32, ptr %a.tmp1 + ;%a.tmp2 = getelementptr inbounds [2 x <16 x i32>], ptr %a, i64 0, i64 1, i64 3 + ;%tmp2 = load i32, ptr %a.tmp2 + ;%a.tmp3 = getelementptr inbounds [2 x <16 x i32>], ptr %a, i64 0, i64 1, i64 0 + ;%tmp3 = load i32, ptr %a.tmp3 + ;%tmp4 = add i32 %tmp1, %tmp2 + ;%tmp5 = add i32 %tmp3, %tmp4 + ;ret i32 %tmp5 + ;%a.tmp2 = getelementptr inbounds [2 x <16 x i32>], ptr %a, i64 0, i64 0, i64 0 + ;%tmp2 = load <16 x i32>, ptr %a.tmp2 + ;store <16 x i32> %tmp2, ptr @g2 + ;store ptr %a.tmp2, ptr @g + ret i32 %tmp1 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 16.0.0", isOptimized: false, 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 = !{i32 7, !"frame-pointer", i32 2} +!9 = !{!"clang version 16.0.0)"} +!10 = distinct !DISubprogram(name: "f", linkageName: "_Z1fv", scope: !1, file: !1, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !13) +!11 = !DISubroutineType(types: !12) +!12 = !{null} +!13 = !{} +!14 = distinct !DIAssignID() +;!15 = !DILocalVariable(name: "a", scope: !10, file: !1, line: 1, type: !16) +!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!17 = !DILocation(line: 0, scope: !10) +!18 = !DILocation(line: 1, column: 19, scope: !10) +!19 = distinct !DIAssignID() +!28 = !DILocalVariable(name: "a", scope: !10, file: !1, line: 3, type: !29) +!29 = !DICompositeType(tag: DW_TAG_array_type, baseType: !32, size: 1024, elements: !30) +!30 = !{!31} +!31 = !DISubrange(count: 2) +!32 = !DIDerivedType(tag: DW_TAG_typedef, name: "int8", file: !1, line: 1, baseType: !33) +!33 = !DICompositeType(tag: DW_TAG_array_type, baseType: !34, size: 512, flags: DIFlagVector, elements: !35) +!34 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!35 = !{!36} +!36 = !DISubrange(count: 16) +;!45 = !DIGlobalVariableExpression(var: !46, expr: !DIExpression()) +;!46 = distinct !DIGlobalVariable(name: "g2", scope: !0, file: !1, line: 2, type: !32, isLocal: false, isDefinition: true) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/unspecified-var-size.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/unspecified-var-size.ll @@ -0,0 +1,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) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/user-memcpy.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/user-memcpy.ll @@ -0,0 +1,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) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-1.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-1.ll @@ -0,0 +1,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) Index: llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-2.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/sroa/vec-2.ll @@ -0,0 +1,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) +