Skip to content

Conversation

@banach-space
Copy link
Contributor

@banach-space banach-space commented Dec 15, 2025

This patch adds lowering support for integer svlen builtins.

Because svlen builtins take scalable vector types (e.g., svuint64_t),
this change also extends cir::VectorType to represent scalable
vectors. Since cir::VectorType is ultimately converted to MLIR’s
builtin VectorType during lowering to LLVM IR, the implementation
follows the same approach: scalability is modelled using an additional
boolean member (isScalable, defaulting to false).

Further work will be needed to properly support scalable vectors within
CIR:

  • cir::VectorType::getTypeSizeInBits currently returns the
    compile-time base vector size. Its meaning and usefulness for scalable
    vectors are unclear and may need re-design.
  • The assembly format for cir::VectorType will require a custom parser
    and printer to encode scalability (and agreement on the concrete
    syntax). This is not required for this patch.

References:

This patch adds lowering support for integer `svlen` builtins.
Floating-point variants are omitted for now and will be added once FP
type helpers (e.g., `getFP32Ty()`) become available.

Because svlen builtins take scalable vector types (e.g., `svuint64_t`),
this change also extends `cir::VectorType` to represent scalable
vectors. Since `cir::VectorType` is ultimately converted to MLIR’s
builtin `VectorType` during lowering to LLVM IR, the implementation
follows the same approach: scalability is modelled using an additional
boolean member (`isScalable`, defaulting to `false`).

Further work will be needed to properly support scalable vectors within
CIR:
* `cir::VectorType::getTypeSizeInBits` currently returns the
  compile-time base vector size. Its meaning and usefulness for scalable
  vectors are unclear and may need re-design.
* The assembly format for `cir::VectorType` will require a custom parser
  and printer to encode scalability (and agreement on the concrete
  syntax). This is not required for this patch.

References:
* https://arm-software.github.io/acle/main/acle.html#markdown-toc-sve-vector-types
* https://developer.arm.com/documentation/100891/0609/coding-considerations/using-sve-intrinsics-directly-in-your-c-code
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Dec 15, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 15, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Andrzej Warzyński (banach-space)

Changes

This patch adds lowering support for integer svlen builtins.
Floating-point variants are omitted for now and will be added once FP
type helpers (e.g., getFP32Ty()) become available.

Because svlen builtins take scalable vector types (e.g., svuint64_t),
this change also extends cir::VectorType to represent scalable
vectors. Since cir::VectorType is ultimately converted to MLIR’s
builtin VectorType during lowering to LLVM IR, the implementation
follows the same approach: scalability is modelled using an additional
boolean member (isScalable, defaulting to false).

Further work will be needed to properly support scalable vectors within
CIR:

  • cir::VectorType::getTypeSizeInBits currently returns the
    compile-time base vector size. Its meaning and usefulness for scalable
    vectors are unclear and may need re-design.
  • The assembly format for cir::VectorType will require a custom parser
    and printer to encode scalability (and agreement on the concrete
    syntax). This is not required for this patch.

References:


Full diff: https://github.com/llvm/llvm-project/pull/172346.diff

8 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+1-2)
  • (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.td (+9-5)
  • (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+2-2)
  • (modified) clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp (+38-8)
  • (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+48)
  • (modified) clang/lib/CIR/Dialect/IR/CIRTypes.cpp (+1-1)
  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+1-1)
  • (added) clang/test/CIR/CodeGenBuiltins/AArch64/acle_sve_len.c (+143)
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index b4b02e24f85cc..8ca07ca3f17b8 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -596,8 +596,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     VectorType vecCast = mlir::cast<VectorType>(lhs.getType());
     IntType integralTy =
         getSIntNTy(getCIRIntOrFloatBitWidth(vecCast.getElementType()));
-    VectorType integralVecTy =
-        VectorType::get(context, integralTy, vecCast.getSize());
+    VectorType integralVecTy = VectorType::get(integralTy, vecCast.getSize());
     return cir::VecCmpOp::create(*this, loc, integralVecTy, kind, lhs, rhs);
   }
 
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 59b97f0c6d39a..3ca56be4e4a10 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -422,8 +422,9 @@ def CIR_VectorType : CIR_Type<"Vector", "vector", [
 ]> {
   let summary = "CIR vector type";
   let description = [{
-    The `!cir.vector` type represents a fixed-size, one-dimensional vector.
-    It takes two parameters: the element type and the number of elements.
+    The `!cir.vector` type represents a one-dimensional vector.
+    It takes three parameters: the element type, the number of elements and the
+    scalability flag (optional, defaults to `false`).
 
     Syntax:
 
@@ -445,7 +446,8 @@ def CIR_VectorType : CIR_Type<"Vector", "vector", [
 
   let parameters = (ins
     CIR_VectorElementType:$elementType,
-    "uint64_t":$size
+    "uint64_t":$size,
+    OptionalParameter<"bool">:$isScalable
   );
 
   let assemblyFormat = [{
@@ -454,9 +456,10 @@ def CIR_VectorType : CIR_Type<"Vector", "vector", [
 
   let builders = [
     TypeBuilderWithInferredContext<(ins
-      "mlir::Type":$elementType, "uint64_t":$size
+      "mlir::Type":$elementType, "uint64_t":$size, CArg<"bool",
+      "false">:$isScalable
     ), [{
-        return $_get(elementType.getContext(), elementType, size);
+        return $_get(elementType.getContext(), elementType, size, isScalable);
     }]>,
   ];
 
@@ -467,6 +470,7 @@ def CIR_VectorType : CIR_Type<"Vector", "vector", [
   }];
 
   let genVerifyDecl = 1;
+  let skipDefaultBuilders = 1;
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 5b10bddd054ea..2a15b1f7703be 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -628,8 +628,8 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   createVecShuffle(mlir::Location loc, mlir::Value vec1, mlir::Value vec2,
                    llvm::ArrayRef<mlir::Attribute> maskAttrs) {
     auto vecType = mlir::cast<cir::VectorType>(vec1.getType());
-    auto resultTy = cir::VectorType::get(getContext(), vecType.getElementType(),
-                                         maskAttrs.size());
+    auto resultTy =
+        cir::VectorType::get(vecType.getElementType(), maskAttrs.size());
     return cir::VecShuffleOp::create(*this, loc, resultTy, vec1, vec2,
                                      getArrayAttr(maskAttrs));
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
index 696180458a2f6..7a9661b727dc7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAArch64.cpp
@@ -11,6 +11,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "CIRGenBuilder.h"
 #include "CIRGenFunction.h"
 #include "clang/CIR/MissingFeatures.h"
 
@@ -30,6 +31,17 @@ using namespace clang;
 using namespace clang::CIRGen;
 using namespace llvm;
 
+template <typename... Operands>
+static mlir::Value emitIntrinsicCallOp(CIRGenBuilderTy &builder,
+                                       mlir::Location loc, const StringRef str,
+                                       const mlir::Type &resTy,
+                                       Operands &&...op) {
+  return cir::LLVMIntrinsicCallOp::create(builder, loc,
+                                          builder.getStringAttr(str), resTy,
+                                          std::forward<Operands>(op)...)
+      .getResult();
+}
+
 std::optional<mlir::Value>
 CIRGenFunction::emitAArch64SVEBuiltinExpr(unsigned builtinID,
                                           const CallExpr *expr) {
@@ -41,6 +53,16 @@ CIRGenFunction::emitAArch64SVEBuiltinExpr(unsigned builtinID,
     return mlir::Value{};
   }
 
+  mlir::Location loc = getLoc(expr->getExprLoc());
+  // Generate vscale * scalingFactor
+  auto vscaleTimesFactor = [&](int32_t scalingFactor) {
+    StringRef intrinsicName = "vscale.i64";
+    auto vscale = emitIntrinsicCallOp(builder, loc, intrinsicName,
+                                      convertType(expr->getType()));
+    return builder.createMul(loc, vscale,
+                             builder.getUInt64(scalingFactor, loc));
+  };
+
   assert(!cir::MissingFeatures::aarch64SVEIntrinsics());
 
   switch (builtinID) {
@@ -101,18 +123,26 @@ CIRGenFunction::emitAArch64SVEBuiltinExpr(unsigned builtinID,
   case SVE::BI__builtin_sve_svdupq_n_s32:
   case SVE::BI__builtin_sve_svpfalse_b:
   case SVE::BI__builtin_sve_svpfalse_c:
-  case SVE::BI__builtin_sve_svlen_bf16:
-  case SVE::BI__builtin_sve_svlen_f16:
-  case SVE::BI__builtin_sve_svlen_f32:
-  case SVE::BI__builtin_sve_svlen_f64:
-  case SVE::BI__builtin_sve_svlen_s8:
-  case SVE::BI__builtin_sve_svlen_s16:
-  case SVE::BI__builtin_sve_svlen_s32:
-  case SVE::BI__builtin_sve_svlen_s64:
+    cgm.errorNYI(expr->getSourceRange(),
+                 std::string("unimplemented AArch64 builtin call: ") +
+                     getContext().BuiltinInfo.getName(builtinID));
+    return mlir::Value{};
   case SVE::BI__builtin_sve_svlen_u8:
+  case SVE::BI__builtin_sve_svlen_s8:
+    return vscaleTimesFactor(16);
   case SVE::BI__builtin_sve_svlen_u16:
+  case SVE::BI__builtin_sve_svlen_s16:
+  case SVE::BI__builtin_sve_svlen_f16:
+  case SVE::BI__builtin_sve_svlen_bf16:
+    return vscaleTimesFactor(8);
   case SVE::BI__builtin_sve_svlen_u32:
+  case SVE::BI__builtin_sve_svlen_s32:
+  case SVE::BI__builtin_sve_svlen_f32:
+    return vscaleTimesFactor(4);
   case SVE::BI__builtin_sve_svlen_u64:
+  case SVE::BI__builtin_sve_svlen_s64:
+  case SVE::BI__builtin_sve_svlen_f64:
+    return vscaleTimesFactor(2);
   case SVE::BI__builtin_sve_svtbl2_u8:
   case SVE::BI__builtin_sve_svtbl2_s8:
   case SVE::BI__builtin_sve_svtbl2_u16:
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 7f000ece8a494..3bb075d7581f7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -7,6 +7,7 @@
 #include "clang/AST/GlobalDecl.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
 
 #include <cassert>
 
@@ -320,6 +321,53 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
           cir::IntType::get(&getMLIRContext(), astContext.getTypeSize(ty),
                             /*isSigned=*/true);
       break;
+
+    // SVE types
+    case BuiltinType::SveInt8:
+      resultType =
+          cir::VectorType::get(builder.getSInt8Ty(), 16, /*isScalable=*/true);
+      break;
+    case BuiltinType::SveUint8:
+      resultType =
+          cir::VectorType::get(builder.getUInt8Ty(), 16, /*isScalable=*/true);
+      break;
+    case BuiltinType::SveInt16:
+      resultType =
+          cir::VectorType::get(builder.getSInt16Ty(), 8, /*isScalable=*/true);
+      break;
+    case BuiltinType::SveUint16:
+      resultType =
+          cir::VectorType::get(builder.getUInt16Ty(), 8, /*isScalable=*/true);
+      break;
+    // TODO: Waiting for FP type helpers
+    // case BuiltinType::SveFloat16:
+    //   resultType = cir::VectorType::get(builder.getF16Type(), 8,
+    //   /*isScalable=*/true); break;
+    case BuiltinType::SveInt32:
+      resultType =
+          cir::VectorType::get(builder.getSInt32Ty(), 4, /*isScalable=*/true);
+      break;
+    case BuiltinType::SveUint32:
+      resultType =
+          cir::VectorType::get(builder.getUInt32Ty(), 4, /*isScalable=*/true);
+      break;
+    // TODO: Waiting for FP type helpers
+    // case BuiltinType::SveFloat32:
+    //   resultType = cir::VectorType::get(builder.getF32Type(), 4,
+    //   /*isScalable=*/true); break;
+    case BuiltinType::SveInt64:
+      resultType =
+          cir::VectorType::get(builder.getSInt64Ty(), 2, /*isScalable=*/true);
+      break;
+    case BuiltinType::SveUint64:
+      resultType =
+          cir::VectorType::get(builder.getUInt64Ty(), 2, /*isScalable=*/true);
+      break;
+    // TODO: Waiting for FP type helpers
+    // case BuiltinType::SveFloat64:
+    //   resultType = cir::VectorType::get(builder.getF64Type(), 2,
+    //   /*isScalable=*/true); break;
+
     // Unsigned integral types.
     case BuiltinType::Char8:
     case BuiltinType::Char16:
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 9a37a4f4e3996..c7531022fdfb8 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -822,7 +822,7 @@ cir::VectorType::getABIAlignment(const ::mlir::DataLayout &dataLayout,
 
 mlir::LogicalResult cir::VectorType::verify(
     llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
-    mlir::Type elementType, uint64_t size) {
+    mlir::Type elementType, uint64_t size, bool scalable) {
   if (size == 0)
     return emitError() << "the number of vector elements must be non-zero";
   return success();
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 7d854997848aa..7c9cf8e2c2e2d 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2910,7 +2910,7 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
   });
   converter.addConversion([&](cir::VectorType type) -> mlir::Type {
     const mlir::Type ty = converter.convertType(type.getElementType());
-    return mlir::VectorType::get(type.getSize(), ty);
+    return mlir::VectorType::get(type.getSize(), ty, {type.getIsScalable()});
   });
   converter.addConversion([&](cir::BoolType type) -> mlir::Type {
     return mlir::IntegerType::get(type.getContext(), 1,
diff --git a/clang/test/CIR/CodeGenBuiltins/AArch64/acle_sve_len.c b/clang/test/CIR/CodeGenBuiltins/AArch64/acle_sve_len.c
new file mode 100644
index 0000000000000..3ad2ddef04030
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/AArch64/acle_sve_len.c
@@ -0,0 +1,143 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// REQUIRES: aarch64-registered-target
+
+// RUN: %clang_cc1 -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-cir -o - %s | FileCheck %s --check-prefixes=ALL,CIR
+// RUN: %clang_cc1 -DSVE_OVERLOADED_FORMS -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-cir -o - %s | FileCheck %s --check-prefixes=ALL,CIR
+//
+// RUN: %clang_cc1 -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,LLVM
+// RUN: %clang_cc1 -DSVE_OVERLOADED_FORMS -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,LLVM
+
+#include <arm_sve.h>
+
+#if defined __ARM_FEATURE_SME
+#define MODE_ATTR __arm_streaming
+#else
+#define MODE_ATTR
+#endif
+
+#ifdef SVE_OVERLOADED_FORMS
+// A simple used,unused... macro, long enough to represent any SVE builtin.
+#define SVE_ACLE_FUNC(A1,A2_UNUSED,A3,A4_UNUSED) A1##A3
+#else
+#define SVE_ACLE_FUNC(A1,A2,A3,A4) A1##A2##A3##A4
+#endif
+
+// ALL-LABEL: @test_svlen_u8
+uint64_t test_svlen_u8(svuint8_t op) MODE_ATTR
+{
+// CIR:     %[[VSCALE:.*]] = cir.call_llvm_intrinsic "vscale.i64"  : () -> !u64i
+// CIR:     %[[C16:.*]] = cir.const #cir.int<16> : !u64i
+// CIR:     %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C16]]) : !u64i
+
+// LLVM:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
+// LLVM:    [[RES:%.*]] = mul i64 [[VSCALE]], 16
+  return SVE_ACLE_FUNC(svlen,_u8,,)(op);
+}
+
+// ALL-LABEL: @test_svlen_s8(
+uint64_t test_svlen_s8(svint8_t op) MODE_ATTR
+{
+// CIR:     %[[VSCALE:.*]] = cir.call_llvm_intrinsic "vscale.i64"  : () -> !u64i
+// CIR:     %[[C16:.*]] = cir.const #cir.int<16> : !u64i
+// CIR:     %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C16]]) : !u64i
+
+// LLVM:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
+// LLVM:    [[RES:%.*]] = mul i64 [[VSCALE]], 16
+  return SVE_ACLE_FUNC(svlen,_s8,,)(op);
+}
+
+// ALL-LABEL: @test_svlen_u16(
+uint64_t test_svlen_u16(svuint16_t op) MODE_ATTR
+{
+// CIR:           %[[VSCALE:.*]] = cir.call_llvm_intrinsic "vscale.i64"  : () -> !u64i
+// CIR:           %[[C8:.*]] = cir.const #cir.int<8> : !u64i
+// CIR:           %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C8]]) : !u64i
+
+// LLVM:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
+// LLVM:    [[RES:%.*]] = mul i64 [[VSCALE]], 8
+  return SVE_ACLE_FUNC(svlen,_u16,,)(op);
+}
+
+// ALL-LABEL: @test_svlen_s16(
+uint64_t test_svlen_s16(svint16_t op) MODE_ATTR
+{
+// CIR:           %[[VSCALE:.*]] = cir.call_llvm_intrinsic "vscale.i64"  : () -> !u64i
+// CIR:           %[[C8:.*]] = cir.const #cir.int<8> : !u64i
+// CIR:           %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C8]]) : !u64i
+
+// LLVM:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
+// LLVM:    [[RES:%.*]] = mul i64 [[VSCALE]], 8
+  return SVE_ACLE_FUNC(svlen,_s16,,)(op);
+}
+
+// TODO: Waiting for FP type helpers
+// uint64_t test_svlen_f16(svfloat16_t op) MODE_ATTR
+// {
+//   return SVE_ACLE_FUNC(svlen,_f16,,)(op);
+// }
+
+// TODO: Waiting for FP type helpers
+// uint64_t test_svlen_bf16(svbfloat16_t op) MODE_ATTR
+// {
+//   return SVE_ACLE_FUNC(svlen,_bf16,,)(op);
+// }
+
+// ALL-LABEL: @test_svlen_u32(
+uint64_t test_svlen_u32(svuint32_t op) MODE_ATTR
+{
+// CIR:           %[[VSCALE:.*]] = cir.call_llvm_intrinsic "vscale.i64"  : () -> !u64i
+// CIR:           %[[C4:.*]] = cir.const #cir.int<4> : !u64i
+// CIR:           %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C4]]) : !u64i
+
+// LLVM:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
+// LLVM:    [[RES:%.*]] = mul i64  [[VSCALE]], 4
+  return SVE_ACLE_FUNC(svlen,_u32,,)(op);
+}
+
+// ALL-LABEL: @test_svlen_s32(
+uint64_t test_svlen_s32(svint32_t op) MODE_ATTR
+{
+// CIR:           %[[VSCALE:.*]] = cir.call_llvm_intrinsic "vscale.i64"  : () -> !u64i
+// CIR:           %[[C4:.*]] = cir.const #cir.int<4> : !u64i
+// CIR:           %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C4]]) : !u64i
+
+// LLVM:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
+// LLVM:    [[RES:%.*]] = mul i64 [[VSCALE]], 4
+  return SVE_ACLE_FUNC(svlen,_s32,,)(op);
+}
+
+// TODO: Waiting for FP type helpers
+// uint64_t test_svlen_f32(svfloat32_t op) MODE_ATTR
+// {
+//   return SVE_ACLE_FUNC(svlen,_f32,,)(op);
+// }
+
+// ALL-LABEL: @test_svlen_u64(
+uint64_t test_svlen_u64(svuint64_t op) MODE_ATTR
+{
+// CIR:           %[[VSCALE:.*]] = cir.call_llvm_intrinsic "vscale.i64"  : () -> !u64i
+// CIR:           %[[C2:.*]] = cir.const #cir.int<2> : !u64i
+// CIR:           %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C2]]) : !u64i
+
+// LLVM:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
+// LLVM:    [[RES:%.*]] = mul i64  [[VSCALE]], 2
+  return SVE_ACLE_FUNC(svlen,_u64,,)(op);
+}
+
+// ALL-LABEL: @test_svlen_s64
+uint64_t test_svlen_s64(svint64_t op) MODE_ATTR
+{
+// CIR:           %[[VSCALE:.*]] = cir.call_llvm_intrinsic "vscale.i64"  : () -> !u64i
+// CIR:           %[[C2:.*]] = cir.const #cir.int<2> : !u64i
+// CIR:           %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C2]]) : !u64i
+
+// LLVM:    [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
+// LLVM:    [[RES:%.*]] = mul i64 [[VSCALE]], 2
+  return SVE_ACLE_FUNC(svlen,_s64,,)(op);
+}
+
+// TODO: Waiting for FP type helpers
+// uint64_t test_svlen_f64(svfloat64_t op) MODE_ATTR
+// {
+//   return SVE_ACLE_FUNC(svlen,_f64,,)(op);
+// }

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting us started on AArch64 builtins!

Comment on lines 59 to 60
StringRef intrinsicName = "vscale.i64";
auto vscale = emitIntrinsicCallOp(builder, loc, intrinsicName,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
StringRef intrinsicName = "vscale.i64";
auto vscale = emitIntrinsicCallOp(builder, loc, intrinsicName,
StringRef intrinsicName = "vscale.i64";
auto vscale = emitIntrinsicCallOp(builder, loc, "vscale",

The suffix will get added automatically during lowering to LLVM IR.

Do you think it's useful to add a cir.vscale operation to make this easier to identify in transformation passes?

Copy link
Contributor Author

@banach-space banach-space Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's useful to add a cir.vscale operation to make this easier to identify in transformation passes?

Yes, similar op from Vector (vector.vscale) has been widely used. That said, I'm not sure how helpful cir.vscale would be just now.

In any case, I am in favour, but I'd defer to a separate PR. Note, we still need to "fix" how scalable vectors are printed in CIR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suffix will get added automatically during lowering to LLVM IR.

Addressed in this commit

StringRef intrinsicName = "vscale.i64";
auto vscale = emitIntrinsicCallOp(builder, loc, intrinsicName,
convertType(expr->getType()));
return builder.createMul(loc, vscale,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return builder.createMul(loc, vscale,
return builder.createNUWAMul(loc, vscale,

I'm not sure why this isn't createNUWMul

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies, that's my sloppiness, thanks for catching this!


mlir::Location loc = getLoc(expr->getExprLoc());
// Generate vscale * scalingFactor
auto vscaleTimesFactor = [&](int32_t scalingFactor) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make this a separate static function rather than a lambda?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in this commit

break;
// TODO: Waiting for FP type helpers
// case BuiltinType::SveFloat16:
// resultType = cir::VectorType::get(builder.getF16Type(), 8,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the type helpers? We already have fP16Ty, floatTy, and doubleTy in CIRGenTypeCache so it should be trivial.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in this commit - hopefully that's what you had in mind.

Comment on lines 7 to 8
// RUN: %clang_cc1 -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,LLVM
// RUN: %clang_cc1 -DSVE_OVERLOADED_FORMS -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,LLVM
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// RUN: %clang_cc1 -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,LLVM
// RUN: %clang_cc1 -DSVE_OVERLOADED_FORMS -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,LLVM
// RUN: %clang_cc1 -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,LLVM
// RUN: %clang_cc1 -DSVE_OVERLOADED_FORMS -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,LLVM
// RUN: %clang_cc1 -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,OGCG
// RUN: %clang_cc1 -DSVE_OVERLOADED_FORMS -triple aarch64 -target-feature +sve -disable-O0-optnone -Werror -Wall -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,OGCG

Can you add checks for OGCG so we can visually compare the LLVM output via CIR to the direct LLVM output to verify that we're doing the same thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

On a related note, I feel that it could be helpful to create some common PyThon substitutions for these very long invocation lines. For example:

%clang_cc1_llvm_c = %clang_cc1 -disable-O0-optnone -Werror -Wall -fclangir -emit-llvm -o - %s 

And then:

//RUN: %clang_cc1_llvm_c -triple aarch64 -target-feature +sve | FileCheck %s --check-prefixes=ALL,LLVM

Or something similar that would make it clearer (and more visible) what the difference between different RUN lines is.

banach-space and others added 3 commits December 16, 2025 13:50
Co-authored-by: Andy Kaylor <akaylor@nvidia.com>
Co-authored-by: Andy Kaylor <akaylor@nvidia.com>
Co-authored-by: Andy Kaylor <akaylor@nvidia.com>
@github-actions
Copy link

github-actions bot commented Dec 16, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@github-actions
Copy link

github-actions bot commented Dec 16, 2025

🐧 Linux x64 Test Results

  • 112974 tests passed
  • 4100 tests skipped

✅ The build succeeded and all tests passed.

* Fix `$elementType` spelling
* Add `getFP` helpers and remove TODOs.
* Fix: `builder.createAMul` -->  `builder.createNUWAMul`
* Replace `vscaleTimesFactor` lambda with a static function:
  `genVscaleTimesFactor`
* Replace `"vscale.i64"` with `"vscale"`.
Comment on lines 457 to 460
let builders = [TypeBuilderWithInferredContext<
(ins "mlir::Type":$element_type, "uint64_t":$size,
CArg<"bool", "false">:$isScalable),
[{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accidental clang-format, reverted in this commit.

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. I have just a couple of nits.

using namespace llvm;

template <typename... Operands>
static mlir::Value emitIntrinsicCallOp(CIRGenBuilderTy &builder,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but as you probably noticed we have this same function in CIRGenBuiltinX86.cpp. At some point we should create a shared definition.

// CIR: %[[C16:.*]] = cir.const #cir.int<16> : !u64i
// CIR: %[[BINOP:.*]] = cir.binop(mul, %[[VSCALE]], %[[C16]]) nuw : !u64i

// LLVM_OGCG_CIR: [[VSCALE:%.*]] = call i64 @llvm.vscale.i64()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This confused me for a second, but I guess this means we're generating the same output between the -fclangir -emit-llvm path and the -emit-llvm path. That's great. Usually, we have minor differences.

@banach-space banach-space changed the title [CIR][AArch64] Add lowering for svlen builtins (1/2) [CIR][AArch64] Add lowering for svlen builtins Dec 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants