[clang] [clang] Allow __builtin_unpredictable in constant expressions (PR #228256)

Paweł Bylica via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 22 03:07:56 PDT 2026


https://github.com/chfast updated https://github.com/llvm/llvm-project/pull/228256

>From 994960fc1d8ecc5da1a6fb5dd83fe4b329bcd2ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pawe=C5=82=20Bylica?= <pawel at hepcolgum.band>
Date: Thu, 1 Oct 2026 14:51:38 +0200
Subject: [PATCH] [clang] Allow __builtin_unpredictable in constant expressions

__builtin_unpredictable returns its argument, but unlike __builtin_expect
and __builtin_expect_with_probability it was not marked Constexpr. Using it
in a constexpr function made the function never produce a constant
expression:

  constexpr int f(int x) {
    if (__builtin_unpredictable(x > 0))
      return 1;
    return 2;
  }
  static_assert(f(1) == 1);

Evaluate it to its argument in both constant evaluators, like
__builtin_expect.

Assisted-by: Claude Code
---
 clang/docs/LanguageExtensions.md              | 3 ++-
 clang/docs/ReleaseNotes.md                    | 3 +++
 clang/include/clang/Basic/Builtins.td         | 2 +-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      | 9 ++++++---
 clang/lib/AST/ExprConstant.cpp                | 1 +
 clang/test/AST/ByteCode/builtin-functions.cpp | 9 +++++++++
 6 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/clang/docs/LanguageExtensions.md b/clang/docs/LanguageExtensions.md
index 3db8d083c40756..a1c6e48b5f26dc 100644
--- a/clang/docs/LanguageExtensions.md
+++ b/clang/docs/LanguageExtensions.md
@@ -3921,7 +3921,8 @@ if (__builtin_unpredictable(x > 0)) {
 **Description**:
 
 The `__builtin_unpredictable()` builtin is expected to be used with control
-flow conditions such as in `if` and `switch` statements.
+flow conditions such as in `if` and `switch` statements. It returns its
+argument and can be used in constant expressions.
 
 Query for this feature with `__has_builtin(__builtin_unpredictable)`.
 
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7fe9977a979c0d..1fbd6b07688ebb 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -339,6 +339,9 @@ documentation](CIR/index.md) for more information regarding ClangIR's status and
   be detected by watching the directories or by comparing their modification
   times.
 
+- `__builtin_unpredictable` can now be used in constant expressions, like
+  `__builtin_expect`.
+
 ### New Compiler Flags
 
 - New option `-fmodules-validate-directory-dependencies` makes an implicitly
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 905c5d29d86e1f..ae0fd5a075c6e2 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1444,7 +1444,7 @@ def VPrintfChk : Builtin {
 
 def Unpredictable : Builtin {
   let Spellings = ["__builtin_unpredictable"];
-  let Attributes = [NoThrow, Const];
+  let Attributes = [NoThrow, Const, Constexpr];
   let Prototype = "long int(long int)";
 }
 
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 96d8cf9b175943..5131ff728ebc39 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -857,18 +857,20 @@ static bool interp__builtin_classify_type(InterpState &S, CodePtr OpPC,
 
 // __builtin_expect(long, long)
 // __builtin_expect_with_probability(long, long, double)
+// __builtin_unpredictable(long)
 static bool interp__builtin_expect(InterpState &S, CodePtr OpPC,
                                    const InterpFrame *Frame,
                                    const CallExpr *Call) {
   // The return value is simply the value of the first parameter.
-  // We ignore the probability.
+  // We ignore the expected value and the probability.
   unsigned NumArgs = Call->getNumArgs();
-  assert(NumArgs == 2 || NumArgs == 3);
+  assert(NumArgs >= 1 && NumArgs <= 3);
 
   PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
   if (NumArgs == 3)
     S.Stk.discard<Floating>();
-  discard(S.Stk, ArgT);
+  if (NumArgs >= 2)
+    discard(S.Stk, ArgT);
   // Top of the stack is now the first paramter. Leave it there as the return
   // value.
 
@@ -4967,6 +4969,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
 
   case Builtin::BI__builtin_expect:
   case Builtin::BI__builtin_expect_with_probability:
+  case Builtin::BI__builtin_unpredictable:
     return interp__builtin_expect(S, OpPC, Frame, Call);
 
   case Builtin::BI__builtin_rotateleft8:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f7e892c6364e5e..a0b2e06b02a1bb 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17640,6 +17640,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
 
   case Builtin::BI__builtin_expect:
   case Builtin::BI__builtin_expect_with_probability:
+  case Builtin::BI__builtin_unpredictable:
     return Visit(E->getArg(0));
 
   case Builtin::BI__builtin_ptrauth_string_discriminator: {
diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp
index 9077a14324b7f5..3295e881956fb3 100644
--- a/clang/test/AST/ByteCode/builtin-functions.cpp
+++ b/clang/test/AST/ByteCode/builtin-functions.cpp
@@ -625,6 +625,15 @@ namespace expect {
   }
   static_assert(__builtin_expect(a(),1) == 12, "");
   static_assert(__builtin_expect_with_probability(a(), 1, 1.0) == 12, "");
+  static_assert(__builtin_unpredictable(a()) == 12, "");
+
+  constexpr int b(int x) {
+    if (__builtin_unpredictable(x > 0))
+      return 1;
+    return 2;
+  }
+  static_assert(b(1) == 1, "");
+  static_assert(b(-1) == 2, "");
 }
 
 namespace rotateleft {



More information about the cfe-commits mailing list