[clang] [clang][CIR] Add code-gen for DUP intrinsics for bool types (PR #223459)
Andrzej WarzyĆski via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 15 05:18:07 PDT 2026
https://github.com/banach-space updated https://github.com/llvm/llvm-project/pull/223459
>From 0467f07b4ab82f6efe4a9549d6b11d31155a23e8 Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <andrzej.warzynski at arm.com>
Date: Thu, 10 Sep 2026 13:26:38 +0000
Subject: [PATCH] [clang][CIR] Add code-gen for DUP intrinsics for bool types
As per SVE ABI, the storage type for svbool_t is `vector<vscale x 16 x
i1>` and that's what the change in `emitToMemory` reflects (see
LowerToLLVM.cpp).
---
.../lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp | 56 ++++++++++++++++++-
clang/lib/CIR/CodeGen/CIRGenFunction.h | 1 +
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 18 ++++--
clang/test/CodeGen/AArch64/sve/dup.c | 41 ++++++++++++++
4 files changed, 111 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
index 83cd4255a5bb4..d681c390fe9de 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
@@ -1556,6 +1556,42 @@ static unsigned getSVEMinEltCount(clang::SVETypeFlags::EltType sveType) {
}
}
+cir::VectorType CIRGenFunction::getSVEType(const SVETypeFlags &typeFlags) {
+ switch (typeFlags.getEltType()) {
+ default:
+ llvm_unreachable("Invalid SVETypeFlag!");
+
+ case SVETypeFlags::EltTyInt8:
+ return cir::VectorType::get(builder.getUInt8Ty(), 16, true);
+ case SVETypeFlags::EltTyInt16:
+ return cir::VectorType::get(builder.getUInt16Ty(), 8, true);
+ case SVETypeFlags::EltTyInt32:
+ return cir::VectorType::get(builder.getUInt32Ty(), 4, true);
+ case SVETypeFlags::EltTyInt64:
+ return cir::VectorType::get(builder.getUInt64Ty(), 2, true);
+
+ case SVETypeFlags::EltTyMFloat8:
+ return cir::VectorType::get(builder.getUInt8Ty(), 16, true);
+ case SVETypeFlags::EltTyFloat16:
+ return cir::VectorType::get(builder.getFp16Ty(), 8, true);
+ case SVETypeFlags::EltTyBFloat16:
+ return cir::VectorType::get(builder.getBF16Type(), 8, true);
+ case SVETypeFlags::EltTyFloat32:
+ return cir::VectorType::get(builder.getF32Type(), 4, true);
+ case SVETypeFlags::EltTyFloat64:
+ return cir::VectorType::get(builder.getDoubleTy(), 2, true);
+
+ case SVETypeFlags::EltTyBool8:
+ return cir::VectorType::get(builder.getUIntNTy(1), 16, true);
+ case SVETypeFlags::EltTyBool16:
+ return cir::VectorType::get(builder.getUIntNTy(1), 8, true);
+ case SVETypeFlags::EltTyBool32:
+ return cir::VectorType::get(builder.getUIntNTy(1), 4, true);
+ case SVETypeFlags::EltTyBool64:
+ return cir::VectorType::get(builder.getUIntNTy(1), 2, true);
+ }
+}
+
// TODO(cir): Share with OGCG
constexpr unsigned sveBitsPerBlock = 128;
@@ -1739,11 +1775,29 @@ CIRGenFunction::emitAArch64SVEBuiltinExpr(unsigned builtinID,
case SVE::BI__builtin_sve_svpmullb_u64:
case SVE::BI__builtin_sve_svpmullb_n_u16:
case SVE::BI__builtin_sve_svpmullb_n_u64:
+ cgm.errorNYI(expr->getSourceRange(),
+ std::string("unimplemented AArch64 builtin call: ") +
+ getContext().BuiltinInfo.getName(builtinID));
+ return mlir::Value{};
case SVE::BI__builtin_sve_svdup_n_b8:
case SVE::BI__builtin_sve_svdup_n_b16:
case SVE::BI__builtin_sve_svdup_n_b32:
- case SVE::BI__builtin_sve_svdup_n_b64:
+ case SVE::BI__builtin_sve_svdup_n_b64: {
+ // Cast from cir.bool (input type) to cir.int<u, 1> (element type of the
+ // result vector).
+ auto dup = builder.createBitcast(ops[0], builder.getUIntNTy(1));
+
+ // Splat
+ dup = cir::VecSplatOp::create(builder, loc, getSVEType(typeFlags), dup);
+
+ // Cast to svbool_t, i.e. <vscale x 16 x i1>. The actual result could be
+ // e.g. <vscale x 8 x i1> (for b16), but only svbool_t (i.e. full
+ // predicate register) is "storable" (as per SVE ABI).
+ return builtinID == SVE::BI__builtin_sve_svdup_n_b8
+ ? dup
+ : emitSVEPredicateCast(dup, 16, loc);
+ }
case SVE::BI__builtin_sve_svdupq_n_b8:
case SVE::BI__builtin_sve_svdupq_n_b16:
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 86a8736980773..f3c6fb9b992bc 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1640,6 +1640,7 @@ class CIRGenFunction : public CIRGenTypeCache {
const CallExpr *expr);
std::optional<mlir::Value> emitAArch64SVEBuiltinExpr(unsigned builtinID,
const CallExpr *expr);
+ cir::VectorType getSVEType(const SVETypeFlags &typeFlags);
mlir::Value emitAlignmentAssumption(mlir::Value ptrValue, QualType ty,
SourceLocation loc,
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 186d91599e806..2fc44b14470d8 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -208,12 +208,22 @@ static mlir::Value emitToMemory(mlir::ConversionPatternRewriter &rewriter,
return createIntCast(rewriter, value, memType);
}
- // Boolean vectors use `iN` as storage type
+ // Boolean vectors use:
+ // * `iN` for fixed-width vectors,
+ // * `<vscale x N x i1>` for scalable vectors,
+ // as storage type.
if (auto vecTy = mlir::dyn_cast<cir::VectorType>(origType)) {
if (mlir::isa<cir::BoolType>(vecTy.getElementType())) {
- uint64_t bytePadded = std::max<uint64_t>(vecTy.getSize(), 8);
- auto resultTy = mlir::IntegerType::get(origType.getContext(), bytePadded);
- value = emitBoolVecConversion(rewriter, value, resultTy.getWidth());
+ mlir::Type resultTy;
+ if (vecTy.getIsScalable())
+ resultTy = mlir::VectorType::get(
+ vecTy.getSize(), vecTy.getElementType(), vecTy.getIsScalable());
+ else {
+ uint64_t bytePadded = std::max<uint64_t>(vecTy.getSize(), 8);
+ resultTy = mlir::IntegerType::get(origType.getContext(), bytePadded);
+ value = emitBoolVecConversion(
+ rewriter, value, dyn_cast<mlir::IntegerType>(resultTy).getWidth());
+ }
return mlir::LLVM::BitcastOp::create(rewriter, value.getLoc(), resultTy,
value);
}
diff --git a/clang/test/CodeGen/AArch64/sve/dup.c b/clang/test/CodeGen/AArch64/sve/dup.c
index fa97d47888bd4..80a7a888339ff 100644
--- a/clang/test/CodeGen/AArch64/sve/dup.c
+++ b/clang/test/CodeGen/AArch64/sve/dup.c
@@ -168,6 +168,47 @@ svfloat64_t test_svdup_n_f64(float64_t op) MODE_ATTR
return SVE_ACLE_FUNC(svdup,_n,_f64,)(op);
}
+// ALL-LABEL: @test_svdup_n_b8(
+svbool_t test_svdup_n_b8(bool op) MODE_ATTR
+{
+// LLVM-SAME: i1{{.*}} [[OP:%.*]])
+// LLVM: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 16 x i1> poison, i1 [[OP:%.*]], i64 0
+// LLVM: [[DOTSPLAT:%.*]] = shufflevector <vscale x 16 x i1> [[DOTSPLATINSERT]], <vscale x 16 x i1> poison, <vscale x 16 x i32> zeroinitializer
+// LLVM: ret <vscale x 16 x i1> [[DOTSPLAT]]
+ return SVE_ACLE_FUNC(svdup,_n,_b8,)(op);
+}
+
+// ALL-LABEL: @test_svdup_n_b16(
+svbool_t test_svdup_n_b16(bool op) MODE_ATTR
+{
+// LLVM-SAME: i1{{.*}} [[OP:%.*]])
+// LLVM: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 8 x i1> poison, i1 [[OP:%.*]], i64 0
+// LLVM: [[DOTSPLAT:%.*]] = shufflevector <vscale x 8 x i1> [[DOTSPLATINSERT]], <vscale x 8 x i1> poison, <vscale x 8 x i32> zeroinitializer
+// LLVM: [[TMP0:%.*]] = {{.*}} call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv8i1(<vscale x 8 x i1> [[DOTSPLAT]])
+// LLVM: ret <vscale x 16 x i1> [[TMP0]]
+ return SVE_ACLE_FUNC(svdup,_n,_b16,)(op);
+}
+
+// ALL-LABEL: @test_svdup_n_b32(
+svbool_t test_svdup_n_b32(bool op) MODE_ATTR
+{
+// LLVM: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 4 x i1> poison, i1 [[OP:%.*]], i64 0
+// LLVM: [[DOTSPLAT:%.*]] = shufflevector <vscale x 4 x i1> [[DOTSPLATINSERT]], <vscale x 4 x i1> poison, <vscale x 4 x i32> zeroinitializer
+// LLVM: [[TMP0:%.*]] = {{.*}} call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> [[DOTSPLAT]])
+// LLVM: ret <vscale x 16 x i1> [[TMP0]]
+ return SVE_ACLE_FUNC(svdup,_n,_b32,)(op);
+}
+
+// ALL-LABEL: @test_svdup_n_b64(
+svbool_t test_svdup_n_b64(bool op) MODE_ATTR
+{
+// LLVM: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 2 x i1> poison, i1 [[OP:%.*]], i64 0
+// LLVM: [[DOTSPLAT:%.*]] = shufflevector <vscale x 2 x i1> [[DOTSPLATINSERT]], <vscale x 2 x i1> poison, <vscale x 2 x i32> zeroinitializer
+// LLVM: [[TMP0:%.*]] = {{.*}} call <vscale x 16 x i1> @llvm.aarch64.sve.convert.to.svbool.nxv2i1(<vscale x 2 x i1> [[DOTSPLAT]])
+// LLVM: ret <vscale x 16 x i1> [[TMP0]]
+ return SVE_ACLE_FUNC(svdup,_n,_b64,)(op);
+}
+
//===------------------------------------------------------===//
// 2. PREDICATED ZERO-ING SVDUP
//===------------------------------------------------------===//
More information about the cfe-commits
mailing list