[clang] [clang][bytecode] Implement arithmetic, bitwise and compound assignment operator (PR #108949)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 17 02:44:38 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: None (yronglin)

<details>
<summary>Changes</summary>

Implement `+`, `-`, `*`, `/` , `%`, `&`, `|`, `^`, `<<`, `>>` and compound assignment operator.

---

Patch is 35.67 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/108949.diff


3 Files Affected:

- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+112-9) 
- (modified) clang/test/AST/ByteCode/constexpr-vectors.cpp (+498-21) 
- (modified) clang/test/SemaCXX/constexpr-vectors.cpp (+1) 


``````````diff
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 7e0775a51aee61..e7a6df58e6f1a6 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -1267,12 +1267,8 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) {
   assert(E->getLHS()->getType()->isVectorType());
   assert(E->getRHS()->getType()->isVectorType());
 
-  // FIXME: Current only support comparison binary operator, add support for
-  // other binary operator.
-  if (!E->isComparisonOp() && !E->isLogicalOp())
-    return this->emitInvalid(E);
   // Prepare storage for result.
-  if (!Initializing) {
+  if (!Initializing || E->isCompoundAssignmentOp()) {
     unsigned LocalIndex = allocateTemporary(E);
     if (!this->emitGetPtrLocal(LocalIndex, E))
       return false;
@@ -1281,26 +1277,67 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) {
   const Expr *LHS = E->getLHS();
   const Expr *RHS = E->getRHS();
   const auto *VecTy = E->getType()->getAs<VectorType>();
+  auto Op = E->isCompoundAssignmentOp()
+                ? BinaryOperator::getOpForCompoundAssignment(E->getOpcode())
+                : E->getOpcode();
 
   // The LHS and RHS of a comparison operator must have the same type. So we
   // just use LHS vector element type here.
   PrimType ElemT = this->classifyVectorElementType(LHS->getType());
   PrimType ResultElemT = this->classifyVectorElementType(E->getType());
 
-  // Evaluate LHS and save value to LHSOffset.
+  // Allocate a local pointer for LHS and RHS.
   unsigned LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, true, false);
+  unsigned RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, true, false);
+
+  // C++17 onwards require that we evaluate the RHS of the compound
+  // assignment op first.
+  if (E->isCompoundAssignmentOp()) {
+    // Evaluate RHS and save value to RHSOffset.
+    if (!this->visit(RHS))
+      return false;
+    if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
+      return false;
+
+    // Evaluate LHS and save value to LHSOffset.
+    if (!this->visit(LHS))
+      return false;
+    if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
+      return false;
+  } else {
+    // Evaluate LHS and save value to LHSOffset.
+    if (!this->visit(LHS))
+      return false;
+    if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
+      return false;
+
+    // Evaluate RHS and save value to RHSOffset.
+    if (!this->visit(RHS))
+      return false;
+    if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
+      return false;
+  }
+
+  // Evaluate LHS and save value to LHSOffset.
   if (!this->visit(LHS))
     return false;
   if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
     return false;
 
   // Evaluate RHS and save value to RHSOffset.
-  unsigned RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, true, false);
   if (!this->visit(RHS))
     return false;
   if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
     return false;
 
+  // BitAdd/BitOr/BitXor/Shl/Shr doesn't support bool type, we need perform the
+  // integer promotion.
+  bool NeedIntPromot = ElemT == PT_Bool && (E->isBitwiseOp() || E->isShiftOp());
+  QualType PromotTy =
+      Ctx.getASTContext().getPromotedIntegerType(Ctx.getASTContext().BoolTy);
+  PrimType PromotT = classifyPrim(PromotTy);
+  PrimType OpT = NeedIntPromot ? PromotT : ElemT;
+
   auto getElem = [=](unsigned Offset, unsigned Index) {
     if (!this->emitGetLocal(PT_Ptr, Offset, E))
       return false;
@@ -1311,16 +1348,63 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) {
         return false;
       if (!this->emitPrimCast(PT_Bool, ResultElemT, VecTy->getElementType(), E))
         return false;
+    } else if (NeedIntPromot) {
+      if (!this->emitPrimCast(ElemT, PromotT, PromotTy, E))
+        return false;
     }
     return true;
   };
 
+#define EMIT_ARITH_OP(OP)                                                      \
+  {                                                                            \
+    if (ElemT == PT_Float) {                                                   \
+      if (!this->emit##OP##f(getFPOptions(E), E))                              \
+        return false;                                                          \
+    } else {                                                                   \
+      if (!this->emit##OP(ElemT, E))                                           \
+        return false;                                                          \
+    }                                                                          \
+    break;                                                                     \
+  }
+
   for (unsigned I = 0; I != VecTy->getNumElements(); ++I) {
     if (!getElem(LHSOffset, I))
       return false;
     if (!getElem(RHSOffset, I))
       return false;
-    switch (E->getOpcode()) {
+    switch (Op) {
+    case BO_Add:
+      EMIT_ARITH_OP(Add)
+    case BO_Sub:
+      EMIT_ARITH_OP(Sub)
+    case BO_Mul:
+      EMIT_ARITH_OP(Mul)
+    case BO_Div:
+      EMIT_ARITH_OP(Div)
+    case BO_Rem:
+      if (!this->emitRem(ElemT, E))
+        return false;
+      break;
+    case BO_And:
+      if (!this->emitBitAnd(OpT, E))
+        return false;
+      break;
+    case BO_Or:
+      if (!this->emitBitOr(OpT, E))
+        return false;
+      break;
+    case BO_Xor:
+      if (!this->emitBitXor(OpT, E))
+        return false;
+      break;
+    case BO_Shl:
+      if (!this->emitShl(OpT, ElemT, E))
+        return false;
+      break;
+    case BO_Shr:
+      if (!this->emitShr(OpT, ElemT, E))
+        return false;
+      break;
     case BO_EQ:
       if (!this->emitEQ(ElemT, E))
         return false;
@@ -1356,7 +1440,7 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) {
         return false;
       break;
     default:
-      llvm_unreachable("Unsupported binary operator");
+      return this->emitInvalid(E);
     }
 
     // The result of the comparison is a vector of the same width and number
@@ -1371,10 +1455,27 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) {
         return false;
     }
 
+    // If we performed an integer promotion, we need to cast the compute result
+    // into result vector element type.
+    if (NeedIntPromot &&
+        !this->emitPrimCast(PromotT, ResultElemT, VecTy->getElementType(), E))
+      return false;
+
     // Initialize array element with the value we just computed.
     if (!this->emitInitElem(ResultElemT, I, E))
       return false;
   }
+
+  if (E->isCompoundAssignmentOp()) {
+    if (!this->emitGetLocal(PT_Ptr, LHSOffset, E))
+      return false;
+    if (!this->emitFlip(PT_Ptr, PT_Ptr, E))
+      return false;
+    if (!this->emitMemcpy(E))
+      return false;
+    if (!this->emitPopPtr(E))
+      return false;
+  }
   return true;
 }
 
@@ -2292,6 +2393,8 @@ bool Compiler<Emitter>::VisitPointerCompoundAssignOperator(
 template <class Emitter>
 bool Compiler<Emitter>::VisitCompoundAssignOperator(
     const CompoundAssignOperator *E) {
+  if (E->getType()->isVectorType())
+    return VisitVectorBinOp(E);
 
   const Expr *LHS = E->getLHS();
   const Expr *RHS = E->getRHS();
diff --git a/clang/test/AST/ByteCode/constexpr-vectors.cpp b/clang/test/AST/ByteCode/constexpr-vectors.cpp
index 7a65b263784586..629cdd0bacb5b6 100644
--- a/clang/test/AST/ByteCode/constexpr-vectors.cpp
+++ b/clang/test/AST/ByteCode/constexpr-vectors.cpp
@@ -10,11 +10,217 @@ using FourI128VecSize __attribute__((vector_size(64))) = __int128;
 
 using FourCharsExtVec __attribute__((ext_vector_type(4))) = char;
 using FourIntsExtVec __attribute__((ext_vector_type(4))) = int;
+using FourLongLongsExtVec __attribute__((ext_vector_type(4))) = long long;
+using FourFloatsExtVec __attribute__((ext_vector_type(4))) = float;
+using FourDoublesExtVec __attribute__((ext_vector_type(4))) = double;
 using FourI128ExtVec __attribute__((ext_vector_type(4))) = __int128;
 
+
+// Next a series of tests to make sure these operations are usable in
+// constexpr functions. Template instantiations don't emit Winvalid-constexpr,
+// so we have to do these as macros.
+#define MathShiftOps(Type)                            \
+  constexpr auto MathShiftOps##Type(Type a, Type b) { \
+    a = a + b;                                        \
+    a = a - b;                                        \
+    a = a * b;                                        \
+    a = a / b;                                        \
+    b = a + 1;                                        \
+    b = a - 1;                                        \
+    b = a * 1;                                        \
+    b = a / 1;                                        \
+    a += a;                                           \
+    a -= a;                                           \
+    a *= a;                                           \
+    a /= a;                                           \
+    b += a;                                           \
+    b -= a;                                           \
+    b *= a;                                           \
+    b /= a;                                           \
+    a < b;                                            \
+    a > b;                                            \
+    a <= b;                                           \
+    a >= b;                                           \
+    a == b;                                           \
+    a != b;                                           \
+    a &&b;                                            \
+    a || b;                                           \
+    auto c = (a, b);                                  \
+    return c;                                         \
+  }
+
+// Ops specific to Integers.
+#define MathShiftOpsInts(Type)                            \
+  constexpr auto MathShiftopsInts##Type(Type a, Type b) { \
+    a = a << b;                                           \
+    a = a >> b;                                           \
+    a = a << 3;                                           \
+    a = a >> 3;                                           \
+    a = 3 << b;                                           \
+    a = 3 >> b;                                           \
+    a <<= b;                                              \
+    a >>= b;                                              \
+    a <<= 3;                                              \
+    a >>= 3;                                              \
+    a = a % b;                                            \
+    a &b;                                                 \
+    a | b;                                                \
+    a ^ b;                                                \
+    return a;                                             \
+  }
+
+MathShiftOps(FourCharsVecSize);
+MathShiftOps(FourIntsVecSize);
+MathShiftOps(FourLongLongsVecSize);
+MathShiftOps(FourFloatsVecSize);
+MathShiftOps(FourDoublesVecSize);
+MathShiftOps(FourCharsExtVec);
+MathShiftOps(FourIntsExtVec);
+MathShiftOps(FourLongLongsExtVec);
+MathShiftOps(FourFloatsExtVec);
+MathShiftOps(FourDoublesExtVec);
+
+MathShiftOpsInts(FourCharsVecSize);
+MathShiftOpsInts(FourIntsVecSize);
+MathShiftOpsInts(FourLongLongsVecSize);
+MathShiftOpsInts(FourCharsExtVec);
+MathShiftOpsInts(FourIntsExtVec);
+MathShiftOpsInts(FourLongLongsExtVec);
+
+template <typename T, typename U>
+constexpr auto CmpMul(T t, U u) {
+  t *= u;
+  return t;
+}
+template <typename T, typename U>
+constexpr auto CmpDiv(T t, U u) {
+  t /= u;
+  return t;
+}
+template <typename T, typename U>
+constexpr auto CmpRem(T t, U u) {
+  t %= u;
+  return t;
+}
+
+template <typename T, typename U>
+constexpr auto CmpAdd(T t, U u) {
+  t += u;
+  return t;
+}
+
+template <typename T, typename U>
+constexpr auto CmpSub(T t, U u) {
+  t -= u;
+  return t;
+}
+
+template <typename T, typename U>
+constexpr auto CmpLSH(T t, U u) {
+  t <<= u;
+  return t;
+}
+
+template <typename T, typename U>
+constexpr auto CmpRSH(T t, U u) {
+  t >>= u;
+  return t;
+}
+
+template <typename T, typename U>
+constexpr auto CmpBinAnd(T t, U u) {
+  t &= u;
+  return t;
+}
+
+template <typename T, typename U>
+constexpr auto CmpBinXOr(T t, U u) {
+  t ^= u;
+  return t;
+}
+
+template <typename T, typename U>
+constexpr auto CmpBinOr(T t, U u) {
+  t |= u;
+  return t;
+}
+
+constexpr auto CmpF(float t, float u) {
+  return __builtin_fabs(t - u) < 0.0001;
+}
+
 // Only int vs float makes a difference here, so we only need to test 1 of each.
 // Test Char to make sure the mixed-nature of shifts around char is evident.
 void CharUsage() {
+  constexpr auto a = FourCharsVecSize{6, 3, 2, 1} +
+            FourCharsVecSize{12, 15, 5, 7};
+  static_assert(a[0] == 18 && a[1] == 18 && a[2] == 7 && a[3] == 8, "");
+
+  constexpr auto b = FourCharsVecSize{19, 15, 13, 12} -
+                     FourCharsVecSize{13, 14, 5, 3};
+  static_assert(b[0] == 6 && b[1] == 1 && b[2] == 8 && b[3] == 9, "");
+
+  constexpr auto c = FourCharsVecSize{8, 4, 2, 1} *
+                     FourCharsVecSize{3, 4, 5, 6};
+  static_assert(c[0] == 24 && c[1] == 16 && c[2] == 10 && c[3] == 6, "");
+
+  constexpr auto d = FourCharsVecSize{12, 12, 10, 10} /
+                     FourCharsVecSize{6, 4, 5, 2};
+  static_assert(d[0] == 2 && d[1] == 3 && d[2] == 2 && d[3] == 5, "");
+
+  constexpr auto e = FourCharsVecSize{12, 12, 10, 10} %
+                     FourCharsVecSize{6, 4, 4, 3};
+  static_assert(e[0] == 0 && e[1] == 0 && e[2] == 2 && e[3] == 1, "");
+
+  constexpr auto f = FourCharsVecSize{6, 3, 2, 1} + 3;
+  static_assert(f[0] == 9 && f[1] == 6 && f[2] == 5 && f[3] == 4, "");
+
+  constexpr auto g = FourCharsVecSize{19, 15, 12, 10} - 3;
+  static_assert(g[0] == 16 && g[1] == 12 && g[2] == 9 && g[3] == 7, "");
+
+  constexpr auto h = FourCharsVecSize{8, 4, 2, 1} * 3;
+  static_assert(h[0] == 24 && h[1] == 12 && h[2] == 6 && h[3] == 3, "");
+
+  constexpr auto j = FourCharsVecSize{12, 15, 18, 21} / 3;
+  static_assert(j[0] == 4 && j[1] == 5 && j[2] == 6 && j[3] == 7, "");
+
+  constexpr auto k = FourCharsVecSize{12, 17, 19, 22} % 3;
+  static_assert(k[0] == 0 && k[1] == 2 && k[2] == 1 && k[3] == 1, "");
+
+  constexpr auto l = 3 + FourCharsVecSize{6, 3, 2, 1};
+  static_assert(l[0] == 9 && l[1] == 6 && l[2] == 5 && l[3] == 4, "");
+
+  constexpr auto m = 20 - FourCharsVecSize{19, 15, 12, 10};
+  static_assert(m[0] == 1 && m[1] == 5 && m[2] == 8 && m[3] == 10, "");
+
+  constexpr auto n = 3 * FourCharsVecSize{8, 4, 2, 1};
+  static_assert(n[0] == 24 && n[1] == 12 && n[2] == 6 && n[3] == 3, "");
+
+  constexpr auto o = 100 / FourCharsVecSize{12, 15, 18, 21};
+  static_assert(o[0] == 8 && o[1] == 6 && o[2] == 5 && o[3] == 4, "");
+
+  constexpr auto p = 100 % FourCharsVecSize{12, 15, 18, 21};
+  static_assert(p[0] == 4 && p[1] == 10 && p[2] == 10 && p[3] == 16, "");
+
+  constexpr auto q = FourCharsVecSize{6, 3, 2, 1} << FourCharsVecSize{1, 1, 2, 2};
+  static_assert(q[0] == 12 && q[1] == 6 && q[2] == 8 && q[3] == 4, "");
+
+  constexpr auto r = FourCharsVecSize{19, 15, 12, 10} >>
+                     FourCharsVecSize{1, 1, 2, 2};
+  static_assert(r[0] == 9 && r[1] == 7 && r[2] == 3 && r[3] == 2, "");
+
+  constexpr auto s = FourCharsVecSize{6, 3, 5, 10} << 1;
+  static_assert(s[0] == 12 && s[1] == 6 && s[2] == 10 && s[3] == 20, "");
+
+  constexpr auto t = FourCharsVecSize{19, 15, 10, 20} >> 1;
+  static_assert(t[0] == 9 && t[1] == 7 && t[2] == 5 && t[3] == 10, "");
+
+  constexpr auto u = 12 << FourCharsVecSize{1, 2, 3, 3};
+  static_assert(u[0] == 24 && u[1] == 48 && u[2] == 96 && u[3] == 96, "");
+
+  constexpr auto v = 12 >> FourCharsVecSize{1, 2, 2, 1};
+  static_assert(v[0] == 6 && v[1] == 3 && v[2] == 3 && v[3] == 6, "");
+
   constexpr auto w = FourCharsVecSize{1, 2, 3, 4} <
                      FourCharsVecSize{4, 3, 2, 1};
   static_assert(w[0] == -1 && w[1] == -1 && w[2] == 0 && w[3] == 0, "");
@@ -57,6 +263,27 @@ void CharUsage() {
   constexpr auto H = FourCharsVecSize{1, 2, 3, 4} != 3;
   static_assert(H[0] == -1 && H[1] == -1 && H[2] == 0 && H[3] == -1, "");
 
+  constexpr auto I = FourCharsVecSize{1, 2, 3, 4} &
+                     FourCharsVecSize{4, 3, 2, 1};
+  static_assert(I[0] == 0 && I[1] == 2 && I[2] == 2 && I[3] == 0, "");
+
+  constexpr auto J = FourCharsVecSize{1, 2, 3, 4} ^
+                     FourCharsVecSize { 4, 3, 2, 1 };
+  static_assert(J[0] == 5 && J[1] == 1 && J[2] == 1 && J[3] == 5, "");
+
+  constexpr auto K = FourCharsVecSize{1, 2, 3, 4} |
+                     FourCharsVecSize{4, 3, 2, 1};
+  static_assert(K[0] == 5 && K[1] == 3 && K[2] == 3 && K[3] == 5, "");
+
+  constexpr auto L = FourCharsVecSize{1, 2, 3, 4} & 3;
+  static_assert(L[0] == 1 && L[1] == 2 && L[2] == 3 && L[3] == 0, "");
+
+  constexpr auto M = FourCharsVecSize{1, 2, 3, 4} ^ 3;
+  static_assert(M[0] == 2 && M[1] == 1 && M[2] == 0 && M[3] == 7, "");
+
+  constexpr auto N = FourCharsVecSize{1, 2, 3, 4} | 3;
+  static_assert(N[0] == 3 && N[1] == 3 && N[2] == 3 && N[3] == 7, "");
+
   constexpr auto O = FourCharsVecSize{5, 0, 6, 0} &&
                      FourCharsVecSize{5, 5, 0, 0};
   static_assert(O[0] == 1 && O[1] == 0 && O[2] == 0 && O[3] == 0, "");
@@ -71,10 +298,39 @@ void CharUsage() {
   constexpr auto R = FourCharsVecSize{5, 0, 6, 0} || 3;
   static_assert(R[0] == 1 && R[1] == 1 && R[2] == 1 && R[3] == 1, "");
 
-  constexpr auto H1 = FourCharsVecSize{-1, -1, 0, -1};
-  constexpr auto InvH = -H1;
+  constexpr auto T = CmpMul(a, b);
+  static_assert(T[0] == 108 && T[1] == 18 && T[2] == 56 && T[3] == 72, "");
+
+  constexpr auto U = CmpDiv(a, b);
+  static_assert(U[0] == 3 && U[1] == 18 && U[2] == 0 && U[3] == 0, "");
+
+  constexpr auto V = CmpRem(a, b);
+  static_assert(V[0] == 0 && V[1] == 0 && V[2] == 7 && V[3] == 8, "");
+
+  constexpr auto X = CmpAdd(a, b);
+  static_assert(X[0] == 24 && X[1] == 19 && X[2] == 15 && X[3] == 17, "");
+
+  constexpr auto Y = CmpSub(a, b);
+  static_assert(Y[0] == 12 && Y[1] == 17 && Y[2] == -1 && Y[3] == -1, "");
+
+  constexpr auto InvH = -H;
   static_assert(InvH[0] == 1 && InvH[1] == 1 && InvH[2] == 0 && InvH[3] == 1, "");
 
+  constexpr auto Z = CmpLSH(a, InvH);
+  static_assert(Z[0] == 36 && Z[1] == 36 && Z[2] == 7 && Z[3] == 16, "");
+
+  constexpr auto aa = CmpRSH(a, InvH);
+  static_assert(aa[0] == 9 && aa[1] == 9 && aa[2] == 7 && aa[3] == 4, "");
+
+  constexpr auto ab = CmpBinAnd(a, b);
+  static_assert(ab[0] == 2 && ab[1] == 0 && ab[2] == 0 && ab[3] == 8, "");
+
+  constexpr auto ac = CmpBinXOr(a, b);
+  static_assert(ac[0] == 20 && ac[1] == 19 && ac[2] == 15 && ac[3] == 1, "");
+
+  constexpr auto ad = CmpBinOr(a, b);
+  static_assert(ad[0] == 22 && ad[1] == 19 && ad[2] == 15 && ad[3] == 9, "");
+
   constexpr auto ae = ~FourCharsVecSize{1, 2, 10, 20};
   static_assert(ae[0] == -2 && ae[1] == -3 && ae[2] == -11 && ae[3] == -21, "");
 
@@ -83,6 +339,75 @@ void CharUsage() {
 }
 
 void CharExtVecUsage() {
+  constexpr auto a = FourCharsExtVec{6, 3, 2, 1} +
+                     FourCharsExtVec{12, 15, 5, 7};
+  static_assert(a[0] == 18 && a[1] == 18 && a[2] == 7 && a[3] == 8, "");
+
+  constexpr auto b = FourCharsExtVec{19, 15, 13, 12} -
+                     FourCharsExtVec{13, 14, 5, 3};
+  static_assert(b[0] == 6 && b[1] == 1 && b[2] == 8 && b[3] == 9, "");
+
+  constexpr auto c = FourCharsExtVec{8, 4, 2, 1} *
+                     FourCharsExtVec{3, 4, 5, 6};
+  static_assert(c[0] == 24 && c[1] == 16 && c[2] == 10 && c[3] == 6, "");
+
+  constexpr auto d = FourCharsExtVec{12, 12, 10, 10} /
+                     FourCharsExtVec{6, 4, 5, 2};
+  static_assert(d[0] == 2 && d[1] == 3 && d[2] == 2 && d[3] == 5, "");
+
+  constexpr auto e = FourCharsExtVec{12, 12, 10, 10} %
+                     FourCharsExtVec{6, 4, 4, 3};
+  static_assert(e[0] == 0 && e[1] == 0 && e[2] == 2 && e[3] == 1, "");
+
+  constexpr auto f = FourCharsExtVec{6, 3, 2, 1} + 3;
+  static_assert(f[0] == 9 && f[1] == 6 && f[2] == 5 && f[3] == 4, "");
+
+  constexpr auto g = FourCharsExtVec{19, 15, 12, 10} - 3;
+  static_assert(g[0] == 16 && g[1] == 12 && g[2] == 9 && g[3] == 7, "");
+
+  constexpr auto h = FourCharsExtVec{8, 4, 2, 1} * 3;
+  static_assert(h[0] == 24 && h[1] == 12 && h[2] == 6 && h[3] == 3, "");
+
+  constexpr auto j = FourCharsExtVec{12, 15, 18, 21} / 3;
+  static_assert(j[0] == 4 && j[1] == 5 && j[2] == 6 && j[3] == 7, "");
+
+  constexpr auto k = FourCharsExtVec{...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/108949


More information about the cfe-commits mailing list