https://github.com/banach-space created 
https://github.com/llvm/llvm-project/pull/223459

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).


From bcdc327a8fe2e3caee72e3796de27ea4a8a2a9bd Mon Sep 17 00:00:00 2001
From: Andrzej Warzynski <[email protected]>
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 83cd4255a5bb4e..ded72ad9104f88 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. <vscale x 16 x
+    // i1>) 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 86a87369807738..f3c6fb9b992bcd 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 186d91599e806e..2fc44b14470d86 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 fa97d47888bd49..80a7a888339ff9 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
 //===------------------------------------------------------===//

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to