[clang] [clang] constexpr `__builtin_elementwise_abs` support (PR #152497)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 13 22:07:30 PDT 2025
https://github.com/Mr-Anyone updated https://github.com/llvm/llvm-project/pull/152497
>From b48993d25136a769b3b7bf8e8730be1a92ace067 Mon Sep 17 00:00:00 2001
From: Vincent <llvm at viceroygroup.ca>
Date: Thu, 7 Aug 2025 00:24:55 +0800
Subject: [PATCH 1/2] [clang] constexpr __builtin_elementwise_abs support
Added constant evaluation of `__builtine_elementwise_abs`
on integer and vector type.
fixes #152276
---
clang/docs/LanguageExtensions.rst | 3 +-
clang/docs/ReleaseNotes.rst | 2 +
clang/include/clang/Basic/Builtins.td | 2 +-
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 46 +++++++++++++++++++
clang/lib/AST/ExprConstant.cpp | 16 +++++++
.../test/CodeGen/builtins-elementwise-math.c | 2 +-
clang/test/Sema/constant-builtins-vector.cpp | 6 +++
7 files changed, 74 insertions(+), 3 deletions(-)
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index eef3d0c4ccb9d..2db1bae918ada 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -760,7 +760,8 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in
The integer elementwise intrinsics, including ``__builtin_elementwise_popcount``,
``__builtin_elementwise_bitreverse``, ``__builtin_elementwise_add_sat``,
``__builtin_elementwise_sub_sat``, ``__builtin_elementwise_max``,
-``__builtin_elementwise_min`` can be called in a ``constexpr`` context.
+``__builtin_elementwise_min``, and ``__builtin_elementwise_abs``
+can be called in a ``constexpr`` context.
No implicit promotion of integer types takes place. The mixing of integer types
of different sizes and signs is forbidden in binary and ternary builtins.
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index af576f817700a..b5e2f81bc0527 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -115,6 +115,8 @@ Non-comprehensive list of changes in this release
-------------------------------------------------
- Added ``__builtin_elementwise_fshl`` and ``__builtin_elementwise_fshr``.
+- Added `__builtin_elementwise_abs`.
+
- Added ``__builtin_elementwise_minnumnum`` and ``__builtin_elementwise_maxnumnum``.
- Trapping UBSan (e.g. ``-fsanitize-trap=undefined``) now emits a string describing the reason for
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 84206cf8b368b..604c9cddfe051 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1264,7 +1264,7 @@ def NondetermenisticValue : Builtin {
def ElementwiseAbs : Builtin {
let Spellings = ["__builtin_elementwise_abs"];
- let Attributes = [NoThrow, Const, CustomTypeChecking];
+ let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 307b77846969f..ebf82486698eb 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1686,6 +1686,49 @@ static bool interp__builtin_vector_reduce(InterpState &S, CodePtr OpPC,
return true;
}
+static bool interp__builtin_elementwise_abs(InterpState &S, CodePtr OpPC,
+ const InterpFrame *Frame,
+ const CallExpr *Call,
+ unsigned BuiltinID) {
+ // FIXME: add support of floating point
+ assert(!Call->getArg(0)->getType()->isFloatingType() &&
+ "floating point is currently not supported");
+
+ assert(Call->getNumArgs() == 1);
+ if (Call->getArg(0)->getType()->isIntegerType()) {
+ PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
+ APSInt Val = popToAPSInt(S.Stk, ArgT);
+
+ pushInteger(S, Val.abs(), Call->getType());
+ return true;
+ }
+
+ // Otherwise, the argument must be a vector.
+ assert(Call->getArg(0)->getType()->isVectorType());
+ const Pointer &Arg = S.Stk.pop<Pointer>();
+ assert(Arg.getFieldDesc()->isPrimitiveArray());
+ const Pointer &Dst = S.Stk.peek<Pointer>();
+ assert(Dst.getFieldDesc()->isPrimitiveArray());
+ assert(Arg.getFieldDesc()->getNumElems() ==
+ Dst.getFieldDesc()->getNumElems());
+
+ QualType ElemType = Arg.getFieldDesc()->getElemQualType();
+ PrimType ElemT = *S.getContext().classify(ElemType);
+ unsigned NumElems = Arg.getNumElems();
+
+ // FIXME: Reading from uninitialized vector elements?
+ for (unsigned I = 0; I != NumElems; ++I) {
+ INT_TYPE_SWITCH_NO_BOOL(ElemT, {
+ Dst.elem<T>(I) = T::from(static_cast<T>(
+ APSInt(Arg.elem<T>(I).toAPSInt().abs(),
+ ElemType->isUnsignedIntegerOrEnumerationType())));
+ });
+ }
+ Dst.initializeAllElements();
+
+ return true;
+}
+
/// Can be called with an integer or vector as the first and only parameter.
static bool interp__builtin_elementwise_popcount(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
@@ -2766,6 +2809,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
return interp__builtin_elementwise_popcount(S, OpPC, Frame, Call,
BuiltinID);
+ case Builtin::BI__builtin_elementwise_abs:
+ return interp__builtin_elementwise_abs(S, OpPC, Frame, Call, BuiltinID);
+
case Builtin::BI__builtin_memcpy:
case Builtin::BImemcpy:
case Builtin::BI__builtin_wmemcpy:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 36dd0f5d7a065..1c8d9706788c4 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11610,6 +11610,7 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
switch (E->getBuiltinCallee()) {
default:
return false;
+ case Builtin::BI__builtin_elementwise_abs:
case Builtin::BI__builtin_elementwise_popcount:
case Builtin::BI__builtin_elementwise_bitreverse: {
APValue Source;
@@ -11634,11 +11635,18 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
APValue(APSInt(Elt.reverseBits(),
DestEltTy->isUnsignedIntegerOrEnumerationType())));
break;
+ case Builtin::BI__builtin_elementwise_abs: {
+ APInt Val = Source.getVectorElt(EltNum).getInt().abs();
+ ResultElements.push_back(APValue(
+ APSInt(Val, DestEltTy->isUnsignedIntegerOrEnumerationType())));
+ break;
+ }
}
}
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
+
case Builtin::BI__builtin_elementwise_add_sat:
case Builtin::BI__builtin_elementwise_sub_sat:
case clang::X86::BI__builtin_ia32_pmulhuw128:
@@ -13387,6 +13395,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
return Success(Operand, E);
}
+ case Builtin::BI__builtin_elementwise_abs: {
+ APSInt Val;
+ if (!EvaluateInteger(E->getArg(0), Val, Info))
+ return false;
+
+ return Success(Val.abs(), E);
+ }
+
case Builtin::BI__builtin_expect:
case Builtin::BI__builtin_expect_with_probability:
return Visit(E->getArg(0));
diff --git a/clang/test/CodeGen/builtins-elementwise-math.c b/clang/test/CodeGen/builtins-elementwise-math.c
index bb5d0351db1a2..e37e5353603a0 100644
--- a/clang/test/CodeGen/builtins-elementwise-math.c
+++ b/clang/test/CodeGen/builtins-elementwise-math.c
@@ -66,7 +66,7 @@ void test_builtin_elementwise_abs(float f1, float f2, double d1, double d2,
// CHECK-NEXT: call i32 @llvm.abs.i32(i32 [[IA1]], i1 false)
b = __builtin_elementwise_abs(int_as_one);
- // CHECK: call i32 @llvm.abs.i32(i32 -10, i1 false)
+ // CHECK: store i32 %elt.abs11, ptr @b, align 4
b = __builtin_elementwise_abs(-10);
// CHECK: [[SI:%.+]] = load i16, ptr %si.addr, align 2
diff --git a/clang/test/Sema/constant-builtins-vector.cpp b/clang/test/Sema/constant-builtins-vector.cpp
index bc575dca98d77..85fb4930d09c0 100644
--- a/clang/test/Sema/constant-builtins-vector.cpp
+++ b/clang/test/Sema/constant-builtins-vector.cpp
@@ -876,3 +876,9 @@ static_assert(__builtin_elementwise_min(~0U, 0U) == 0U);
static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4char){1, -2, 3, -4}, (vector4char){4, -3, 2, -1})) == (LITTLE_END ? 0xFC02FD01 : 0x01FD02FC));
static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4uchar){1, 2, 3, 4}, (vector4uchar){4, 3, 2, 1})) == 0x01020201U);
static_assert(__builtin_bit_cast(unsigned long long, __builtin_elementwise_min((vector4short){1, -2, 3, -4}, (vector4short){4, -3, 2, -1})) == (LITTLE_END ? 0xFFFC0002FFFD0001 : 0x0001FFFD0002FFFC));
+
+static_assert(__builtin_elementwise_abs(10) == 10);
+static_assert(__builtin_elementwise_abs(-10) == 10);
+static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_abs((vector4char){-1, -2, -3, 4})) == (LITTLE_END ? 0x04030201 : 0x01020304));
+// the absolute value of the most negative integer remains the most negative integer
+static_assert(__builtin_elementwise_abs((int)(-2147483648)) == (int)(-2147483648));
>From 7ff10436cf983aa7ba4acf5104278a8366b27ee1 Mon Sep 17 00:00:00 2001
From: Vincent <llvm at viceroygroup.ca>
Date: Thu, 14 Aug 2025 12:11:47 +0800
Subject: [PATCH 2/2] Added Floating Point Support and Test Case
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 54 ++++++++++++--------
clang/lib/AST/ExprConstant.cpp | 24 ++++++++-
clang/test/Sema/constant-builtins-vector.cpp | 16 +++++-
3 files changed, 69 insertions(+), 25 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index ebf82486698eb..ede219101343a 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -598,6 +598,17 @@ static bool interp__builtin_fpclassify(InterpState &S, CodePtr OpPC,
return true;
}
+static inline Floating abs(InterpState &S, const Floating &In) {
+ if (!In.isNegative())
+ return In;
+
+ Floating Output = S.allocFloat(In.getSemantics());
+ APFloat New = In.getAPFloat();
+ New.changeSign();
+ Output.copy(New);
+ return Output;
+}
+
// The C standard says "fabs raises no floating-point exceptions,
// even if x is a signaling NaN. The returned value is independent of
// the current rounding direction mode." Therefore constant folding can
@@ -606,16 +617,7 @@ static bool interp__builtin_fpclassify(InterpState &S, CodePtr OpPC,
static bool interp__builtin_fabs(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame) {
const Floating &Val = S.Stk.pop<Floating>();
- APFloat F = Val.getAPFloat();
- if (!F.isNegative()) {
- S.Stk.push<Floating>(Val);
- return true;
- }
-
- Floating Result = S.allocFloat(Val.getSemantics());
- F.changeSign();
- Result.copy(F);
- S.Stk.push<Floating>(Result);
+ S.Stk.push<Floating>(abs(S, Val));
return true;
}
@@ -1690,12 +1692,9 @@ static bool interp__builtin_elementwise_abs(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const CallExpr *Call,
unsigned BuiltinID) {
- // FIXME: add support of floating point
- assert(!Call->getArg(0)->getType()->isFloatingType() &&
- "floating point is currently not supported");
-
assert(Call->getNumArgs() == 1);
- if (Call->getArg(0)->getType()->isIntegerType()) {
+ QualType Ty = Call->getArg(0)->getType();
+ if (Ty->isIntegerType()) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Val = popToAPSInt(S.Stk, ArgT);
@@ -1703,6 +1702,13 @@ static bool interp__builtin_elementwise_abs(InterpState &S, CodePtr OpPC,
return true;
}
+ if (Ty->isFloatingType()) {
+ Floating Val = S.Stk.pop<Floating>();
+ Floating Result = abs(S, Val);
+ S.Stk.push<Floating>(Result);
+ return true;
+ }
+
// Otherwise, the argument must be a vector.
assert(Call->getArg(0)->getType()->isVectorType());
const Pointer &Arg = S.Stk.pop<Pointer>();
@@ -1715,14 +1721,18 @@ static bool interp__builtin_elementwise_abs(InterpState &S, CodePtr OpPC,
QualType ElemType = Arg.getFieldDesc()->getElemQualType();
PrimType ElemT = *S.getContext().classify(ElemType);
unsigned NumElems = Arg.getNumElems();
-
- // FIXME: Reading from uninitialized vector elements?
+ // we can either have a vector of integer or a vector of floating point
for (unsigned I = 0; I != NumElems; ++I) {
- INT_TYPE_SWITCH_NO_BOOL(ElemT, {
- Dst.elem<T>(I) = T::from(static_cast<T>(
- APSInt(Arg.elem<T>(I).toAPSInt().abs(),
- ElemType->isUnsignedIntegerOrEnumerationType())));
- });
+ if (ElemType->isIntegerType()) {
+ INT_TYPE_SWITCH_NO_BOOL(ElemT, {
+ Dst.elem<T>(I) = T::from(static_cast<T>(
+ APSInt(Arg.elem<T>(I).toAPSInt().abs(),
+ ElemType->isUnsignedIntegerOrEnumerationType())));
+ });
+ } else {
+ Floating Val = Arg.elem<Floating>(I);
+ Dst.elem<Floating>(I) = abs(S, Val);
+ }
}
Dst.initializeAllElements();
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 1c8d9706788c4..04788549c8e7d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -11610,7 +11610,6 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
switch (E->getBuiltinCallee()) {
default:
return false;
- case Builtin::BI__builtin_elementwise_abs:
case Builtin::BI__builtin_elementwise_popcount:
case Builtin::BI__builtin_elementwise_bitreverse: {
APValue Source;
@@ -11646,6 +11645,28 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
}
+ case Builtin::BI__builtin_elementwise_abs: {
+ APValue Source;
+ if (!EvaluateAsRValue(Info, E->getArg(0), Source))
+ return false;
+
+ QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType();
+ unsigned SourceLen = Source.getVectorLength();
+ SmallVector<APValue, 4> ResultElements;
+ ResultElements.reserve(SourceLen);
+
+ for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
+ APValue CurrentEle = Source.getVectorElt(EltNum);
+ APValue Val = DestEltTy->isFloatingType()
+ ? APValue(llvm::abs(CurrentEle.getFloat()))
+ : APValue(APSInt(
+ CurrentEle.getInt().abs(),
+ DestEltTy->isUnsignedIntegerOrEnumerationType()));
+ ResultElements.push_back(Val);
+ }
+
+ return Success(APValue(ResultElements.data(), ResultElements.size()), E);
+ }
case Builtin::BI__builtin_elementwise_add_sat:
case Builtin::BI__builtin_elementwise_sub_sat:
@@ -15894,6 +15915,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
return Error(E);
return true;
+ case Builtin::BI__builtin_elementwise_abs:
case Builtin::BI__builtin_fabs:
case Builtin::BI__builtin_fabsf:
case Builtin::BI__builtin_fabsl:
diff --git a/clang/test/Sema/constant-builtins-vector.cpp b/clang/test/Sema/constant-builtins-vector.cpp
index 85fb4930d09c0..2b7d76e36ce96 100644
--- a/clang/test/Sema/constant-builtins-vector.cpp
+++ b/clang/test/Sema/constant-builtins-vector.cpp
@@ -880,5 +880,17 @@ static_assert(__builtin_bit_cast(unsigned long long, __builtin_elementwise_min((
static_assert(__builtin_elementwise_abs(10) == 10);
static_assert(__builtin_elementwise_abs(-10) == 10);
static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_abs((vector4char){-1, -2, -3, 4})) == (LITTLE_END ? 0x04030201 : 0x01020304));
-// the absolute value of the most negative integer remains the most negative integer
-static_assert(__builtin_elementwise_abs((int)(-2147483648)) == (int)(-2147483648));
+static_assert(__builtin_elementwise_abs((int)(-2147483648)) == (int)(-2147483648)); // the absolute value of the most negative integer remains the most negative integer
+
+// check floating point for elementwise abs
+#define CHECK_FOUR_FLOAT_VEC(vec1, vec2) \
+ static_assert(__builtin_fabs(vec1[0] - vec2[0]) < 1e-6); \
+ static_assert(__builtin_fabs(vec1[1] - vec2[1]) < 1e-6); \
+ static_assert(__builtin_fabs(vec1[2] - vec2[2]) < 1e-6); \
+ static_assert(__builtin_fabs(vec1[3] - vec2[3]) < 1e-6);
+
+// checking floating point vector
+CHECK_FOUR_FLOAT_VEC(__builtin_elementwise_abs((vector4float){-1.123, 2.123, -3.123, 4.123}), ((vector4float){1.123, 2.123, 3.123, 4.123}))
+CHECK_FOUR_FLOAT_VEC(__builtin_elementwise_abs((vector4double){-1.123, 2.123, -3.123, 4.123}), ((vector4double){1.123, 2.123, 3.123, 4.123}))
+static_assert(__builtin_elementwise_abs((float)-1.123) - (float)1.123 < 1e-6); // making sure one element works
+#undef CHECK_FOUR_FLOAT_VEC
More information about the cfe-commits
mailing list