[clang] [clang][CIR] Add lowering for vcvt_n_ and vcvtq_n_ conversion intrinsics (PR #190961)
Andrzej WarzyĆski via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 8 04:59:50 PDT 2026
https://github.com/banach-space created https://github.com/llvm/llvm-project/pull/190961
This PR adds lowering for the conversion intrinsics with an immediate
argument (identified by `_n_` in the intrinsic name), excluding FP16
variants.
It also moves the corresponding tests from:
* clang/test/CodeGen/AArch64/neon_intrinsics.c
to:
* clang/test/CodeGen/AArch64/neon/intrinsics.c
The lowering follows the existing implementation in
CodeGen/TargetBuiltins/ARM.cpp and adds the `getFloatNeonType` helper
to support it. The remaining changes are code motion and refactoring.
Reference:
[1] https://arm-software.github.io/acle/neon_intrinsics/advsimd.html#conversions
>From 6cb2006d8a1991c2557d817b81e95a6f0cdd6650 Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <andrzej.warzynski at arm.com>
Date: Tue, 7 Apr 2026 18:45:42 +0000
Subject: [PATCH] [clang][CIR] Add lowering for vcvt_n_ and vcvtq_n_ conversion
intrinsics
This PR adds lowering for the conversion intrinsics with an immediate
argument (identified by `_n_` in the intrinsic name), excluding FP16
variants.
It also moves the corresponding tests from:
* clang/test/CodeGen/AArch64/neon_intrinsics.c
to:
* clang/test/CodeGen/AArch64/neon/intrinsics.c
The lowering follows the existing implementation in
CodeGen/TargetBuiltins/ARM.cpp and adds the `getFloatNeonType` helper
to support it. The remaining changes are code motion and refactoring.
Reference:
[1] https://arm-software.github.io/acle/neon_intrinsics/advsimd.html#conversions
---
.../lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp | 344 ++++++++++--------
clang/test/CodeGen/AArch64/neon-intrinsics.c | 201 ----------
clang/test/CodeGen/AArch64/neon/intrinsics.c | 222 +++++++++++
3 files changed, 419 insertions(+), 348 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
index d9d303cd07b92..071a81392aba0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
@@ -80,6 +80,169 @@ findARMVectorIntrinsicInMap(ArrayRef<ARMVectorIntrinsicInfo> intrinsicMap,
return nullptr;
}
+//===----------------------------------------------------------------------===//
+// Generic helpers
+//===----------------------------------------------------------------------===//
+static llvm::StringRef getLLVMIntrNameNoPrefix(llvm::Intrinsic::ID intrID) {
+ llvm::StringRef llvmIntrName = llvm::Intrinsic::getBaseName(intrID);
+ assert(llvmIntrName.starts_with("llvm.") && "Not an LLVM intrinsic!");
+ return llvmIntrName.drop_front(/*strlen("llvm.")=*/5);
+}
+
+//===----------------------------------------------------------------------===//
+// NEON helpers
+//===----------------------------------------------------------------------===//
+/// Return true if BuiltinID is an overloaded Neon intrinsic with an extra
+/// argument that specifies the vector type. The additional argument is meant
+/// for Sema checking (see `CheckNeonBuiltinFunctionCall`) and this function
+/// should be kept consistent with the logic in Sema.
+/// TODO: Make this return false for SISD builtins.
+/// TODO(cir): Share this with ARM.cpp
+static bool hasExtraNeonArgument(unsigned builtinID) {
+ // Required by the headers included below, but not in this particular
+ // function.
+ [[maybe_unused]] int PtrArgNum = -1;
+ [[maybe_unused]] bool HasConstPtr = false;
+
+ // The mask encodes the type. We don't care about the actual value. Instead,
+ // we just check whether its been set.
+ uint64_t mask = 0;
+ switch (builtinID) {
+#define GET_NEON_OVERLOAD_CHECK
+#include "clang/Basic/arm_fp16.inc"
+#include "clang/Basic/arm_neon.inc"
+#undef GET_NEON_OVERLOAD_CHECK
+ // Non-neon builtins for controling VFP that take extra argument for
+ // discriminating the type.
+ case ARM::BI__builtin_arm_vcvtr_f:
+ case ARM::BI__builtin_arm_vcvtr_d:
+ mask = 1;
+ }
+ switch (builtinID) {
+ default:
+ break;
+ }
+
+ return mask != 0;
+}
+
+static cir::VectorType getFloatNeonType(CIRGenFunction &cgf,
+ NeonTypeFlags intTypeFlags) {
+ int isQuad = intTypeFlags.isQuad();
+ switch (intTypeFlags.getEltType()) {
+ case NeonTypeFlags::Int16:
+ return cir::VectorType::get(cgf.fP16Ty, (4 << isQuad));
+ case NeonTypeFlags::Int32:
+ return cir::VectorType::get(cgf.floatTy, (2 << isQuad));
+ case NeonTypeFlags::Int64:
+ return cir::VectorType::get(cgf.doubleTy, (1 << isQuad));
+ default:
+ llvm_unreachable("Type can't be converted to floating-point!");
+ }
+}
+
+// TODO(cir): Remove `cgm` from the list of arguments once all NYI(s) are gone.
+template <typename Operation>
+static mlir::Value
+emitNeonCallToOp(CIRGenModule &cgm, CIRGenBuilderTy &builder,
+ llvm::SmallVector<mlir::Type> argTypes,
+ llvm::SmallVectorImpl<mlir::Value> &args,
+ std::optional<llvm::StringRef> intrinsicName,
+ mlir::Type funcResTy, mlir::Location loc,
+ bool isConstrainedFPIntrinsic = false, unsigned shift = 0,
+ bool rightshift = false) {
+ // TODO(cir): Consider removing the following unreachable when we have
+ // emitConstrainedFPCall feature implemented
+ assert(!cir::MissingFeatures::emitConstrainedFPCall());
+ if (isConstrainedFPIntrinsic)
+ cgm.errorNYI(loc, std::string("constrained FP intrinsic"));
+
+ for (unsigned j = 0; j < argTypes.size(); ++j) {
+ if (isConstrainedFPIntrinsic) {
+ assert(!cir::MissingFeatures::emitConstrainedFPCall());
+ }
+ if (shift > 0 && shift == j) {
+ cgm.errorNYI(loc, std::string("intrinsic requiring a shift Op"));
+ } else {
+ args[j] = builder.createBitcast(args[j], argTypes[j]);
+ }
+ }
+ if (isConstrainedFPIntrinsic) {
+ assert(!cir::MissingFeatures::emitConstrainedFPCall());
+ return nullptr;
+ }
+ if constexpr (std::is_same_v<Operation, cir::LLVMIntrinsicCallOp>) {
+ return Operation::create(builder, loc,
+ builder.getStringAttr(intrinsicName.value()),
+ funcResTy, args)
+ .getResult();
+ } else {
+ return Operation::create(builder, loc, funcResTy, args).getResult();
+ }
+}
+
+// TODO(cir): Remove `cgm` from the list of arguments once all NYI(s) are gone.
+static mlir::Value emitNeonCall(CIRGenModule &cgm, CIRGenBuilderTy &builder,
+ llvm::SmallVector<mlir::Type> argTypes,
+ llvm::SmallVectorImpl<mlir::Value> &args,
+ llvm::StringRef intrinsicName,
+ mlir::Type funcResTy, mlir::Location loc,
+ bool isConstrainedFPIntrinsic = false,
+ unsigned shift = 0, bool rightshift = false) {
+ return emitNeonCallToOp<cir::LLVMIntrinsicCallOp>(
+ cgm, builder, std::move(argTypes), args, intrinsicName, funcResTy, loc,
+ isConstrainedFPIntrinsic, shift, rightshift);
+}
+
+static mlir::Value emitCommonNeonSISDBuiltinExpr(
+ CIRGenFunction &cgf, const ARMVectorIntrinsicInfo &info,
+ llvm::SmallVectorImpl<mlir::Value> &ops, const CallExpr *expr) {
+ assert(info.LLVMIntrinsic && "Generic code assumes a valid intrinsic");
+
+ switch (info.BuiltinID) {
+ case NEON::BI__builtin_neon_vcled_s64:
+ case NEON::BI__builtin_neon_vcled_u64:
+ case NEON::BI__builtin_neon_vcles_f32:
+ case NEON::BI__builtin_neon_vcled_f64:
+ case NEON::BI__builtin_neon_vcltd_s64:
+ case NEON::BI__builtin_neon_vcltd_u64:
+ case NEON::BI__builtin_neon_vclts_f32:
+ case NEON::BI__builtin_neon_vcltd_f64:
+ case NEON::BI__builtin_neon_vcales_f32:
+ case NEON::BI__builtin_neon_vcaled_f64:
+ case NEON::BI__builtin_neon_vcalts_f32:
+ case NEON::BI__builtin_neon_vcaltd_f64:
+ cgf.cgm.errorNYI(expr->getSourceRange(),
+ std::string("unimplemented AArch64 builtin call: ") +
+ cgf.getContext().BuiltinInfo.getName(info.BuiltinID));
+ break;
+ }
+
+ llvm::StringRef llvmIntrName = getLLVMIntrNameNoPrefix(
+ static_cast<llvm::Intrinsic::ID>(info.LLVMIntrinsic));
+ mlir::Location loc = cgf.getLoc(expr->getExprLoc());
+
+ // The switch stmt is intended to help catch NYI cases and will be removed
+ // once the CIR implementation is complete. Avoid adding specialized
+ // code in cases - that should only be required for a handful of examples.
+ switch (info.BuiltinID) {
+ default:
+ cgf.cgm.errorNYI(expr->getSourceRange(),
+ std::string("unimplemented AArch64 builtin call: ") +
+ cgf.getContext().BuiltinInfo.getName(info.BuiltinID));
+ break;
+ case NEON::BI__builtin_neon_vabdd_f64:
+ case NEON::BI__builtin_neon_vabds_f32:
+ case NEON::BI__builtin_neon_vshld_s64:
+ case NEON::BI__builtin_neon_vshld_u64:
+ return emitNeonCall(cgf.cgm, cgf.getBuilder(),
+ {cgf.convertType(expr->getArg(0)->getType())}, ops,
+ llvmIntrName, cgf.convertType(expr->getType()), loc);
+ }
+
+ return nullptr;
+}
+
//===----------------------------------------------------------------------===//
// Emit-helpers
//===----------------------------------------------------------------------===//
@@ -201,6 +364,7 @@ static mlir::Value emitCommonNeonBuiltinExpr(
// Determine the type of this overloaded NEON intrinsic.
NeonTypeFlags neonType(neonTypeConst->getZExtValue());
const bool hasLegalHalfType = cgf.getTarget().hasFastHalfType();
+ const bool usgn = neonType.isUnsigned();
// The value of allowBFloatArgsAndRet is true for AArch64, but it should
// come from ABI info.
@@ -271,10 +435,23 @@ static mlir::Value emitCommonNeonBuiltinExpr(
case NEON::BI__builtin_neon_vcvt_n_f16_u16:
case NEON::BI__builtin_neon_vcvtq_n_f16_s16:
case NEON::BI__builtin_neon_vcvtq_n_f16_u16:
+ cgf.cgm.errorNYI(expr->getSourceRange(),
+ std::string("unimplemented AArch64 builtin call: ") +
+ ctx.BuiltinInfo.getName(builtinID));
+ return mlir::Value{};
case NEON::BI__builtin_neon_vcvt_n_f32_v:
case NEON::BI__builtin_neon_vcvt_n_f64_v:
case NEON::BI__builtin_neon_vcvtq_n_f32_v:
- case NEON::BI__builtin_neon_vcvtq_n_f64_v:
+ case NEON::BI__builtin_neon_vcvtq_n_f64_v: {
+ // The constant argument to an _n_ intrinsic always is Int32Ty.
+ mlir::Type cstIntTy = cgf.sInt32Ty;
+ llvm::StringRef llvmIntrName =
+ getLLVMIntrNameNoPrefix(static_cast<llvm::Intrinsic::ID>(
+ usgn ? llvmIntrinsic : altLLVMIntrinsic));
+ return emitNeonCall(cgf.getCIRGenModule(), cgf.getBuilder(),
+ /*argTypes=*/{vTy, cstIntTy}, ops, llvmIntrName,
+ /*funcResTy=*/getFloatNeonType(cgf, neonType), loc);
+ }
case NEON::BI__builtin_neon_vcvt_n_s16_f16:
case NEON::BI__builtin_neon_vcvt_n_s32_v:
case NEON::BI__builtin_neon_vcvt_n_u16_f16:
@@ -286,7 +463,17 @@ static mlir::Value emitCommonNeonBuiltinExpr(
case NEON::BI__builtin_neon_vcvtq_n_u16_f16:
case NEON::BI__builtin_neon_vcvtq_n_u32_v:
case NEON::BI__builtin_neon_vcvtq_n_s64_v:
- case NEON::BI__builtin_neon_vcvtq_n_u64_v:
+ case NEON::BI__builtin_neon_vcvtq_n_u64_v: {
+ // The constant argument to an _n_ intrinsic always is Int32Ty.
+ mlir::Type cstIntTy = cgf.sInt32Ty;
+ llvm::StringRef llvmIntrName = getLLVMIntrNameNoPrefix(
+ static_cast<llvm::Intrinsic::ID>(llvmIntrinsic));
+ return emitNeonCall(
+ cgf.getCIRGenModule(), cgf.getBuilder(),
+ /*argTypes=*/{getFloatNeonType(cgf, neonType), cstIntTy}, ops,
+ llvmIntrName,
+ /*funcResTy=*/vTy, loc);
+ }
case NEON::BI__builtin_neon_vcvt_s32_v:
case NEON::BI__builtin_neon_vcvt_u32_v:
case NEON::BI__builtin_neon_vcvt_s64_v:
@@ -537,12 +724,6 @@ bool CIRGenFunction::getAArch64SVEProcessedOperands(
return true;
}
-static llvm::StringRef getLLVMIntrNameNoPrefix(llvm::Intrinsic::ID intrID) {
- llvm::StringRef llvmIntrName = llvm::Intrinsic::getBaseName(intrID);
- assert(llvmIntrName.starts_with("llvm.") && "Not an LLVM intrinsic!");
- return llvmIntrName.drop_front(/*strlen("llvm.")=*/5);
-}
-
// Reinterpret the input predicate so that it can be used to correctly isolate
// the elements of the specified datatype.
mlir::Value CIRGenFunction::emitSVEPredicateCast(mlir::Value pred,
@@ -629,145 +810,6 @@ static cir::VectorType getSVEVectorForElementType(CIRGenModule &cgm,
return cir::VectorType::get(eltTy, numElts, /*is_scalable=*/true);
}
-//===----------------------------------------------------------------------===//
-// NEON helpers
-//===----------------------------------------------------------------------===//
-/// Return true if BuiltinID is an overloaded Neon intrinsic with an extra
-/// argument that specifies the vector type. The additional argument is meant
-/// for Sema checking (see `CheckNeonBuiltinFunctionCall`) and this function
-/// should be kept consistent with the logic in Sema.
-/// TODO: Make this return false for SISD builtins.
-/// TODO(cir): Share this with ARM.cpp
-static bool hasExtraNeonArgument(unsigned builtinID) {
- // Required by the headers included below, but not in this particular
- // function.
- [[maybe_unused]] int PtrArgNum = -1;
- [[maybe_unused]] bool HasConstPtr = false;
-
- // The mask encodes the type. We don't care about the actual value. Instead,
- // we just check whether its been set.
- uint64_t mask = 0;
- switch (builtinID) {
-#define GET_NEON_OVERLOAD_CHECK
-#include "clang/Basic/arm_fp16.inc"
-#include "clang/Basic/arm_neon.inc"
-#undef GET_NEON_OVERLOAD_CHECK
- // Non-neon builtins for controling VFP that take extra argument for
- // discriminating the type.
- case ARM::BI__builtin_arm_vcvtr_f:
- case ARM::BI__builtin_arm_vcvtr_d:
- mask = 1;
- }
- switch (builtinID) {
- default:
- break;
- }
-
- return mask != 0;
-}
-
-// TODO(cir): Remove `cgm` from the list of arguments once all NYI(s) are gone.
-template <typename Operation>
-static mlir::Value
-emitNeonCallToOp(CIRGenModule &cgm, CIRGenBuilderTy &builder,
- llvm::SmallVector<mlir::Type> argTypes,
- llvm::SmallVectorImpl<mlir::Value> &args,
- std::optional<llvm::StringRef> intrinsicName,
- mlir::Type funcResTy, mlir::Location loc,
- bool isConstrainedFPIntrinsic = false, unsigned shift = 0,
- bool rightshift = false) {
- // TODO(cir): Consider removing the following unreachable when we have
- // emitConstrainedFPCall feature implemented
- assert(!cir::MissingFeatures::emitConstrainedFPCall());
- if (isConstrainedFPIntrinsic)
- cgm.errorNYI(loc, std::string("constrained FP intrinsic"));
-
- for (unsigned j = 0; j < argTypes.size(); ++j) {
- if (isConstrainedFPIntrinsic) {
- assert(!cir::MissingFeatures::emitConstrainedFPCall());
- }
- if (shift > 0 && shift == j) {
- cgm.errorNYI(loc, std::string("intrinsic requiring a shift Op"));
- } else {
- args[j] = builder.createBitcast(args[j], argTypes[j]);
- }
- }
- if (isConstrainedFPIntrinsic) {
- assert(!cir::MissingFeatures::emitConstrainedFPCall());
- return nullptr;
- }
- if constexpr (std::is_same_v<Operation, cir::LLVMIntrinsicCallOp>) {
- return Operation::create(builder, loc,
- builder.getStringAttr(intrinsicName.value()),
- funcResTy, args)
- .getResult();
- } else {
- return Operation::create(builder, loc, funcResTy, args).getResult();
- }
-}
-
-// TODO(cir): Remove `cgm` from the list of arguments once all NYI(s) are gone.
-static mlir::Value emitNeonCall(CIRGenModule &cgm, CIRGenBuilderTy &builder,
- llvm::SmallVector<mlir::Type> argTypes,
- llvm::SmallVectorImpl<mlir::Value> &args,
- llvm::StringRef intrinsicName,
- mlir::Type funcResTy, mlir::Location loc,
- bool isConstrainedFPIntrinsic = false,
- unsigned shift = 0, bool rightshift = false) {
- return emitNeonCallToOp<cir::LLVMIntrinsicCallOp>(
- cgm, builder, std::move(argTypes), args, intrinsicName, funcResTy, loc,
- isConstrainedFPIntrinsic, shift, rightshift);
-}
-
-static mlir::Value emitCommonNeonSISDBuiltinExpr(
- CIRGenFunction &cgf, const ARMVectorIntrinsicInfo &info,
- llvm::SmallVectorImpl<mlir::Value> &ops, const CallExpr *expr) {
- assert(info.LLVMIntrinsic && "Generic code assumes a valid intrinsic");
-
- switch (info.BuiltinID) {
- case NEON::BI__builtin_neon_vcled_s64:
- case NEON::BI__builtin_neon_vcled_u64:
- case NEON::BI__builtin_neon_vcles_f32:
- case NEON::BI__builtin_neon_vcled_f64:
- case NEON::BI__builtin_neon_vcltd_s64:
- case NEON::BI__builtin_neon_vcltd_u64:
- case NEON::BI__builtin_neon_vclts_f32:
- case NEON::BI__builtin_neon_vcltd_f64:
- case NEON::BI__builtin_neon_vcales_f32:
- case NEON::BI__builtin_neon_vcaled_f64:
- case NEON::BI__builtin_neon_vcalts_f32:
- case NEON::BI__builtin_neon_vcaltd_f64:
- cgf.cgm.errorNYI(expr->getSourceRange(),
- std::string("unimplemented AArch64 builtin call: ") +
- cgf.getContext().BuiltinInfo.getName(info.BuiltinID));
- break;
- }
-
- llvm::StringRef llvmIntrName = getLLVMIntrNameNoPrefix(
- static_cast<llvm::Intrinsic::ID>(info.LLVMIntrinsic));
- mlir::Location loc = cgf.getLoc(expr->getExprLoc());
-
- // The switch stmt is intended to help catch NYI cases and will be removed
- // once the CIR implementation is complete. Avoid adding specialized
- // code in cases - that should only be required for a handful of examples.
- switch (info.BuiltinID) {
- default:
- cgf.cgm.errorNYI(expr->getSourceRange(),
- std::string("unimplemented AArch64 builtin call: ") +
- cgf.getContext().BuiltinInfo.getName(info.BuiltinID));
- break;
- case NEON::BI__builtin_neon_vabdd_f64:
- case NEON::BI__builtin_neon_vabds_f32:
- case NEON::BI__builtin_neon_vshld_s64:
- case NEON::BI__builtin_neon_vshld_u64:
- return emitNeonCall(cgf.cgm, cgf.getBuilder(),
- {cgf.convertType(expr->getArg(0)->getType())}, ops,
- llvmIntrName, cgf.convertType(expr->getType()), loc);
- }
-
- return nullptr;
-}
-
//===----------------------------------------------------------------------===//
// SVE helpers
//===----------------------------------------------------------------------===//
@@ -2331,8 +2373,16 @@ CIRGenFunction::emitAArch64BuiltinExpr(unsigned builtinID, const CallExpr *expr,
case NEON::BI__builtin_neon_vrnd64zq_f64:
case NEON::BI__builtin_neon_vrnd_v:
case NEON::BI__builtin_neon_vrndq_v:
+ cgm.errorNYI(expr->getSourceRange(),
+ std::string("unimplemented AArch64 builtin call: ") +
+ getContext().BuiltinInfo.getName(builtinID));
+ return mlir::Value{};
case NEON::BI__builtin_neon_vcvt_f64_v:
case NEON::BI__builtin_neon_vcvtq_f64_v:
+ ops[0] = builder.createBitcast(ops[0], ty);
+ ty = getNeonType(
+ this, NeonTypeFlags(NeonTypeFlags::Float64, false, type.isQuad()), loc);
+ return builder.createCast(loc, cir::CastKind::int_to_float, ops[0], ty);
case NEON::BI__builtin_neon_vcvt_f64_f32:
case NEON::BI__builtin_neon_vcvt_f32_f64:
case NEON::BI__builtin_neon_vcvt_s32_v:
diff --git a/clang/test/CodeGen/AArch64/neon-intrinsics.c b/clang/test/CodeGen/AArch64/neon-intrinsics.c
index 80bb22cc43c78..77ccb12932ba9 100644
--- a/clang/test/CodeGen/AArch64/neon-intrinsics.c
+++ b/clang/test/CodeGen/AArch64/neon-intrinsics.c
@@ -8804,156 +8804,6 @@ uint64x2_t test_vmovl_high_u32(uint32x4_t a) {
return vmovl_high_u32(a);
}
-// CHECK-LABEL: define dso_local <2 x float> @test_vcvt_n_f32_s32(
-// CHECK-SAME: <2 x i32> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <2 x i32> [[A]] to <8 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP0]] to <2 x i32>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <2 x float> @llvm.aarch64.neon.vcvtfxs2fp.v2f32.v2i32(<2 x i32> [[VCVT_N]], i32 31)
-// CHECK-NEXT: ret <2 x float> [[VCVT_N1]]
-//
-float32x2_t test_vcvt_n_f32_s32(int32x2_t a) {
- return vcvt_n_f32_s32(a, 31);
-}
-
-// CHECK-LABEL: define dso_local <4 x float> @test_vcvtq_n_f32_s32(
-// CHECK-SAME: <4 x i32> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <4 x i32> [[A]] to <16 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP0]] to <4 x i32>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <4 x float> @llvm.aarch64.neon.vcvtfxs2fp.v4f32.v4i32(<4 x i32> [[VCVT_N]], i32 31)
-// CHECK-NEXT: ret <4 x float> [[VCVT_N1]]
-//
-float32x4_t test_vcvtq_n_f32_s32(int32x4_t a) {
- return vcvtq_n_f32_s32(a, 31);
-}
-
-// CHECK-LABEL: define dso_local <2 x double> @test_vcvtq_n_f64_s64(
-// CHECK-SAME: <2 x i64> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <2 x i64> [[A]] to <16 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP0]] to <2 x i64>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <2 x double> @llvm.aarch64.neon.vcvtfxs2fp.v2f64.v2i64(<2 x i64> [[VCVT_N]], i32 50)
-// CHECK-NEXT: ret <2 x double> [[VCVT_N1]]
-//
-float64x2_t test_vcvtq_n_f64_s64(int64x2_t a) {
- return vcvtq_n_f64_s64(a, 50);
-}
-
-// CHECK-LABEL: define dso_local <2 x float> @test_vcvt_n_f32_u32(
-// CHECK-SAME: <2 x i32> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <2 x i32> [[A]] to <8 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP0]] to <2 x i32>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <2 x float> @llvm.aarch64.neon.vcvtfxu2fp.v2f32.v2i32(<2 x i32> [[VCVT_N]], i32 31)
-// CHECK-NEXT: ret <2 x float> [[VCVT_N1]]
-//
-float32x2_t test_vcvt_n_f32_u32(uint32x2_t a) {
- return vcvt_n_f32_u32(a, 31);
-}
-
-// CHECK-LABEL: define dso_local <4 x float> @test_vcvtq_n_f32_u32(
-// CHECK-SAME: <4 x i32> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <4 x i32> [[A]] to <16 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP0]] to <4 x i32>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <4 x float> @llvm.aarch64.neon.vcvtfxu2fp.v4f32.v4i32(<4 x i32> [[VCVT_N]], i32 31)
-// CHECK-NEXT: ret <4 x float> [[VCVT_N1]]
-//
-float32x4_t test_vcvtq_n_f32_u32(uint32x4_t a) {
- return vcvtq_n_f32_u32(a, 31);
-}
-
-// CHECK-LABEL: define dso_local <2 x double> @test_vcvtq_n_f64_u64(
-// CHECK-SAME: <2 x i64> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <2 x i64> [[A]] to <16 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP0]] to <2 x i64>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <2 x double> @llvm.aarch64.neon.vcvtfxu2fp.v2f64.v2i64(<2 x i64> [[VCVT_N]], i32 50)
-// CHECK-NEXT: ret <2 x double> [[VCVT_N1]]
-//
-float64x2_t test_vcvtq_n_f64_u64(uint64x2_t a) {
- return vcvtq_n_f64_u64(a, 50);
-}
-
-// CHECK-LABEL: define dso_local <2 x i32> @test_vcvt_n_s32_f32(
-// CHECK-SAME: <2 x float> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <2 x float> [[A]] to <2 x i32>
-// CHECK-NEXT: [[TMP1:%.*]] = bitcast <2 x i32> [[TMP0]] to <8 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP1]] to <2 x float>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <2 x i32> @llvm.aarch64.neon.vcvtfp2fxs.v2i32.v2f32(<2 x float> [[VCVT_N]], i32 31)
-// CHECK-NEXT: ret <2 x i32> [[VCVT_N1]]
-//
-int32x2_t test_vcvt_n_s32_f32(float32x2_t a) {
- return vcvt_n_s32_f32(a, 31);
-}
-
-// CHECK-LABEL: define dso_local <4 x i32> @test_vcvtq_n_s32_f32(
-// CHECK-SAME: <4 x float> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <4 x float> [[A]] to <4 x i32>
-// CHECK-NEXT: [[TMP1:%.*]] = bitcast <4 x i32> [[TMP0]] to <16 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP1]] to <4 x float>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <4 x i32> @llvm.aarch64.neon.vcvtfp2fxs.v4i32.v4f32(<4 x float> [[VCVT_N]], i32 31)
-// CHECK-NEXT: ret <4 x i32> [[VCVT_N1]]
-//
-int32x4_t test_vcvtq_n_s32_f32(float32x4_t a) {
- return vcvtq_n_s32_f32(a, 31);
-}
-
-// CHECK-LABEL: define dso_local <2 x i64> @test_vcvtq_n_s64_f64(
-// CHECK-SAME: <2 x double> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <2 x double> [[A]] to <2 x i64>
-// CHECK-NEXT: [[TMP1:%.*]] = bitcast <2 x i64> [[TMP0]] to <16 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP1]] to <2 x double>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <2 x i64> @llvm.aarch64.neon.vcvtfp2fxs.v2i64.v2f64(<2 x double> [[VCVT_N]], i32 50)
-// CHECK-NEXT: ret <2 x i64> [[VCVT_N1]]
-//
-int64x2_t test_vcvtq_n_s64_f64(float64x2_t a) {
- return vcvtq_n_s64_f64(a, 50);
-}
-
-// CHECK-LABEL: define dso_local <2 x i32> @test_vcvt_n_u32_f32(
-// CHECK-SAME: <2 x float> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <2 x float> [[A]] to <2 x i32>
-// CHECK-NEXT: [[TMP1:%.*]] = bitcast <2 x i32> [[TMP0]] to <8 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP1]] to <2 x float>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <2 x i32> @llvm.aarch64.neon.vcvtfp2fxu.v2i32.v2f32(<2 x float> [[VCVT_N]], i32 31)
-// CHECK-NEXT: ret <2 x i32> [[VCVT_N1]]
-//
-uint32x2_t test_vcvt_n_u32_f32(float32x2_t a) {
- return vcvt_n_u32_f32(a, 31);
-}
-
-// CHECK-LABEL: define dso_local <4 x i32> @test_vcvtq_n_u32_f32(
-// CHECK-SAME: <4 x float> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <4 x float> [[A]] to <4 x i32>
-// CHECK-NEXT: [[TMP1:%.*]] = bitcast <4 x i32> [[TMP0]] to <16 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP1]] to <4 x float>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <4 x i32> @llvm.aarch64.neon.vcvtfp2fxu.v4i32.v4f32(<4 x float> [[VCVT_N]], i32 31)
-// CHECK-NEXT: ret <4 x i32> [[VCVT_N1]]
-//
-uint32x4_t test_vcvtq_n_u32_f32(float32x4_t a) {
- return vcvtq_n_u32_f32(a, 31);
-}
-
-// CHECK-LABEL: define dso_local <2 x i64> @test_vcvtq_n_u64_f64(
-// CHECK-SAME: <2 x double> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <2 x double> [[A]] to <2 x i64>
-// CHECK-NEXT: [[TMP1:%.*]] = bitcast <2 x i64> [[TMP0]] to <16 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP1]] to <2 x double>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <2 x i64> @llvm.aarch64.neon.vcvtfp2fxu.v2i64.v2f64(<2 x double> [[VCVT_N]], i32 50)
-// CHECK-NEXT: ret <2 x i64> [[VCVT_N1]]
-//
-uint64x2_t test_vcvtq_n_u64_f64(float64x2_t a) {
- return vcvtq_n_u64_f64(a, 50);
-}
-
// CHECK-LABEL: define dso_local <8 x i16> @test_vaddl_s8(
// CHECK-SAME: <8 x i8> noundef [[A:%.*]], <8 x i8> noundef [[B:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
@@ -22671,57 +22521,6 @@ float64x1_t test_vcvt_f64_u64(uint64x1_t a) {
return vcvt_f64_u64(a);
}
-// CHECK-LABEL: define dso_local <1 x i64> @test_vcvt_n_s64_f64(
-// CHECK-SAME: <1 x double> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <1 x double> [[A]] to i64
-// CHECK-NEXT: [[__S0_SROA_0_0_VEC_INSERT:%.*]] = insertelement <1 x i64> undef, i64 [[TMP0]], i32 0
-// CHECK-NEXT: [[TMP1:%.*]] = bitcast <1 x i64> [[__S0_SROA_0_0_VEC_INSERT]] to <8 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP1]] to <1 x double>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <1 x i64> @llvm.aarch64.neon.vcvtfp2fxs.v1i64.v1f64(<1 x double> [[VCVT_N]], i32 64)
-// CHECK-NEXT: ret <1 x i64> [[VCVT_N1]]
-//
-int64x1_t test_vcvt_n_s64_f64(float64x1_t a) {
- return vcvt_n_s64_f64(a, 64);
-}
-
-// CHECK-LABEL: define dso_local <1 x i64> @test_vcvt_n_u64_f64(
-// CHECK-SAME: <1 x double> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <1 x double> [[A]] to i64
-// CHECK-NEXT: [[__S0_SROA_0_0_VEC_INSERT:%.*]] = insertelement <1 x i64> undef, i64 [[TMP0]], i32 0
-// CHECK-NEXT: [[TMP1:%.*]] = bitcast <1 x i64> [[__S0_SROA_0_0_VEC_INSERT]] to <8 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP1]] to <1 x double>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <1 x i64> @llvm.aarch64.neon.vcvtfp2fxu.v1i64.v1f64(<1 x double> [[VCVT_N]], i32 64)
-// CHECK-NEXT: ret <1 x i64> [[VCVT_N1]]
-//
-uint64x1_t test_vcvt_n_u64_f64(float64x1_t a) {
- return vcvt_n_u64_f64(a, 64);
-}
-
-// CHECK-LABEL: define dso_local <1 x double> @test_vcvt_n_f64_s64(
-// CHECK-SAME: <1 x i64> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <1 x i64> [[A]] to <8 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP0]] to <1 x i64>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <1 x double> @llvm.aarch64.neon.vcvtfxs2fp.v1f64.v1i64(<1 x i64> [[VCVT_N]], i32 64)
-// CHECK-NEXT: ret <1 x double> [[VCVT_N1]]
-//
-float64x1_t test_vcvt_n_f64_s64(int64x1_t a) {
- return vcvt_n_f64_s64(a, 64);
-}
-
-// CHECK-LABEL: define dso_local <1 x double> @test_vcvt_n_f64_u64(
-// CHECK-SAME: <1 x i64> noundef [[A:%.*]]) #[[ATTR0]] {
-// CHECK-NEXT: [[ENTRY:.*:]]
-// CHECK-NEXT: [[TMP0:%.*]] = bitcast <1 x i64> [[A]] to <8 x i8>
-// CHECK-NEXT: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP0]] to <1 x i64>
-// CHECK-NEXT: [[VCVT_N1:%.*]] = call <1 x double> @llvm.aarch64.neon.vcvtfxu2fp.v1f64.v1i64(<1 x i64> [[VCVT_N]], i32 64)
-// CHECK-NEXT: ret <1 x double> [[VCVT_N1]]
-//
-float64x1_t test_vcvt_n_f64_u64(uint64x1_t a) {
- return vcvt_n_f64_u64(a, 64);
-}
// CHECK-LABEL: define dso_local <1 x double> @test_vrndn_f64(
// CHECK-SAME: <1 x double> noundef [[A:%.*]]) #[[ATTR0]] {
diff --git a/clang/test/CodeGen/AArch64/neon/intrinsics.c b/clang/test/CodeGen/AArch64/neon/intrinsics.c
index 6fd5bfdc98174..bdd68c15907ee 100644
--- a/clang/test/CodeGen/AArch64/neon/intrinsics.c
+++ b/clang/test/CodeGen/AArch64/neon/intrinsics.c
@@ -1518,3 +1518,225 @@ poly16x8_t test_vmull_high_p8(poly8x16_t a, poly8x16_t b) {
// LLVM-NEXT: ret <8 x i16> [[VMULL_I_I]]
return vmull_high_p8(a, b);
}
+
+//===------------------------------------------------------===//
+// 2.1.4.1 Convearions
+// https://arm-software.github.io/acle/neon_intrinsics/advsimd.html#conversions
+//===------------------------------------------------------===//
+// ALL-LABEL: @test_vcvt_n_f32_s32(
+float32x2_t test_vcvt_n_f32_s32(int32x2_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<31>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfxs2fp" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <2 x i32> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <2 x i32> [[A]] to <8 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP0]] to <2 x i32>
+// LLVM: [[VCVT_N1:%.*]] = call <2 x float> @llvm.aarch64.neon.vcvtfxs2fp.v2f32.v2i32(<2 x i32> [[VCVT_N]], i32 31)
+// LLVM: ret <2 x float> [[VCVT_N1]]
+ return vcvt_n_f32_s32(a, 31);
+}
+
+// ALL-LABEL: @test_vcvtq_n_f32_s32(
+// LLVM-SAME: <4 x i32> {{.*}} [[A:%.*]])
+float32x4_t test_vcvtq_n_f32_s32(int32x4_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<31>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfxs2fp" %{{.*}}, [[CST]]
+
+// LLVM: [[TMP0:%.*]] = bitcast <4 x i32> [[A]] to <16 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP0]] to <4 x i32>
+// LLVM: [[VCVT_N1:%.*]] = call <4 x float> @llvm.aarch64.neon.vcvtfxs2fp.v4f32.v4i32(<4 x i32> [[VCVT_N]], i32 31)
+// LLVM: ret <4 x float> [[VCVT_N1]]
+ return vcvtq_n_f32_s32(a, 31);
+}
+
+// ALL-LABEL: @test_vcvtq_n_f64_s64(
+float64x2_t test_vcvtq_n_f64_s64(int64x2_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<50>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfxs2fp" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <2 x i64> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <2 x i64> [[A]] to <16 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP0]] to <2 x i64>
+// LLVM: [[VCVT_N1:%.*]] = call <2 x double> @llvm.aarch64.neon.vcvtfxs2fp.v2f64.v2i64(<2 x i64> [[VCVT_N]], i32 50)
+// LLVM: ret <2 x double> [[VCVT_N1]]
+ return vcvtq_n_f64_s64(a, 50);
+}
+
+// ALL-LABEL: @test_vcvt_n_f32_u32(
+float32x2_t test_vcvt_n_f32_u32(uint32x2_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<31>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfxu2fp" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <2 x i32> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <2 x i32> [[A]] to <8 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP0]] to <2 x i32>
+// LLVM: [[VCVT_N1:%.*]] = call <2 x float> @llvm.aarch64.neon.vcvtfxu2fp.v2f32.v2i32(<2 x i32> [[VCVT_N]], i32 31)
+// LLVM: ret <2 x float> [[VCVT_N1]]
+ return vcvt_n_f32_u32(a, 31);
+}
+
+// ALL-LABEL: @test_vcvtq_n_f32_u32(
+float32x4_t test_vcvtq_n_f32_u32(uint32x4_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<31>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfxu2fp" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <4 x i32> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <4 x i32> [[A]] to <16 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP0]] to <4 x i32>
+// LLVM: [[VCVT_N1:%.*]] = call <4 x float> @llvm.aarch64.neon.vcvtfxu2fp.v4f32.v4i32(<4 x i32> [[VCVT_N]], i32 31)
+// LLVM: ret <4 x float> [[VCVT_N1]]
+ return vcvtq_n_f32_u32(a, 31);
+}
+
+// ALL-LABEL: @test_vcvtq_n_f64_u64(
+float64x2_t test_vcvtq_n_f64_u64(uint64x2_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<50>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfxu2fp" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <2 x i64> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <2 x i64> [[A]] to <16 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP0]] to <2 x i64>
+// LLVM: [[VCVT_N1:%.*]] = call <2 x double> @llvm.aarch64.neon.vcvtfxu2fp.v2f64.v2i64(<2 x i64> [[VCVT_N]], i32 50)
+// LLVM: ret <2 x double> [[VCVT_N1]]
+ return vcvtq_n_f64_u64(a, 50);
+}
+
+// ALL-LABEL: @test_vcvt_n_s32_f32(
+int32x2_t test_vcvt_n_s32_f32(float32x2_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<31>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfp2fxs" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <2 x float> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <2 x float> [[A]] to <2 x i32>
+// LLVM: [[TMP1:%.*]] = bitcast <2 x i32> [[TMP0]] to <8 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP1]] to <2 x float>
+// LLVM: [[VCVT_N1:%.*]] = call <2 x i32> @llvm.aarch64.neon.vcvtfp2fxs.v2i32.v2f32(<2 x float> [[VCVT_N]], i32 31)
+// LLVM: ret <2 x i32> [[VCVT_N1]]
+ return vcvt_n_s32_f32(a, 31);
+}
+
+// ALL-LABEL: @test_vcvtq_n_s32_f32(
+int32x4_t test_vcvtq_n_s32_f32(float32x4_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<31>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfp2fxs" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <4 x float> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <4 x float> [[A]] to <4 x i32>
+// LLVM: [[TMP1:%.*]] = bitcast <4 x i32> [[TMP0]] to <16 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP1]] to <4 x float>
+// LLVM: [[VCVT_N1:%.*]] = call <4 x i32> @llvm.aarch64.neon.vcvtfp2fxs.v4i32.v4f32(<4 x float> [[VCVT_N]], i32 31)
+// LLVM: ret <4 x i32> [[VCVT_N1]]
+ return vcvtq_n_s32_f32(a, 31);
+}
+
+// ALL-LABEL: @test_vcvtq_n_s64_f64(
+int64x2_t test_vcvtq_n_s64_f64(float64x2_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<50>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfp2fxs" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <2 x double> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <2 x double> [[A]] to <2 x i64>
+// LLVM: [[TMP1:%.*]] = bitcast <2 x i64> [[TMP0]] to <16 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP1]] to <2 x double>
+// LLVM: [[VCVT_N1:%.*]] = call <2 x i64> @llvm.aarch64.neon.vcvtfp2fxs.v2i64.v2f64(<2 x double> [[VCVT_N]], i32 50)
+// LLVM: ret <2 x i64> [[VCVT_N1]]
+ return vcvtq_n_s64_f64(a, 50);
+}
+
+// ALL-LABEL: @test_vcvt_n_u32_f32(
+uint32x2_t test_vcvt_n_u32_f32(float32x2_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<31>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfp2fxu" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <2 x float> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <2 x float> [[A]] to <2 x i32>
+// LLVM: [[TMP1:%.*]] = bitcast <2 x i32> [[TMP0]] to <8 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP1]] to <2 x float>
+// LLVM: [[VCVT_N1:%.*]] = call <2 x i32> @llvm.aarch64.neon.vcvtfp2fxu.v2i32.v2f32(<2 x float> [[VCVT_N]], i32 31)
+// LLVM: ret <2 x i32> [[VCVT_N1]]
+ return vcvt_n_u32_f32(a, 31);
+}
+
+// ALL-LABEL: @test_vcvtq_n_u32_f32(
+uint32x4_t test_vcvtq_n_u32_f32(float32x4_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<31>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfp2fxu" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <4 x float> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <4 x float> [[A]] to <4 x i32>
+// LLVM: [[TMP1:%.*]] = bitcast <4 x i32> [[TMP0]] to <16 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP1]] to <4 x float>
+// LLVM: [[VCVT_N1:%.*]] = call <4 x i32> @llvm.aarch64.neon.vcvtfp2fxu.v4i32.v4f32(<4 x float> [[VCVT_N]], i32 31)
+// LLVM: ret <4 x i32> [[VCVT_N1]]
+ return vcvtq_n_u32_f32(a, 31);
+}
+
+// ALL-LABEL: @test_vcvtq_n_u64_f64(
+uint64x2_t test_vcvtq_n_u64_f64(float64x2_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<50>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfp2fxu" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <2 x double> {{.*}} [[A:%.*]])
+// LLVM: [[TMP0:%.*]] = bitcast <2 x double> [[A]] to <2 x i64>
+// LLVM: [[TMP1:%.*]] = bitcast <2 x i64> [[TMP0]] to <16 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <16 x i8> [[TMP1]] to <2 x double>
+// LLVM: [[VCVT_N1:%.*]] = call <2 x i64> @llvm.aarch64.neon.vcvtfp2fxu.v2i64.v2f64(<2 x double> [[VCVT_N]], i32 50)
+// LLVM: ret <2 x i64> [[VCVT_N1]]
+ return vcvtq_n_u64_f64(a, 50);
+}
+
+// ALL-LABEL: @test_vcvt_n_s64_f64(
+int64x1_t test_vcvt_n_s64_f64(float64x1_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<64>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfp2fxs" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <1 x double> {{.*}} [[A:%.*]]) #[[ATTR0]] {
+// LLVM: [[TMP0:%.*]] = bitcast <1 x double> [[A]] to i64
+// LLVM: [[__S0_SROA_0_0_VEC_INSERT:%.*]] = insertelement <1 x i64> undef, i64 [[TMP0]], i32 0
+// LLVM: [[TMP1:%.*]] = bitcast <1 x i64> [[__S0_SROA_0_0_VEC_INSERT]] to <8 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP1]] to <1 x double>
+// LLVM: [[VCVT_N1:%.*]] = call <1 x i64> @llvm.aarch64.neon.vcvtfp2fxs.v1i64.v1f64(<1 x double> [[VCVT_N]], i32 64)
+// LLVM: ret <1 x i64> [[VCVT_N1]]
+ return vcvt_n_s64_f64(a, 64);
+}
+
+// ALL-LABEL: @test_vcvt_n_u64_f64(
+uint64x1_t test_vcvt_n_u64_f64(float64x1_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<64>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfp2fxu" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <1 x double> {{.*}} [[A:%.*]]) #[[ATTR0]] {
+// LLVM: [[TMP0:%.*]] = bitcast <1 x double> [[A]] to i64
+// LLVM: [[__S0_SROA_0_0_VEC_INSERT:%.*]] = insertelement <1 x i64> undef, i64 [[TMP0]], i32 0
+// LLVM: [[TMP1:%.*]] = bitcast <1 x i64> [[__S0_SROA_0_0_VEC_INSERT]] to <8 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP1]] to <1 x double>
+// LLVM: [[VCVT_N1:%.*]] = call <1 x i64> @llvm.aarch64.neon.vcvtfp2fxu.v1i64.v1f64(<1 x double> [[VCVT_N]], i32 64)
+// LLVM: ret <1 x i64> [[VCVT_N1]]
+ return vcvt_n_u64_f64(a, 64);
+}
+
+// ALL-LABEL: @test_vcvt_n_f64_s64(
+float64x1_t test_vcvt_n_f64_s64(int64x1_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<64>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfxs2fp" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <1 x i64> {{.*}} [[A:%.*]]) #[[ATTR0]] {
+// LLVM: [[TMP0:%.*]] = bitcast <1 x i64> [[A]] to <8 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP0]] to <1 x i64>
+// LLVM: [[VCVT_N1:%.*]] = call <1 x double> @llvm.aarch64.neon.vcvtfxs2fp.v1f64.v1i64(<1 x i64> [[VCVT_N]], i32 64)
+// LLVM: ret <1 x double> [[VCVT_N1]]
+ return vcvt_n_f64_s64(a, 64);
+}
+
+// ALL-LABEL: @test_vcvt_n_f64_u64(
+float64x1_t test_vcvt_n_f64_u64(uint64x1_t a) {
+// CIR: [[CST:%.*]] = cir.const #cir.int<64>
+// CIR: cir.call_llvm_intrinsic "aarch64.neon.vcvtfxu2fp" %{{.*}}, [[CST]]
+
+// LLVM-SAME: <1 x i64> {{.*}} [[A:%.*]]) #[[ATTR0]] {
+// LLVM: [[TMP0:%.*]] = bitcast <1 x i64> [[A]] to <8 x i8>
+// LLVM: [[VCVT_N:%.*]] = bitcast <8 x i8> [[TMP0]] to <1 x i64>
+// LLVM: [[VCVT_N1:%.*]] = call <1 x double> @llvm.aarch64.neon.vcvtfxu2fp.v1f64.v1i64(<1 x i64> [[VCVT_N]], i32 64)
+// LLVM: ret <1 x double> [[VCVT_N1]]
+ return vcvt_n_f64_u64(a, 64);
+}
More information about the cfe-commits
mailing list