[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