[clang] [clang] Implement constexpr bit_cast for vectors (PR #66894)

via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 24 04:53:38 PDT 2023


https://github.com/DaMatrix updated https://github.com/llvm/llvm-project/pull/66894

>From 8768741800aae37a825864e2ee782484ed073ce9 Mon Sep 17 00:00:00 2001
From: DaPorkchop_ <daporkchop at daporkchop.net>
Date: Sun, 13 Aug 2023 22:39:12 +0200
Subject: [PATCH] [clang] Implement constexpr bit_cast for vectors

---
 clang/lib/AST/ExprConstant.cpp                | 181 ++++++++++++------
 .../SemaCXX/constexpr-builtin-bit-cast.cpp    |  38 ++++
 2 files changed, 162 insertions(+), 57 deletions(-)

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index fea06b97259fe31..8cd6a0e390ee0f8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2732,6 +2732,92 @@ static bool truncateBitfieldValue(EvalInfo &Info, const Expr *E,
   return true;
 }
 
+static bool BitcastAPIntToVector(EvalInfo &Info, const VectorType *VTy,
+                                 const llvm::APInt &SValInt,
+                                 SmallVectorImpl<APValue> &Elts) {
+  QualType EltTy = VTy->getElementType();
+  unsigned NElts = VTy->getNumElements();
+  unsigned EltSize =
+      VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy);
+
+  if ((NElts * EltSize) % Info.Ctx.getCharWidth() != 0) {
+    // The vector's size in bits is not a multiple of the target's byte size,
+    // so its layout is unspecified. For now, we'll simply treat these cases as
+    // unsupported (this should only be possible with OpenCL bool vectors whose
+    // element count isn't a multiple of the byte size).
+    return false;
+  }
+
+  Elts.reserve(NElts);
+  bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian();
+  if (EltTy->isRealFloatingType()) {
+    const llvm::fltSemantics &Sem = Info.Ctx.getFloatTypeSemantics(EltTy);
+    unsigned FloatEltSize = EltSize;
+    if (&Sem == &APFloat::x87DoubleExtended())
+      FloatEltSize = 80;
+    for (unsigned i = 0; i < NElts; i++) {
+      llvm::APInt Elt;
+      if (BigEndian)
+        Elt = SValInt.rotl(i * EltSize + FloatEltSize).trunc(FloatEltSize);
+      else
+        Elt = SValInt.rotr(i * EltSize).trunc(FloatEltSize);
+      Elts.push_back(APValue(APFloat(Sem, Elt)));
+    }
+  } else if (EltTy->isIntegerType()) {
+    for (unsigned i = 0; i < NElts; i++) {
+      llvm::APInt Elt;
+      if (BigEndian)
+        Elt = SValInt.rotl(i * EltSize + EltSize).zextOrTrunc(EltSize);
+      else
+        Elt = SValInt.rotr(i * EltSize).zextOrTrunc(EltSize);
+      Elts.push_back(APValue(APSInt(Elt, !EltTy->isSignedIntegerType())));
+    }
+  } else {
+    return false;
+  }
+  return true;
+}
+
+static bool BitcastVectorToAPInt(EvalInfo &Info, const VectorType *VTy,
+                                 const APValue &SVal, llvm::APInt &Res) {
+  QualType EltTy = VTy->getElementType();
+  unsigned NElts = VTy->getNumElements();
+  unsigned EltSize =
+      VTy->isExtVectorBoolType() ? 1 : Info.Ctx.getTypeSize(EltTy);
+  unsigned VecSize = Info.Ctx.getTypeSize(VTy);
+
+  if ((NElts * EltSize) % Info.Ctx.getCharWidth() != 0) {
+    // The vector's size in bits is not a multiple of the target's byte size,
+    // so its layout is unspecified. For now, we'll simply treat these cases as
+    // unsupported (this should only be possible with OpenCL bool vectors whose
+    // element count isn't a multiple of the byte size).
+    return false;
+  }
+
+  bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian();
+  Res = llvm::APInt::getZero(VecSize);
+  for (unsigned i = 0; i < SVal.getVectorLength(); i++) {
+    const APValue &Elt = SVal.getVectorElt(i);
+    llvm::APInt EltAsInt;
+    if (Elt.isInt()) {
+      EltAsInt = Elt.getInt();
+    } else if (Elt.isFloat()) {
+      EltAsInt = Elt.getFloat().bitcastToAPInt();
+    } else {
+      // Don't try to handle vectors of anything other than int or float
+      // (not sure if it's possible to hit this case).
+      return false;
+    }
+    unsigned BaseEltSize = EltAsInt.getBitWidth();
+    if (BigEndian)
+      Res |= EltAsInt.zextOrTrunc(VecSize).rotr(i * EltSize + BaseEltSize);
+    else
+      Res |= EltAsInt.zextOrTrunc(VecSize).rotl(i * EltSize);
+  }
+
+  return true;
+}
+
 static bool EvalAndBitcastToAPInt(EvalInfo &Info, const Expr *E,
                                   llvm::APInt &Res) {
   APValue SVal;
@@ -2745,34 +2831,9 @@ static bool EvalAndBitcastToAPInt(EvalInfo &Info, const Expr *E,
     Res = SVal.getFloat().bitcastToAPInt();
     return true;
   }
-  if (SVal.isVector()) {
-    QualType VecTy = E->getType();
-    unsigned VecSize = Info.Ctx.getTypeSize(VecTy);
-    QualType EltTy = VecTy->castAs<VectorType>()->getElementType();
-    unsigned EltSize = Info.Ctx.getTypeSize(EltTy);
-    bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian();
-    Res = llvm::APInt::getZero(VecSize);
-    for (unsigned i = 0; i < SVal.getVectorLength(); i++) {
-      APValue &Elt = SVal.getVectorElt(i);
-      llvm::APInt EltAsInt;
-      if (Elt.isInt()) {
-        EltAsInt = Elt.getInt();
-      } else if (Elt.isFloat()) {
-        EltAsInt = Elt.getFloat().bitcastToAPInt();
-      } else {
-        // Don't try to handle vectors of anything other than int or float
-        // (not sure if it's possible to hit this case).
-        Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
-        return false;
-      }
-      unsigned BaseEltSize = EltAsInt.getBitWidth();
-      if (BigEndian)
-        Res |= EltAsInt.zextOrTrunc(VecSize).rotr(i*EltSize+BaseEltSize);
-      else
-        Res |= EltAsInt.zextOrTrunc(VecSize).rotl(i*EltSize);
-    }
+  if (SVal.isVector() &&
+      BitcastVectorToAPInt(Info, E->getType()->castAs<VectorType>(), SVal, Res))
     return true;
-  }
   // Give up if the input isn't an int, float, or vector.  For example, we
   // reject "(v4i16)(intptr_t)&a".
   Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
@@ -7005,10 +7066,11 @@ class APValueToBufferConverter {
       return visitArray(Val, Ty, Offset);
     case APValue::Struct:
       return visitRecord(Val, Ty, Offset);
+    case APValue::Vector:
+      return visitVector(Val, Ty, Offset);
 
     case APValue::ComplexInt:
     case APValue::ComplexFloat:
-    case APValue::Vector:
     case APValue::FixedPoint:
       // FIXME: We should support these.
 
@@ -7095,6 +7157,22 @@ class APValueToBufferConverter {
     return true;
   }
 
+  bool visitVector(const APValue &Val, QualType Ty, CharUnits Offset) {
+    const VectorType *VTy = Ty->castAs<VectorType>();
+
+    APInt Bits;
+    if (!BitcastVectorToAPInt(Info, VTy, Val, Bits))
+      return false;
+
+    assert(Bits.getBitWidth() == Info.Ctx.getTypeSize(Ty));
+
+    unsigned Width = Bits.getBitWidth();
+    SmallVector<uint8_t, 8> Bytes(Width / 8);
+    llvm::StoreIntToMemory(Bits, &*Bytes.begin(), Width / 8);
+    Buffer.writeObject(Offset, Bytes);
+    return true;
+  }
+
   bool visitInt(const APSInt &Val, QualType Ty, CharUnits Offset) {
     APSInt AdjustedVal = Val;
     unsigned Width = AdjustedVal.getBitWidth();
@@ -7103,7 +7181,7 @@ class APValueToBufferConverter {
       AdjustedVal = AdjustedVal.extend(Width);
     }
 
-    SmallVector<unsigned char, 8> Bytes(Width / 8);
+    SmallVector<uint8_t, 8> Bytes(Width / 8);
     llvm::StoreIntToMemory(AdjustedVal, &*Bytes.begin(), Width / 8);
     Buffer.writeObject(Offset, Bytes);
     return true;
@@ -7304,6 +7382,21 @@ class BufferToAPValueConverter {
     return ArrayValue;
   }
 
+  std::optional<APValue> visit(const VectorType *Ty, CharUnits Offset) {
+    SmallVector<uint8_t, 8> Bytes;
+    if (!Buffer.readObject(Offset, Info.Ctx.getTypeSizeInChars(Ty), Bytes))
+      return std::nullopt;
+
+    APInt SValInt = APInt::getZero(Info.Ctx.getTypeSize(Ty));
+    llvm::LoadIntFromMemory(SValInt, &*Bytes.begin(), Bytes.size());
+
+    SmallVector<APValue, 4> Elts;
+    if (!BitcastAPIntToVector(Info, Ty, SValInt, Elts))
+      return std::nullopt;
+
+    return APValue(Elts.data(), Elts.size());
+  }
+
   std::optional<APValue> visit(const Type *Ty, CharUnits Offset) {
     return unsupportedType(QualType(Ty, 0));
   }
@@ -10515,36 +10608,10 @@ bool VectorExprEvaluator::VisitCastExpr(const CastExpr *E) {
     llvm::APInt SValInt;
     if (!EvalAndBitcastToAPInt(Info, SE, SValInt))
       return false;
-    // Extract the elements
-    QualType EltTy = VTy->getElementType();
-    unsigned EltSize = Info.Ctx.getTypeSize(EltTy);
-    bool BigEndian = Info.Ctx.getTargetInfo().isBigEndian();
+
     SmallVector<APValue, 4> Elts;
-    if (EltTy->isRealFloatingType()) {
-      const llvm::fltSemantics &Sem = Info.Ctx.getFloatTypeSemantics(EltTy);
-      unsigned FloatEltSize = EltSize;
-      if (&Sem == &APFloat::x87DoubleExtended())
-        FloatEltSize = 80;
-      for (unsigned i = 0; i < NElts; i++) {
-        llvm::APInt Elt;
-        if (BigEndian)
-          Elt = SValInt.rotl(i * EltSize + FloatEltSize).trunc(FloatEltSize);
-        else
-          Elt = SValInt.rotr(i * EltSize).trunc(FloatEltSize);
-        Elts.push_back(APValue(APFloat(Sem, Elt)));
-      }
-    } else if (EltTy->isIntegerType()) {
-      for (unsigned i = 0; i < NElts; i++) {
-        llvm::APInt Elt;
-        if (BigEndian)
-          Elt = SValInt.rotl(i*EltSize+EltSize).zextOrTrunc(EltSize);
-        else
-          Elt = SValInt.rotr(i*EltSize).zextOrTrunc(EltSize);
-        Elts.push_back(APValue(APSInt(Elt, !EltTy->isSignedIntegerType())));
-      }
-    } else {
+    if (!BitcastAPIntToVector(Info, VTy, SValInt, Elts))
       return Error(E);
-    }
     return Success(Elts, E);
   }
   default:
diff --git a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
index a6ebe0572d063bb..2b1c9a5d3ea8a7a 100644
--- a/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
+++ b/clang/test/SemaCXX/constexpr-builtin-bit-cast.cpp
@@ -463,3 +463,41 @@ static_assert(bit_cast<long double>(ld539) == fivehundredandthirtynine, "");
 static_assert(round_trip<__int128_t>(34.0L));
 #endif
 }
+
+namespace test_vector {
+
+typedef unsigned uint2 __attribute__((vector_size(2 * sizeof(unsigned))));
+typedef char byte8 __attribute__((vector_size(sizeof(unsigned long long))));
+
+constexpr uint2 test_vector = { 0x0C05FEFE, 0xCAFEBABE };
+
+static_assert(bit_cast<unsigned long long>(test_vector) == (LITTLE_END
+                                                                ? 0xCAFEBABE0C05FEFE
+                                                                : 0x0C05FEFECAFEBABE), "");
+
+static_assert(round_trip<uint2>(0xCAFEBABE0C05FEFEULL), "");
+static_assert(round_trip<byte8>(0xCAFEBABE0C05FEFEULL), "");
+
+typedef bool bool8 __attribute__((ext_vector_type(8)));
+typedef bool bool9 __attribute__((ext_vector_type(9)));
+typedef bool bool16 __attribute__((ext_vector_type(16)));
+typedef bool bool17 __attribute__((ext_vector_type(17)));
+typedef bool bool32 __attribute__((ext_vector_type(32)));
+typedef bool bool128 __attribute__((ext_vector_type(128)));
+
+static_assert(bit_cast<unsigned char>(bool8{1,0,1,0,1,0,1,0}) == (LITTLE_END ? 0x55 : 0xAA), "");
+static_assert(round_trip<bool8>(static_cast<unsigned char>(0)), "");
+static_assert(round_trip<bool8>(static_cast<unsigned char>(1)), "");
+static_assert(round_trip<bool8>(static_cast<unsigned char>(0x55)), "");
+
+static_assert(bit_cast<unsigned short>(bool16{1,1,1,1,1,0,0,0, 1,1,1,1,0,1,0,0}) == (LITTLE_END ? 0x2F1F : 0xF8F4), "");
+
+static_assert(round_trip<bool16>(static_cast<short>(0xCAFE)), "");
+static_assert(round_trip<bool32>(static_cast<int>(0xCAFEBABE)), "");
+static_assert(round_trip<bool128>(static_cast<__int128_t>(0xCAFEBABE0C05FEFEULL)), "");
+
+static_assert(bit_cast<unsigned short>(bool9{1,1,0,1,0,1,0,1,0}) == (LITTLE_END ? 0xAB : 0x1AA), ""); // expected-error{{static assertion expression is not an integral constant expression}}
+static_assert(round_trip<bool9>(static_cast<unsigned short>(0)), ""); // expected-error{{static assertion expression is not an integral constant expression}}
+static_assert(round_trip<bool17>(static_cast<unsigned int>(0x0001CAFE)), ""); // expected-error{{static assertion expression is not an integral constant expression}}
+
+}



More information about the cfe-commits mailing list