Skip to content

Commit ee0d706

Browse files
authored
[clang][bytecode] Implement constexpr vector unary operators +, -, ~, ! (#105996)
Implement constexpr vector unary operators +, -, ~ and ! . - Follow the current constant interpreter. All of our boolean operations on vector types should be '-1' for the 'truth' type. - Move the following functions from `Sema` to `ASTContext`, because we used it in new interpreter. ```C++ QualType GetSignedVectorType(QualType V); QualType GetSignedSizelessVectorType(QualType V); ``` --------- Signed-off-by: yronglin <[email protected]>
1 parent 1bc7057 commit ee0d706

File tree

3 files changed

+202
-0
lines changed

3 files changed

+202
-0
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4991,6 +4991,8 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
49914991
const Expr *SubExpr = E->getSubExpr();
49924992
if (SubExpr->getType()->isAnyComplexType())
49934993
return this->VisitComplexUnaryOperator(E);
4994+
if (SubExpr->getType()->isVectorType())
4995+
return this->VisitVectorUnaryOperator(E);
49944996
std::optional<PrimType> T = classify(SubExpr->getType());
49954997

49964998
switch (E->getOpcode()) {
@@ -5312,6 +5314,110 @@ bool Compiler<Emitter>::VisitComplexUnaryOperator(const UnaryOperator *E) {
53125314
return true;
53135315
}
53145316

5317+
template <class Emitter>
5318+
bool Compiler<Emitter>::VisitVectorUnaryOperator(const UnaryOperator *E) {
5319+
const Expr *SubExpr = E->getSubExpr();
5320+
assert(SubExpr->getType()->isVectorType());
5321+
5322+
if (DiscardResult)
5323+
return this->discard(SubExpr);
5324+
5325+
auto UnaryOp = E->getOpcode();
5326+
if (UnaryOp != UO_Plus && UnaryOp != UO_Minus && UnaryOp != UO_LNot &&
5327+
UnaryOp != UO_Not)
5328+
return this->emitInvalid(E);
5329+
5330+
// Nothing to do here.
5331+
if (UnaryOp == UO_Plus)
5332+
return this->delegate(SubExpr);
5333+
5334+
if (!Initializing) {
5335+
std::optional<unsigned> LocalIndex = allocateLocal(SubExpr);
5336+
if (!LocalIndex)
5337+
return false;
5338+
if (!this->emitGetPtrLocal(*LocalIndex, E))
5339+
return false;
5340+
}
5341+
5342+
// The offset of the temporary, if we created one.
5343+
unsigned SubExprOffset =
5344+
this->allocateLocalPrimitive(SubExpr, PT_Ptr, true, false);
5345+
if (!this->visit(SubExpr))
5346+
return false;
5347+
if (!this->emitSetLocal(PT_Ptr, SubExprOffset, E))
5348+
return false;
5349+
5350+
const auto *VecTy = SubExpr->getType()->getAs<VectorType>();
5351+
PrimType ElemT = classifyVectorElementType(SubExpr->getType());
5352+
auto getElem = [=](unsigned Offset, unsigned Index) -> bool {
5353+
if (!this->emitGetLocal(PT_Ptr, Offset, E))
5354+
return false;
5355+
return this->emitArrayElemPop(ElemT, Index, E);
5356+
};
5357+
5358+
switch (UnaryOp) {
5359+
case UO_Minus:
5360+
for (unsigned I = 0; I != VecTy->getNumElements(); ++I) {
5361+
if (!getElem(SubExprOffset, I))
5362+
return false;
5363+
if (!this->emitNeg(ElemT, E))
5364+
return false;
5365+
if (!this->emitInitElem(ElemT, I, E))
5366+
return false;
5367+
}
5368+
break;
5369+
case UO_LNot: { // !x
5370+
// In C++, the logic operators !, &&, || are available for vectors. !v is
5371+
// equivalent to v == 0.
5372+
//
5373+
// The result of the comparison is a vector of the same width and number of
5374+
// elements as the comparison operands with a signed integral element type.
5375+
//
5376+
// https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html
5377+
QualType ResultVecTy = E->getType();
5378+
PrimType ResultVecElemT =
5379+
classifyPrim(ResultVecTy->getAs<VectorType>()->getElementType());
5380+
for (unsigned I = 0; I != VecTy->getNumElements(); ++I) {
5381+
if (!getElem(SubExprOffset, I))
5382+
return false;
5383+
// operator ! on vectors returns -1 for 'truth', so negate it.
5384+
if (!this->emitPrimCast(ElemT, PT_Bool, Ctx.getASTContext().BoolTy, E))
5385+
return false;
5386+
if (!this->emitInv(E))
5387+
return false;
5388+
if (!this->emitPrimCast(PT_Bool, ElemT, VecTy->getElementType(), E))
5389+
return false;
5390+
if (!this->emitNeg(ElemT, E))
5391+
return false;
5392+
if (ElemT != ResultVecElemT &&
5393+
!this->emitPrimCast(ElemT, ResultVecElemT, ResultVecTy, E))
5394+
return false;
5395+
if (!this->emitInitElem(ResultVecElemT, I, E))
5396+
return false;
5397+
}
5398+
break;
5399+
}
5400+
case UO_Not: // ~x
5401+
for (unsigned I = 0; I != VecTy->getNumElements(); ++I) {
5402+
if (!getElem(SubExprOffset, I))
5403+
return false;
5404+
if (ElemT == PT_Bool) {
5405+
if (!this->emitInv(E))
5406+
return false;
5407+
} else {
5408+
if (!this->emitComp(ElemT, E))
5409+
return false;
5410+
}
5411+
if (!this->emitInitElem(ElemT, I, E))
5412+
return false;
5413+
}
5414+
break;
5415+
default:
5416+
llvm_unreachable("Unsupported unary operators should be handled up front");
5417+
}
5418+
return true;
5419+
}
5420+
53155421
template <class Emitter>
53165422
bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
53175423
if (DiscardResult)

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
139139
bool VisitGNUNullExpr(const GNUNullExpr *E);
140140
bool VisitCXXThisExpr(const CXXThisExpr *E);
141141
bool VisitUnaryOperator(const UnaryOperator *E);
142+
bool VisitVectorUnaryOperator(const UnaryOperator *E);
142143
bool VisitComplexUnaryOperator(const UnaryOperator *E);
143144
bool VisitDeclRefExpr(const DeclRefExpr *E);
144145
bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E);
@@ -349,6 +350,11 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
349350
return *this->classify(ElemType);
350351
}
351352

353+
PrimType classifyVectorElementType(QualType T) const {
354+
assert(T->isVectorType());
355+
return *this->classify(T->getAs<VectorType>()->getElementType());
356+
}
357+
352358
bool emitComplexReal(const Expr *SubExpr);
353359
bool emitComplexBoolCast(const Expr *E);
354360
bool emitComplexComparison(const Expr *LHS, const Expr *RHS,
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -std=c++14 -fsyntax-only -verify
2+
// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -fexperimental-new-constant-interpreter -std=c++14 -fsyntax-only -verify
3+
4+
using FourCharsVecSize __attribute__((vector_size(4))) = char;
5+
using FourIntsVecSize __attribute__((vector_size(16))) = int;
6+
using FourLongLongsVecSize __attribute__((vector_size(32))) = long long;
7+
using FourFloatsVecSize __attribute__((vector_size(16))) = float;
8+
using FourDoublesVecSize __attribute__((vector_size(32))) = double;
9+
using FourI128VecSize __attribute__((vector_size(64))) = __int128;
10+
11+
using FourCharsExtVec __attribute__((ext_vector_type(4))) = char;
12+
using FourIntsExtVec __attribute__((ext_vector_type(4))) = int;
13+
using FourI128ExtVec __attribute__((ext_vector_type(4))) = __int128;
14+
15+
// Only int vs float makes a difference here, so we only need to test 1 of each.
16+
// Test Char to make sure the mixed-nature of shifts around char is evident.
17+
void CharUsage() {
18+
constexpr auto H = FourCharsVecSize{-1, -1, 0, -1};
19+
constexpr auto InvH = -H;
20+
static_assert(InvH[0] == 1 && InvH[1] == 1 && InvH[2] == 0 && InvH[3] == 1, "");
21+
22+
constexpr auto ae = ~FourCharsVecSize{1, 2, 10, 20};
23+
static_assert(ae[0] == -2 && ae[1] == -3 && ae[2] == -11 && ae[3] == -21, "");
24+
25+
constexpr auto af = !FourCharsVecSize{0, 1, 8, -1};
26+
static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, "");
27+
}
28+
29+
void CharExtVecUsage() {
30+
constexpr auto H = FourCharsExtVec{-1, -1, 0, -1};
31+
constexpr auto InvH = -H;
32+
static_assert(InvH[0] == 1 && InvH[1] == 1 && InvH[2] == 0 && InvH[3] == 1, "");
33+
34+
constexpr auto ae = ~FourCharsExtVec{1, 2, 10, 20};
35+
static_assert(ae[0] == -2 && ae[1] == -3 && ae[2] == -11 && ae[3] == -21, "");
36+
37+
constexpr auto af = !FourCharsExtVec{0, 1, 8, -1};
38+
static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, "");
39+
}
40+
41+
void FloatUsage() {
42+
constexpr auto Y = FourFloatsVecSize{1.200000e+01, 1.700000e+01, -1.000000e+00, -1.000000e+00};
43+
constexpr auto Z = -Y;
44+
static_assert(Z[0] == -1.200000e+01 && Z[1] == -1.700000e+01 && Z[2] == 1.000000e+00 && Z[3] == 1.000000e+00, "");
45+
46+
// Operator ~ is illegal on floats.
47+
constexpr auto ae = ~FourFloatsVecSize{0, 1, 8, -1}; // expected-error {{invalid argument type}}
48+
49+
constexpr auto af = !FourFloatsVecSize{0, 1, 8, -1};
50+
static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, "");
51+
}
52+
53+
void FloatVecUsage() {
54+
constexpr auto Y = FourFloatsVecSize{1.200000e+01, 1.700000e+01, -1.000000e+00, -1.000000e+00};
55+
constexpr auto Z = -Y;
56+
static_assert(Z[0] == -1.200000e+01 && Z[1] == -1.700000e+01 && Z[2] == 1.000000e+00 && Z[3] == 1.000000e+00, "");
57+
58+
// Operator ~ is illegal on floats.
59+
constexpr auto ae = ~FourFloatsVecSize{0, 1, 8, -1}; // expected-error {{invalid argument type}}
60+
61+
constexpr auto af = !FourFloatsVecSize{0, 1, 8, -1};
62+
static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, "");
63+
}
64+
65+
void I128Usage() {
66+
// Operator ~ is illegal on floats, so no test for that.
67+
constexpr auto c = ~FourI128VecSize{1, 2, 10, 20};
68+
static_assert(c[0] == -2 && c[1] == -3 && c[2] == -11 && c[3] == -21, "");
69+
70+
constexpr auto d = !FourI128VecSize{0, 1, 8, -1};
71+
static_assert(d[0] == -1 && d[1] == 0 && d[2] == 0 && d[3] == 0, "");
72+
}
73+
74+
void I128VecUsage() {
75+
// Operator ~ is illegal on floats, so no test for that.
76+
constexpr auto c = ~FourI128ExtVec{1, 2, 10, 20};
77+
static_assert(c[0] == -2 && c[1] == -3 && c[2] == -11 && c[3] == -21, "");
78+
79+
constexpr auto d = !FourI128ExtVec{0, 1, 8, -1};
80+
static_assert(d[0] == -1 && d[1] == 0 && d[2] == 0 && d[3] == 0, "");
81+
}
82+
83+
using FourBoolsExtVec __attribute__((ext_vector_type(4))) = bool;
84+
void BoolVecUsage() {
85+
constexpr auto j = !FourBoolsExtVec{true, false, true, false};
86+
static_assert(j[0] == false && j[1] == true && j[2] == false && j[3] == true, "");
87+
88+
constexpr auto k = ~FourBoolsExtVec{true, false, true, false};
89+
static_assert(k[0] == false && k[1] == true && k[2] == false && k[3] == true, "");
90+
}

0 commit comments

Comments
 (0)