[clang] [clang] Constexpr for __builtin_shufflevector and __builtin_convertvector (PR #76615)

Pol M via cfe-commits cfe-commits at lists.llvm.org
Sun Mar 31 09:20:59 PDT 2024


Pol Marcet =?utf-8?q?Sardà?= <polmarcetsarda at gmail.com>,
Pol Marcet =?utf-8?q?Sardà?= <polmarcetsarda at gmail.com>,Pol M
 <polmarcetsarda at gmail.com>,
Pol Marcet =?utf-8?q?Sardà?= <polmarcetsarda at gmail.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/76615 at github.com>


https://github.com/Destroyerrrocket updated https://github.com/llvm/llvm-project/pull/76615

>From cba67a73ea1e59eb8eeb4e702d77f329028f4c22 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pol=20Marcet=20Sard=C3=A0?= <polmarcetsarda at gmail.com>
Date: Sat, 30 Dec 2023 13:59:00 +0100
Subject: [PATCH 1/5] [clang] Constexpr for __builtin_shufflevector and
 __builtin_convertvector

Summary:

This patch adds constexpr support for __builtin_shufflevector
and __builtin_convertvector.

A small oddity encountered was that the arg to the intrinsics may be an
lvalue without any sort of implicit cast of any kind. I solved this
through the EvaluateVectorOrLValue function, which treats the lvalue as
if it was in an rvalue cast, which gets me the desired vector.
---
 clang/docs/LanguageExtensions.rst     |   5 +-
 clang/docs/ReleaseNotes.rst           |   2 +
 clang/lib/AST/ExprConstant.cpp        | 138 +++++++++++++++++++++++++-
 clang/test/Sema/constant-builtins-2.c |  61 ++++++++++++
 4 files changed, 203 insertions(+), 3 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 23a7f4f5d5b926..5f06c3d4b86f94 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -2853,7 +2853,7 @@ Query for this feature with ``__has_builtin(__builtin_dump_struct)``
 ``__builtin_shufflevector`` is used to express generic vector
 permutation/shuffle/swizzle operations.  This builtin is also very important
 for the implementation of various target-specific header files like
-``<xmmintrin.h>``.
+``<xmmintrin.h>``. This builtin can be used within constant expressions.
 
 **Syntax**:
 
@@ -2907,7 +2907,8 @@ Query for this feature with ``__has_builtin(__builtin_shufflevector)``.
 
 ``__builtin_convertvector`` is used to express generic vector
 type-conversion operations. The input vector and the output vector
-type must have the same number of elements.
+type must have the same number of elements. This builtin can be used within
+constant expressions.
 
 **Syntax**:
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0c8fec691bf3c9..b6f1407436ed4f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -237,6 +237,8 @@ Non-comprehensive list of changes in this release
 * Since MSVC 19.33 added undocumented attribute ``[[msvc::constexpr]]``, this release adds the attribute as well.
 
 * Added ``#pragma clang fp reciprocal``.
+* Builtins ``__builtin_shufflevector()`` and ``__builtin_convertvector()`` may now be used within constant
+ expressions.
 
 New Compiler Flags
 ------------------
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f6aeee1a4e935d..e8afa10fe7aaac 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2702,7 +2702,8 @@ static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E,
 static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
                                    QualType SrcType, QualType DestType,
                                    APFloat &Result) {
-  assert(isa<CastExpr>(E) || isa<CompoundAssignOperator>(E));
+  assert(isa<CastExpr>(E) || isa<CompoundAssignOperator>(E) ||
+         isa<ConvertVectorExpr>(E));
   llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
   APFloat::opStatus St;
   APFloat Value = Result;
@@ -10643,6 +10644,9 @@ namespace {
     bool VisitUnaryImag(const UnaryOperator *E);
     bool VisitBinaryOperator(const BinaryOperator *E);
     bool VisitUnaryOperator(const UnaryOperator *E);
+    bool VisitConvertVectorExpr(const ConvertVectorExpr *E);
+    bool VisitShuffleVectorExpr(const ShuffleVectorExpr *E);
+
     // FIXME: Missing: conditional operator (for GNU
     //                 conditional select), shufflevector, ExtVectorElementExpr
   };
@@ -10895,6 +10899,138 @@ bool VectorExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
   return Success(APValue(ResultElements.data(), ResultElements.size()), E);
 }
 
+static bool EvaluateVectorOrLValue(APValue &Result, EvalInfo &Info,
+                                   const Expr *E, const QualType &Type) {
+  if (!Evaluate(Result, Info, E))
+    return false;
+
+  if (Result.isLValue()) {
+    // Source of the data is an lvalue; Manually handle the lvalue as if
+    // it was an rvalue to get the current APValue.
+    LValue LValueFound;
+    LValueFound.setFrom(Info.Ctx, Result);
+    if (!handleLValueToRValueConversion(Info, E, Type, LValueFound, Result)) {
+      return false;
+    }
+  }
+
+  if (!Result.isVector()) {
+    return false;
+  }
+  return true;
+}
+
+static bool handleVectorConversion(EvalInfo &Info, const FPOptions FPO,
+                                   const Expr *E, QualType SourceTy,
+                                   QualType DestTy, APValue const &Original,
+                                   APValue &Result) {
+  if (SourceTy->isIntegerType()) {
+    if (DestTy->isRealFloatingType()) {
+      Result = APValue(APFloat(0.0));
+      return HandleIntToFloatCast(Info, E, FPO, SourceTy, Original.getInt(),
+                                  DestTy, Result.getFloat());
+    }
+    if (DestTy->isIntegerType()) {
+      Result = APValue(
+          HandleIntToIntCast(Info, E, DestTy, SourceTy, Original.getInt()));
+      return true;
+    }
+  } else if (SourceTy->isRealFloatingType()) {
+    if (DestTy->isRealFloatingType()) {
+      Result = Original;
+      return HandleFloatToFloatCast(Info, E, SourceTy, DestTy,
+                                    Result.getFloat());
+    }
+    if (DestTy->isIntegerType()) {
+      Result = APValue(APSInt());
+      return HandleFloatToIntCast(Info, E, SourceTy, Original.getFloat(),
+                                  DestTy, Result.getInt());
+    }
+  }
+  return false;
+}
+
+bool VectorExprEvaluator::VisitConvertVectorExpr(const ConvertVectorExpr *E) {
+  APValue Source;
+  QualType SourceVecType = E->getSrcExpr()->getType();
+  if (!EvaluateVectorOrLValue(Source, Info, E->getSrcExpr(), SourceVecType))
+    return false;
+
+  QualType DestTy = E->getType()->castAs<VectorType>()->getElementType();
+  QualType SourceTy = SourceVecType->castAs<VectorType>()->getElementType();
+
+  const FPOptions FPO = E->getFPFeaturesInEffect(Info.Ctx.getLangOpts());
+
+  SmallVector<APValue, 4> ResultElements;
+  ResultElements.reserve(Source.getVectorLength());
+  for (unsigned EltNum = 0; EltNum < Source.getVectorLength(); ++EltNum) {
+    APValue Elt;
+    if (!handleVectorConversion(Info, FPO, E, SourceTy, DestTy,
+                                Source.getVectorElt(EltNum), Elt))
+      return false;
+    ResultElements.push_back(std::move(Elt));
+  }
+
+  return Success(APValue(ResultElements.data(), ResultElements.size()), E);
+}
+
+static bool handleVectorShuffle(EvalInfo &Info, const ShuffleVectorExpr *E,
+                                QualType ElemType, APValue const &VecVal1,
+                                APValue const &VecVal2, unsigned EltNum,
+                                APValue &Result) {
+  unsigned const TotalElementsInAVector = VecVal1.getVectorLength();
+
+  Expr const *IndexExpr = E->getExpr(2 + EltNum);
+  APSInt IndexVal;
+  if (!EvaluateInteger(IndexExpr, IndexVal, Info)) {
+    return false;
+  }
+
+  uint32_t index = IndexVal.getZExtValue();
+  // The spec says that -1 should be treated as undef for optimizations,
+  // but in constexpr we need to choose a value. We'll choose 0.
+  if (index >= TotalElementsInAVector * 2) {
+    index = 0;
+  }
+
+  if (index >= TotalElementsInAVector) {
+    Result = VecVal2.getVectorElt(index - TotalElementsInAVector);
+  } else {
+    Result = VecVal1.getVectorElt(index);
+  }
+  return true;
+}
+
+bool VectorExprEvaluator::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) {
+  APValue VecVal1;
+  const Expr *Vec1 = E->getExpr(0);
+  if (!EvaluateVectorOrLValue(VecVal1, Info, Vec1, Vec1->getType()))
+    return false;
+  APValue VecVal2;
+  const Expr *Vec2 = E->getExpr(1);
+  if (!EvaluateVectorOrLValue(VecVal2, Info, Vec2, Vec2->getType()))
+    return false;
+
+  VectorType const *DestVecTy = E->getType()->castAs<VectorType>();
+  if (!DestVecTy) {
+    return false;
+  }
+  QualType DestElTy = DestVecTy->getElementType();
+
+  auto TotalElementsInOutputVector = DestVecTy->getNumElements();
+
+  SmallVector<APValue, 4> ResultElements;
+  ResultElements.reserve(TotalElementsInOutputVector);
+  for (unsigned EltNum = 0; EltNum < TotalElementsInOutputVector; ++EltNum) {
+    APValue Elt;
+    if (!handleVectorShuffle(Info, E, DestElTy, VecVal1, VecVal2, EltNum, Elt))
+      return false;
+    ResultElements.push_back(std::move(Elt));
+  }
+
+  return Success(APValue(ResultElements.data(), ResultElements.size()), E);
+}
+
 //===----------------------------------------------------------------------===//
 // Array Evaluation
 //===----------------------------------------------------------------------===//
diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c
index 2bdd7b06daabfe..c6bb602149e193 100644
--- a/clang/test/Sema/constant-builtins-2.c
+++ b/clang/test/Sema/constant-builtins-2.c
@@ -302,3 +302,64 @@ extern __typeof__(__builtin_expect(0, 0)) bi0;
 // Strings
 int array1[__builtin_strlen("ab\0cd")];
 int array2[(sizeof(array1)/sizeof(int)) == 2? 1 : -1];
+
+typedef double vector4double __attribute__((__vector_size__(32)));
+typedef float vector4float __attribute__((__vector_size__(16)));
+typedef long long vector4long __attribute__((__vector_size__(32)));
+typedef int vector4int __attribute__((__vector_size__(16)));
+typedef short vector4short __attribute__((__vector_size__(8)));
+typedef char vector4char __attribute__((__vector_size__(4)));
+typedef double vector8double __attribute__((__vector_size__(64)));
+typedef float vector8float __attribute__((__vector_size__(32)));
+typedef long long vector8long __attribute__((__vector_size__(64)));
+typedef int vector8int __attribute__((__vector_size__(32)));
+typedef short vector8short __attribute__((__vector_size__(16)));
+typedef char vector8char __attribute__((__vector_size__(8)));
+
+// Convert vector
+#define CHECK_NUM(__size, __typeFrom, __typeTo, ...)                            \
+  vector##__size##__typeTo                                                      \
+      from_##vector##__size##__typeFrom##_to_##vector##__size##__typeTo##_var = \
+          __builtin_convertvector((vector##__size##__typeFrom){__VA_ARGS__},    \
+                                  vector##__size##__typeTo);
+#define CHECK_TO_ALL_TYPES(__size, __typeFrom, ...)                            \
+  CHECK_NUM(__size, __typeFrom, double, __VA_ARGS__)                           \
+  CHECK_NUM(__size, __typeFrom, float, __VA_ARGS__)                            \
+  CHECK_NUM(__size, __typeFrom, long, __VA_ARGS__)                             \
+  CHECK_NUM(__size, __typeFrom, int, __VA_ARGS__)                              \
+  CHECK_NUM(__size, __typeFrom, short, __VA_ARGS__)                            \
+  CHECK_NUM(__size, __typeFrom, char, __VA_ARGS__)
+
+#define CHECK_ALL_COMBINATIONS(__size, ...)                                    \
+  CHECK_TO_ALL_TYPES(__size, double, __VA_ARGS__)                              \
+  CHECK_TO_ALL_TYPES(__size, float, __VA_ARGS__)                               \
+  CHECK_TO_ALL_TYPES(__size, long, __VA_ARGS__)                                \
+  CHECK_TO_ALL_TYPES(__size, int, __VA_ARGS__)                                 \
+  CHECK_TO_ALL_TYPES(__size, short, __VA_ARGS__)                               \
+  CHECK_TO_ALL_TYPES(__size, char, __VA_ARGS__)
+
+CHECK_ALL_COMBINATIONS(4, 0, 1, 2, 3);
+CHECK_ALL_COMBINATIONS(8, 0, 1, 2, 3, 4, 5, 6, 7);
+#undef CHECK_ALL_COMBINATIONS
+#undef CHECK_TO_ALL_TYPES
+#undef CHECK_NUM
+
+// Shuffle vector
+vector4int const vector4intConst1 = {0, 1, 2, 3};
+vector4int const vector4intConst2 = {4, 5, 6, 7};
+vector8int const vector8intConst = {};
+
+vector4int vectorShuffle1 =
+    __builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 1, 2, 3);
+vector4int vectorShuffle2 =
+    __builtin_shufflevector(vector4intConst1, vector4intConst2, 4, 5, 6, 7);
+vector4int vectorShuffle3 =
+    __builtin_shufflevector(vector4intConst1, vector4intConst2, -1, -1, -1, -1);
+vector4int vectorShuffle4 =
+    __builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 2, 4, 6);
+vector8int vectorShuffle5 = __builtin_shufflevector(
+    vector8intConst, vector8intConst, 0, 2, 4, 6, 8, 10, 12, 14);
+vector4int vectorShuffle6 = __builtin_shufflevector(
+    vector8intConst, vector8intConst, 0, 2, 4, 6);
+vector8int vectorShuffle7 =
+    __builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 2, 4, 6, 1, 3, 5, 7);

>From 28177a86c8bdfc1c0f0077419fd2def4a1854c3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pol=20Marcet=20Sard=C3=A0?= <polmarcetsarda at gmail.com>
Date: Thu, 4 Jan 2024 11:26:08 +0100
Subject: [PATCH 2/5] Address suggestions from RKSimon

---
 clang/lib/AST/ExprConstant.cpp              | 28 +++---
 clang/test/Sema/constant-builtins-2.c       | 61 -------------
 clang/test/Sema/constat_builtins_vector.cpp | 99 +++++++++++++++++++++
 3 files changed, 110 insertions(+), 78 deletions(-)
 create mode 100644 clang/test/Sema/constat_builtins_vector.cpp

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index e8afa10fe7aaac..711d306ceb8e58 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10909,15 +10909,11 @@ static bool EvaluateVectorOrLValue(APValue &Result, EvalInfo &Info,
     // it was an rvalue to get the current APValue.
     LValue LValueFound;
     LValueFound.setFrom(Info.Ctx, Result);
-    if (!handleLValueToRValueConversion(Info, E, Type, LValueFound, Result)) {
+    if (!handleLValueToRValueConversion(Info, E, Type, LValueFound, Result))
       return false;
-    }
   }
 
-  if (!Result.isVector()) {
-    return false;
-  }
-  return true;
+  return Result.isVector();
 }
 
 static bool handleVectorConversion(EvalInfo &Info, const FPOptions FPO,
@@ -10961,9 +10957,10 @@ bool VectorExprEvaluator::VisitConvertVectorExpr(const ConvertVectorExpr *E) {
 
   const FPOptions FPO = E->getFPFeaturesInEffect(Info.Ctx.getLangOpts());
 
+  auto SourceLen = Source.getVectorLength();
   SmallVector<APValue, 4> ResultElements;
-  ResultElements.reserve(Source.getVectorLength());
-  for (unsigned EltNum = 0; EltNum < Source.getVectorLength(); ++EltNum) {
+  ResultElements.reserve(SourceLen);
+  for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
     APValue Elt;
     if (!handleVectorConversion(Info, FPO, E, SourceTy, DestTy,
                                 Source.getVectorElt(EltNum), Elt))
@@ -10982,22 +10979,19 @@ static bool handleVectorShuffle(EvalInfo &Info, const ShuffleVectorExpr *E,
 
   Expr const *IndexExpr = E->getExpr(2 + EltNum);
   APSInt IndexVal;
-  if (!EvaluateInteger(IndexExpr, IndexVal, Info)) {
+  if (!EvaluateInteger(IndexExpr, IndexVal, Info))
     return false;
-  }
 
   uint32_t index = IndexVal.getZExtValue();
   // The spec says that -1 should be treated as undef for optimizations,
   // but in constexpr we need to choose a value. We'll choose 0.
-  if (index >= TotalElementsInAVector * 2) {
+  if (index >= TotalElementsInAVector * 2)
     index = 0;
-  }
 
-  if (index >= TotalElementsInAVector) {
+  if (index >= TotalElementsInAVector)
     Result = VecVal2.getVectorElt(index - TotalElementsInAVector);
-  } else {
+  else
     Result = VecVal1.getVectorElt(index);
-  }
   return true;
 }
 
@@ -11012,9 +11006,9 @@ bool VectorExprEvaluator::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) {
     return false;
 
   VectorType const *DestVecTy = E->getType()->castAs<VectorType>();
-  if (!DestVecTy) {
+  if (!DestVecTy)
     return false;
-  }
+
   QualType DestElTy = DestVecTy->getElementType();
 
   auto TotalElementsInOutputVector = DestVecTy->getNumElements();
diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c
index c6bb602149e193..2bdd7b06daabfe 100644
--- a/clang/test/Sema/constant-builtins-2.c
+++ b/clang/test/Sema/constant-builtins-2.c
@@ -302,64 +302,3 @@ extern __typeof__(__builtin_expect(0, 0)) bi0;
 // Strings
 int array1[__builtin_strlen("ab\0cd")];
 int array2[(sizeof(array1)/sizeof(int)) == 2? 1 : -1];
-
-typedef double vector4double __attribute__((__vector_size__(32)));
-typedef float vector4float __attribute__((__vector_size__(16)));
-typedef long long vector4long __attribute__((__vector_size__(32)));
-typedef int vector4int __attribute__((__vector_size__(16)));
-typedef short vector4short __attribute__((__vector_size__(8)));
-typedef char vector4char __attribute__((__vector_size__(4)));
-typedef double vector8double __attribute__((__vector_size__(64)));
-typedef float vector8float __attribute__((__vector_size__(32)));
-typedef long long vector8long __attribute__((__vector_size__(64)));
-typedef int vector8int __attribute__((__vector_size__(32)));
-typedef short vector8short __attribute__((__vector_size__(16)));
-typedef char vector8char __attribute__((__vector_size__(8)));
-
-// Convert vector
-#define CHECK_NUM(__size, __typeFrom, __typeTo, ...)                            \
-  vector##__size##__typeTo                                                      \
-      from_##vector##__size##__typeFrom##_to_##vector##__size##__typeTo##_var = \
-          __builtin_convertvector((vector##__size##__typeFrom){__VA_ARGS__},    \
-                                  vector##__size##__typeTo);
-#define CHECK_TO_ALL_TYPES(__size, __typeFrom, ...)                            \
-  CHECK_NUM(__size, __typeFrom, double, __VA_ARGS__)                           \
-  CHECK_NUM(__size, __typeFrom, float, __VA_ARGS__)                            \
-  CHECK_NUM(__size, __typeFrom, long, __VA_ARGS__)                             \
-  CHECK_NUM(__size, __typeFrom, int, __VA_ARGS__)                              \
-  CHECK_NUM(__size, __typeFrom, short, __VA_ARGS__)                            \
-  CHECK_NUM(__size, __typeFrom, char, __VA_ARGS__)
-
-#define CHECK_ALL_COMBINATIONS(__size, ...)                                    \
-  CHECK_TO_ALL_TYPES(__size, double, __VA_ARGS__)                              \
-  CHECK_TO_ALL_TYPES(__size, float, __VA_ARGS__)                               \
-  CHECK_TO_ALL_TYPES(__size, long, __VA_ARGS__)                                \
-  CHECK_TO_ALL_TYPES(__size, int, __VA_ARGS__)                                 \
-  CHECK_TO_ALL_TYPES(__size, short, __VA_ARGS__)                               \
-  CHECK_TO_ALL_TYPES(__size, char, __VA_ARGS__)
-
-CHECK_ALL_COMBINATIONS(4, 0, 1, 2, 3);
-CHECK_ALL_COMBINATIONS(8, 0, 1, 2, 3, 4, 5, 6, 7);
-#undef CHECK_ALL_COMBINATIONS
-#undef CHECK_TO_ALL_TYPES
-#undef CHECK_NUM
-
-// Shuffle vector
-vector4int const vector4intConst1 = {0, 1, 2, 3};
-vector4int const vector4intConst2 = {4, 5, 6, 7};
-vector8int const vector8intConst = {};
-
-vector4int vectorShuffle1 =
-    __builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 1, 2, 3);
-vector4int vectorShuffle2 =
-    __builtin_shufflevector(vector4intConst1, vector4intConst2, 4, 5, 6, 7);
-vector4int vectorShuffle3 =
-    __builtin_shufflevector(vector4intConst1, vector4intConst2, -1, -1, -1, -1);
-vector4int vectorShuffle4 =
-    __builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 2, 4, 6);
-vector8int vectorShuffle5 = __builtin_shufflevector(
-    vector8intConst, vector8intConst, 0, 2, 4, 6, 8, 10, 12, 14);
-vector4int vectorShuffle6 = __builtin_shufflevector(
-    vector8intConst, vector8intConst, 0, 2, 4, 6);
-vector8int vectorShuffle7 =
-    __builtin_shufflevector(vector4intConst1, vector4intConst2, 0, 2, 4, 6, 1, 3, 5, 7);
diff --git a/clang/test/Sema/constat_builtins_vector.cpp b/clang/test/Sema/constat_builtins_vector.cpp
new file mode 100644
index 00000000000000..afd5904e7afeab
--- /dev/null
+++ b/clang/test/Sema/constat_builtins_vector.cpp
@@ -0,0 +1,99 @@
+// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only %s
+// expected-no-diagnostics
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define LITTLE_END 1
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define LITTLE_END 0
+#else
+#error "huh?"
+#endif
+
+typedef double vector4double __attribute__((__vector_size__(32)));
+typedef float vector4float __attribute__((__vector_size__(16)));
+typedef long long vector4long __attribute__((__vector_size__(32)));
+typedef int vector4int __attribute__((__vector_size__(16)));
+typedef short vector4short __attribute__((__vector_size__(8)));
+typedef char vector4char __attribute__((__vector_size__(4)));
+typedef double vector8double __attribute__((__vector_size__(64)));
+typedef float vector8float __attribute__((__vector_size__(32)));
+typedef long long vector8long __attribute__((__vector_size__(64)));
+typedef int vector8int __attribute__((__vector_size__(32)));
+typedef short vector8short __attribute__((__vector_size__(16)));
+typedef char vector8char __attribute__((__vector_size__(8)));
+
+#define CHECK_NUM(__size, __typeFrom, __typeTo, ...)                            \
+  constexpr vector##__size##__typeTo                                            \
+      from_##vector##__size##__typeFrom##_to_##vector##__size##__typeTo##_var = \
+          __builtin_convertvector((vector##__size##__typeFrom){__VA_ARGS__},    \
+                                  vector##__size##__typeTo);
+#define CHECK_TO_ALL_TYPES(__size, __typeFrom, ...)                            \
+  CHECK_NUM(__size, __typeFrom, double, __VA_ARGS__)                           \
+  CHECK_NUM(__size, __typeFrom, float, __VA_ARGS__)                            \
+  CHECK_NUM(__size, __typeFrom, long, __VA_ARGS__)                             \
+  CHECK_NUM(__size, __typeFrom, int, __VA_ARGS__)                              \
+  CHECK_NUM(__size, __typeFrom, short, __VA_ARGS__)                            \
+  CHECK_NUM(__size, __typeFrom, char, __VA_ARGS__)                             \
+  static_assert(                                                               \
+      __builtin_bit_cast(                                                      \
+          unsigned,                                                            \
+          __builtin_shufflevector(                                             \
+              from_vector##__size##__typeFrom##_to_vector##__size##char_var,   \
+              from_vector##__size##__typeFrom##_to_vector##__size##char_var,   \
+              0, 1, 2, 3)) == (LITTLE_END ? 0x03020100 : 0x00010203));         \
+  static_assert(                                                               \
+      __builtin_bit_cast(                                                      \
+          unsigned long long,                                                  \
+          __builtin_shufflevector(                                             \
+              from_vector##__size##__typeFrom##_to_vector##__size##short_var,  \
+              from_vector##__size##__typeFrom##_to_vector##__size##short_var,  \
+              0, 1, 2, 3)) ==                                                  \
+      (LITTLE_END ? 0x0003000200010000 : 0x0000000100020003));
+
+#define CHECK_ALL_COMBINATIONS(__size, ...)                                    \
+  CHECK_TO_ALL_TYPES(__size, double, __VA_ARGS__)                              \
+  CHECK_TO_ALL_TYPES(__size, float, __VA_ARGS__)                               \
+  CHECK_TO_ALL_TYPES(__size, long, __VA_ARGS__)                                \
+  CHECK_TO_ALL_TYPES(__size, int, __VA_ARGS__)                                 \
+  CHECK_TO_ALL_TYPES(__size, short, __VA_ARGS__)                               \
+  CHECK_TO_ALL_TYPES(__size, char, __VA_ARGS__)
+
+CHECK_ALL_COMBINATIONS(4, 0, 1, 2, 3);
+CHECK_ALL_COMBINATIONS(8, 0, 1, 2, 3, 4, 5, 6, 7);
+#undef CHECK_ALL_COMBINATIONS
+#undef CHECK_TO_ALL_TYPES
+#undef CHECK_NUM
+
+// Shuffle vector
+constexpr vector4char vector4charConst1 = {0, 1, 2, 3};
+constexpr vector4char vector4charConst2 = {4, 5, 6, 7};
+constexpr vector8char vector8intConst = {8, 9, 10, 11, 12, 13, 14, 15};
+
+constexpr vector4char vectorShuffle1 =
+    __builtin_shufflevector(vector4charConst1, vector4charConst2, 0, 1, 2, 3);
+static_assert(__builtin_bit_cast(unsigned, vectorShuffle1) ==
+              (LITTLE_END ? 0x03020100 : 0x00010203));
+constexpr vector4char vectorShuffle2 =
+    __builtin_shufflevector(vector4charConst1, vector4charConst2, 4, 5, 6, 7);
+static_assert(__builtin_bit_cast(unsigned, vectorShuffle2) ==
+              (LITTLE_END ? 0x07060504 : 0x04050607));
+constexpr vector4char vectorShuffle3 = __builtin_shufflevector(
+    vector4charConst1, vector4charConst2, -1, -1, -1, -1);
+static_assert(__builtin_bit_cast(unsigned, vectorShuffle3) ==
+              (LITTLE_END ? 0x00000000 : 0x00000000));
+constexpr vector4char vectorShuffle4 =
+    __builtin_shufflevector(vector4charConst1, vector4charConst2, 0, 2, 4, 6);
+static_assert(__builtin_bit_cast(unsigned, vectorShuffle4) ==
+              (LITTLE_END ? 0x06040200 : 0x00020406));
+constexpr vector8char vectorShuffle5 = __builtin_shufflevector(
+    vector8intConst, vector8intConst, 0, 2, 4, 6, 8, 10, 12, 14);
+static_assert(__builtin_bit_cast(unsigned long long, vectorShuffle5) ==
+              (LITTLE_END ? 0x0E0C0A080E0C0A08 : 0x080A0C0E080A0C0E));
+constexpr vector4char vectorShuffle6 =
+    __builtin_shufflevector(vector8intConst, vector8intConst, 0, 2, 4, 6);
+static_assert(__builtin_bit_cast(unsigned, vectorShuffle6) ==
+              (LITTLE_END ? 0x0E0C0A08 : 0x080A0C0E));
+constexpr vector8char vectorShuffle7 = __builtin_shufflevector(
+    vector4charConst1, vector4charConst2, 0, 2, 4, 6, 1, 3, 5, 7);
+static_assert(__builtin_bit_cast(unsigned long long, vectorShuffle7) ==
+              (LITTLE_END ? 0x0705030106040200 : 0x0002040601030507));

>From bb908ba2eb02d60a3f481e0547a26587d0fca340 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pol=20Marcet=20Sard=C3=A0?= <polmarcetsarda at gmail.com>
Date: Thu, 4 Jan 2024 12:31:08 +0100
Subject: [PATCH 3/5] Fix typo in file name

---
 .../{constat_builtins_vector.cpp => constant_builtins_vector.cpp} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename clang/test/Sema/{constat_builtins_vector.cpp => constant_builtins_vector.cpp} (100%)

diff --git a/clang/test/Sema/constat_builtins_vector.cpp b/clang/test/Sema/constant_builtins_vector.cpp
similarity index 100%
rename from clang/test/Sema/constat_builtins_vector.cpp
rename to clang/test/Sema/constant_builtins_vector.cpp

>From 3b7ec630c020182ddc251b9bec3bcc07c752fb0f Mon Sep 17 00:00:00 2001
From: Pol M <polmarcetsarda at gmail.com>
Date: Fri, 5 Jan 2024 15:34:02 +0100
Subject: [PATCH 4/5] Update ReleaseNotes.rst

---
 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 b6f1407436ed4f..d8716673fbdcba 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -238,7 +238,7 @@ Non-comprehensive list of changes in this release
 
 * Added ``#pragma clang fp reciprocal``.
 * Builtins ``__builtin_shufflevector()`` and ``__builtin_convertvector()`` may now be used within constant
- expressions.
+  expressions.
 
 New Compiler Flags
 ------------------

>From a97fd838760389bc96dc5e3764f09b088bc8e207 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pol=20Marcet=20Sard=C3=A0?= <polmarcetsarda at gmail.com>
Date: Sun, 31 Mar 2024 18:18:45 +0200
Subject: [PATCH 5/5] Following the review of sethp, I have made the following
 changes:

-- Added diagnostic for the undefined shuffle of -1
-- Validated support for _BitInt
-- A bunch of other minnor tweaks here and there
---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 +
 clang/lib/AST/ExprConstant.cpp                | 69 ++++++++-----------
 clang/test/Sema/constant_builtins_vector.cpp  | 51 ++++++++++----
 clang/test/Sema/convertvector.c               |  3 +
 4 files changed, 70 insertions(+), 55 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index aebb7d9b945c33..76ab1fbd0a6cd0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10217,6 +10217,8 @@ def err_shufflevector_nonconstant_argument : Error<
 def err_shufflevector_argument_too_large : Error<
   "index for __builtin_shufflevector must be less than the total number "
   "of vector elements">;
+def err_shufflevector_minus_one_is_undefined_behavior_constexpr : Error<
+  "index for __builtin_shufflevector must be within the bounds of the input vectors in a constexpr context. An index of -1 at position %0 was found. -1 is only allowed at runtime.">;
 
 def err_convertvector_non_vector : Error<
   "first argument to __builtin_convertvector must be a vector">;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 711d306ceb8e58..dedd1b115c75de 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2702,8 +2702,11 @@ static bool checkFloatingPointResult(EvalInfo &Info, const Expr *E,
 static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E,
                                    QualType SrcType, QualType DestType,
                                    APFloat &Result) {
-  assert(isa<CastExpr>(E) || isa<CompoundAssignOperator>(E) ||
-         isa<ConvertVectorExpr>(E));
+  assert((isa<CastExpr>(E) || isa<CompoundAssignOperator>(E) ||
+          isa<ConvertVectorExpr>(E)) &&
+         "HandleFloatToFloatCast has been checked with only CastExpr, "
+         "CompoundAssignOperator and ConvertVectorExpr. Please either validate "
+         "the new expression or address the root cause of this usage.");
   llvm::RoundingMode RM = getActiveRoundingMode(Info, E);
   APFloat::opStatus St;
   APFloat Value = Result;
@@ -10648,7 +10651,7 @@ namespace {
     bool VisitShuffleVectorExpr(const ShuffleVectorExpr *E);
 
     // FIXME: Missing: conditional operator (for GNU
-    //                 conditional select), shufflevector, ExtVectorElementExpr
+    //                 conditional select), ExtVectorElementExpr
   };
 } // end anonymous namespace
 
@@ -10899,27 +10902,10 @@ bool VectorExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
   return Success(APValue(ResultElements.data(), ResultElements.size()), E);
 }
 
-static bool EvaluateVectorOrLValue(APValue &Result, EvalInfo &Info,
-                                   const Expr *E, const QualType &Type) {
-  if (!Evaluate(Result, Info, E))
-    return false;
-
-  if (Result.isLValue()) {
-    // Source of the data is an lvalue; Manually handle the lvalue as if
-    // it was an rvalue to get the current APValue.
-    LValue LValueFound;
-    LValueFound.setFrom(Info.Ctx, Result);
-    if (!handleLValueToRValueConversion(Info, E, Type, LValueFound, Result))
-      return false;
-  }
-
-  return Result.isVector();
-}
-
-static bool handleVectorConversion(EvalInfo &Info, const FPOptions FPO,
-                                   const Expr *E, QualType SourceTy,
-                                   QualType DestTy, APValue const &Original,
-                                   APValue &Result) {
+static bool handleVectorElementCast(EvalInfo &Info, const FPOptions FPO,
+                                    const Expr *E, QualType SourceTy,
+                                    QualType DestTy, APValue const &Original,
+                                    APValue &Result) {
   if (SourceTy->isIntegerType()) {
     if (DestTy->isRealFloatingType()) {
       Result = APValue(APFloat(0.0));
@@ -10949,7 +10935,7 @@ static bool handleVectorConversion(EvalInfo &Info, const FPOptions FPO,
 bool VectorExprEvaluator::VisitConvertVectorExpr(const ConvertVectorExpr *E) {
   APValue Source;
   QualType SourceVecType = E->getSrcExpr()->getType();
-  if (!EvaluateVectorOrLValue(Source, Info, E->getSrcExpr(), SourceVecType))
+  if (!EvaluateAsRValue(Info, E->getSrcExpr(), Source))
     return false;
 
   QualType DestTy = E->getType()->castAs<VectorType>()->getElementType();
@@ -10962,8 +10948,8 @@ bool VectorExprEvaluator::VisitConvertVectorExpr(const ConvertVectorExpr *E) {
   ResultElements.reserve(SourceLen);
   for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
     APValue Elt;
-    if (!handleVectorConversion(Info, FPO, E, SourceTy, DestTy,
-                                Source.getVectorElt(EltNum), Elt))
+    if (!handleVectorElementCast(Info, FPO, E, SourceTy, DestTy,
+                                 Source.getVectorElt(EltNum), Elt))
       return false;
     ResultElements.push_back(std::move(Elt));
   }
@@ -10975,21 +10961,24 @@ static bool handleVectorShuffle(EvalInfo &Info, const ShuffleVectorExpr *E,
                                 QualType ElemType, APValue const &VecVal1,
                                 APValue const &VecVal2, unsigned EltNum,
                                 APValue &Result) {
-  unsigned const TotalElementsInAVector = VecVal1.getVectorLength();
-
-  Expr const *IndexExpr = E->getExpr(2 + EltNum);
-  APSInt IndexVal;
-  if (!EvaluateInteger(IndexExpr, IndexVal, Info))
-    return false;
+  unsigned const TotalElementsInInputVector = VecVal1.getVectorLength();
 
-  uint32_t index = IndexVal.getZExtValue();
+  APSInt IndexVal = E->getShuffleMaskIdx(Info.Ctx, EltNum);
+  int64_t index = IndexVal.getExtValue();
   // The spec says that -1 should be treated as undef for optimizations,
   // but in constexpr we need to choose a value. We'll choose 0.
-  if (index >= TotalElementsInAVector * 2)
-    index = 0;
+  if (index == -1) {
+    Info.FFDiag(
+        E, diag::err_shufflevector_minus_one_is_undefined_behavior_constexpr)
+        << EltNum;
+    return false;
+  }
+
+  if (index < 0 || index >= TotalElementsInInputVector * 2)
+    llvm_unreachable("Out of bounds shuffle index");
 
-  if (index >= TotalElementsInAVector)
-    Result = VecVal2.getVectorElt(index - TotalElementsInAVector);
+  if (index >= TotalElementsInInputVector)
+    Result = VecVal2.getVectorElt(index - TotalElementsInInputVector);
   else
     Result = VecVal1.getVectorElt(index);
   return true;
@@ -10998,11 +10987,11 @@ static bool handleVectorShuffle(EvalInfo &Info, const ShuffleVectorExpr *E,
 bool VectorExprEvaluator::VisitShuffleVectorExpr(const ShuffleVectorExpr *E) {
   APValue VecVal1;
   const Expr *Vec1 = E->getExpr(0);
-  if (!EvaluateVectorOrLValue(VecVal1, Info, Vec1, Vec1->getType()))
+  if (!EvaluateAsRValue(Info, Vec1, VecVal1))
     return false;
   APValue VecVal2;
   const Expr *Vec2 = E->getExpr(1);
-  if (!EvaluateVectorOrLValue(VecVal2, Info, Vec2, Vec2->getType()))
+  if (!EvaluateAsRValue(Info, Vec2, VecVal2))
     return false;
 
   VectorType const *DestVecTy = E->getType()->castAs<VectorType>();
diff --git a/clang/test/Sema/constant_builtins_vector.cpp b/clang/test/Sema/constant_builtins_vector.cpp
index afd5904e7afeab..73f8eed22abcbd 100644
--- a/clang/test/Sema/constant_builtins_vector.cpp
+++ b/clang/test/Sema/constant_builtins_vector.cpp
@@ -1,5 +1,4 @@
-// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only %s
-// expected-no-diagnostics
+// RUN: %clang_cc1 -verify -std=c++2a -fsyntax-only -Wno-bit-int-extension %s
 
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 #define LITTLE_END 1
@@ -9,18 +8,29 @@
 #error "huh?"
 #endif
 
+// We also support _BitInt as long as it is >=8 and a power of 2.
+typedef _BitInt(8) BitInt8;
+typedef _BitInt(32) BitInt32;
+typedef _BitInt(128) BitInt128;
+
 typedef double vector4double __attribute__((__vector_size__(32)));
 typedef float vector4float __attribute__((__vector_size__(16)));
 typedef long long vector4long __attribute__((__vector_size__(32)));
 typedef int vector4int __attribute__((__vector_size__(16)));
 typedef short vector4short __attribute__((__vector_size__(8)));
 typedef char vector4char __attribute__((__vector_size__(4)));
+typedef BitInt8 vector4BitInt8 __attribute__((__vector_size__(4)));
+typedef BitInt32 vector4BitInt32 __attribute__((__vector_size__(16)));
+typedef BitInt128 vector4BitInt128 __attribute__((__vector_size__(64)));
 typedef double vector8double __attribute__((__vector_size__(64)));
 typedef float vector8float __attribute__((__vector_size__(32)));
 typedef long long vector8long __attribute__((__vector_size__(64)));
 typedef int vector8int __attribute__((__vector_size__(32)));
 typedef short vector8short __attribute__((__vector_size__(16)));
 typedef char vector8char __attribute__((__vector_size__(8)));
+typedef BitInt8 vector8BitInt8 __attribute__((__vector_size__(8)));
+typedef BitInt32 vector8BitInt32 __attribute__((__vector_size__(32)));
+typedef BitInt128 vector8BitInt128 __attribute__((__vector_size__(128)));
 
 #define CHECK_NUM(__size, __typeFrom, __typeTo, ...)                            \
   constexpr vector##__size##__typeTo                                            \
@@ -34,6 +44,9 @@ typedef char vector8char __attribute__((__vector_size__(8)));
   CHECK_NUM(__size, __typeFrom, int, __VA_ARGS__)                              \
   CHECK_NUM(__size, __typeFrom, short, __VA_ARGS__)                            \
   CHECK_NUM(__size, __typeFrom, char, __VA_ARGS__)                             \
+  CHECK_NUM(__size, __typeFrom, BitInt8, __VA_ARGS__)                          \
+  CHECK_NUM(__size, __typeFrom, BitInt32, __VA_ARGS__)                         \
+  CHECK_NUM(__size, __typeFrom, BitInt128, __VA_ARGS__)                        \
   static_assert(                                                               \
       __builtin_bit_cast(                                                      \
           unsigned,                                                            \
@@ -56,7 +69,10 @@ typedef char vector8char __attribute__((__vector_size__(8)));
   CHECK_TO_ALL_TYPES(__size, long, __VA_ARGS__)                                \
   CHECK_TO_ALL_TYPES(__size, int, __VA_ARGS__)                                 \
   CHECK_TO_ALL_TYPES(__size, short, __VA_ARGS__)                               \
-  CHECK_TO_ALL_TYPES(__size, char, __VA_ARGS__)
+  CHECK_TO_ALL_TYPES(__size, char, __VA_ARGS__)                                \
+  CHECK_TO_ALL_TYPES(__size, BitInt8, __VA_ARGS__)                             \
+  CHECK_TO_ALL_TYPES(__size, BitInt32, __VA_ARGS__)                            \
+  CHECK_TO_ALL_TYPES(__size, BitInt128, __VA_ARGS__)
 
 CHECK_ALL_COMBINATIONS(4, 0, 1, 2, 3);
 CHECK_ALL_COMBINATIONS(8, 0, 1, 2, 3, 4, 5, 6, 7);
@@ -77,23 +93,28 @@ constexpr vector4char vectorShuffle2 =
     __builtin_shufflevector(vector4charConst1, vector4charConst2, 4, 5, 6, 7);
 static_assert(__builtin_bit_cast(unsigned, vectorShuffle2) ==
               (LITTLE_END ? 0x07060504 : 0x04050607));
-constexpr vector4char vectorShuffle3 = __builtin_shufflevector(
-    vector4charConst1, vector4charConst2, -1, -1, -1, -1);
-static_assert(__builtin_bit_cast(unsigned, vectorShuffle3) ==
-              (LITTLE_END ? 0x00000000 : 0x00000000));
-constexpr vector4char vectorShuffle4 =
+constexpr vector4char vectorShuffle3 =
     __builtin_shufflevector(vector4charConst1, vector4charConst2, 0, 2, 4, 6);
-static_assert(__builtin_bit_cast(unsigned, vectorShuffle4) ==
+static_assert(__builtin_bit_cast(unsigned, vectorShuffle3) ==
               (LITTLE_END ? 0x06040200 : 0x00020406));
-constexpr vector8char vectorShuffle5 = __builtin_shufflevector(
+constexpr vector8char vectorShuffle4 = __builtin_shufflevector(
     vector8intConst, vector8intConst, 0, 2, 4, 6, 8, 10, 12, 14);
-static_assert(__builtin_bit_cast(unsigned long long, vectorShuffle5) ==
+static_assert(__builtin_bit_cast(unsigned long long, vectorShuffle4) ==
               (LITTLE_END ? 0x0E0C0A080E0C0A08 : 0x080A0C0E080A0C0E));
-constexpr vector4char vectorShuffle6 =
+constexpr vector4char vectorShuffle5 =
     __builtin_shufflevector(vector8intConst, vector8intConst, 0, 2, 4, 6);
-static_assert(__builtin_bit_cast(unsigned, vectorShuffle6) ==
+static_assert(__builtin_bit_cast(unsigned, vectorShuffle5) ==
               (LITTLE_END ? 0x0E0C0A08 : 0x080A0C0E));
-constexpr vector8char vectorShuffle7 = __builtin_shufflevector(
+constexpr vector8char vectorShuffle6 = __builtin_shufflevector(
     vector4charConst1, vector4charConst2, 0, 2, 4, 6, 1, 3, 5, 7);
-static_assert(__builtin_bit_cast(unsigned long long, vectorShuffle7) ==
+static_assert(__builtin_bit_cast(unsigned long long, vectorShuffle6) ==
               (LITTLE_END ? 0x0705030106040200 : 0x0002040601030507));
+
+constexpr vector4char
+    vectorShuffleFail1 = // expected-error {{constexpr variable 'vectorShuffleFail1'\
+ must be initialized by a constant expression}}
+    __builtin_shufflevector( // expected-error {{index for __builtin_shufflevector must be within\
+ the bounds of the input vectors in a constexpr context. An index of\
+ -1 at position 0 was found. -1 is only allowed at runtime.}}
+        vector4charConst1,
+        vector4charConst2, -1, -1, -1, -1);
diff --git a/clang/test/Sema/convertvector.c b/clang/test/Sema/convertvector.c
index 8ae43c3ba3d493..1ff04af9098185 100644
--- a/clang/test/Sema/convertvector.c
+++ b/clang/test/Sema/convertvector.c
@@ -15,3 +15,6 @@ vector8float foo3(double x) {
   return __builtin_convertvector(x, vector8float);  // expected-error {{must be a vector}}
 }
 
+float foo4(float x) {
+  return __builtin_convertvector(x, float); // expected-error {{first argument to __builtin_convertvector must be a vector}}
+}



More information about the cfe-commits mailing list