diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp index 0973fcf94ee90..6735bf7dc52db 100644 --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -306,11 +306,15 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) { // 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 *LocAIA = LocA->getInlinedAt(); + auto *LocBIA = LocB->getInlinedAt(); + auto MergeLocPair = [&C, LocAIA, + LocBIA](const DILocation *L1, const DILocation *L2, + DILocation *InlinedAt) -> DILocation * { if (L1 == L2) return DILocation::get(C, L1->getLine(), L1->getColumn(), L1->getScope(), - InlinedAt); + InlinedAt, L1->isImplicitCode(), + L1->getAtomGroup(), L1->getAtomRank()); // If the locations originate from different subprograms we can't produce // a common location. @@ -346,8 +350,47 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) { bool SameCol = L1->getColumn() == L2->getColumn(); unsigned Line = SameLine ? L1->getLine() : 0; unsigned Col = SameLine && SameCol ? L1->getColumn() : 0; - - return DILocation::get(C, Line, Col, Scope, InlinedAt); + bool IsImplicitCode = L1->isImplicitCode() && L2->isImplicitCode(); + + // Discard source location atom if the line becomes 0. And there's nothing + // further to do if neither location has an atom number. + if (!SameLine || !(L1->getAtomGroup() || L2->getAtomGroup())) + return DILocation::get(C, Line, Col, Scope, InlinedAt, IsImplicitCode, + /*AtomGroup*/ 0, /*AtomRank*/ 0); + + uint64_t Group = 0; + uint64_t Rank = 0; + // If we're preserving the same matching inlined-at field we can + // preserve the atom. + if (LocBIA == LocAIA && InlinedAt == LocBIA) { + // Deterministically keep the lowest non-zero ranking atom group + // number. + // FIXME: It would be nice if we could track that an instruction + // belongs to two source atoms. + bool UseL1Atom = [L1, L2]() { + if (L1->getAtomRank() == L2->getAtomRank()) { + // Arbitrarily choose the lowest non-zero group number. + if (!L1->getAtomGroup() || !L2->getAtomGroup()) + return !L2->getAtomGroup(); + return L1->getAtomGroup() < L2->getAtomGroup(); + } + // Choose the lowest non-zero rank. + if (!L1->getAtomRank() || !L2->getAtomRank()) + return !L2->getAtomRank(); + return L1->getAtomRank() < L2->getAtomRank(); + }(); + Group = UseL1Atom ? L1->getAtomGroup() : L2->getAtomGroup(); + Rank = UseL1Atom ? L1->getAtomRank() : L2->getAtomRank(); + } else { + // If either instruction is part of a source atom, reassign it a new + // atom group. This essentially regresses to non-key-instructions + // behaviour (now that it's the only instruction in its group it'll + // probably get is_stmt applied). + Group = C.incNextDILocationAtomGroup(); + Rank = 1; + } + return DILocation::get(C, Line, Col, Scope, InlinedAt, IsImplicitCode, + Group, Rank); }; DILocation *Result = ARIt != ALocs.rend() ? (*ARIt)->getInlinedAt() : nullptr; @@ -374,7 +417,10 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) { // 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); + // Key Instructions: it's fine to drop atom group and rank here, as line 0 + // is a nonsensical is_stmt location. + return DILocation::get(C, 0, 0, LocA->getScope(), nullptr, false, + /*AtomGroup*/ 0, /*AtomRank*/ 0); } std::optional diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp index 9b25d0b91abdd..56367e07b9d09 100644 --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -1468,6 +1468,160 @@ TEST_F(DILocationTest, Merge) { EXPECT_EQ(N, M2->getScope()); PickMergedSourceLocations = false; } + +#ifdef EXPERIMENTAL_KEY_INSTRUCTIONS +#define EXPECT_ATOM(Loc, Group, Rank) \ + EXPECT_EQ(Group, M->getAtomGroup()); \ + EXPECT_EQ(Rank, M->getAtomRank()); +#else +#define EXPECT_ATOM(Loc, Group, Rank) \ + EXPECT_EQ(0u, M->getAtomGroup()); \ + EXPECT_EQ(0u, M->getAtomRank()); \ + (void)Group; \ + (void)Rank; +#endif + // Identical, including source atom numbers. + { + auto *A = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 1); + auto *B = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 1); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, /*AtomGroup*/ 1u, 1u); + // DILocations are uniqued, so we can check equality by ptr. + EXPECT_EQ(M, DILocation::getMergedLocation(A, B)); + } + + // Identical but different atom ranks (same atom) - choose the lowest nonzero + // rank. + { + auto *A = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 1); + auto *B = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 2); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, /*AtomGroup*/ 1u, /*AtomRank*/ 1u); + EXPECT_EQ(M, DILocation::getMergedLocation(B, A)); + + A = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 0); + B = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 2); + M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, /*AtomGroup*/ 1u, /*AtomRank*/ 2u); + EXPECT_EQ(M, DILocation::getMergedLocation(B, A)); + } + + // Identical but different atom ranks (different atom) - choose the lowest + // nonzero rank. + { + auto *A = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 1); + auto *B = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 2, + /*AtomRank*/ 2); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, 1u, 1u); + EXPECT_EQ(M, DILocation::getMergedLocation(B, A)); + + A = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 0); + B = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 2, + /*AtomRank*/ 2); + M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, /*AtomGroup*/ 2u, /*AtomRank*/ 2u); + EXPECT_EQ(M, DILocation::getMergedLocation(B, A)); + } + + // Identical but equal atom rank (different atom) - choose the lowest non-zero + // group (arbitrary choice for deterministic behaviour). + { + auto *A = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 1, + /*AtomRank*/ 1); + auto *B = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 2, + /*AtomRank*/ 1); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, 1u, 1u); + EXPECT_EQ(M, DILocation::getMergedLocation(B, A)); + + A = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 0, + /*AtomRank*/ 1); + B = DILocation::get(Context, 2, 7, N, nullptr, false, /*AtomGroup*/ 2, + /*AtomRank*/ 1); + M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, /*AtomGroup*/ 2u, /*AtomRank*/ 1u); + EXPECT_EQ(M, DILocation::getMergedLocation(B, A)); + } + + // Completely different except same atom numbers. Zero out the atoms. + { + auto *I = DILocation::get(Context, 2, 7, N); + auto *A = DILocation::get(Context, 1, 6, S, I, false, /*AtomGroup*/ 1, + /*AtomRank*/ 1); + auto *B = DILocation::get(Context, 2, 7, getSubprogram(), nullptr, false, + /*AtomGroup*/ 1, /*AtomRank*/ 1); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_EQ(0u, M->getLine()); + EXPECT_EQ(0u, M->getColumn()); + EXPECT_TRUE(isa(M->getScope())); + EXPECT_EQ(S, M->getScope()); + EXPECT_EQ(nullptr, M->getInlinedAt()); + } + + // Same inlined-at chain but different atoms. Choose the lowest + // non-zero group (arbitrary choice for deterministic behaviour). + { + auto *I = DILocation::get(Context, 1, 7, N); + auto *F = getSubprogram(); + auto *A = DILocation::get(Context, 1, 1, F, I, false, /*AtomGroup*/ 1, + /*AtomRank*/ 2); + auto *B = DILocation::get(Context, 1, 1, F, I, false, /*AtomGroup*/ 2, + /*AtomRank*/ 2); + auto *M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, /*AtomGroup*/ 1u, /*AtomRank*/ 2u); + EXPECT_EQ(M, DILocation::getMergedLocation(B, A)); + + A = DILocation::get(Context, 1, 1, F, I, false, /*AtomGroup*/ 1, + /*AtomRank*/ 2); + B = DILocation::get(Context, 1, 1, F, I, false, /*AtomGroup*/ 2, + /*AtomRank*/ 0); + M = DILocation::getMergedLocation(A, B); + EXPECT_ATOM(M, /*AtomGroup*/ 1u, /*AtomRank*/ 2u); + EXPECT_EQ(M, DILocation::getMergedLocation(B, A)); + } + + // Partially equal inlined-at chain but different atoms. Generate a new atom + // group (if either have a group number). This configuration seems unlikely + // to occur as line numbers must match, but isn't impossible. + { + // Reset global counter to ensure EXPECT numbers line up. + Context.pImpl->NextAtomGroup = 1; + // x1 -> y2 -> z4 + // y3 -> z4 + auto *FX = getSubprogram(); + auto *FY = getSubprogram(); + auto *FZ = getSubprogram(); + auto *Z4 = DILocation::get(Context, 1, 4, FZ); + auto *Y3IntoZ4 = DILocation::get(Context, 1, 3, FY, Z4, false, + /*AtomGroup*/ 1, /*AtomRank*/ 1); + auto *Y2IntoZ4 = DILocation::get(Context, 1, 2, FY, Z4); + auto *X1IntoY2 = DILocation::get(Context, 1, 1, FX, Y2IntoZ4); + auto *M = DILocation::getMergedLocation(X1IntoY2, Y3IntoZ4); + EXPECT_EQ(M->getScope(), FY); + EXPECT_EQ(M->getInlinedAt()->getScope(), FZ); + EXPECT_ATOM(M, /*AtomGroup*/ 2u, /*AtomRank*/ 1u); + + // This swapped merge will produce a new atom group too. + M = DILocation::getMergedLocation(Y3IntoZ4, X1IntoY2); + + // Same again, even if the atom numbers match. + auto *X1IntoY2SameAtom = DILocation::get(Context, 1, 1, FX, Y2IntoZ4, false, + /*AtomGroup*/ 1, /*AtomRank*/ 1); + M = DILocation::getMergedLocation(X1IntoY2SameAtom, Y3IntoZ4); + EXPECT_ATOM(M, /*AtomGroup*/ 4u, /*AtomRank*/ 1u); + M = DILocation::getMergedLocation(Y3IntoZ4, X1IntoY2SameAtom); + EXPECT_ATOM(M, /*AtomGroup*/ 5u, /*AtomRank*/ 1u); + } +#undef EXPECT_ATOM } TEST_F(DILocationTest, getDistinct) {