Skip to content

Conversation

ckoparkar
Copy link
Contributor

Fixes #153151.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" llvm:adt clang:bytecode Issues for the clang bytecode constexpr interpreter labels Aug 14, 2025
@ckoparkar
Copy link
Contributor Author

/cc @RKSimon @arsenm

@llvmbot
Copy link
Member

llvmbot commented Aug 14, 2025

@llvm/pr-subscribers-llvm-adt

@llvm/pr-subscribers-clang

Author: Chaitanya Koparkar (ckoparkar)

Changes

Fixes #153151.


Full diff: https://github.com/llvm/llvm-project/pull/153572.diff

6 Files Affected:

  • (modified) clang/include/clang/Basic/Builtins.td (+2-2)
  • (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+81)
  • (modified) clang/lib/AST/ExprConstShared.h (+4)
  • (modified) clang/lib/AST/ExprConstant.cpp (+77)
  • (modified) clang/test/Sema/constant-builtins-vector.cpp (+48)
  • (modified) llvm/include/llvm/ADT/APSInt.h (+6)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 84206cf8b368b..c5ad9a5d59dea 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1516,13 +1516,13 @@ def ElementwiseSubSat : Builtin {
 
 def ElementwiseFshl : Builtin {
   let Spellings = ["__builtin_elementwise_fshl"];
-  let Attributes = [NoThrow, Const, CustomTypeChecking];
+  let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
 def ElementwiseFshr : Builtin {
   let Spellings = ["__builtin_elementwise_fshr"];
-  let Attributes = [NoThrow, Const, CustomTypeChecking];
+  let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
   let Prototype = "void(...)";
 }
 
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 307b77846969f..015f0443ede54 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2399,6 +2399,83 @@ static bool interp__builtin_elementwise_maxmin(InterpState &S, CodePtr OpPC,
   return true;
 }
 
+static bool interp__builtin_elementwise_fsh(InterpState &S, CodePtr OpPC,
+                                            const CallExpr *Call,
+                                            unsigned BuiltinID) {
+  assert(Call->getNumArgs() == 3);
+
+  const QualType Arg1Type = Call->getArg(0)->getType();
+  const QualType Arg2Type = Call->getArg(1)->getType();
+  const QualType Arg3Type = Call->getArg(2)->getType();
+
+  // Non-vector integer types.
+  if (!Arg1Type->isVectorType()) {
+    assert(!Arg2Type->isVectorType());
+    assert(!Arg3Type->isVectorType());
+
+    APSInt Shift = popToAPSInt(
+        S.Stk, *S.getContext().classify(Call->getArg(2)->getType()));
+    APSInt Lo = popToAPSInt(
+        S.Stk, *S.getContext().classify(Call->getArg(1)->getType()));
+    APSInt Hi = popToAPSInt(
+        S.Stk, *S.getContext().classify(Call->getArg(0)->getType()));
+    APSInt Result;
+    if (BuiltinID == Builtin::BI__builtin_elementwise_fshl) {
+      Result = HandleFshl(Hi, Lo, Shift);
+    } else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr) {
+      Result = HandleFshr(Hi, Lo, Shift);
+    } else {
+      llvm_unreachable("Wrong builtin ID");
+    }
+    pushInteger(S, Result, Call->getType());
+    return true;
+  }
+
+  // Vector type.
+  assert(Arg1Type->isVectorType() &&
+         Arg2Type->isVectorType() &&
+         Arg3Type->isVectorType());
+
+  const VectorType *VecT = Arg1Type->castAs<VectorType>();
+  PrimType ElemT = *S.getContext().classify(VecT->getElementType());
+  unsigned NumElems = VecT->getNumElements();
+
+  assert(VecT->getElementType() ==
+         Arg2Type->castAs<VectorType>()->getElementType() &&
+         VecT->getElementType() ==
+         Arg3Type->castAs<VectorType>()->getElementType());
+  assert(NumElems == Arg2Type->castAs<VectorType>()->getNumElements() &&
+         NumElems == Arg3Type->castAs<VectorType>()->getNumElements());
+  assert(VecT->getElementType()->isIntegralOrEnumerationType());
+
+  const Pointer &VecShift = S.Stk.pop<Pointer>();
+  const Pointer &VecLo = S.Stk.pop<Pointer>();
+  const Pointer &VecHi = S.Stk.pop<Pointer>();
+  const Pointer &Dst = S.Stk.peek<Pointer>();
+  for (unsigned I = 0; I != NumElems; ++I) {
+      APSInt Hi;
+      APSInt Lo;
+      APSInt Shift;
+      INT_TYPE_SWITCH_NO_BOOL(ElemT, {
+          Hi = VecHi.elem<T>(I).toAPSInt();
+          Lo = VecLo.elem<T>(I).toAPSInt();
+          Shift = VecShift.elem<T>(I).toAPSInt();
+        });
+      APSInt Result;
+      if (BuiltinID == Builtin::BI__builtin_elementwise_fshl) {
+        Result = HandleFshl(Hi, Lo, Shift);
+      } else if (BuiltinID == Builtin::BI__builtin_elementwise_fshr) {
+        Result = HandleFshr(Hi, Lo, Shift);
+      } else {
+        llvm_unreachable("Wrong builtin ID");
+      }
+      INT_TYPE_SWITCH_NO_BOOL(ElemT,
+                              { Dst.elem<T>(I) = static_cast<T>(Result); });
+  }
+  Dst.initializeAllElements();
+  return true;
+}
+
 bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
                       uint32_t BuiltinID) {
   if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
@@ -2810,6 +2887,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
   case Builtin::BI__builtin_elementwise_min:
     return interp__builtin_elementwise_maxmin(S, OpPC, Call, BuiltinID);
 
+  case Builtin::BI__builtin_elementwise_fshl:
+  case Builtin::BI__builtin_elementwise_fshr:
+    return interp__builtin_elementwise_fsh(S, OpPC, Call, BuiltinID);
+
   default:
     S.FFDiag(S.Current->getLocation(OpPC),
              diag::note_invalid_subexpr_in_const_expr)
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index 401ae629c86bf..a1c5607ced700 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -18,6 +18,7 @@
 
 namespace llvm {
 class APFloat;
+class APSInt;
 }
 namespace clang {
 class QualType;
@@ -71,6 +72,9 @@ void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C,
                              llvm::APFloat D, llvm::APFloat &ResR,
                              llvm::APFloat &ResI);
 
+llvm::APSInt HandleFshl(llvm::APSInt Hi, llvm::APSInt Lo, llvm::APSInt Shift);
+llvm::APSInt HandleFshr(llvm::APSInt Hi, llvm::APSInt Lo, llvm::APSInt Shift);
+
 CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
                          UnaryExprOrTypeTrait ExprKind);
 
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 36dd0f5d7a065..083d84f7375fe 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11723,6 +11723,39 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
 
     return Success(APValue(ResultElements.data(), ResultElements.size()), E);
   }
+  case Builtin::BI__builtin_elementwise_fshl:
+  case Builtin::BI__builtin_elementwise_fshr: {
+    APValue SourceHi, SourceLo, SourceShift;
+    if (!EvaluateAsRValue(Info, E->getArg(0), SourceHi) ||
+        !EvaluateAsRValue(Info, E->getArg(1), SourceLo) ||
+        !EvaluateAsRValue(Info, E->getArg(2), SourceShift))
+      return false;
+
+    QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType();
+
+    if (!DestEltTy->isIntegerType())
+      return false;
+
+    unsigned SourceLen = SourceHi.getVectorLength();
+    SmallVector<APValue> ResultElements;
+    ResultElements.reserve(SourceLen);
+
+    for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
+      APSInt Hi = SourceHi.getVectorElt(EltNum).getInt();
+      APSInt Lo = SourceLo.getVectorElt(EltNum).getInt();
+      APSInt Shift = SourceShift.getVectorElt(EltNum).getInt();
+      switch (E->getBuiltinCallee()) {
+      case Builtin::BI__builtin_elementwise_fshl:
+        ResultElements.push_back(APValue(HandleFshl(Hi, Lo, Shift)));
+        break;
+      case Builtin::BI__builtin_elementwise_fshr:
+        ResultElements.push_back(APValue(HandleFshr(Hi, Lo, Shift)));
+        break;
+      }
+    }
+
+    return Success(APValue(ResultElements.data(), ResultElements.size()), E);
+  }
   }
 }
 
@@ -13638,6 +13671,28 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
     APInt Result = std::min(LHS, RHS);
     return Success(APSInt(Result, !LHS.isSigned()), E);
   }
+  case Builtin::BI__builtin_elementwise_fshl:
+  case Builtin::BI__builtin_elementwise_fshr: {
+    if (!E->getArg(0)->isPRValue() ||
+        !E->getArg(1)->isPRValue() ||
+        !E->getArg(2)->isPRValue())
+      return false;
+    APSInt Hi, Lo, Shift;
+    if (!EvaluateInteger(E->getArg(0), Hi, Info) ||
+        !EvaluateInteger(E->getArg(1), Lo, Info) ||
+        !EvaluateInteger(E->getArg(2), Shift, Info))
+      return false;
+    switch (BuiltinOp) {
+    case Builtin::BI__builtin_elementwise_fshl: {
+      APSInt Result = HandleFshl(Hi, Lo, Shift);
+      return Success(Result, E);
+    }
+    case Builtin::BI__builtin_elementwise_fshr: {
+      APSInt Result = HandleFshr(Hi, Lo, Shift);
+      return Success(Result, E);
+    }
+    }
+  }
   case Builtin::BIstrlen:
   case Builtin::BIwcslen:
     // A call to strlen is not a constant expression.
@@ -16405,6 +16460,28 @@ void HandleComplexComplexDiv(APFloat A, APFloat B, APFloat C, APFloat D,
   }
 }
 
+// fshl(X,Y,Z): (X << (Z % BW)) | (Y >> (BW - (Z % BW)))
+APSInt HandleFshl(APSInt Hi, APSInt Lo, APSInt Shift) {
+  bool IsUnsigned = Hi.isUnsigned();
+  APSInt BitWidth(llvm::APInt(Hi.getBitWidth(),
+                              static_cast<uint64_t>(Hi.getBitWidth())),
+                  IsUnsigned);
+  return APSInt((Hi << (Shift % BitWidth)) |
+                (Lo >> (BitWidth - (Shift % BitWidth))),
+                IsUnsigned);
+}
+
+// fshr(X,Y,Z): (X << (BW - (Z % BW))) | (Y >> (Z % BW))
+APSInt HandleFshr(APSInt Hi, APSInt Lo, APSInt Shift) {
+  bool IsUnsigned = Hi.isUnsigned();
+  APSInt BitWidth(llvm::APInt(Hi.getBitWidth(),
+                              static_cast<uint64_t>(Hi.getBitWidth())),
+                  IsUnsigned);
+  return APSInt((Hi << (BitWidth - (Shift % BitWidth))) |
+                (Lo >> (Shift % BitWidth)),
+                IsUnsigned);
+}
+
 bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
   if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
     return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
diff --git a/clang/test/Sema/constant-builtins-vector.cpp b/clang/test/Sema/constant-builtins-vector.cpp
index bc575dca98d77..c5f9c3956780e 100644
--- a/clang/test/Sema/constant-builtins-vector.cpp
+++ b/clang/test/Sema/constant-builtins-vector.cpp
@@ -876,3 +876,51 @@ static_assert(__builtin_elementwise_min(~0U, 0U) == 0U);
 static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4char){1, -2, 3, -4}, (vector4char){4, -3, 2, -1})) == (LITTLE_END ? 0xFC02FD01 : 0x01FD02FC));
 static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4uchar){1, 2, 3, 4}, (vector4uchar){4, 3, 2, 1})) == 0x01020201U);
 static_assert(__builtin_bit_cast(unsigned long long, __builtin_elementwise_min((vector4short){1, -2, 3, -4}, (vector4short){4, -3, 2, -1})) == (LITTLE_END ? 0xFFFC0002FFFD0001 : 0x0001FFFD0002FFFC));
+
+static_assert(__builtin_elementwise_fshl((unsigned char)255, (unsigned char)0, (unsigned char)8) == (unsigned char)255);
+static_assert(__builtin_elementwise_fshl((char)127, (char)0, (char)8) == (char)127);
+static_assert(__builtin_elementwise_fshl((unsigned char)0, (unsigned char)255, (unsigned char)8) == (unsigned char)0);
+static_assert(__builtin_elementwise_fshl((char)0, (char)127, (char)8) == (char)0);
+static_assert(__builtin_elementwise_fshr((unsigned char)255, (unsigned char)0, (unsigned char)8) == (unsigned char)0);
+static_assert(__builtin_elementwise_fshr((char)127, (char)0, (char)8) == (char)0);
+static_assert(__builtin_elementwise_fshr((unsigned char)0, (unsigned char)255, (unsigned char)8) == (unsigned char)255);
+static_assert(__builtin_elementwise_fshr((char)0, (char)127, (char)8) == (char)127);
+static_assert(__builtin_elementwise_fshl((unsigned int)4294967295, (unsigned int)0, (unsigned int)32) == (unsigned int)4294967295);
+static_assert(__builtin_elementwise_fshl((int)2147483647, (int)0, (int)32) == (int)2147483647);
+static_assert(__builtin_elementwise_fshl((unsigned int)0, (unsigned int)4294967295, (unsigned int)32) == (unsigned int)0);
+static_assert(__builtin_elementwise_fshl((int)0, (int)2147483647, (int)32) == (int)0);
+static_assert(__builtin_elementwise_fshr((unsigned int)4294967295, (unsigned int)0, (unsigned int)32) == (unsigned int)0);
+static_assert(__builtin_elementwise_fshr((int)2147483647, (int)0, (int)32) == (int)0);
+static_assert(__builtin_elementwise_fshr((unsigned int)0, (unsigned int)4294967295, (unsigned int)32) == (unsigned int)4294967295);
+static_assert(__builtin_elementwise_fshr((int)0, (int)2147483647, (int)32) == (int)2147483647);
+static_assert(__builtin_elementwise_fshl((unsigned long long)18446744073709551615ULL, (unsigned long long)0, (unsigned long long)64) == (unsigned long long)18446744073709551615ULL);
+static_assert(__builtin_elementwise_fshl((long long)9223372036854775807, (long long)0, (long long)64) == (long long)9223372036854775807);
+static_assert(__builtin_elementwise_fshl((unsigned long long)0, (unsigned long long)18446744073709551615ULL, (unsigned long long)64) == (unsigned long long)0);
+static_assert(__builtin_elementwise_fshl((long long)0, (long long)9223372036854775807, (long long)64) == (long long)0);
+static_assert(__builtin_elementwise_fshr((unsigned long long)18446744073709551615ULL, (unsigned long long)0, (unsigned long long)64) == (unsigned long long)0);
+static_assert(__builtin_elementwise_fshr((long long)9223372036854775807, (long long)0, (long long)64) == (long long)0);
+static_assert(__builtin_elementwise_fshr((unsigned long long)0, (unsigned long long)18446744073709551615ULL, (unsigned long long)64) == (unsigned long long)18446744073709551615ULL);
+static_assert(__builtin_elementwise_fshr((long long)0, (long long)9223372036854775807, (long long)64) == (long long)9223372036854775807);
+//
+static_assert(__builtin_elementwise_fshl((short) 1, (short) 2, (short) 3) == (short)8);
+static_assert(__builtin_elementwise_fshl((short) 2, (short) 1, (short) 3) == (short)16);
+static_assert(__builtin_elementwise_fshl(1, 2 , 2) == 4);
+static_assert(__builtin_elementwise_fshl(2L, 1L , 2L) == 8L);
+static_assert(__builtin_elementwise_fshr((unsigned char)1, (unsigned char)2, (unsigned char)3) == (unsigned char)32);
+//
+constexpr vector4uchar v4s_fshl_var =
+  __builtin_elementwise_fshl((vector4uchar){255, 15, 0, 2},
+                             (vector4uchar){0, 15, 255, 1},
+                             (vector4uchar){15, 11, 8, 3});
+static_assert(v4s_fshl_var[0] == 128);
+static_assert(v4s_fshl_var[1] == 120);
+static_assert(v4s_fshl_var[2] == 0);
+static_assert(v4s_fshl_var[3] == 16);
+constexpr vector4uchar v4s_fshr_var =
+  __builtin_elementwise_fshr((vector4uchar){255, 15, 0, 1},
+                             (vector4uchar){0, 15, 255, 2},
+                             (vector4uchar){15, 11, 8, 3});
+static_assert(v4s_fshr_var[0] == 254);
+static_assert(v4s_fshr_var[1] == 225);
+static_assert(v4s_fshr_var[2] == 255);
+static_assert(v4s_fshr_var[3] == 32);
diff --git a/llvm/include/llvm/ADT/APSInt.h b/llvm/include/llvm/ADT/APSInt.h
index 88a7a6e71c817..6112abed92100 100644
--- a/llvm/include/llvm/ADT/APSInt.h
+++ b/llvm/include/llvm/ADT/APSInt.h
@@ -152,6 +152,9 @@ class [[nodiscard]] APSInt : public APInt {
   APSInt operator>>(unsigned Amt) const {
     return IsUnsigned ? APSInt(lshr(Amt), true) : APSInt(ashr(Amt), false);
   }
+  APSInt operator>>(const APSInt &Amt) const {
+    return IsUnsigned ? APSInt(lshr(Amt), true) : APSInt(ashr(Amt), false);
+  }
   APSInt &operator>>=(unsigned Amt) {
     if (IsUnsigned)
       lshrInPlace(Amt);
@@ -211,6 +214,9 @@ class [[nodiscard]] APSInt : public APInt {
   APSInt operator<<(unsigned Bits) const {
     return APSInt(static_cast<const APInt &>(*this) << Bits, IsUnsigned);
   }
+  APSInt operator<<(const APSInt &Amt) const {
+    return APSInt(static_cast<const APInt &>(*this) << Amt, IsUnsigned);
+  }
   APSInt &operator<<=(unsigned Amt) {
     static_cast<APInt &>(*this) <<= Amt;
     return *this;

@RKSimon RKSimon requested review from el-ev and RKSimon August 14, 2025 12:54
Copy link
Member

@kuhar kuhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move the ADT changes to a separate PR and add unit tests?

@ckoparkar
Copy link
Contributor Author

Can you move the ADT changes to a separate PR and add unit tests?

@kuhar Will do. I'll wait for some reviews on the usage of that ADT change to be sure that it's actually going to be required for this and then create that separate PR.

@RKSimon
Copy link
Collaborator

RKSimon commented Aug 14, 2025

APIntOps fshl/r might be cleaner.

@ckoparkar
Copy link
Contributor Author

Sounds like the implementation of fshl/r looks OK. Let me create another PR for the ADT changes like kuhar suggested, I'll add the fshl/r functions there.

@ckoparkar
Copy link
Contributor Author

Working on updating this to use the new fshl/r APIntOps.

Copy link

github-actions bot commented Aug 22, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp -- clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ExprConstant.cpp clang/test/Sema/constant-builtins-vector.cpp
View the diff from clang-format here.
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 651447ffa..c40da9c46 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2857,7 +2857,7 @@ static bool interp__builtin_elementwise_fsh(InterpState &S, CodePtr OpPC,
     assert(!Arg1Type->isVectorType());
     assert(!Arg2Type->isVectorType());
     const APSInt &Shift =
-      popToAPSInt(S.Stk, *S.getContext().classify(Arg2Type));
+        popToAPSInt(S.Stk, *S.getContext().classify(Arg2Type));
     const APSInt &Lo = popToAPSInt(S.Stk, *S.getContext().classify(Arg1Type));
     const APSInt &Hi = popToAPSInt(S.Stk, *S.getContext().classify(Arg0Type));
     APSInt Result;

@ckoparkar
Copy link
Contributor Author

The other PRs this required have been merged, this is ready for review again.

@RKSimon RKSimon requested a review from tbaederr August 25, 2025 18:00
assert(VecT->getElementType() ==
Arg2Type->castAs<VectorType>()->getElementType() &&
VecT->getElementType() ==
Arg3Type->castAs<VectorType>()->getElementType());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Can you add these assertions in the current interpreter as well?
  2. I think they aren't exactly correct and will fail if one vector is of type int and the other of type const int. However, I'm not sure if that's even possible atm.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, let me check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These types are not considered equal much before constexpr evaluation:

typedef const short vector4cshort __attribute__((__vector_size__(8)));
typedef short vector4short __attribute__((__vector_size__(8)));
error: arguments are of different types ('vector4cshort' (vector of 4 'const short' values) vs 'vector4short' (vector of 4 'short' values))
   25 |   constexpr auto chk = __builtin_elementwise_fshl(x,y,z);
      |                                                     ^

Do you think I should still add these assertions to the current interpreter?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think the assertions still make sense in both cases.

I think we should also use ASTContext::hasSameUnqualifiedType() in Sema::BuiltinElementwiseTernaryMath() like we do in BuiltinVectorMath().

See also 4d650ef, but that can be a follow-up commit (which will have to adjust the assertions as well).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I didn't clock this behavior as incorrect. But it might be better to resolve this first rather than changing the assertions later? I think the only other ternary op we've made constexpr is fma. Let me file an issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #155405.

@ckoparkar ckoparkar marked this pull request as draft August 28, 2025 11:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:bytecode Issues for the clang bytecode constexpr interpreter clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category llvm:adt
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Clang] Allow __builtin_elementwise_fshl/fshr builtin intrinsics to be used in constexpr
7 participants