[clang] c8a5b30 - [SveEmitter] Add range checks for immediates and predicate patterns.
Sander de Smalen via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 14 09:05:15 PDT 2020
Author: Sander de Smalen
Date: 2020-04-14T16:49:32+01:00
New Revision: c8a5b30bac695e9fbb592cf77364a60ebd6e0dbd
URL: https://github.com/llvm/llvm-project/commit/c8a5b30bac695e9fbb592cf77364a60ebd6e0dbd
DIFF: https://github.com/llvm/llvm-project/commit/c8a5b30bac695e9fbb592cf77364a60ebd6e0dbd.diff
LOG: [SveEmitter] Add range checks for immediates and predicate patterns.
Summary:
This patch adds a mechanism to easily add range checks for a builtin's
immediate operands. This patch is tested with the qdech intrinsic, which takes
both an enum for the predicate pattern, as well as an immediate for the
multiplier.
Reviewers: efriedma, SjoerdMeijer, rovka
Reviewed By: efriedma, SjoerdMeijer
Subscribers: mgorny, tschuett, mgrang, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D76678
Added:
clang/test/CodeGen/aarch64-sve-intrinsics/acle_sve_qdech.c
clang/test/CodeGen/aarch64-sve-intrinsics/negative/acle_sve_qdech.c
Modified:
clang/include/clang/Basic/CMakeLists.txt
clang/include/clang/Basic/TargetBuiltins.h
clang/include/clang/Basic/arm_sve.td
clang/include/clang/Sema/Sema.h
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/Sema/SemaChecking.cpp
clang/utils/TableGen/SveEmitter.cpp
clang/utils/TableGen/TableGen.cpp
clang/utils/TableGen/TableGenBackends.h
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/CMakeLists.txt b/clang/include/clang/Basic/CMakeLists.txt
index 5eda48e8f250..47a3198a0e91 100644
--- a/clang/include/clang/Basic/CMakeLists.txt
+++ b/clang/include/clang/Basic/CMakeLists.txt
@@ -69,6 +69,9 @@ clang_tablegen(arm_sve_builtin_cg.inc -gen-arm-sve-builtin-codegen
clang_tablegen(arm_sve_typeflags.inc -gen-arm-sve-typeflags
SOURCE arm_sve.td
TARGET ClangARMSveTypeFlags)
+clang_tablegen(arm_sve_sema_rangechecks.inc -gen-arm-sve-sema-rangechecks
+ SOURCE arm_sve.td
+ TARGET ClangARMSveSemaRangeChecks)
clang_tablegen(arm_cde_builtins.inc -gen-arm-cde-builtin-def
SOURCE arm_cde.td
TARGET ClangARMCdeBuiltinsDef)
diff --git a/clang/include/clang/Basic/TargetBuiltins.h b/clang/include/clang/Basic/TargetBuiltins.h
index 468167957974..0821926a6881 100644
--- a/clang/include/clang/Basic/TargetBuiltins.h
+++ b/clang/include/clang/Basic/TargetBuiltins.h
@@ -190,6 +190,13 @@ namespace clang {
#include "clang/Basic/arm_sve_typeflags.inc"
#undef LLVM_GET_SVE_MERGETYPES
};
+
+ enum ImmCheckType {
+#define LLVM_GET_SVE_IMMCHECKTYPES
+#include "clang/Basic/arm_sve_typeflags.inc"
+#undef LLVM_GET_SVE_IMMCHECKTYPES
+ };
+
SVETypeFlags(uint64_t F) : Flags(F) {
EltTypeShift = llvm::countTrailingZeros(EltTypeMask);
MemEltTypeShift = llvm::countTrailingZeros(MemEltTypeMask);
diff --git a/clang/include/clang/Basic/arm_sve.td b/clang/include/clang/Basic/arm_sve.td
index 9ed4ae86e1b7..75fd3ca499d0 100644
--- a/clang/include/clang/Basic/arm_sve.td
+++ b/clang/include/clang/Basic/arm_sve.td
@@ -61,6 +61,10 @@
// d: default
// c: const pointer type
// P: predicate type
+//
+// i: constant uint64_t
+//
+// I: Predicate Pattern (sv_pattern)
// l: int64_t
@@ -147,9 +151,22 @@ def IsStructLoad : FlagType<0x00020000>;
def IsStructStore : FlagType<0x00040000>;
def IsZExtReturn : FlagType<0x00080000>; // Return value is sign-extend by default
+// These must be kept in sync with the flags in include/clang/Basic/TargetBuiltins.h
+class ImmCheckType<int val> {
+ int Value = val;
+}
+def ImmCheck0_31 : ImmCheckType<0>; // 0..31 (used for e.g. predicate patterns)
+def ImmCheck1_16 : ImmCheckType<1>; // 1..16
+
+class ImmCheck<int arg, ImmCheckType kind, int eltSizeArg = -1> {
+ int Arg = arg;
+ int EltSizeArg = eltSizeArg;
+ ImmCheckType Kind = kind;
+}
+
// Every intrinsic subclasses Inst.
class Inst<string n, string p, string t, MergeType mt, string i,
- list<FlagType> ft, MemEltType met> {
+ list<FlagType> ft, list<ImmCheck> ch, MemEltType met> {
string Name = n;
string Prototype = p;
string Types = t;
@@ -158,13 +175,21 @@ class Inst<string n, string p, string t, MergeType mt, string i,
string MergeSuffix = mt.Suffix;
string LLVMIntrinsic = i;
list<FlagType> Flags = ft;
+ list<ImmCheck> ImmChecks = ch;
int MemEltType = met.Value;
}
+// SInst: Instruction with signed/unsigned suffix (e.g., "s8", "u8")
+class SInst<string n, string p, string t, MergeType mt, string i = "",
+ list<FlagType> ft = [], list<ImmCheck> ch = []>
+ : Inst<n, p, t, mt, i, ft, ch, MemEltTyDefault> {
+}
+
// MInst: Instructions which access memory
class MInst<string n, string p, string t, list<FlagType> f,
- MemEltType met=MemEltTyDefault, string i="">
- : Inst<n, p, t, MergeNone, i, f, met> {}
+ MemEltType met = MemEltTyDefault, string i = "">
+ : Inst<n, p, t, MergeNone, i, f, [], met> {
+}
////////////////////////////////////////////////////////////////////////////////
// Loads
@@ -256,3 +281,8 @@ def SVSTNT1 : MInst<"svstnt1[_{d}]", "vPpd", "csilUcUsUiUlhfd", [IsStore], MemEl
// Store one vector, with no truncation, non-temporal (scalar base, VL displacement)
def SVSTNT1_VNUM : MInst<"svstnt1_vnum[_{d}]", "vPpld", "csilUcUsUiUlhfd", [IsStore], MemEltTyDefault, "aarch64_sve_stnt1">;
+
+////////////////////////////////////////////////////////////////////////////////
+// Saturating scalar arithmetic
+def SVQDECH_S : SInst<"svqdech_pat[_{d}]", "ddIi", "s", MergeNone, "aarch64_sve_sqdech", [], [ImmCheck<2, ImmCheck1_16>]>;
+def SVQDECH_U : SInst<"svqdech_pat[_{d}]", "ddIi", "Us", MergeNone, "aarch64_sve_uqdech", [], [ImmCheck<2, ImmCheck1_16>]>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 521b741ae509..c85c5db5f652 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11974,6 +11974,7 @@ class Sema final {
unsigned MaxWidth);
bool CheckNeonBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool CheckMVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
+ bool CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool CheckCDEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool CheckARMCoprocessorImmediate(const Expr *CoprocArg, bool WantCDE);
bool CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index a7a29c13697e..6e3a3dfe50bd 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -7464,6 +7464,39 @@ Value *CodeGenFunction::vectorWrapScalar16(Value *Op) {
return Op;
}
+// Return the llvm vector type corresponding to the specified element TypeFlags.
+llvm::Type *CodeGenFunction::getSVEType(const SVETypeFlags &TypeFlags) {
+ switch (TypeFlags.getEltType()) {
+ default:
+ llvm_unreachable("Invalid SVETypeFlag!");
+
+ case SVETypeFlags::EltTyInt8:
+ return llvm::VectorType::get(Builder.getInt8Ty(), {16, true});
+ case SVETypeFlags::EltTyInt16:
+ return llvm::VectorType::get(Builder.getInt16Ty(), {8, true});
+ case SVETypeFlags::EltTyInt32:
+ return llvm::VectorType::get(Builder.getInt32Ty(), {4, true});
+ case SVETypeFlags::EltTyInt64:
+ return llvm::VectorType::get(Builder.getInt64Ty(), {2, true});
+
+ case SVETypeFlags::EltTyFloat16:
+ return llvm::VectorType::get(Builder.getHalfTy(), {8, true});
+ case SVETypeFlags::EltTyFloat32:
+ return llvm::VectorType::get(Builder.getFloatTy(), {4, true});
+ case SVETypeFlags::EltTyFloat64:
+ return llvm::VectorType::get(Builder.getDoubleTy(), {2, true});
+
+ case SVETypeFlags::EltTyBool8:
+ return llvm::VectorType::get(Builder.getInt1Ty(), {16, true});
+ case SVETypeFlags::EltTyBool16:
+ return llvm::VectorType::get(Builder.getInt1Ty(), {8, true});
+ case SVETypeFlags::EltTyBool32:
+ return llvm::VectorType::get(Builder.getInt1Ty(), {4, true});
+ case SVETypeFlags::EltTyBool64:
+ return llvm::VectorType::get(Builder.getInt1Ty(), {2, true});
+ }
+}
+
// Reinterpret the input predicate so that it can be used to correctly isolate
// the elements of the specified datatype.
Value *CodeGenFunction::EmitSVEPredicateCast(Value *Pred,
@@ -7572,8 +7605,19 @@ Value *CodeGenFunction::EmitAArch64SVEBuiltinExpr(unsigned BuiltinID,
for (unsigned i = 0, e = E->getNumArgs(); i != e; i++) {
if ((ICEArguments & (1 << i)) == 0)
Ops.push_back(EmitScalarExpr(E->getArg(i)));
- else
- llvm_unreachable("Not yet implemented");
+ else {
+ // If this is required to be a constant, constant fold it so that we know
+ // that the generated intrinsic gets a ConstantInt.
+ llvm::APSInt Result;
+ if (!E->getArg(i)->isIntegerConstantExpr(Result, getContext()))
+ llvm_unreachable("Expected argument to be a constant");
+
+ // Immediates for SVE llvm intrinsics are always 32bit. We can safely
+ // truncate because the immediate has been range checked and no valid
+ // immediate requires more than a handful of bits.
+ Result = Result.extOrTrunc(32);
+ Ops.push_back(llvm::ConstantInt::get(getLLVMContext(), Result));
+ }
}
auto *Builtin = findARMVectorIntrinsicInMap(AArch64SVEIntrinsicMap, BuiltinID,
@@ -7585,6 +7629,13 @@ Value *CodeGenFunction::EmitAArch64SVEBuiltinExpr(unsigned BuiltinID,
TypeFlags.isZExtReturn());
else if (TypeFlags.isStore())
return EmitSVEMaskedStore(E, Ops, Builtin->LLVMIntrinsic);
+ else if (Builtin->LLVMIntrinsic != 0) {
+ llvm::Type* OverloadedTy = getSVEType(TypeFlags);
+
+ Function *F = CGM.getIntrinsic(Builtin->LLVMIntrinsic, OverloadedTy);
+ Value *Call = Builder.CreateCall(F, Ops);
+ return Call;
+ }
/// Should not happen
return nullptr;
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 32ca12700536..2429f5d4a445 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -77,6 +77,7 @@ class ObjCAtThrowStmt;
class ObjCAtSynchronizedStmt;
class ObjCAutoreleasePoolStmt;
class ReturnsNonNullAttr;
+class SVETypeFlags;
namespace analyze_os_log {
class OSLogBufferLayout;
@@ -3903,6 +3904,7 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Type *Ty, bool usgn, const char *name);
llvm::Value *vectorWrapScalar16(llvm::Value *Op);
+ llvm::Type *getSVEType(const SVETypeFlags &TypeFlags);
llvm::Value *EmitSVEPredicateCast(llvm::Value *Pred, llvm::VectorType *VTy);
llvm::Value *EmitSVEMaskedLoad(const CallExpr *, llvm::Type *ReturnTy,
SmallVectorImpl<llvm::Value *> &Ops,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index dc5147687376..e7bc4994e540 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1998,6 +1998,39 @@ static QualType getNeonEltType(NeonTypeFlags Flags, ASTContext &Context,
llvm_unreachable("Invalid NeonTypeFlag!");
}
+bool Sema::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
+ // Range check SVE intrinsics that take immediate values.
+ SmallVector<std::tuple<int,int,int>, 3> ImmChecks;
+
+ switch (BuiltinID) {
+ default:
+ return false;
+#define GET_SVE_IMMEDIATE_CHECK
+#include "clang/Basic/arm_sve_sema_rangechecks.inc"
+#undef GET_SVE_IMMEDIATE_CHECK
+ }
+
+ // Perform all the immediate checks for this builtin call.
+ bool HasError = false;
+ for (auto &I : ImmChecks) {
+ int ArgNum, CheckTy, ElementSizeInBits;
+ std::tie(ArgNum, CheckTy, ElementSizeInBits) = I;
+
+ switch ((SVETypeFlags::ImmCheckType)CheckTy) {
+ case SVETypeFlags::ImmCheck0_31:
+ if (SemaBuiltinConstantArgRange(TheCall, ArgNum, 0, 31))
+ HasError = true;
+ break;
+ case SVETypeFlags::ImmCheck1_16:
+ if (SemaBuiltinConstantArgRange(TheCall, ArgNum, 1, 16))
+ HasError = true;
+ break;
+ }
+ }
+
+ return HasError;
+}
+
bool Sema::CheckNeonBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
llvm::APSInt Result;
uint64_t mask = 0;
@@ -2352,6 +2385,9 @@ bool Sema::CheckAArch64BuiltinFunctionCall(unsigned BuiltinID,
if (CheckNeonBuiltinFunctionCall(BuiltinID, TheCall))
return true;
+ if (CheckSVEBuiltinFunctionCall(BuiltinID, TheCall))
+ return true;
+
// For intrinsics which take an immediate value as part of the instruction,
// range check them here.
unsigned i = 0, l = 0, u = 0;
diff --git a/clang/test/CodeGen/aarch64-sve-intrinsics/acle_sve_qdech.c b/clang/test/CodeGen/aarch64-sve-intrinsics/acle_sve_qdech.c
new file mode 100644
index 000000000000..e70c5739c2f9
--- /dev/null
+++ b/clang/test/CodeGen/aarch64-sve-intrinsics/acle_sve_qdech.c
@@ -0,0 +1,163 @@
+// RUN: %clang_cc1 -D__ARM_FEATURE_SVE -triple aarch64-none-linux-gnu -target-feature +sve -fallow-half-arguments-and-returns -S -O1 -Werror -Wall -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -D__ARM_FEATURE_SVE -DSVE_OVERLOADED_FORMS -triple aarch64-none-linux-gnu -target-feature +sve -fallow-half-arguments-and-returns -S -O1 -Werror -Wall -emit-llvm -o - %s | FileCheck %s
+
+#include <arm_sve.h>
+
+#ifdef SVE_OVERLOADED_FORMS
+// A simple used,unused... macro, long enough to represent any SVE builtin.
+#define SVE_ACLE_FUNC(A1,A2_UNUSED,A3,A4_UNUSED) A1##A3
+#else
+#define SVE_ACLE_FUNC(A1,A2,A3,A4) A1##A2##A3##A4
+#endif
+
+svint16_t test_svqdech_pat_s16(svint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_s16
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.sqdech.nxv8i16(<vscale x 8 x i16> %op, i32 0, i32 1)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return svqdech_pat_s16(op, SV_POW2, 1);
+}
+
+svint16_t test_svqdech_pat_s16_all(svint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_s16_all
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.sqdech.nxv8i16(<vscale x 8 x i16> %op, i32 31, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return svqdech_pat_s16(op, SV_ALL, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_pow2(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_pow2
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 0, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_POW2, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl1(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl1
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 1, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL1, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl2(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl2
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 2, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL2, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl3(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl3
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 3, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL3, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl4(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl4
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 4, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL4, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl5(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl5
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 5, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL5, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl6(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl6
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 6, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL6, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl7(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl7
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 7, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL7, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl8(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl8
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 8, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL8, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl16(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl16
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 9, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL16, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl32(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl32
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 10, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL32, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl64(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl64
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 11, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL64, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl128(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl128
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 12, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL128, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_vl256(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_vl256
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 13, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_VL256, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_mul4(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_mul4
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 29, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_MUL4, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_mul3(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_mul3
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 30, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_MUL3, 16);
+}
+
+svuint16_t test_svqdech_pat_u16_all(svuint16_t op)
+{
+ // CHECK-LABEL: test_svqdech_pat_u16_all
+ // CHECK: %[[INTRINSIC:.*]] = call <vscale x 8 x i16> @llvm.aarch64.sve.uqdech.nxv8i16(<vscale x 8 x i16> %op, i32 31, i32 16)
+ // CHECK: ret <vscale x 8 x i16> %[[INTRINSIC]]
+ return SVE_ACLE_FUNC(svqdech_pat,_u16,,)(op, SV_ALL, 16);
+}
diff --git a/clang/test/CodeGen/aarch64-sve-intrinsics/negative/acle_sve_qdech.c b/clang/test/CodeGen/aarch64-sve-intrinsics/negative/acle_sve_qdech.c
new file mode 100644
index 000000000000..71ecd73fa403
--- /dev/null
+++ b/clang/test/CodeGen/aarch64-sve-intrinsics/negative/acle_sve_qdech.c
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -D__ARM_FEATURE_SVE -triple aarch64-none-linux-gnu -target-feature +sve -fallow-half-arguments-and-returns -fsyntax-only -verify %s
+// RUN: %clang_cc1 -D__ARM_FEATURE_SVE -DSVE_OVERLOADED_FORMS -triple aarch64-none-linux-gnu -target-feature +sve -fallow-half-arguments-and-returns -fsyntax-only -verify %s
+
+#ifdef SVE_OVERLOADED_FORMS
+// A simple used,unused... macro, long enough to represent any SVE builtin.
+#define SVE_ACLE_FUNC(A1,A2_UNUSED,A3,A4_UNUSED) A1##A3
+#else
+#define SVE_ACLE_FUNC(A1,A2,A3,A4) A1##A2##A3##A4
+#endif
+
+#include <arm_sve.h>
+
+svint16_t test_svqdech_pat_s16(svint16_t op)
+{
+ // expected-error at +1 {{argument value 0 is outside the valid range [1, 16]}}
+ return svqdech_pat_s16(op, SV_VL8, 0);
+}
+
+svint16_t test_svqdech_pat_s16_2(svint16_t op)
+{
+ // expected-error at +1 {{argument value 17 is outside the valid range [1, 16]}}
+ return svqdech_pat_s16(op, SV_VL16, 17);
+}
+
+svuint16_t test_svqdech_pat_u16(svuint16_t op)
+{
+ // expected-error at +1 {{argument value 0 is outside the valid range [1, 16]}}
+ return svqdech_pat_u16(op, SV_VL32, 0);
+}
+
+svuint16_t test_svqdech_pat_u16_2(svuint16_t op)
+{
+ // expected-error at +1 {{argument value 17 is outside the valid range [1, 16]}}
+ return svqdech_pat_u16(op, SV_VL64, 17);
+}
diff --git a/clang/utils/TableGen/SveEmitter.cpp b/clang/utils/TableGen/SveEmitter.cpp
index dc009e5ca443..5f9f5d5ede8c 100644
--- a/clang/utils/TableGen/SveEmitter.cpp
+++ b/clang/utils/TableGen/SveEmitter.cpp
@@ -46,6 +46,22 @@ using TypeSpec = std::string;
namespace {
+class ImmCheck {
+ unsigned Arg;
+ unsigned Kind;
+ unsigned ElementSizeInBits;
+
+public:
+ ImmCheck(unsigned Arg, unsigned Kind, unsigned ElementSizeInBits = 0)
+ : Arg(Arg), Kind(Kind), ElementSizeInBits(ElementSizeInBits) {}
+ ImmCheck(const ImmCheck &Other) = default;
+ ~ImmCheck() = default;
+
+ unsigned getArg() const { return Arg; }
+ unsigned getKind() const { return Kind; }
+ unsigned getElementSizeInBits() const { return ElementSizeInBits; }
+};
+
class SVEType {
TypeSpec TS;
bool Float, Signed, Immediate, Void, Constant, Pointer;
@@ -146,11 +162,13 @@ class Intrinsic {
uint64_t Flags;
+ SmallVector<ImmCheck, 2> ImmChecks;
+
public:
Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy,
StringRef MergeSuffix, uint64_t MemoryElementTy, StringRef LLVMName,
- uint64_t Flags, TypeSpec BT, ClassKind Class, SVEEmitter &Emitter,
- StringRef Guard);
+ uint64_t Flags, ArrayRef<ImmCheck> ImmChecks, TypeSpec BT,
+ ClassKind Class, SVEEmitter &Emitter, StringRef Guard);
~Intrinsic()=default;
@@ -171,6 +189,8 @@ class Intrinsic {
uint64_t getFlags() const { return Flags; }
bool isFlagSet(uint64_t Flag) const { return Flags & Flag;}
+ ArrayRef<ImmCheck> getImmChecks() const { return ImmChecks; }
+
/// Return the type string for a BUILTIN() macro in Builtins.def.
std::string getBuiltinTypeStr();
@@ -204,6 +224,7 @@ class SVEEmitter {
llvm::StringMap<uint64_t> MemEltTypes;
llvm::StringMap<uint64_t> FlagTypes;
llvm::StringMap<uint64_t> MergeTypes;
+ llvm::StringMap<uint64_t> ImmCheckTypes;
public:
SVEEmitter(RecordKeeper &R) : Records(R) {
@@ -215,6 +236,16 @@ class SVEEmitter {
FlagTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value");
for (auto *RV : Records.getAllDerivedDefinitions("MergeType"))
MergeTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value");
+ for (auto *RV : Records.getAllDerivedDefinitions("ImmCheckType"))
+ ImmCheckTypes[RV->getNameInitAsString()] = RV->getValueAsInt("Value");
+ }
+
+ /// Returns the enum value for the immcheck type
+ unsigned getEnumValueForImmCheck(StringRef C) const {
+ auto It = ImmCheckTypes.find(C);
+ if (It != ImmCheckTypes.end())
+ return It->getValue();
+ llvm_unreachable("Unsupported imm check");
}
// Returns the SVETypeFlags for a given value and mask.
@@ -258,6 +289,9 @@ class SVEEmitter {
/// Emit all the information needed to map builtin -> LLVM IR intrinsic.
void createCodeGenMap(raw_ostream &o);
+ /// Emit all the range checks for the immediates.
+ void createRangeChecks(raw_ostream &o);
+
/// Create the SVETypeFlags used in CGBuiltins
void createTypeFlags(raw_ostream &o);
@@ -428,6 +462,23 @@ void SVEType::applyModifier(char Mod) {
Bitwidth = 16;
ElementBitwidth = 1;
break;
+ case 'i':
+ Predicate = false;
+ Float = false;
+ ElementBitwidth = Bitwidth = 64;
+ NumVectors = 0;
+ Signed = false;
+ Immediate = true;
+ break;
+ case 'I':
+ Predicate = false;
+ Float = false;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ Signed = true;
+ Immediate = true;
+ PredicatePattern = true;
+ break;
case 'l':
Predicate = false;
Signed = true;
@@ -531,16 +582,25 @@ void SVEType::applyModifier(char Mod) {
Intrinsic::Intrinsic(StringRef Name, StringRef Proto, uint64_t MergeTy,
StringRef MergeSuffix, uint64_t MemoryElementTy,
- StringRef LLVMName, uint64_t Flags, TypeSpec BT,
- ClassKind Class, SVEEmitter &Emitter, StringRef Guard)
+ StringRef LLVMName, uint64_t Flags,
+ ArrayRef<ImmCheck> Checks, TypeSpec BT, ClassKind Class,
+ SVEEmitter &Emitter, StringRef Guard)
: Name(Name.str()), LLVMName(LLVMName), Proto(Proto.str()),
BaseTypeSpec(BT), Class(Class), Guard(Guard.str()),
- MergeSuffix(MergeSuffix.str()), BaseType(BT, 'd'), Flags(Flags) {
+ MergeSuffix(MergeSuffix.str()), BaseType(BT, 'd'), Flags(Flags),
+ ImmChecks(Checks.begin(), Checks.end()) {
// Types[0] is the return value.
for (unsigned I = 0; I < Proto.size(); ++I) {
SVEType T(BaseTypeSpec, Proto[I]);
Types.push_back(T);
+
+ // Add range checks for immediates
+ if (I > 0) {
+ if (T.isPredicatePattern())
+ ImmChecks.emplace_back(
+ I - 1, Emitter.getEnumValueForImmCheck("ImmCheck0_31"));
+ }
}
// Set flags based on properties
@@ -714,6 +774,7 @@ void SVEEmitter::createIntrinsic(
StringRef MergeSuffix = R->getValueAsString("MergeSuffix");
uint64_t MemEltType = R->getValueAsInt("MemEltType");
std::vector<Record*> FlagsList = R->getValueAsListOfDefs("Flags");
+ std::vector<Record*> ImmCheckList = R->getValueAsListOfDefs("ImmChecks");
int64_t Flags = 0;
for (auto FlagRec : FlagsList)
@@ -737,15 +798,30 @@ void SVEEmitter::createIntrinsic(
// Create an Intrinsic for each type spec.
for (auto TS : TypeSpecs) {
- Out.push_back(std::make_unique<Intrinsic>(Name, Proto, Merge, MergeSuffix,
- MemEltType, LLVMName, Flags, TS,
- ClassS, *this, Guard));
+ // Collate a list of range/option checks for the immediates.
+ SmallVector<ImmCheck, 2> ImmChecks;
+ for (auto *R : ImmCheckList) {
+ unsigned Arg = R->getValueAsInt("Arg");
+ unsigned EltSizeArg = R->getValueAsInt("EltSizeArg");
+ unsigned Kind = R->getValueAsDef("Kind")->getValueAsInt("Value");
+
+ unsigned ElementSizeInBits = 0;
+ if (EltSizeArg >= 0)
+ ElementSizeInBits =
+ SVEType(TS, Proto[EltSizeArg + /* offset by return arg */ 1])
+ .getElementSizeInBits();
+ ImmChecks.push_back(ImmCheck(Arg, Kind, ElementSizeInBits));
+ }
+
+ Out.push_back(std::make_unique<Intrinsic>(
+ Name, Proto, Merge, MergeSuffix, MemEltType, LLVMName, Flags, ImmChecks,
+ TS, ClassS, *this, Guard));
// Also generate the short-form (e.g. svadd_m) for the given type-spec.
if (Intrinsic::isOverloadedIntrinsic(Name))
- Out.push_back(std::make_unique<Intrinsic>(Name, Proto, Merge, MergeSuffix,
- MemEltType, LLVMName, Flags, TS,
- ClassG, *this, Guard));
+ Out.push_back(std::make_unique<Intrinsic>(
+ Name, Proto, Merge, MergeSuffix, MemEltType, LLVMName, Flags,
+ ImmChecks, TS, ClassG, *this, Guard));
}
}
@@ -795,6 +871,27 @@ void SVEEmitter::createHeader(raw_ostream &OS) {
OS << "typedef __SVFloat64_t svfloat64_t;\n";
OS << "typedef __SVBool_t svbool_t;\n\n";
+ OS << "typedef enum\n";
+ OS << "{\n";
+ OS << " SV_POW2 = 0,\n";
+ OS << " SV_VL1 = 1,\n";
+ OS << " SV_VL2 = 2,\n";
+ OS << " SV_VL3 = 3,\n";
+ OS << " SV_VL4 = 4,\n";
+ OS << " SV_VL5 = 5,\n";
+ OS << " SV_VL6 = 6,\n";
+ OS << " SV_VL7 = 7,\n";
+ OS << " SV_VL8 = 8,\n";
+ OS << " SV_VL16 = 9,\n";
+ OS << " SV_VL32 = 10,\n";
+ OS << " SV_VL64 = 11,\n";
+ OS << " SV_VL128 = 12,\n";
+ OS << " SV_VL256 = 13,\n";
+ OS << " SV_MUL4 = 29,\n";
+ OS << " SV_MUL3 = 30,\n";
+ OS << " SV_ALL = 31\n";
+ OS << "} sv_pattern;\n\n";
+
OS << "/* Function attributes */\n";
OS << "#define __aio static inline __attribute__((__always_inline__, "
"__nodebug__, __overloadable__))\n\n";
@@ -897,6 +994,41 @@ void SVEEmitter::createCodeGenMap(raw_ostream &OS) {
OS << "#endif\n\n";
}
+void SVEEmitter::createRangeChecks(raw_ostream &OS) {
+ std::vector<Record *> RV = Records.getAllDerivedDefinitions("Inst");
+ SmallVector<std::unique_ptr<Intrinsic>, 128> Defs;
+ for (auto *R : RV)
+ createIntrinsic(R, Defs);
+
+ // The mappings must be sorted based on BuiltinID.
+ llvm::sort(Defs, [](const std::unique_ptr<Intrinsic> &A,
+ const std::unique_ptr<Intrinsic> &B) {
+ return A->getMangledName() < B->getMangledName();
+ });
+
+
+ OS << "#ifdef GET_SVE_IMMEDIATE_CHECK\n";
+
+ // Ensure these are only emitted once.
+ std::set<std::string> Emitted;
+
+ for (auto &Def : Defs) {
+ if (Emitted.find(Def->getMangledName()) != Emitted.end() ||
+ Def->getImmChecks().empty())
+ continue;
+
+ OS << "case SVE::BI__builtin_sve_" << Def->getMangledName() << ":\n";
+ for (auto &Check : Def->getImmChecks())
+ OS << "ImmChecks.push_back(std::make_tuple(" << Check.getArg() << ", "
+ << Check.getKind() << ", " << Check.getElementSizeInBits() << "));\n";
+ OS << " break;\n";
+
+ Emitted.insert(Def->getMangledName());
+ }
+
+ OS << "#endif\n\n";
+}
+
/// Create the SVETypeFlags used in CGBuiltins
void SVEEmitter::createTypeFlags(raw_ostream &OS) {
OS << "#ifdef LLVM_GET_SVE_TYPEFLAGS\n";
@@ -918,6 +1050,11 @@ void SVEEmitter::createTypeFlags(raw_ostream &OS) {
for (auto &KV : MergeTypes)
OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n";
OS << "#endif\n\n";
+
+ OS << "#ifdef LLVM_GET_SVE_IMMCHECKTYPES\n";
+ for (auto &KV : ImmCheckTypes)
+ OS << " " << KV.getKey() << " = " << KV.getValue() << ",\n";
+ OS << "#endif\n\n";
}
namespace clang {
@@ -932,6 +1069,11 @@ void EmitSveBuiltins(RecordKeeper &Records, raw_ostream &OS) {
void EmitSveBuiltinCG(RecordKeeper &Records, raw_ostream &OS) {
SVEEmitter(Records).createCodeGenMap(OS);
}
+
+void EmitSveRangeChecks(RecordKeeper &Records, raw_ostream &OS) {
+ SVEEmitter(Records).createRangeChecks(OS);
+}
+
void EmitSveTypeFlags(RecordKeeper &Records, raw_ostream &OS) {
SVEEmitter(Records).createTypeFlags(OS);
}
diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp
index 393cd840d189..43b59468ec2e 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -74,6 +74,7 @@ enum ActionType {
GenArmSveBuiltins,
GenArmSveBuiltinCG,
GenArmSveTypeFlags,
+ GenArmSveRangeChecks,
GenArmCdeHeader,
GenArmCdeBuiltinDef,
GenArmCdeBuiltinSema,
@@ -197,6 +198,8 @@ cl::opt<ActionType> Action(
"Generate arm_sve_builtin_cg_map.inc for clang"),
clEnumValN(GenArmSveTypeFlags, "gen-arm-sve-typeflags",
"Generate arm_sve_typeflags.inc for clang"),
+ clEnumValN(GenArmSveRangeChecks, "gen-arm-sve-sema-rangechecks",
+ "Generate arm_sve_sema_rangechecks.inc for clang"),
clEnumValN(GenArmMveHeader, "gen-arm-mve-header",
"Generate arm_mve.h for clang"),
clEnumValN(GenArmMveBuiltinDef, "gen-arm-mve-builtin-def",
@@ -390,6 +393,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenArmSveTypeFlags:
EmitSveTypeFlags(Records, OS);
break;
+ case GenArmSveRangeChecks:
+ EmitSveRangeChecks(Records, OS);
+ break;
case GenArmCdeHeader:
EmitCdeHeader(Records, OS);
break;
diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h
index ee9d447c50e6..0fd125b40a58 100644
--- a/clang/utils/TableGen/TableGenBackends.h
+++ b/clang/utils/TableGen/TableGenBackends.h
@@ -95,6 +95,7 @@ void EmitSveHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitSveBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitSveBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitSveTypeFlags(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitSveRangeChecks(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitMveBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
More information about the cfe-commits
mailing list