[llvm] [WIP][SPARC] Add TTI implementation for getting register numbers and widths (PR #180660)

via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 17 06:10:55 PST 2026


https://github.com/koachan updated https://github.com/llvm/llvm-project/pull/180660

>From 3c55e4ff774c8a09d5be749a5b32362c76ccd598 Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Tue, 10 Feb 2026 08:31:41 +0700
Subject: [PATCH 1/2] [SPARC] Add TTI implementation for getting register
 numbers and widths

Correctly inform transform passes about our registers; this prevents
the issue with the `find-last` test where the loop vectorizer pass
mistakenly thinks that the backend has vector capabilities.

Fixes https://github.com/sparclinux/issues/issues/69
---
 .../Target/Sparc/SparcTargetTransformInfo.cpp | 49 +++++++++++++++++++
 .../Target/Sparc/SparcTargetTransformInfo.h   | 11 ++++-
 2 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/Sparc/SparcTargetTransformInfo.cpp b/llvm/lib/Target/Sparc/SparcTargetTransformInfo.cpp
index cd8167eb742b7..1a1ceea0ffc51 100644
--- a/llvm/lib/Target/Sparc/SparcTargetTransformInfo.cpp
+++ b/llvm/lib/Target/Sparc/SparcTargetTransformInfo.cpp
@@ -20,3 +20,52 @@ SparcTTIImpl::getPopcntSupport(unsigned TyWidth) const {
     return TTI::PSK_FastHardware;
   return TTI::PSK_Software;
 }
+
+unsigned SparcTTIImpl::getRegisterClassForType(bool Vector, Type *Ty) const {
+  if (Vector)
+    return VRRC;
+  if (Ty &&
+      (Ty->getScalarType()->isFloatTy() || Ty->getScalarType()->isDoubleTy()))
+    return FPRRC;
+  if (Ty && (Ty->getScalarType()->isFP128Ty()))
+    return FP128RRC;
+  return GPRRC;
+}
+
+unsigned SparcTTIImpl::getNumberOfRegisters(unsigned ClassID) const {
+  switch (ClassID) {
+  case GPRRC:
+    // %g0, %g6, %g7, %o6, %i6, and %i7 are used for special purposes so we
+    // discount them here.
+    return 26;
+  case FPRRC:
+    return 32;
+  case FP128RRC:
+    return 16;
+  case VRRC:
+    // TODO We have vector capabilities as part of the VIS extensions, but the
+    // codegen doesn't currently use it. Revisit this when vector codegen is
+    // ready.
+    return 0;
+  }
+
+  llvm_unreachable("Unsupported register class");
+}
+
+TypeSize
+SparcTTIImpl::getRegisterBitWidth(TargetTransformInfo::RegisterKind K) const {
+  switch (K) {
+  case TargetTransformInfo::RGK_Scalar:
+    // TODO When targeting V8+ ABI, G and O registers are 64-bit.
+    return TypeSize::getFixed(ST->is64Bit() ? 64 : 32);
+  case TargetTransformInfo::RGK_FixedWidthVector:
+    // TODO We have vector capabilities as part of the VIS extensions, but the
+    // codegen doesn't currently use it. Revisit this when vector codegen is
+    // ready.
+    return TypeSize::getFixed(0);
+  case TargetTransformInfo::RGK_ScalableVector:
+    return TypeSize::getScalable(0);
+  }
+
+  llvm_unreachable("Unsupported register kind");
+}
diff --git a/llvm/lib/Target/Sparc/SparcTargetTransformInfo.h b/llvm/lib/Target/Sparc/SparcTargetTransformInfo.h
index 3bf9a197ae1d5..2d4b94c9995fe 100644
--- a/llvm/lib/Target/Sparc/SparcTargetTransformInfo.h
+++ b/llvm/lib/Target/Sparc/SparcTargetTransformInfo.h
@@ -40,9 +40,18 @@ class SparcTTIImpl final : public BasicTTIImplBase<SparcTTIImpl> {
 
   /// \name Scalar TTI Implementations
   /// @{
-
   TTI::PopcntSupportKind getPopcntSupport(unsigned TyWidth) const override;
   /// @}
+
+  /// \name Vector TTI Implementations
+  /// @{
+  enum SparcRegisterClass { GPRRC, FPRRC, FP128RRC, VRRC };
+  unsigned getNumberOfRegisters(unsigned ClassID) const override;
+  unsigned getRegisterClassForType(bool Vector,
+                                   Type *Ty = nullptr) const override;
+  TypeSize
+  getRegisterBitWidth(TargetTransformInfo::RegisterKind K) const override;
+  /// @}
 };
 
 } // end namespace llvm

>From 8523b88123e97ddfdd8160240c8065311c913b17 Mon Sep 17 00:00:00 2001
From: Koakuma <koachan at protonmail.com>
Date: Tue, 17 Feb 2026 21:09:25 +0700
Subject: [PATCH 2/2] Add tests

---
 .../LoopVectorize/Sparc/no-vectorize.ll       | 123 ++++++++++++++++++
 1 file changed, 123 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopVectorize/Sparc/no-vectorize.ll

diff --git a/llvm/test/Transforms/LoopVectorize/Sparc/no-vectorize.ll b/llvm/test/Transforms/LoopVectorize/Sparc/no-vectorize.ll
new file mode 100644
index 0000000000000..1df0d8ea42184
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/Sparc/no-vectorize.ll
@@ -0,0 +1,123 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=loop-vectorize -mtriple sparc -S | FileCheck %s -check-prefixes=SPARC,SPARC32
+; RUN: opt < %s -passes=loop-vectorize -mtriple sparcv9 -S | FileCheck %s -check-prefixes=SPARC,SPARC64
+
+;; At the moment the backend doesn't support vector instructions.
+;; Make sure that nothing gets vectorized, even those with explicit hints.
+;; The code is based on the C++ reproducer at https://github.com/sparclinux/issues/issues/69#issuecomment-3822053617
+
+%struct.function = type { ptr }
+
+ at main_Rdx = dso_local local_unnamed_addr global i16 0, align 2
+ at .str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
+
+define dso_local noundef i32 @main() {
+; SPARC-LABEL: define dso_local noundef i32 @main() {
+; SPARC-NEXT:  [[ENTRY:.*:]]
+; SPARC-NEXT:    [[AGG_TMP:%.*]] = alloca [[STRUCT_FUNCTION:%.*]], align 4
+; SPARC-NEXT:    [[AGG_TMP2:%.*]] = alloca [[STRUCT_FUNCTION]], align 4
+; SPARC-NEXT:    store ptr @a, ptr [[AGG_TMP]], align 4
+; SPARC-NEXT:    store ptr @b, ptr [[AGG_TMP2]], align 4
+; SPARC-NEXT:    tail call void @c(ptr byval([[STRUCT_FUNCTION]]) [[AGG_TMP]], ptr byval([[STRUCT_FUNCTION]]) [[AGG_TMP2]], ptr @.str)
+; SPARC-NEXT:    ret i32 0
+;
+entry:
+  %agg.tmp = alloca %struct.function, align 4
+  %agg.tmp2 = alloca %struct.function, align 4
+  store ptr @a, ptr %agg.tmp, align 4
+  store ptr @b, ptr %agg.tmp2, align 4
+  tail call void @c(ptr byval(%struct.function) %agg.tmp, ptr byval(%struct.function) %agg.tmp2, ptr @.str)
+  ret i32 0
+}
+
+define internal noundef signext i16 @a() {
+; SPARC-LABEL: define internal noundef signext i16 @a() {
+; SPARC-NEXT:  [[ENTRY:.*:]]
+; SPARC-NEXT:    unreachable
+;
+entry:
+  unreachable
+}
+
+define internal noundef signext i16 @b(ptr %__functor, ptr %__args1, ptr %__args3) {
+; SPARC-LABEL: define internal noundef signext i16 @b(
+; SPARC-SAME: ptr [[__FUNCTOR:%.*]], ptr [[__ARGS1:%.*]], ptr [[__ARGS3:%.*]]) {
+; SPARC-NEXT:  [[ENTRY:.*:]]
+; SPARC-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[__ARGS1]], align 4
+; SPARC-NEXT:    [[TMP1:%.*]] = load i16, ptr [[__ARGS3]], align 2
+; SPARC-NEXT:    tail call fastcc void @d(ptr [[TMP0]], i16 [[TMP1]])
+; SPARC-NEXT:    unreachable
+;
+entry:
+  %0 = load ptr, ptr %__args1, align 4
+  %1 = load i16, ptr %__args3, align 2
+  tail call fastcc void @d(ptr %0, i16 %1)
+  unreachable
+}
+
+declare dso_local void @c(ptr byval(%struct.function), ptr byval(%struct.function), ptr noundef)
+
+define internal fastcc void @d(ptr %__args1, i16 %__args3) {
+; SPARC-LABEL: define internal fastcc void @d(
+; SPARC-SAME: ptr [[__ARGS1:%.*]], i16 [[__ARGS3:%.*]]) {
+; SPARC-NEXT:  [[ENTRY:.*:]]
+; SPARC-NEXT:    [[TOBOOL_NOT2_I_I:%.*]] = icmp eq i16 [[__ARGS3]], 0
+; SPARC-NEXT:    br i1 [[TOBOOL_NOT2_I_I]], label %[[EXIT:.*]], label %[[FOR_BODY_PREHEADER_I_I:.*]]
+; SPARC:       [[FOR_BODY_PREHEADER_I_I]]:
+; SPARC-NEXT:    [[MAIN_RDX_PROMOTED_I_I:%.*]] = load i16, ptr @main_Rdx, align 2
+; SPARC-NEXT:    br label %[[FOR_BODY_I_I:.*]]
+; SPARC:       [[FOR_BODY_I_I]]:
+; SPARC-NEXT:    [[I_04_I_I:%.*]] = phi i16 [ [[SUB_I_I:%.*]], %[[FOR_BODY_I_I]] ], [ [[__ARGS3]], %[[FOR_BODY_PREHEADER_I_I]] ]
+; SPARC-NEXT:    [[COND13_I_I:%.*]] = phi i16 [ [[COND_I_I:%.*]], %[[FOR_BODY_I_I]] ], [ [[MAIN_RDX_PROMOTED_I_I]], %[[FOR_BODY_PREHEADER_I_I]] ]
+; SPARC-NEXT:    [[IDXPROM_I_I:%.*]] = sext i16 [[I_04_I_I]] to i32
+; SPARC-NEXT:    [[ARRAYIDX_I_I:%.*]] = getelementptr inbounds i16, ptr [[__ARGS1]], i32 [[IDXPROM_I_I]]
+; SPARC-NEXT:    [[TMP0:%.*]] = load i16, ptr [[ARRAYIDX_I_I]], align 2
+; SPARC-NEXT:    [[TOBOOL2_NOT_I_I:%.*]] = icmp eq i16 [[TMP0]], 0
+; SPARC-NEXT:    [[COND_I_I]] = select i1 [[TOBOOL2_NOT_I_I]], i16 [[COND13_I_I]], i16 [[TMP0]]
+; SPARC-NEXT:    store i16 [[COND_I_I]], ptr @main_Rdx, align 2
+; SPARC-NEXT:    [[SUB_I_I]] = add i16 [[I_04_I_I]], -1
+; SPARC-NEXT:    [[TOBOOL_NOT_I_I:%.*]] = icmp eq i16 [[SUB_I_I]], 0
+; SPARC-NEXT:    br i1 [[TOBOOL_NOT_I_I]], label %[[EXIT]], label %[[FOR_BODY_I_I]], !llvm.loop [[LOOP0:![0-9]+]]
+; SPARC:       [[EXIT]]:
+; SPARC-NEXT:    ret void
+;
+entry:
+  %tobool.not2.i.i = icmp eq i16 %__args3, 0
+  br i1 %tobool.not2.i.i, label %exit, label %for.body.preheader.i.i
+
+for.body.preheader.i.i:
+  %main_Rdx.promoted.i.i = load i16, ptr @main_Rdx, align 2
+  br label %for.body.i.i
+
+for.body.i.i:
+  %I.04.i.i = phi i16 [ %sub.i.i, %for.body.i.i ], [ %__args3, %for.body.preheader.i.i ]
+  %cond13.i.i = phi i16 [ %cond.i.i, %for.body.i.i ], [ %main_Rdx.promoted.i.i, %for.body.preheader.i.i ]
+  %idxprom.i.i = sext i16 %I.04.i.i to i32
+  %arrayidx.i.i = getelementptr inbounds i16, ptr %__args1, i32 %idxprom.i.i
+  %0 = load i16, ptr %arrayidx.i.i, align 2
+  %tobool2.not.i.i = icmp eq i16 %0, 0
+  %cond.i.i = select i1 %tobool2.not.i.i, i16 %cond13.i.i, i16 %0
+  store i16 %cond.i.i, ptr @main_Rdx, align 2
+  %sub.i.i = add i16 %I.04.i.i, -1
+  %tobool.not.i.i = icmp eq i16 %sub.i.i, 0
+  br i1 %tobool.not.i.i, label %exit, label %for.body.i.i, !llvm.loop !1
+
+exit:
+  ret void
+}
+
+!1 = distinct !{!1, !2, !3}
+!2 = !{!"llvm.loop.mustprogress"}
+!3 = !{!"llvm.loop.vectorize.enable", i1 true}
+;.
+; SPARC32: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]], [[META2:![0-9]+]]}
+; SPARC32: [[META1]] = !{!"llvm.loop.mustprogress"}
+; SPARC32: [[META2]] = !{!"llvm.loop.vectorize.enable", i1 true}
+;.
+; SPARC64: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]], [[META2:![0-9]+]]}
+; SPARC64: [[META1]] = !{!"llvm.loop.mustprogress"}
+; SPARC64: [[META2]] = !{!"llvm.loop.vectorize.enable", i1 true}
+;.
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; SPARC32: {{.*}}
+; SPARC64: {{.*}}



More information about the llvm-commits mailing list