diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp index 915cdd301f2c7..41a675d2f287d 100644 --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -118,30 +118,43 @@ DILocation *DILocation::getMergedLocations(ArrayRef Locs) { return Merged; } -DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) { - if (!LocA || !LocB) - return nullptr; - - if (LocA == LocB) - return LocA; - - LLVMContext &C = LocA->getContext(); - - using LocVec = SmallVector; +/// Merge two locations that may be nested as textual inclusions, +/// e.g. via inlinedAt or nested LexicalBlocks. +/// +/// \param GetLocationKey Function to determine whether locations/scopes are +/// considered matching. +/// \param ShouldStop Function that determines if include chain of +/// location has ended. +/// \param NextLoc Should return next location/object in include chain. +/// \param MergeLocPair Function to merge two possibly matching locations +/// from include chain. +/// \param DefaultLocation Function that returns location of the first matching +/// nested object. +/// \param LocA First incoming location. +/// \param LocA Second incoming location. +template +static DILocation * +MergeNestedLocations(GetLocationKeyFn GetLocationKey, ShouldStopFn ShouldStop, + NextLocFn NextLoc, MergeLocPairFn MergeLocPair, + DefaultLocationFn DefaultLocation, DILocation *LocA, + DILocation *LocB) { + using LocVec = SmallVector; LocVec ALocs; LocVec BLocs; - SmallDenseMap, unsigned, - 4> - ALookup; - // Walk through LocA and its inlined-at locations, populate them in ALocs and - // save the index for the subprogram and inlined-at pair, which we use to find + LLVMContext &C = LocA->getContext(); + + SmallDenseMap ALookup; + // Walk through LocA, populate them in ALocs and + // save the index, which we use to find // a matching starting location in LocB's chain. - for (auto [L, I] = std::make_pair(LocA, 0U); L; L = L->getInlinedAt(), I++) { + using iter_t = std::pair; + for (auto [L, I] = iter_t(LocA, 0U); ShouldStop(L); L = NextLoc(L), I++) { ALocs.push_back(L); - auto Res = ALookup.try_emplace( - {L->getScope()->getSubprogram(), L->getInlinedAt()}, I); - assert(Res.second && "Multiple pairs in a location chain?"); + auto Res = ALookup.try_emplace(GetLocationKey(L), I); + assert(Res.second && "Multiple pairs in a location chain?"); (void)Res; } @@ -149,17 +162,17 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) { LocVec::reverse_iterator BRIt = BLocs.rend(); // Populate BLocs and look for a matching starting location, the first - // location with the same subprogram and inlined-at location as in LocA's - // chain. Since the two locations have the same inlined-at location we do + // location with the same key as in LocA's + // chain. Since the two locations have the same key we do // not need to look at those parts of the chains. - for (auto [L, I] = std::make_pair(LocB, 0U); L; L = L->getInlinedAt(), I++) { + for (auto [L, I] = iter_t(LocB, 0U); ShouldStop(L); L = NextLoc(L), I++) { BLocs.push_back(L); if (ARIt != ALocs.rend()) // We have already found a matching starting location. continue; - auto IT = ALookup.find({L->getScope()->getSubprogram(), L->getInlinedAt()}); + auto IT = ALookup.find(GetLocationKey(L)); if (IT == ALookup.end()) continue; @@ -174,10 +187,140 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) { break; } + DILocation *Result = ARIt != ALocs.rend() ? DefaultLocation(*ARIt) : nullptr; + + // If we have found a common starting location, walk up the chains + // and try to produce common locations. + for (; ARIt != ALocs.rend() && BRIt != BLocs.rend(); ++ARIt, ++BRIt) { + DILocation *Tmp = MergeLocPair(*ARIt, *BRIt, Result); + + if (!Tmp) + // We have walked up to a point in te chains where the two locations + // are irreconsilable. At this point Result contains the nearest common + // location in the location chains of LocA and LocB, so we break here. + break; + + Result = Tmp; + } + + if (auto *ResultL = dyn_cast_if_present(Result)) + return ResultL; + + // We ended up with LocA and LocB as irreconsilable locations. Produce a + // location at 0:0 with one of the locations' scope. + return DILocation::get(C, 0, 0, LocA->getScope(), nullptr); +} + +/// Returns the nearest common scope for two locations inside a subprogram. +static DIScope *GetNearestCommonScope(DILocation *L1, DILocation *L2) { + DIScope *S1 = L1->getScope(); + DIScope *S2 = L2->getScope(); + + // Try matching DILexicalBlockFiles enclosing L1, L2. + if (auto *LBF1 = dyn_cast(S1)) { + if (auto *LBF2 = dyn_cast(S2)) { + if (LBF1->getFile() && LBF1->getFile() == LBF2->getFile() && + LBF1->getDiscriminator() == LBF2->getDiscriminator()) { + return LBF1; + } + } + } + + // Try matching DILexicalBlocks enclosing L1, L2. + if (auto *LB1 = dyn_cast(S1)) { + if (auto *LB2 = dyn_cast(S2)) { + if (LB1->getFile() && LB1->getFile() == LB2->getFile() && + LB1->getLine() == LB2->getLine() && + LB1->getColumn() == LB2->getColumn()) { + return LB1; + } + } + } + + SmallPtrSet Scopes; + for (; S1; S1 = S1->getScope()) { + Scopes.insert(S1); + if (isa(S1)) + break; + } + + for (; S2; S2 = S2->getScope()) { + if (Scopes.count(S2)) + return S2; + if (isa(S2)) + break; + } + + return nullptr; +} + +/// Returns next parent scope having different file within DISubprogram +/// or nullptr. +static DILocalScope *NextScopeWithDifferentFile(MDNode *LocOrScope) { + assert((isa(LocOrScope) || isa(LocOrScope)) && + "DILocation or DILocalScope expected"); + + DIScope *S = nullptr; + DIFile *F = nullptr; + if (auto *Loc = dyn_cast(LocOrScope)) { + S = Loc->getScope(); + F = Loc->getFile(); + } else { + auto *LS = dyn_cast(LocOrScope); + S = LS->getScope(); + F = LS->getFile(); + } + + while (isa_and_present(S) && !isa(S)) { + if (auto *LB = dyn_cast_if_present(S)) { + if (F != LB->getFile()) { + return LB; + } + } + S = S->getScope(); + } + + return nullptr; +} + +/// Get DILocation and it's DIFile from either DILocation, DISubprogram or +/// DILexicalBlock. +static std::pair +GetLocAndFile(LLVMContext &C, MDNode *N, DILocation *InlinedAt) { + DILocation *Loc = nullptr; + DIFile *File = nullptr; + if (auto *InputLoc = dyn_cast(N)) { + Loc = InputLoc; + File = InputLoc->getFile(); + } else if (auto *LB = dyn_cast(N)) { + Loc = DILocation::get(C, LB->getLine(), LB->getColumn(), LB, InlinedAt); + File = LB->getFile(); + } else if (auto *SP = dyn_cast(N)) { + Loc = DILocation::get(C, SP->getLine(), 0, SP, InlinedAt); + File = SP->getFile(); + } + + return std::make_pair(Loc, File); +} + +DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) { + if (!LocA || !LocB) + return nullptr; + + if (LocA == LocB) + return LocA; + + LLVMContext &C = LocA->getContext(); + + auto IsChainOver = [](MDNode *L) -> bool { return L; }; // Merge the two locations if possible, using the supplied // inlined-at location for the created location. - auto MergeLocPair = [&C](const DILocation *L1, const DILocation *L2, - DILocation *InlinedAt) -> DILocation * { + auto MergeInlinedLocations = + [&C, IsChainOver](MDNode *N1, MDNode *N2, + DILocation *InlinedAt) -> DILocation * { + auto *L1 = cast(N1); + auto *L2 = cast(N2); + if (L1 == L2) return DILocation::get(C, L1->getLine(), L1->getColumn(), L1->getScope(), InlinedAt); @@ -187,61 +330,82 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) { if (L1->getScope()->getSubprogram() != L2->getScope()->getSubprogram()) return nullptr; - // Return the nearest common scope inside a subprogram. - auto GetNearestCommonScope = [](DIScope *S1, DIScope *S2) -> DIScope * { - SmallPtrSet Scopes; - for (; S1; S1 = S1->getScope()) { - Scopes.insert(S1); - if (isa(S1)) - break; - } - - for (; S2; S2 = S2->getScope()) { - if (Scopes.count(S2)) - return S2; - if (isa(S2)) - break; + // We should traverse DILexicalBlock's that enclose input locations and + // have different file attribute. + using NestedLexicalBlocksMatchKey = DIFile *; + // We try to match equal scopes or locations belonging to the same file. + auto GetLocOrScopeMatchKey = [](MDNode *N) -> NestedLexicalBlocksMatchKey { + if (auto *Loc = dyn_cast_if_present(N)) { + return Loc->getFile(); + } else if (auto *LS = dyn_cast_if_present(N)) { + return LS->getFile(); } return nullptr; }; - auto Scope = GetNearestCommonScope(L1->getScope(), L2->getScope()); - assert(Scope && "No common scope in the same subprogram?"); - - bool SameLine = L1->getLine() == L2->getLine(); - bool SameCol = L1->getColumn() == L2->getColumn(); - unsigned Line = SameLine ? L1->getLine() : 0; - unsigned Col = SameLine && SameCol ? L1->getColumn() : 0; + auto MergeTextualInclusions = + [&C, InlinedAt](MDNode *LocOrScope1, MDNode *LocOrScope2, + DILocation *PrevLoc) -> DILocation * { + assert((isa_and_present(LocOrScope1) || + isa_and_present(LocOrScope1)) && + "Incorrect nested location type"); + assert((isa_and_present(LocOrScope2) || + isa_and_present(LocOrScope2)) && + "Incorrect nested location type"); + + auto [L1, F1] = GetLocAndFile(C, LocOrScope1, InlinedAt); + auto [L2, F2] = GetLocAndFile(C, LocOrScope2, InlinedAt); + + assert(L1->getScope()->getSubprogram() == + L2->getScope()->getSubprogram() && + "Locations from different subprograms?"); + + if (L1 == L2) { + auto *Result = DILocation::get(C, L1->getLine(), L1->getColumn(), + L1->getScope(), InlinedAt); + return Result; + } - return DILocation::get(C, Line, Col, Scope, InlinedAt); - }; + DIScope *Scope = GetNearestCommonScope(L1, L2); + assert(Scope && "No common scope in the same subprogram?"); - DILocation *Result = ARIt != ALocs.rend() ? (*ARIt)->getInlinedAt() : nullptr; + // If the common scope from the same file as incoming locations + // can't be found, try another pair of locations. + if (Scope->getFile() != F1 || Scope->getFile() != F2) { + return nullptr; + } - // If we have found a common starting location, walk up the inlined-at chains - // and try to produce common locations. - for (; ARIt != ALocs.rend() && BRIt != BLocs.rend(); ++ARIt, ++BRIt) { - DILocation *Tmp = MergeLocPair(*ARIt, *BRIt, Result); + bool SameLine = L1->getLine() == L2->getLine(); + bool SameCol = L1->getColumn() == L2->getColumn(); + unsigned Line = SameLine ? L1->getLine() : 0; + unsigned Col = SameLine && SameCol ? L1->getColumn() : 0; - if (!Tmp) - // We have walked up to a point in the chains where the two locations - // are irreconsilable. At this point Result contains the nearest common - // location in the inlined-at chains of LocA and LocB, so we break here. - break; - - Result = Tmp; - } + auto *Result = DILocation::get(C, Line, Col, Scope, InlinedAt); + return Result; + }; - if (Result) - return Result; + return MergeNestedLocations( + GetLocOrScopeMatchKey, IsChainOver, NextScopeWithDifferentFile, + MergeTextualInclusions, [](MDNode *N) { return nullptr; }, L1, L2); + }; - // We ended up with LocA and LocB as irreconsilable locations. Produce a - // location at 0:0 with one of the locations' scope. The function has - // historically picked A's scope, and a nullptr inlined-at location, so that - // behavior is mimicked here but I am not sure if this is always the correct - // way to handle this. - return DILocation::get(C, 0, 0, LocA->getScope(), nullptr); + // When traversing inlinedAt chain, we consider locations within the same + // subprogram and inlinedAt location matching. + using InlinedAtLookupKey = std::pair; + auto GetInlinedAtKey = [](MDNode *N) { + auto *L = cast(N); + return InlinedAtLookupKey(L->getScope()->getSubprogram(), + L->getInlinedAt()); + }; + auto NextInlinedAt = [](MDNode *N) { + auto *L = cast(N); + return L->getInlinedAt(); + }; + return MergeNestedLocations( + GetInlinedAtKey, IsChainOver, NextInlinedAt, MergeInlinedLocations, + [GetInlinedAtKey](MDNode *N) { return GetInlinedAtKey(N).second; }, LocA, + LocB); } std::optional diff --git a/llvm/test/DebugInfo/AArch64/merge-nested-block-loc.ll b/llvm/test/DebugInfo/AArch64/merge-nested-block-loc.ll new file mode 100644 index 0000000000000..ed087c1e90166 --- /dev/null +++ b/llvm/test/DebugInfo/AArch64/merge-nested-block-loc.ll @@ -0,0 +1,204 @@ +; RUN: opt -mtriple=aarch64-unknown-linux-gnu -S %s -passes=sroa -o - | FileCheck %s + +; In this test we want to ensure that the location of phi instruction merged from locations +; of %mul3 and %mul9 belongs to the correct scope (DILexicalBlockFile), so that line +; number of that location belongs to the corresponding file. + +; Generated with clang from +; 1 # 1 "1.c" 1 +; 2 # 1 "1.c" 2 +; 3 int foo(int a) { +; 4 int i = 0; +; 5 if ((a & 1) == 1) { +; 6 a -= 1; +; 7 # 1 "m.c" 1 +; 8 # 40 "m.c" +; 9 i += a; +; 10 i -= 10*a; +; 11 i *= a*a; +; 12 # 6 "1.c" 2 +; 13 } else { +; 14 a += 3; +; 15 # 1 "m.c" 1 +; 16 # 40 "m.c" +; 17 i += a; +; 18 i -= 10*a; +; 19 i *= a*a; +; 20 # 9 "1.c" 2 +; 21 } +; 22 return i; +; 23 } + +; ModuleID = 'repro.c' +source_filename = "repro.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32" +target triple = "aarch64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define dso_local i32 @foo(i32 noundef %a) #0 !dbg !9 { +; CHECK: phi i32 {{.*}}, !dbg [[PHILOC:![0-9]+]] +; +entry: + %a.addr = alloca i32, align 4, !DIAssignID !17 + #dbg_assign(i1 undef, !15, !DIExpression(), !17, ptr %a.addr, !DIExpression(), !18) + %i = alloca i32, align 4, !DIAssignID !19 + #dbg_assign(i1 undef, !16, !DIExpression(), !19, ptr %i, !DIExpression(), !18) + store i32 %a, ptr %a.addr, align 4, !tbaa !20, !DIAssignID !24 + #dbg_assign(i32 %a, !15, !DIExpression(), !24, ptr %a.addr, !DIExpression(), !18) + call void @llvm.lifetime.start.p0(i64 4, ptr %i) #2, !dbg !25 + store i32 0, ptr %i, align 4, !dbg !26, !tbaa !20, !DIAssignID !27 + #dbg_assign(i32 0, !16, !DIExpression(), !27, ptr %i, !DIExpression(), !18) + %0 = load i32, ptr %a.addr, align 4, !dbg !28, !tbaa !20 + %and = and i32 %0, 1, !dbg !30 + %cmp = icmp eq i32 %and, 1, !dbg !31 + br i1 %cmp, label %if.then, label %if.else, !dbg !31 + +if.then: ; preds = %entry + %1 = load i32, ptr %a.addr, align 4, !dbg !32, !tbaa !20 + %sub = sub nsw i32 %1, 1, !dbg !32 + store i32 %sub, ptr %a.addr, align 4, !dbg !32, !tbaa !20, !DIAssignID !34 + #dbg_assign(i32 %sub, !15, !DIExpression(), !34, ptr %a.addr, !DIExpression(), !18) + %2 = load i32, ptr %a.addr, align 4, !dbg !35, !tbaa !20 + %3 = load i32, ptr %i, align 4, !dbg !38, !tbaa !20 + %add = add nsw i32 %3, %2, !dbg !38 + store i32 %add, ptr %i, align 4, !dbg !38, !tbaa !20, !DIAssignID !39 + #dbg_assign(i32 %add, !16, !DIExpression(), !39, ptr %i, !DIExpression(), !18) + %4 = load i32, ptr %a.addr, align 4, !dbg !40, !tbaa !20 + %mul = mul nsw i32 10, %4, !dbg !41 + %5 = load i32, ptr %i, align 4, !dbg !42, !tbaa !20 + %sub1 = sub nsw i32 %5, %mul, !dbg !42 + store i32 %sub1, ptr %i, align 4, !dbg !42, !tbaa !20, !DIAssignID !43 + #dbg_assign(i32 %sub1, !16, !DIExpression(), !43, ptr %i, !DIExpression(), !18) + %6 = load i32, ptr %a.addr, align 4, !dbg !44, !tbaa !20 + %7 = load i32, ptr %a.addr, align 4, !dbg !45, !tbaa !20 + %mul2 = mul nsw i32 %6, %7, !dbg !46 + %8 = load i32, ptr %i, align 4, !dbg !47, !tbaa !20 + %mul3 = mul nsw i32 %8, %mul2, !dbg !47 + store i32 %mul3, ptr %i, align 4, !dbg !47, !tbaa !20, !DIAssignID !48 + #dbg_assign(i32 %mul3, !16, !DIExpression(), !48, ptr %i, !DIExpression(), !18) + br label %if.end, !dbg !49 + +if.else: ; preds = %entry + %9 = load i32, ptr %a.addr, align 4, !dbg !51, !tbaa !20 + %add4 = add nsw i32 %9, 3, !dbg !51 + store i32 %add4, ptr %a.addr, align 4, !dbg !51, !tbaa !20, !DIAssignID !53 + #dbg_assign(i32 %add4, !15, !DIExpression(), !53, ptr %a.addr, !DIExpression(), !18) + %10 = load i32, ptr %a.addr, align 4, !dbg !54, !tbaa !20 + %11 = load i32, ptr %i, align 4, !dbg !56, !tbaa !20 + %add5 = add nsw i32 %11, %10, !dbg !56 + store i32 %add5, ptr %i, align 4, !dbg !56, !tbaa !20, !DIAssignID !57 + #dbg_assign(i32 %add5, !16, !DIExpression(), !57, ptr %i, !DIExpression(), !18) + %12 = load i32, ptr %a.addr, align 4, !dbg !58, !tbaa !20 + %mul6 = mul nsw i32 10, %12, !dbg !59 + %13 = load i32, ptr %i, align 4, !dbg !60, !tbaa !20 + %sub7 = sub nsw i32 %13, %mul6, !dbg !60 + store i32 %sub7, ptr %i, align 4, !dbg !60, !tbaa !20, !DIAssignID !61 + #dbg_assign(i32 %sub7, !16, !DIExpression(), !61, ptr %i, !DIExpression(), !18) + %14 = load i32, ptr %a.addr, align 4, !dbg !62, !tbaa !20 + %15 = load i32, ptr %a.addr, align 4, !dbg !63, !tbaa !20 + %mul8 = mul nsw i32 %14, %15, !dbg !64 + %16 = load i32, ptr %i, align 4, !dbg !65, !tbaa !20 + %mul9 = mul nsw i32 %16, %mul8, !dbg !65 + store i32 %mul9, ptr %i, align 4, !dbg !65, !tbaa !20, !DIAssignID !66 + #dbg_assign(i32 %mul9, !16, !DIExpression(), !66, ptr %i, !DIExpression(), !18) + br label %if.end + +if.end: ; preds = %if.else, %if.then + %17 = load i32, ptr %i, align 4, !dbg !67, !tbaa !20 + call void @llvm.lifetime.end.p0(i64 4, ptr %i) #2, !dbg !68 + ret i32 %17, !dbg !69 +} + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1 + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1 + +attributes #0 = { nounwind uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+neon,+v8a,-fmv" } +attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #2 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 20.0.0git (git@github.com:llvm/llvm-project.git c8ee1164bd6ae2f0a603c53d1d29ad5a3225c5cd)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "repro.c", directory: "", checksumkind: CSK_MD5, checksum: "51454d2babc57d5ea92df6734236bd39") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 2} +!6 = !{i32 7, !"frame-pointer", i32 1} +!7 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!8 = !{!"clang version 20.0.0git (git@github.com:llvm/llvm-project.git c8ee1164bd6ae2f0a603c53d1d29ad5a3225c5cd)"} +!9 = distinct !DISubprogram(name: "foo", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14) +!10 = !DIFile(filename: "1.c", directory: "") +!11 = !DISubroutineType(types: !12) +!12 = !{!13, !13} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !{!15, !16} +!15 = !DILocalVariable(name: "a", arg: 1, scope: !9, file: !10, line: 1, type: !13) +!16 = !DILocalVariable(name: "i", scope: !9, file: !10, line: 2, type: !13) +!17 = distinct !DIAssignID() +!18 = !DILocation(line: 0, scope: !9) +!19 = distinct !DIAssignID() +!20 = !{!21, !21, i64 0} +!21 = !{!"int", !22, i64 0} +!22 = !{!"omnipotent char", !23, i64 0} +!23 = !{!"Simple C/C++ TBAA"} +!24 = distinct !DIAssignID() +!25 = !DILocation(line: 2, column: 3, scope: !9) +!26 = !DILocation(line: 2, column: 7, scope: !9) +!27 = distinct !DIAssignID() +!28 = !DILocation(line: 3, column: 8, scope: !29) +!29 = distinct !DILexicalBlock(scope: !9, file: !10, line: 3, column: 7) +!30 = !DILocation(line: 3, column: 10, scope: !29) +!31 = !DILocation(line: 3, column: 15, scope: !29) +!32 = !DILocation(line: 4, column: 7, scope: !33) +!33 = distinct !DILexicalBlock(scope: !29, file: !10, line: 3, column: 21) +!34 = distinct !DIAssignID() +!35 = !DILocation(line: 40, column: 6, scope: !36) +!36 = !DILexicalBlockFile(scope: !33, file: !37, discriminator: 0) +!37 = !DIFile(filename: "m.c", directory: "") +!38 = !DILocation(line: 40, column: 3, scope: !36) +!39 = distinct !DIAssignID() +!40 = !DILocation(line: 41, column: 9, scope: !36) +!41 = !DILocation(line: 41, column: 8, scope: !36) +!42 = !DILocation(line: 41, column: 3, scope: !36) +!43 = distinct !DIAssignID() +!44 = !DILocation(line: 42, column: 6, scope: !36) +!45 = !DILocation(line: 42, column: 8, scope: !36) +!46 = !DILocation(line: 42, column: 7, scope: !36) +!47 = !DILocation(line: 42, column: 3, scope: !36) +!48 = distinct !DIAssignID() +!49 = !DILocation(line: 6, column: 2, scope: !50) +!50 = !DILexicalBlockFile(scope: !33, file: !10, discriminator: 0) +!51 = !DILocation(line: 7, column: 7, scope: !52) +!52 = distinct !DILexicalBlock(scope: !29, file: !10, line: 6, column: 9) +!53 = distinct !DIAssignID() +!54 = !DILocation(line: 40, column: 6, scope: !55) +!55 = !DILexicalBlockFile(scope: !52, file: !37, discriminator: 0) +!56 = !DILocation(line: 40, column: 3, scope: !55) +!57 = distinct !DIAssignID() +!58 = !DILocation(line: 41, column: 9, scope: !55) +!59 = !DILocation(line: 41, column: 8, scope: !55) +!60 = !DILocation(line: 41, column: 3, scope: !55) +!61 = distinct !DIAssignID() +!62 = !DILocation(line: 42, column: 6, scope: !55) +!63 = !DILocation(line: 42, column: 8, scope: !55) +!64 = !DILocation(line: 42, column: 7, scope: !55) +!65 = !DILocation(line: 42, column: 3, scope: !55) +!66 = distinct !DIAssignID() +!67 = !DILocation(line: 10, column: 10, scope: !9) +!68 = !DILocation(line: 11, column: 1, scope: !9) +!69 = !DILocation(line: 10, column: 3, scope: !9) + +;. +; CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "foo", scope: [[FILE1:![0-9]+]], file: [[FILE1]], line: 1 +; CHECK: [[FILE1]] = !DIFile(filename: "1.c", directory: "") +; CHECK: [[LB1:![0-9]+]] = distinct !DILexicalBlock(scope: [[SP]], file: [[FILE1]], line: 3, column: 7) +; CHECK: [[LB2:![0-9]+]] = distinct !DILexicalBlock(scope: [[LB1]], file: [[FILE1]], line: 3, column: 21) +; CHECK: [[LBF:![0-9]+]] = !DILexicalBlockFile(scope: [[LB2]], file: [[FILE2:![0-9]+]], discriminator: 0) +; CHECK: [[FILE2]] = !DIFile(filename: "m.c", directory: "") +; CHECK: [[PHILOC]] = !DILocation(line: 42, column: 3, scope: [[LBF]]) diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp index 628221339c89b..cc13ef6ad9da0 100644 --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -85,10 +85,10 @@ class MetadataTest : public testing::Test { return DISubroutineType::getDistinct(Context, DINode::FlagZero, 0, getNode(nullptr)); } - DISubprogram *getSubprogram() { - return DISubprogram::getDistinct( - Context, nullptr, "", "", nullptr, 0, nullptr, 0, nullptr, 0, 0, - DINode::FlagZero, DISubprogram::SPFlagZero, nullptr); + DISubprogram *getSubprogram(DIFile *F = nullptr) { + return DISubprogram::getDistinct(Context, nullptr, "", "", F, 0, nullptr, 0, + nullptr, 0, 0, DINode::FlagZero, + DISubprogram::SPFlagZero, nullptr); } DIFile *getFile() { return DIFile::getDistinct(Context, "file.c", "/path/to/dir"); @@ -918,8 +918,9 @@ TEST_F(MDNodeTest, deleteTemporaryWithTrackingRef) { typedef MetadataTest DILocationTest; TEST_F(DILocationTest, Merge) { - DISubprogram *N = getSubprogram(); - DIScope *S = DILexicalBlock::get(Context, N, getFile(), 3, 4); + DIFile *F = getFile(); + DISubprogram *N = getSubprogram(F); + DIScope *S = DILexicalBlock::get(Context, N, F, 3, 4); { // Identical. @@ -974,6 +975,114 @@ TEST_F(DILocationTest, Merge) { EXPECT_EQ(N, M->getScope()); } + { + // Different files, same line numbers, same subprogram. + auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir"); + auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir"); + DISubprogram *N = getSubprogram(F1); + auto *LBF = DILexicalBlockFile::get(Context, N, F2, 0); + auto *A = DILocation::get(Context, 1, 6, N); + auto *B = DILocation::get(Context, 1, 6, LBF); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_EQ(0u, M->getLine()); + EXPECT_EQ(0u, M->getColumn()); + EXPECT_EQ(N, M->getScope()); + } + + { + // Different files, same line numbers. + auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir"); + auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir"); + DISubprogram *N = getSubprogram(F1); + auto *LB = DILexicalBlock::getDistinct(Context, N, F1, 4, 9); + auto *LBF = DILexicalBlockFile::get(Context, LB, F2, 0); + auto *A = DILocation::get(Context, 1, 6, LB); + auto *B = DILocation::get(Context, 1, 6, LBF); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_EQ(0u, M->getLine()); + EXPECT_EQ(0u, M->getColumn()); + EXPECT_EQ(LB, M->getScope()); + } + + { + // Different files, same line numbers, + // both locations have DILexicalBlockFile scopes. + auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir"); + auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir"); + auto *F3 = DIFile::getDistinct(Context, "file3.c", "/path/to/dir"); + DISubprogram *N = getSubprogram(F1); + auto *LB = DILexicalBlock::getDistinct(Context, N, F1, 4, 9); + auto *LBF1 = DILexicalBlockFile::get(Context, LB, F2, 0); + auto *LBF2 = DILexicalBlockFile::get(Context, LB, F3, 0); + auto *A = DILocation::get(Context, 1, 6, LBF1); + auto *B = DILocation::get(Context, 1, 6, LBF2); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_EQ(4u, M->getLine()); + EXPECT_EQ(9u, M->getColumn()); + EXPECT_EQ(LB, M->getScope()); + } + + { + // Same file, same line numbers, but different LBF objects. + // both locations have DILexicalBlockFile scope. + auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir"); + DISubprogram *N = getSubprogram(F1); + auto *LB1 = DILexicalBlock::getDistinct(Context, N, F1, 4, 9); + auto *LB2 = DILexicalBlock::getDistinct(Context, N, F1, 5, 9); + auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir"); + auto *LBF1 = DILexicalBlockFile::get(Context, LB1, F2, 0); + auto *LBF2 = DILexicalBlockFile::get(Context, LB2, F2, 0); + auto *A = DILocation::get(Context, 1, 6, LBF1); + auto *B = DILocation::get(Context, 1, 6, LBF2); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_EQ(1u, M->getLine()); + EXPECT_EQ(6u, M->getColumn()); + EXPECT_EQ(LBF1, M->getScope()); + } + + { + // Merge locations A and B, where B is included in A's file + // at the same position as A's position. + auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir"); + DISubprogram *N = getSubprogram(F1); + auto *LB = DILexicalBlock::getDistinct(Context, N, F1, 4, 9); + auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir"); + auto *LBF = DILexicalBlockFile::get(Context, LB, F2, 0); + auto *A = DILocation::get(Context, 4, 9, LB); + auto *B = DILocation::get(Context, 1, 6, LBF); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_EQ(4u, M->getLine()); + EXPECT_EQ(9u, M->getColumn()); + EXPECT_EQ(LB, M->getScope()); + } + + { + // Different locations from different files having common lexical blocks + auto *F1 = DIFile::getDistinct(Context, "file1.c", "/path/to/dir"); + DISubprogram *N = getSubprogram(F1); + + auto *LBCommon = DILexicalBlock::getDistinct(Context, N, F1, 4, 9); + + auto *F2 = DIFile::getDistinct(Context, "file2.c", "/path/to/dir"); + LBCommon = DILexicalBlock::getDistinct(Context, LBCommon, F2, 5, 9); + + auto *F3 = DIFile::getDistinct(Context, "file3.c", "/path/to/dir"); + auto *LB1 = DILexicalBlock::getDistinct(Context, LBCommon, F3, 6, 9); + + auto *F4 = DIFile::getDistinct(Context, "file4.c", "/path/to/dir"); + auto *LB2 = DILexicalBlock::getDistinct(Context, LBCommon, F4, 7, 9); + + auto *F5 = DIFile::getDistinct(Context, "file5.c", "/path/to/dir"); + auto *LBF1 = DILexicalBlockFile::get(Context, LB1, F5, 0); + + auto *A = DILocation::get(Context, 8, 9, LB2); + auto *B = DILocation::get(Context, 9, 6, LBF1); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_EQ(5u, M->getLine()); + EXPECT_EQ(9u, M->getColumn()); + EXPECT_EQ(LBCommon, M->getScope()); + } + { // Different function, same inlined-at. auto *F = getFile();