Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ Non-comprehensive list of changes in this release
- Deprecated float types support from ``__builtin_elementwise_max`` and
``__builtin_elementwise_min``.

- ``__builtin_elementwise_maxnum`` and ``__builtin_elementwise_minnum`` can now
be used in constant expressions.

- Added header ``endian.h`` which contains byte order helpers specified in POSIX

- Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -1647,13 +1647,13 @@ def ElementwiseMin : Builtin {

def ElementwiseMaxNum : Builtin {
let Spellings = ["__builtin_elementwise_maxnum"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}

def ElementwiseMinNum : Builtin {
let Spellings = ["__builtin_elementwise_minnum"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Attributes = [NoThrow, Const, CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}

Expand Down
122 changes: 42 additions & 80 deletions clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2621,13 +2621,29 @@ static bool interp__builtin_elementwise_fp_binop(
Fn,
bool IsScalar = false) {
assert((Call->getNumArgs() == 2) || (Call->getNumArgs() == 3));

// Single floating-point case.
if (!Call->getArg(0)->getType()->isVectorType()) {
assert(!Call->getArg(1)->getType()->isVectorType());
assert(Call->getNumArgs() == 2);
const Floating &RHS = S.Stk.pop<Floating>();
const Floating &LHS = S.Stk.pop<Floating>();
std::optional<APFloat> Result =
Fn(LHS.getAPFloat(), RHS.getAPFloat(), std::nullopt);
if (!Result)
return false;
Floating R = S.allocFloat(Result->getSemantics());
R.copy(*Result);
S.Stk.push<Floating>(R);
return true;
}

const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>();
assert(VT->getElementType()->isFloatingType());
unsigned NumElems = VT->getNumElements();

// Vector case.
assert(Call->getArg(0)->getType()->isVectorType() &&
Call->getArg(1)->getType()->isVectorType());
assert(Call->getArg(1)->getType()->isVectorType());
assert(VT->getElementType() ==
Call->getArg(1)->getType()->castAs<VectorType>()->getElementType());
assert(VT->getNumElements() ==
Expand Down Expand Up @@ -2819,82 +2835,6 @@ interp__builtin_ia32_pack(InterpState &S, CodePtr, const CallExpr *E,
return true;
}

static bool interp__builtin_elementwise_maxmin(InterpState &S, CodePtr OpPC,
const CallExpr *Call,
unsigned BuiltinID) {
assert(Call->getNumArgs() == 2);

QualType Arg0Type = Call->getArg(0)->getType();

// TODO: Support floating-point types.
if (!(Arg0Type->isIntegerType() ||
(Arg0Type->isVectorType() &&
Arg0Type->castAs<VectorType>()->getElementType()->isIntegerType())))
return false;

if (!Arg0Type->isVectorType()) {
assert(!Call->getArg(1)->getType()->isVectorType());
APSInt RHS;
if (!popToAPSInt(S, Call->getArg(1), RHS))
return false;
APSInt LHS;
if (!popToAPSInt(S, Arg0Type, LHS))
return false;
APInt Result;
if (BuiltinID == Builtin::BI__builtin_elementwise_max) {
Result = std::max(LHS, RHS);
} else if (BuiltinID == Builtin::BI__builtin_elementwise_min) {
Result = std::min(LHS, RHS);
} else {
llvm_unreachable("Wrong builtin ID");
}

pushInteger(S, APSInt(Result, !LHS.isSigned()), Call->getType());
return true;
}

// Vector case.
assert(Call->getArg(0)->getType()->isVectorType() &&
Call->getArg(1)->getType()->isVectorType());
const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>();
assert(VT->getElementType() ==
Call->getArg(1)->getType()->castAs<VectorType>()->getElementType());
assert(VT->getNumElements() ==
Call->getArg(1)->getType()->castAs<VectorType>()->getNumElements());
assert(VT->getElementType()->isIntegralOrEnumerationType());

const Pointer &RHS = S.Stk.pop<Pointer>();
const Pointer &LHS = S.Stk.pop<Pointer>();
const Pointer &Dst = S.Stk.peek<Pointer>();
PrimType ElemT = *S.getContext().classify(VT->getElementType());
unsigned NumElems = VT->getNumElements();
for (unsigned I = 0; I != NumElems; ++I) {
APSInt Elem1;
APSInt Elem2;
INT_TYPE_SWITCH_NO_BOOL(ElemT, {
Elem1 = LHS.elem<T>(I).toAPSInt();
Elem2 = RHS.elem<T>(I).toAPSInt();
});

APSInt Result;
if (BuiltinID == Builtin::BI__builtin_elementwise_max) {
Result = APSInt(std::max(Elem1, Elem2),
Call->getType()->isUnsignedIntegerOrEnumerationType());
} else if (BuiltinID == Builtin::BI__builtin_elementwise_min) {
Result = APSInt(std::min(Elem1, Elem2),
Call->getType()->isUnsignedIntegerOrEnumerationType());
} else {
llvm_unreachable("Wrong builtin ID");
}

INT_TYPE_SWITCH_NO_BOOL(ElemT,
{ Dst.elem<T>(I) = static_cast<T>(Result); });
}
Dst.initializeAllElements();

return true;
}

static bool interp__builtin_ia32_pmul(
InterpState &S, CodePtr OpPC, const CallExpr *Call,
llvm::function_ref<APInt(const APSInt &, const APSInt &, const APSInt &,
Expand Down Expand Up @@ -5452,8 +5392,30 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
[](const APSInt &LHS, const APSInt &RHS) { return LHS.rotr(RHS); });

case Builtin::BI__builtin_elementwise_max:
case Builtin::BI__builtin_elementwise_min:
return interp__builtin_elementwise_maxmin(S, OpPC, Call, BuiltinID);
case Builtin::BI__builtin_elementwise_min: {
QualType Arg0Type = Call->getArg(0)->getType();
QualType ElemType = Arg0Type->isVectorType()
? Arg0Type->castAs<VectorType>()->getElementType()
: Arg0Type;
if (!ElemType->isIntegerType())
return false;
bool IsMax = BuiltinID == Builtin::BI__builtin_elementwise_max;
return interp__builtin_elementwise_int_binop(
S, OpPC, Call, [IsMax](const APSInt &LHS, const APSInt &RHS) {
return IsMax ? std::max(LHS, RHS) : std::min(LHS, RHS);
});
}

case Builtin::BI__builtin_elementwise_maxnum:
case Builtin::BI__builtin_elementwise_minnum: {
bool IsMax = BuiltinID == Builtin::BI__builtin_elementwise_maxnum;
return interp__builtin_elementwise_fp_binop(
S, OpPC, Call,
[IsMax](const APFloat &LHS, const APFloat &RHS,
std::optional<APSInt>) -> std::optional<APFloat> {
return IsMax ? maxnum(LHS, RHS) : minnum(LHS, RHS);
});
}

case clang::X86::BI__builtin_ia32_phaddw128:
case clang::X86::BI__builtin_ia32_phaddw256:
Expand Down
48 changes: 18 additions & 30 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12867,38 +12867,24 @@ bool VectorExprEvaluator::VisitCallExpr(const CallExpr *E) {

case Builtin::BI__builtin_elementwise_max:
case Builtin::BI__builtin_elementwise_min: {
APValue SourceLHS, SourceRHS;
if (!EvaluateAsRValue(Info, E->getArg(0), SourceLHS) ||
!EvaluateAsRValue(Info, E->getArg(1), SourceRHS))
return false;

bool IsMax = E->getBuiltinCallee() == Builtin::BI__builtin_elementwise_max;
QualType DestEltTy = E->getType()->castAs<VectorType>()->getElementType();

if (!DestEltTy->isIntegerType())
return false;
return EvaluateBinOpExpr([IsMax](const APSInt &LHS, const APSInt &RHS) {
return IsMax ? std::max(LHS, RHS) : std::min(LHS, RHS);
});
}

unsigned SourceLen = SourceLHS.getVectorLength();
SmallVector<APValue, 4> ResultElements;
ResultElements.reserve(SourceLen);

for (unsigned EltNum = 0; EltNum < SourceLen; ++EltNum) {
APSInt LHS = SourceLHS.getVectorElt(EltNum).getInt();
APSInt RHS = SourceRHS.getVectorElt(EltNum).getInt();
switch (E->getBuiltinCallee()) {
case Builtin::BI__builtin_elementwise_max:
ResultElements.push_back(
APValue(APSInt(std::max(LHS, RHS),
DestEltTy->isUnsignedIntegerOrEnumerationType())));
break;
case Builtin::BI__builtin_elementwise_min:
ResultElements.push_back(
APValue(APSInt(std::min(LHS, RHS),
DestEltTy->isUnsignedIntegerOrEnumerationType())));
break;
}
}

return Success(APValue(ResultElements.data(), ResultElements.size()), E);
case Builtin::BI__builtin_elementwise_maxnum:
case Builtin::BI__builtin_elementwise_minnum: {
bool IsMax =
E->getBuiltinCallee() == Builtin::BI__builtin_elementwise_maxnum;
return EvaluateFpBinOpExpr(
[IsMax](const APFloat &LHS, const APFloat &RHS,
std::optional<APSInt>) -> std::optional<APFloat> {
return IsMax ? maxnum(LHS, RHS) : minnum(LHS, RHS);
});
}
case X86::BI__builtin_ia32_vpshldd128:
case X86::BI__builtin_ia32_vpshldd256:
Expand Down Expand Up @@ -19918,7 +19904,8 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
case Builtin::BI__builtin_fmaxf:
case Builtin::BI__builtin_fmaxl:
case Builtin::BI__builtin_fmaxf16:
case Builtin::BI__builtin_fmaxf128: {
case Builtin::BI__builtin_fmaxf128:
case Builtin::BI__builtin_elementwise_maxnum: {
APFloat RHS(0.);
if (!EvaluateFloat(E->getArg(0), Result, Info) ||
!EvaluateFloat(E->getArg(1), RHS, Info))
Expand All @@ -19931,7 +19918,8 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
case Builtin::BI__builtin_fminf:
case Builtin::BI__builtin_fminl:
case Builtin::BI__builtin_fminf16:
case Builtin::BI__builtin_fminf128: {
case Builtin::BI__builtin_fminf128:
case Builtin::BI__builtin_elementwise_minnum: {
APFloat RHS(0.);
if (!EvaluateFloat(E->getArg(0), Result, Info) ||
!EvaluateFloat(E->getArg(1), RHS, Info))
Expand Down
52 changes: 52 additions & 0 deletions clang/test/Sema/constant-builtins-vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,58 @@ static_assert(__builtin_bit_cast(unsigned, __builtin_elementwise_min((vector4cha
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_maxnum(1.0f, 2.0f) == 2.0f);
static_assert(__builtin_elementwise_maxnum(-1.0f, 1.0f) == 1.0f);
static_assert(__builtin_elementwise_maxnum(1.0, 2.0) == 2.0);
static_assert(__builtin_elementwise_maxnum(-1.0, 1.0) == 1.0);
static_assert(__builtin_elementwise_maxnum(__builtin_nanf(""), 1.0f) == 1.0f);
static_assert(__builtin_elementwise_maxnum(1.0f, __builtin_nanf("")) == 1.0f);
static_assert(__builtin_isnan(__builtin_elementwise_maxnum(__builtin_nanf(""), __builtin_nanf(""))));
static_assert(__builtin_elementwise_maxnum(__builtin_inff(), 1.0f) == __builtin_inff());
static_assert(__builtin_elementwise_maxnum(-__builtin_inff(), 1.0f) == 1.0f);

static_assert(__builtin_elementwise_minnum(1.0f, 2.0f) == 1.0f);
static_assert(__builtin_elementwise_minnum(-1.0f, 1.0f) == -1.0f);
static_assert(__builtin_elementwise_minnum(1.0, 2.0) == 1.0);
static_assert(__builtin_elementwise_minnum(-1.0, 1.0) == -1.0);
static_assert(__builtin_elementwise_minnum(__builtin_nanf(""), 1.0f) == 1.0f);
static_assert(__builtin_elementwise_minnum(1.0f, __builtin_nanf("")) == 1.0f);
static_assert(__builtin_isnan(__builtin_elementwise_minnum(__builtin_nanf(""), __builtin_nanf(""))));
static_assert(__builtin_elementwise_minnum(__builtin_inff(), 1.0f) == 1.0f);
static_assert(__builtin_elementwise_minnum(-__builtin_inff(), 1.0f) == -__builtin_inff());

constexpr vector4float maxf_vec =
__builtin_elementwise_maxnum((vector4float){1.0f, -2.0f, 3.0f, -4.0f},
(vector4float){4.0f, -3.0f, 2.0f, -1.0f});
static_assert(maxf_vec[0] == 4.0f && maxf_vec[1] == -2.0f &&
maxf_vec[2] == 3.0f && maxf_vec[3] == -1.0f);
constexpr vector4float minf_vec =
__builtin_elementwise_minnum((vector4float){1.0f, -2.0f, 3.0f, -4.0f},
(vector4float){4.0f, -3.0f, 2.0f, -1.0f});
static_assert(minf_vec[0] == 1.0f && minf_vec[1] == -3.0f &&
minf_vec[2] == 2.0f && minf_vec[3] == -4.0f);
constexpr vector4double maxd_vec =
__builtin_elementwise_maxnum((vector4double){1.0, -2.0, 3.0, -4.0},
(vector4double){4.0, -3.0, 2.0, -1.0});
static_assert(maxd_vec[0] == 4.0 && maxd_vec[1] == -2.0 &&
maxd_vec[2] == 3.0 && maxd_vec[3] == -1.0);
constexpr vector4double mind_vec =
__builtin_elementwise_minnum((vector4double){1.0, -2.0, 3.0, -4.0},
(vector4double){4.0, -3.0, 2.0, -1.0});
static_assert(mind_vec[0] == 1.0 && mind_vec[1] == -3.0 &&
mind_vec[2] == 2.0 && mind_vec[3] == -4.0);

constexpr vector4float maxf_nan_inf = __builtin_elementwise_maxnum(
(vector4float){__builtin_nanf(""), 1.0f, __builtin_inff(), 1.0f},
(vector4float){1.0f, __builtin_nanf(""), 1.0f, -__builtin_inff()});
static_assert(maxf_nan_inf[0] == 1.0f && maxf_nan_inf[1] == 1.0f &&
maxf_nan_inf[2] == __builtin_inff() && maxf_nan_inf[3] == 1.0f);
constexpr vector4float minf_nan_inf = __builtin_elementwise_minnum(
(vector4float){__builtin_nanf(""), 1.0f, __builtin_inff(), 1.0f},
(vector4float){1.0f, __builtin_nanf(""), 1.0f, -__builtin_inff()});
static_assert(minf_nan_inf[0] == 1.0f && minf_nan_inf[1] == 1.0f &&
minf_nan_inf[2] == 1.0f && minf_nan_inf[3] == -__builtin_inff());

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));
Expand Down