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 <[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 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 //===------------------------------------------------------===// _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
