[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