[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