Skip to content

Commit d3bbdc7

Browse files
Mr-AnyoneRKSimon
andauthored
[clang] constexpr __builtin_elementwise_abs support (#152497)
Added constant evaluation support for `__builtin_elementwise_abs` on integer, float and vector type. fixes #152276 --------- Co-authored-by: Simon Pilgrim <[email protected]>
1 parent 3bc3b4c commit d3bbdc7

File tree

7 files changed

+122
-13
lines changed

7 files changed

+122
-13
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,8 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in
760760
The integer elementwise intrinsics, including ``__builtin_elementwise_popcount``,
761761
``__builtin_elementwise_bitreverse``, ``__builtin_elementwise_add_sat``,
762762
``__builtin_elementwise_sub_sat``, ``__builtin_elementwise_max``,
763-
``__builtin_elementwise_min`` can be called in a ``constexpr`` context.
763+
``__builtin_elementwise_min``, and ``__builtin_elementwise_abs``
764+
can be called in a ``constexpr`` context.
764765

765766
No implicit promotion of integer types takes place. The mixing of integer types
766767
of different sizes and signs is forbidden in binary and ternary builtins.

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ Non-comprehensive list of changes in this release
115115
-------------------------------------------------
116116
- Added ``__builtin_elementwise_fshl`` and ``__builtin_elementwise_fshr``.
117117

118+
- ``__builtin_elementwise_abs`` can now be used in constant expression.
119+
118120
- Added ``__builtin_elementwise_minnumnum`` and ``__builtin_elementwise_maxnumnum``.
119121

120122
- Trapping UBSan (e.g. ``-fsanitize-trap=undefined``) now emits a string describing the reason for

clang/include/clang/Basic/Builtins.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ def NondetermenisticValue : Builtin {
12641264

12651265
def ElementwiseAbs : Builtin {
12661266
let Spellings = ["__builtin_elementwise_abs"];
1267-
let Attributes = [NoThrow, Const, CustomTypeChecking];
1267+
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
12681268
let Prototype = "void(...)";
12691269
}
12701270

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,17 @@ static bool interp__builtin_fpclassify(InterpState &S, CodePtr OpPC,
598598
return true;
599599
}
600600

601+
static inline Floating abs(InterpState &S, const Floating &In) {
602+
if (!In.isNegative())
603+
return In;
604+
605+
Floating Output = S.allocFloat(In.getSemantics());
606+
APFloat New = In.getAPFloat();
607+
New.changeSign();
608+
Output.copy(New);
609+
return Output;
610+
}
611+
601612
// The C standard says "fabs raises no floating-point exceptions,
602613
// even if x is a signaling NaN. The returned value is independent of
603614
// the current rounding direction mode." Therefore constant folding can
@@ -606,16 +617,7 @@ static bool interp__builtin_fpclassify(InterpState &S, CodePtr OpPC,
606617
static bool interp__builtin_fabs(InterpState &S, CodePtr OpPC,
607618
const InterpFrame *Frame) {
608619
const Floating &Val = S.Stk.pop<Floating>();
609-
APFloat F = Val.getAPFloat();
610-
if (!F.isNegative()) {
611-
S.Stk.push<Floating>(Val);
612-
return true;
613-
}
614-
615-
Floating Result = S.allocFloat(Val.getSemantics());
616-
F.changeSign();
617-
Result.copy(F);
618-
S.Stk.push<Floating>(Result);
620+
S.Stk.push<Floating>(abs(S, Val));
619621
return true;
620622
}
621623

@@ -1686,6 +1688,57 @@ static bool interp__builtin_vector_reduce(InterpState &S, CodePtr OpPC,
16861688
return true;
16871689
}
16881690

1691+
static bool interp__builtin_elementwise_abs(InterpState &S, CodePtr OpPC,
1692+
const InterpFrame *Frame,
1693+
const CallExpr *Call,
1694+
unsigned BuiltinID) {
1695+
assert(Call->getNumArgs() == 1);
1696+
QualType Ty = Call->getArg(0)->getType();
1697+
if (Ty->isIntegerType()) {
1698+
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
1699+
APSInt Val = popToAPSInt(S.Stk, ArgT);
1700+
1701+
pushInteger(S, Val.abs(), Call->getType());
1702+
return true;
1703+
}
1704+
1705+
if (Ty->isFloatingType()) {
1706+
Floating Val = S.Stk.pop<Floating>();
1707+
Floating Result = abs(S, Val);
1708+
S.Stk.push<Floating>(Result);
1709+
return true;
1710+
}
1711+
1712+
// Otherwise, the argument must be a vector.
1713+
assert(Call->getArg(0)->getType()->isVectorType());
1714+
const Pointer &Arg = S.Stk.pop<Pointer>();
1715+
assert(Arg.getFieldDesc()->isPrimitiveArray());
1716+
const Pointer &Dst = S.Stk.peek<Pointer>();
1717+
assert(Dst.getFieldDesc()->isPrimitiveArray());
1718+
assert(Arg.getFieldDesc()->getNumElems() ==
1719+
Dst.getFieldDesc()->getNumElems());
1720+
1721+
QualType ElemType = Arg.getFieldDesc()->getElemQualType();
1722+
PrimType ElemT = *S.getContext().classify(ElemType);
1723+
unsigned NumElems = Arg.getNumElems();
1724+
// we can either have a vector of integer or a vector of floating point
1725+
for (unsigned I = 0; I != NumElems; ++I) {
1726+
if (ElemType->isIntegerType()) {
1727+
INT_TYPE_SWITCH_NO_BOOL(ElemT, {
1728+
Dst.elem<T>(I) = T::from(static_cast<T>(
1729+
APSInt(Arg.elem<T>(I).toAPSInt().abs(),
1730+
ElemType->isUnsignedIntegerOrEnumerationType())));
1731+
});
1732+
} else {
1733+
Floating Val = Arg.elem<Floating>(I);
1734+
Dst.elem<Floating>(I) = abs(S, Val);
1735+
}
1736+
}
1737+
Dst.initializeAllElements();
1738+
1739+
return true;
1740+
}
1741+
16891742
/// Can be called with an integer or vector as the first and only parameter.
16901743
static bool interp__builtin_elementwise_popcount(InterpState &S, CodePtr OpPC,
16911744
const InterpFrame *Frame,
@@ -2774,6 +2827,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
27742827
return interp__builtin_elementwise_popcount(S, OpPC, Frame, Call,
27752828
BuiltinID);
27762829

2830+
case Builtin::BI__builtin_elementwise_abs:
2831+
return interp__builtin_elementwise_abs(S, OpPC, Frame, Call, BuiltinID);
2832+
27772833
case Builtin::BI__builtin_memcpy:
27782834
case Builtin::BImemcpy:
27792835
case Builtin::BI__builtin_wmemcpy:

clang/lib/AST/ExprConstant.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11639,6 +11639,29 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {
1163911639

1164011640
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
1164111641
}
11642+
case Builtin::BI__builtin_elementwise_abs: {
11643+
APValue Source;
11644+
if (!EvaluateAsRValue(Info, E->getArg(0), Source))
11645+
return false;
11646+
11647+
QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType();
11648+
unsigned SourceLen = Source.getVectorLength();
11649+
SmallVector<APValue, 4> ResultElements;
11650+
ResultElements.reserve(SourceLen);
11651+
11652+
for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
11653+
APValue CurrentEle = Source.getVectorElt(EltNum);
11654+
APValue Val = DestEltTy->isFloatingType()
11655+
? APValue(llvm::abs(CurrentEle.getFloat()))
11656+
: APValue(APSInt(
11657+
CurrentEle.getInt().abs(),
11658+
DestEltTy->isUnsignedIntegerOrEnumerationType()));
11659+
ResultElements.push_back(Val);
11660+
}
11661+
11662+
return Success(APValue(ResultElements.data(), ResultElements.size()), E);
11663+
}
11664+
1164211665
case Builtin::BI__builtin_elementwise_add_sat:
1164311666
case Builtin::BI__builtin_elementwise_sub_sat:
1164411667
case clang::X86::BI__builtin_ia32_pmulhuw128:
@@ -13387,6 +13410,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1338713410
return Success(Operand, E);
1338813411
}
1338913412

13413+
case Builtin::BI__builtin_elementwise_abs: {
13414+
APSInt Val;
13415+
if (!EvaluateInteger(E->getArg(0), Val, Info))
13416+
return false;
13417+
13418+
return Success(Val.abs(), E);
13419+
}
13420+
1339013421
case Builtin::BI__builtin_expect:
1339113422
case Builtin::BI__builtin_expect_with_probability:
1339213423
return Visit(E->getArg(0));
@@ -15878,6 +15909,7 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
1587815909
return Error(E);
1587915910
return true;
1588015911

15912+
case Builtin::BI__builtin_elementwise_abs:
1588115913
case Builtin::BI__builtin_fabs:
1588215914
case Builtin::BI__builtin_fabsf:
1588315915
case Builtin::BI__builtin_fabsl:

clang/test/CodeGen/builtins-elementwise-math.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ void test_builtin_elementwise_abs(float f1, float f2, double d1, double d2,
6666
// CHECK-NEXT: call i32 @llvm.abs.i32(i32 [[IA1]], i1 false)
6767
b = __builtin_elementwise_abs(int_as_one);
6868

69-
// CHECK: call i32 @llvm.abs.i32(i32 -10, i1 false)
69+
// CHECK: store i32 %elt.abs11, ptr @b, align 4
7070
b = __builtin_elementwise_abs(-10);
7171

7272
// CHECK: [[SI:%.+]] = load i16, ptr %si.addr, align 2

clang/test/Sema/constant-builtins-vector.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,3 +876,21 @@ static_assert(__builtin_elementwise_min(~0U, 0U) == 0U);
876876
static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4char){1, -2, 3, -4}, (vector4char){4, -3, 2, -1})) == (LITTLE_END ? 0xFC02FD01 : 0x01FD02FC));
877877
static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4uchar){1, 2, 3, 4}, (vector4uchar){4, 3, 2, 1})) == 0x01020201U);
878878
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));
879+
880+
static_assert(__builtin_elementwise_abs(10) == 10);
881+
static_assert(__builtin_elementwise_abs(-10) == 10);
882+
static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_abs((vector4char){-1, -2, -3, 4})) == (LITTLE_END ? 0x04030201 : 0x01020304));
883+
static_assert(__builtin_elementwise_abs((int)(-2147483648)) == (int)(-2147483648)); // the absolute value of the most negative integer remains the most negative integer
884+
885+
// check floating point for elementwise abs
886+
#define CHECK_FOUR_FLOAT_VEC(vec1, vec2) \
887+
static_assert(__builtin_fabs(vec1[0] - vec2[0]) < 1e-6); \
888+
static_assert(__builtin_fabs(vec1[1] - vec2[1]) < 1e-6); \
889+
static_assert(__builtin_fabs(vec1[2] - vec2[2]) < 1e-6); \
890+
static_assert(__builtin_fabs(vec1[3] - vec2[3]) < 1e-6);
891+
892+
// checking floating point vector
893+
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}))
894+
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}))
895+
static_assert(__builtin_elementwise_abs((float)-1.123) - (float)1.123 < 1e-6); // making sure one element works
896+
#undef CHECK_FOUR_FLOAT_VEC

0 commit comments

Comments
 (0)