[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
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index a7a29c13..6e3a3df 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -7464,6 +7464,39 @@
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 @@
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 @@
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 32ca127..2429f5d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -77,6 +77,7 @@
class ObjCAtSynchronizedStmt;
class ObjCAutoreleasePoolStmt;
class ReturnsNonNullAttr;
+class SVETypeFlags;
namespace analyze_os_log {
class OSLogBufferLayout;
@@ -3903,6 +3904,7 @@
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 dc51476..e7bc499 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1998,6 +1998,39 @@
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 @@
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;