Author: Oleksandr Tarasiuk
Date: 2026-01-30T15:17:56+02:00
New Revision: d473a87a165660bbf690ba4d030c4fda85ac67e8

URL: 
https://github.com/llvm/llvm-project/commit/d473a87a165660bbf690ba4d030c4fda85ac67e8
DIFF: 
https://github.com/llvm/llvm-project/commit/d473a87a165660bbf690ba4d030c4fda85ac67e8.diff

LOG: [Clang] speed up -Wassign-enum via enumerator caching (#176560)

Fixes #176454

---

This patch addresses a performance issue in `-Wassign-enum` where
enumerator values were repeatedly rebuilt and sorted for each assignment
check, leading to excessive compile-time overhead for large enums.


The following charts demonstrate the benchmark results before and after
caching enumerator values

Before
<img width="640" height="480" alt="before_enum_assign"
src="https://github.com/user-attachments/assets/cbc9de29-32cd-452e-84e9-383dcf334bac";
/>

After
<img width="756" height="579" alt="after_enum_assign"
src="https://github.com/user-attachments/assets/891b3839-9d70-4e60-ab21-de98ce4cca0a";
/>

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaStmt.cpp
    clang/test/Sema/warn-outof-range-assign-enum.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bb40cff50b8f0..de155256999d0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -202,6 +202,8 @@ Improvements to Clang's diagnostics
       DanglingView(std::string s) : view(s) {}  // warning: address of stack 
memory escapes to a field
     };
 
+- 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 7b3479bbc3677..0ba3daab764b7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3544,6 +3544,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.
+  llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt>>
+      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 5ab10fdfc7b74..ba5ba80d6a0bc 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -1761,30 +1761,30 @@ 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);
+  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()) {
+      Values.push_back(EC->getInitVal());
+      AdjustAPSInt(Values.back(), DstWidth, DstIsSigned);
+    }
+
+    if (Values.empty())
+      return;
+
+    llvm::sort(Values);
+    Values.erase(llvm::unique(Values), Values.end());
   }
-  if (EnumVals.empty())
+
+  if (llvm::binary_search(Values, *RHSVal))
     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);
 }
-


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to