https://github.com/dzbarsky created 
https://github.com/llvm/llvm-project/pull/202633

CheckShift is instantiated for each primitive left-hand and right-hand type
and for both shift directions. Its four diagnostic paths are cold, but each
instantiation currently emits the diagnostic construction and undefined
behavior handling.

Move those paths to the non-template, noinline DiagnoseShiftFailure function.
Valid shifts retain the existing checks and do not call the outlined function.
Invalid shifts preserve the same diagnostic arguments and call
noteUndefinedBehavior exactly once.

On arm64 macOS with a Release build of standalone Clang, this changes:

  linked binary:  222,173,192 -> 221,924,960 bytes (-248,232)
  stripped binary: 128,407,464 -> 128,291,224 bytes (-116,240)
  __TEXT,__text:   98,369,916 -> 98,255,864 bytes (-114,052)

A 50,000-expression valid-shift benchmark covered primitive integers,
__int128, and _BitInt. Across 100 alternating process-CPU measurements, user
CPU changed from 80.460 to 80.630 seconds total (+0.21%); the paired median
was unchanged and the paired 95% interval was -0.87% to +1.29%. Generated
objects were byte-identical.

Validated all RUN modes from clang/test/AST/ByteCode/shifts.cpp and intap.cpp
under both constant interpreters, including C++11, C++17, C++20, and the ARM
target variants. Baseline and candidate diagnostics were byte-identical.

Work towards #202616

>From b8245dada7a6f37f09c39f2c974628582b2e8b05 Mon Sep 17 00:00:00 2001
From: David Zbarsky <[email protected]>
Date: Tue, 9 Jun 2026 02:21:56 -0400
Subject: [PATCH] [clang][AST] Outline constant-interpreter shift diagnostics

CheckShift is instantiated for each primitive left-hand and right-hand type
and for both shift directions. Its four diagnostic paths are cold, but each
instantiation currently emits the diagnostic construction and undefined
behavior handling.

Move those paths to the non-template, noinline DiagnoseShiftFailure function.
Valid shifts retain the existing checks and do not call the outlined function.
Invalid shifts preserve the same diagnostic arguments and call
noteUndefinedBehavior exactly once.

On arm64 macOS with a Release build of standalone Clang, this changes:

  linked binary:  222,173,192 -> 221,924,960 bytes (-248,232)
  stripped binary: 128,407,464 -> 128,291,224 bytes (-116,240)
  __TEXT,__text:   98,369,916 -> 98,255,864 bytes (-114,052)

A 50,000-expression valid-shift benchmark covered primitive integers,
__int128, and _BitInt. Across 100 alternating process-CPU measurements, user
CPU changed from 80.460 to 80.630 seconds total (+0.21%); the paired median
was unchanged and the paired 95% interval was -0.87% to +1.29%. Generated
objects were byte-identical.

Validated all RUN modes from clang/test/AST/ByteCode/shifts.cpp and intap.cpp
under both constant interpreters, including C++11, C++17, C++20, and the ARM
target variants. Baseline and candidate diagnostics were byte-identical.
---
 clang/lib/AST/ByteCode/Interp.cpp | 27 ++++++++++++++++++++++++
 clang/lib/AST/ByteCode/Interp.h   | 35 ++++++++++++++++++-------------
 2 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index f6cac7aeb9fb5..27e86b986aee0 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -225,6 +225,33 @@ namespace interp {
 PRESERVE_NONE static bool BCP(InterpState &S, CodePtr &RealPC, int32_t Offset,
                               PrimType PT);
 
+bool DiagnoseShiftFailure(InterpState &S, CodePtr OpPC, ShiftFailure Failure,
+                          const APSInt *Value, unsigned Bits) {
+  switch (Failure) {
+  case ShiftFailure::NegativeCount:
+    assert(Value);
+    S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_negative_shift)
+        << *Value;
+    break;
+  case ShiftFailure::TooLarge: {
+    assert(Value);
+    const Expr *E = S.Current->getExpr(OpPC);
+    S.CCEDiag(E, diag::note_constexpr_large_shift)
+        << *Value << E->getType() << Bits;
+    break;
+  }
+  case ShiftFailure::NegativeLeftOperand:
+    assert(Value);
+    S.CCEDiag(S.Current->getExpr(OpPC), 
diag::note_constexpr_lshift_of_negative)
+        << *Value;
+    break;
+  case ShiftFailure::DiscardsBits:
+    S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards);
+    break;
+  }
+  return S.noteUndefinedBehavior();
+}
+
 static void popArg(InterpState &S, const Expr *Arg) {
   PrimType Ty = S.getContext().classify(Arg).value_or(PT_Ptr);
   TYPE_SWITCH(Ty, S.Stk.discard<T>());
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 4d72204f51db9..a69aa75738844 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -35,6 +35,7 @@
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/ScopeExit.h"
+#include "llvm/Support/Compiler.h"
 #include <type_traits>
 
 // preserve_none causes problems when asan is enabled on both AArch64 and other
@@ -147,25 +148,33 @@ bool CheckDynamicCast(InterpState &S, CodePtr OpPC);
 
 enum class ShiftDir { Left, Right };
 
+enum class ShiftFailure {
+  NegativeCount,
+  TooLarge,
+  NegativeLeftOperand,
+  DiscardsBits,
+};
+
+LLVM_ATTRIBUTE_NOINLINE bool DiagnoseShiftFailure(InterpState &S, CodePtr OpPC,
+                                                  ShiftFailure Failure,
+                                                  const APSInt *Value = 
nullptr,
+                                                  unsigned Bits = 0);
+
 /// Checks if the shift operation is legal.
 template <ShiftDir Dir, typename LT, typename RT>
 bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
                 unsigned Bits) {
   if (RHS.isNegative()) {
-    const SourceInfo &Loc = S.Current->getSource(OpPC);
-    S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
-    if (!S.noteUndefinedBehavior())
+    const APSInt Value = RHS.toAPSInt();
+    if (!DiagnoseShiftFailure(S, OpPC, ShiftFailure::NegativeCount, &Value))
       return false;
   }
 
   // C++11 [expr.shift]p1: Shift width must be less than the bit width of
   // the shifted type.
   if (Bits > 1 && RHS >= Bits) {
-    const Expr *E = S.Current->getExpr(OpPC);
-    const APSInt Val = RHS.toAPSInt();
-    QualType Ty = E->getType();
-    S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
-    if (!S.noteUndefinedBehavior())
+    const APSInt Value = RHS.toAPSInt();
+    if (!DiagnoseShiftFailure(S, OpPC, ShiftFailure::TooLarge, &Value, Bits))
       return false;
   }
 
@@ -174,15 +183,13 @@ bool CheckShift(InterpState &S, CodePtr OpPC, const LT 
&LHS, const RT &RHS,
       // C++11 [expr.shift]p2: A signed left shift must have a non-negative
       // operand, and must not overflow the corresponding unsigned type.
       if (LHS.isNegative()) {
-        const Expr *E = S.Current->getExpr(OpPC);
-        S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << 
LHS.toAPSInt();
-        if (!S.noteUndefinedBehavior())
+        const APSInt Value = LHS.toAPSInt();
+        if (!DiagnoseShiftFailure(S, OpPC, ShiftFailure::NegativeLeftOperand,
+                                  &Value))
           return false;
       } else if (LHS.toUnsigned().countLeadingZeros() <
                  static_cast<unsigned>(RHS)) {
-        const Expr *E = S.Current->getExpr(OpPC);
-        S.CCEDiag(E, diag::note_constexpr_lshift_discards);
-        if (!S.noteUndefinedBehavior())
+        if (!DiagnoseShiftFailure(S, OpPC, ShiftFailure::DiscardsBits))
           return false;
       }
     }

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

Reply via email to