https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/176560
>From 0651b94ef5e9708f16bf5eed37379abc88417c0b Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Sat, 17 Jan 2026 13:14:41 +0200 Subject: [PATCH 1/8] [Clang] speed up -Wassign-enum via enumerator caching --- clang/docs/ReleaseNotes.rst | 1 + clang/include/clang/Sema/Sema.h | 4 ++ clang/lib/Sema/SemaStmt.cpp | 47 ++++++++++--------- .../test/Sema/warn-outof-range-assign-enum.c | 13 ++++- 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 4139d1d80ed4a..c9e19128af74e 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -105,6 +105,7 @@ Attribute Changes in Clang Improvements to Clang's diagnostics ----------------------------------- +- Improved `-Wassign-enum` performance by caching enum enumerator values. (#GH176454) Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 23eb954ce774c..7929f26fd92d0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3522,6 +3522,10 @@ class Sema final : public SemaBase { /// attribute. mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache; + /// A cache of enumerator values for enums checked by -Wassign-enum. + mutable llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt, 64>> + AssignEnumCache; + /// WeakUndeclaredIdentifiers - Identifiers contained in \#pragma weak before /// declared. Rare. May alias another identifier, declared or undeclared. /// diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 1b1643250d05e..18788ed4b428f 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1761,30 +1761,35 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, return; } - typedef SmallVector<std::pair<llvm::APSInt, EnumConstantDecl *>, 64> - EnumValsTy; - EnumValsTy EnumVals; - - // Gather all enum values, set their type and sort them, - // allowing easier comparison with rhs constant. - for (auto *EDI : ED->enumerators()) { - llvm::APSInt Val = EDI->getInitVal(); - AdjustAPSInt(Val, DstWidth, DstIsSigned); - EnumVals.emplace_back(Val, EDI); + auto EnumValuesCmp = [](const llvm::APSInt &A, const llvm::APSInt &B) { + return A < B; + }; + + const EnumDecl *Key = ED->getCanonicalDecl(); + auto [It, Inserted] = AssignEnumCache.try_emplace(Key); + auto &Values = It->second; + + if (Inserted) { + Values.reserve(std::distance(ED->enumerator_begin(), ED->enumerator_end())); + + for (auto *EC : ED->enumerators()) { + llvm::APSInt V = EC->getInitVal(); + AdjustAPSInt(V, DstWidth, DstIsSigned); + Values.push_back(V); + } + + if (Values.empty()) + return; + + llvm::sort(Values, EnumValuesCmp); + Values.erase(llvm::unique(Values), Values.end()); } - if (EnumVals.empty()) + + if (llvm::binary_search(Values, *RHSVal, EnumValuesCmp)) return; - llvm::stable_sort(EnumVals, CmpEnumVals); - EnumValsTy::iterator EIend = llvm::unique(EnumVals, EqEnumVals); - // See which values aren't in the enum. - EnumValsTy::const_iterator EI = EnumVals.begin(); - while (EI != EIend && EI->first < *RHSVal) - EI++; - if (EI == EIend || EI->first != *RHSVal) { - Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment) - << DstType.getUnqualifiedType(); - } + Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment) + << DstType.getUnqualifiedType(); } StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc, diff --git a/clang/test/Sema/warn-outof-range-assign-enum.c b/clang/test/Sema/warn-outof-range-assign-enum.c index 23c78497b37e4..0d8a4dab114e2 100644 --- a/clang/test/Sema/warn-outof-range-assign-enum.c +++ b/clang/test/Sema/warn-outof-range-assign-enum.c @@ -50,6 +50,18 @@ void f(void) x += 1; // expected-warning {{integer constant not in range of enumerated type}} } +typedef enum OutOfOrderTestEnum { + OO1 = 100, + OO2 = 50, + OO3 = 75, + OO4 = 9, + OO5 = 99 +} OutOfOrderTestEnum; + +OutOfOrderTestEnum t1 = 75; +OutOfOrderTestEnum t2 = 9; +OutOfOrderTestEnum t3 = 76; // expected-warning {{integer constant not in range of enumerated type 'OutOfOrderTestEnum'}} + int main(void) { CCTestEnum test = 1; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}} test = 600; // expected-warning {{integer constant not in range of enumerated type 'CCTestEnum'}} @@ -58,4 +70,3 @@ int main(void) { foo(4); foo(Two+1); } - >From 349f1b4313de78f441785d78a56ff05133ef37d1 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Wed, 21 Jan 2026 17:36:02 +0200 Subject: [PATCH 2/8] remove unnecessary mutable specifier --- clang/include/clang/Sema/Sema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 7929f26fd92d0..04c660e273cef 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3523,7 +3523,7 @@ class Sema final : public SemaBase { mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache; /// A cache of enumerator values for enums checked by -Wassign-enum. - mutable llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt, 64>> + llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt, 64>> AssignEnumCache; /// WeakUndeclaredIdentifiers - Identifiers contained in \#pragma weak before >From b406581516820b54d22d45303f960d6b73c6469c Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Wed, 21 Jan 2026 17:37:16 +0200 Subject: [PATCH 3/8] use default sort comparator --- clang/lib/Sema/SemaStmt.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 18788ed4b428f..1585496ef8768 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1761,10 +1761,6 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, return; } - auto EnumValuesCmp = [](const llvm::APSInt &A, const llvm::APSInt &B) { - return A < B; - }; - const EnumDecl *Key = ED->getCanonicalDecl(); auto [It, Inserted] = AssignEnumCache.try_emplace(Key); auto &Values = It->second; @@ -1774,18 +1770,18 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, for (auto *EC : ED->enumerators()) { llvm::APSInt V = EC->getInitVal(); - AdjustAPSInt(V, DstWidth, DstIsSigned); Values.push_back(V); + AdjustAPSInt(Values.back(), DstWidth, DstIsSigned); } if (Values.empty()) return; - llvm::sort(Values, EnumValuesCmp); + llvm::sort(Values); Values.erase(llvm::unique(Values), Values.end()); } - if (llvm::binary_search(Values, *RHSVal, EnumValuesCmp)) + if (llvm::binary_search(Values, *RHSVal)) return; Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment) >From 24b83338fc727484cbd85b112a8ddd45a411b44d Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Wed, 21 Jan 2026 18:02:56 +0200 Subject: [PATCH 4/8] cleanup --- clang/lib/Sema/SemaStmt.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 1585496ef8768..2aab1dbfb0265 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1769,8 +1769,7 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, Values.reserve(std::distance(ED->enumerator_begin(), ED->enumerator_end())); for (auto *EC : ED->enumerators()) { - llvm::APSInt V = EC->getInitVal(); - Values.push_back(V); + Values.push_back(EC->getInitVal()); AdjustAPSInt(Values.back(), DstWidth, DstIsSigned); } >From ecc9b350b850bf4d71eb25a4f9cde0fd14ce7d98 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Tue, 27 Jan 2026 23:39:58 +0200 Subject: [PATCH 5/8] use enums .size() instead of std::distance for reserve() --- clang/lib/Sema/SemaStmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 2aab1dbfb0265..f62a6944644f5 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1766,7 +1766,7 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, auto &Values = It->second; if (Inserted) { - Values.reserve(std::distance(ED->enumerator_begin(), ED->enumerator_end())); + Values.reserve(ED->enumerators().size()); for (auto *EC : ED->enumerators()) { Values.push_back(EC->getInitVal()); >From 3e9c6be72c8d3663f2b24e9e509f8de01547eae5 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Wed, 28 Jan 2026 00:35:05 +0200 Subject: [PATCH 6/8] update release notes --- clang/docs/ReleaseNotes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index de8b276988c8a..029b93e2bcdba 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -168,7 +168,7 @@ Improvements to Clang's diagnostics note: param returned here int* p(int *in) { return in; } ^~ -- Improved `-Wassign-enum` performance by caching enum enumerator values. (#GH176454) +- Improved ``-Wassign-enum`` performance by caching enum enumerator values. (#GH176454) - Added ``-Wlifetime-safety-noescape`` to detect misuse of ``[[clang::noescape]]`` annotation where the parameter escapes through return. For example: >From 4f167b5e90fbe6803c724bc6cef6ac3778c8c047 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Wed, 28 Jan 2026 00:35:12 +0200 Subject: [PATCH 7/8] use range_size --- clang/lib/Sema/SemaStmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 97556a0f124cf..5ecdcba106765 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1766,7 +1766,7 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, auto &Values = It->second; if (Inserted) { - Values.reserve(ED->enumerators().size()); + Values.reserve(llvm::range_size(ED->enumerators())); for (auto *EC : ED->enumerators()) { Values.push_back(EC->getInitVal()); >From 536ad38948ebf9f7e2076313ff627ba94c61b02b Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Wed, 28 Jan 2026 00:52:40 +0200 Subject: [PATCH 8/8] revert std::distance --- clang/lib/Sema/SemaStmt.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 5ecdcba106765..2aab1dbfb0265 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1766,7 +1766,7 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, auto &Values = It->second; if (Inserted) { - Values.reserve(llvm::range_size(ED->enumerators())); + Values.reserve(std::distance(ED->enumerator_begin(), ED->enumerator_end())); for (auto *EC : ED->enumerators()) { Values.push_back(EC->getInitVal()); @@ -2753,6 +2753,14 @@ StmtResult Sema::BuildCXXForRangeStmt( diag::err_for_range_incomplete_type)) return StmtError(); + // P2718R0 - Lifetime extension in range-based for loops. + if (getLangOpts().CPlusPlus23 && !LifetimeExtendTemps.empty()) { + InitializedEntity Entity = + InitializedEntity::InitializeVariable(RangeVar); + for (auto *MTE : LifetimeExtendTemps) + MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber()); + } + // Build auto __begin = begin-expr, __end = end-expr. // Divide by 2, since the variables are in the inner scope (loop body). const auto DepthStr = std::to_string(S->getDepth() / 2); @@ -3009,13 +3017,6 @@ StmtResult Sema::BuildCXXForRangeStmt( if (getLangOpts().OpenMP >= 50 && BeginDeclStmt.isUsable()) OpenMP().ActOnOpenMPLoopInitialization(ForLoc, BeginDeclStmt.get()); - // P2718R0 - Lifetime extension in range-based for loops. - if (getLangOpts().CPlusPlus23 && !LifetimeExtendTemps.empty()) { - InitializedEntity Entity = InitializedEntity::InitializeVariable(RangeVar); - for (auto *MTE : LifetimeExtendTemps) - MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber()); - } - return new (Context) CXXForRangeStmt( InitStmt, RangeDS, cast_or_null<DeclStmt>(BeginDeclStmt.get()), cast_or_null<DeclStmt>(EndDeclStmt.get()), NotEqExpr.get(), _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
