[llvm] [NVPTX] Add range attributes for cluster rank intrinsics to preserve signed non-negativity (PR #224727)

Daniel Donenfeld via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 10:54:32 PDT 2026


https://github.com/daniel-donenfeld updated https://github.com/llvm/llvm-project/pull/224727

>From 140af05faa24eda3761469e0dca7a81c0d62409d Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Fri, 18 Sep 2026 19:50:52 +0000
Subject: [PATCH 1/3] [NVPTX] Add InstCombine tests for cluster rank ranges

---
 .../InstCombine/NVPTX/intrinsic-range.ll      | 38 +++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/NVPTX/intrinsic-range.ll

diff --git a/llvm/test/Transforms/InstCombine/NVPTX/intrinsic-range.ll b/llvm/test/Transforms/InstCombine/NVPTX/intrinsic-range.ll
new file mode 100644
index 0000000000000..4a086946cca3f
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/NVPTX/intrinsic-range.ll
@@ -0,0 +1,38 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -S -passes=instcombine -mtriple=nvptx64-nvidia-cuda | FileCheck %s
+
+declare i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
+declare i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
+
+define i1 @ctarank_is_nonnegative() {
+; CHECK-LABEL: define i1 @ctarank_is_nonnegative() {
+; CHECK-NEXT:    [[RANK:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[RANK]], -1
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rank = call i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
+  %cmp = icmp sge i32 %rank, 0
+  ret i1 %cmp
+}
+
+define i1 @nctarank_is_positive() {
+; CHECK-LABEL: define i1 @nctarank_is_positive() {
+; CHECK-NEXT:    [[RANK:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
+; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[RANK]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %rank = call i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
+  %cmp = icmp sgt i32 %rank, 0
+  ret i1 %cmp
+}
+
+define i32 @ctarank_sdiv_16() {
+; CHECK-LABEL: define i32 @ctarank_sdiv_16() {
+; CHECK-NEXT:    [[RANK:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
+; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[RANK]], 16
+; CHECK-NEXT:    ret i32 [[DIV]]
+;
+  %rank = call i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
+  %div = sdiv i32 %rank, 16
+  ret i32 %div
+}

>From 784d2ac4e1e8a6a434f7bb427e2299132a6572da Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Fri, 11 Sep 2026 20:18:17 +0000
Subject: [PATCH 2/3] Upstream cluster cta metadata

---
 llvm/include/llvm/IR/IntrinsicsNVVM.td                 |  8 ++++++--
 llvm/test/CodeGen/NVPTX/intr-range.ll                  |  5 +++++
 .../Transforms/InstCombine/NVPTX/intrinsic-range.ll    | 10 +++-------
 3 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/llvm/include/llvm/IR/IntrinsicsNVVM.td b/llvm/include/llvm/IR/IntrinsicsNVVM.td
index 4fb9aef1596e0..2ca9bea8b719b 100644
--- a/llvm/include/llvm/IR/IntrinsicsNVVM.td
+++ b/llvm/include/llvm/IR/IntrinsicsNVVM.td
@@ -2758,8 +2758,12 @@ defm int_nvvm_read_ptx_sreg_nclusterid : PTXReadSRegIntrinsicNB_v4i32<MAX_GRID_N
 defm int_nvvm_read_ptx_sreg_cluster_ctaid : PTXReadSRegIntrinsicNB_v4i32<MAX_GRID_ID_RANGE>;
 defm int_nvvm_read_ptx_sreg_cluster_nctaid : PTXReadSRegIntrinsicNB_v4i32<MAX_GRID_NID_RANGE>;
 
-def int_nvvm_read_ptx_sreg_cluster_ctarank : PTXReadSRegIntrinsicNB_r32;
-def int_nvvm_read_ptx_sreg_cluster_nctarank : PTXReadSRegIntrinsicNB_r32;
+// ctarank is an index within cluster CTA ranks (non-negative).
+// nctarank is the number of CTA ranks in cluster (positive).
+def int_nvvm_read_ptx_sreg_cluster_ctarank
+  : PTXReadSRegIntrinsicNB_r32<[Range<RetIndex, 0, !add(MAX_GRID_SIZE_X, 1)>]>;
+def int_nvvm_read_ptx_sreg_cluster_nctarank
+  : PTXReadSRegIntrinsicNB_r32<[Range<RetIndex, 1, !add(MAX_GRID_SIZE_X, 1)>]>;
 
 //
 // Reserved Shared Memory Intrinsics
diff --git a/llvm/test/CodeGen/NVPTX/intr-range.ll b/llvm/test/CodeGen/NVPTX/intr-range.ll
index 1fb43dc038f25..b82704aefab1a 100644
--- a/llvm/test/CodeGen/NVPTX/intr-range.ll
+++ b/llvm/test/CodeGen/NVPTX/intr-range.ll
@@ -178,6 +178,8 @@ define ptx_kernel i32 @test_cluster_dim() "nvvm.cluster_dim"="4,4,1" {
 ; DEFAULT-DAG: declare noundef range(i32 1, 65536)       i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.y()
 ; DEFAULT-DAG: declare noundef range(i32 1, 65536)       i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.z()
 ; DEFAULT-DAG: declare noundef range(i32 0, 1)           i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.w()
+; DEFAULT-DAG: declare noundef range(i32 0, -2147483648) i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
+; DEFAULT-DAG: declare noundef range(i32 1, -2147483648) i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
 
 declare i32 @llvm.nvvm.read.ptx.sreg.tid.x()
 declare i32 @llvm.nvvm.read.ptx.sreg.tid.y()
@@ -222,6 +224,9 @@ declare i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.y()
 declare i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.z()
 declare i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.w()
 
+declare i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
+declare i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
+
 
 ; The product of all maxntid dims can exceed INT32_MAX.  Make sure we still get
 ; correct ranges on (n)tid.(x|y).
diff --git a/llvm/test/Transforms/InstCombine/NVPTX/intrinsic-range.ll b/llvm/test/Transforms/InstCombine/NVPTX/intrinsic-range.ll
index 4a086946cca3f..f25408cde3692 100644
--- a/llvm/test/Transforms/InstCombine/NVPTX/intrinsic-range.ll
+++ b/llvm/test/Transforms/InstCombine/NVPTX/intrinsic-range.ll
@@ -6,9 +6,7 @@ declare i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
 
 define i1 @ctarank_is_nonnegative() {
 ; CHECK-LABEL: define i1 @ctarank_is_nonnegative() {
-; CHECK-NEXT:    [[RANK:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[RANK]], -1
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %rank = call i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
   %cmp = icmp sge i32 %rank, 0
@@ -17,9 +15,7 @@ define i1 @ctarank_is_nonnegative() {
 
 define i1 @nctarank_is_positive() {
 ; CHECK-LABEL: define i1 @nctarank_is_positive() {
-; CHECK-NEXT:    [[RANK:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
-; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i32 [[RANK]], 0
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %rank = call i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
   %cmp = icmp sgt i32 %rank, 0
@@ -29,7 +25,7 @@ define i1 @nctarank_is_positive() {
 define i32 @ctarank_sdiv_16() {
 ; CHECK-LABEL: define i32 @ctarank_sdiv_16() {
 ; CHECK-NEXT:    [[RANK:%.*]] = call i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
-; CHECK-NEXT:    [[DIV:%.*]] = sdiv i32 [[RANK]], 16
+; CHECK-NEXT:    [[DIV:%.*]] = lshr i32 [[RANK]], 4
 ; CHECK-NEXT:    ret i32 [[DIV]]
 ;
   %rank = call i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()

>From 366520a984a6e293b58aa0ac29cf25a40d46892e Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Mon, 21 Sep 2026 17:54:08 +0000
Subject: [PATCH 3/3] Update maximum to byte size for ctarank

---
 llvm/include/llvm/IR/IntrinsicsNVVM.td | 8 ++++++--
 llvm/test/CodeGen/NVPTX/intr-range.ll  | 4 ++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/IR/IntrinsicsNVVM.td b/llvm/include/llvm/IR/IntrinsicsNVVM.td
index 2ca9bea8b719b..9dbab5783167a 100644
--- a/llvm/include/llvm/IR/IntrinsicsNVVM.td
+++ b/llvm/include/llvm/IR/IntrinsicsNVVM.td
@@ -158,6 +158,8 @@ defvar MAX_GRID_SIZE_X = 0x7fffffff;
 defvar MAX_GRID_SIZE_Y = 0xffff;
 defvar MAX_GRID_SIZE_Z = 0xffff;
 
+defvar MAX_CLUSTER_RANK = 0x100;
+
 defvar MAX_BLOCK_SIZE_X = 1024;
 defvar MAX_BLOCK_SIZE_Y = 1024;
 defvar MAX_BLOCK_SIZE_Z = 64;
@@ -2761,9 +2763,11 @@ defm int_nvvm_read_ptx_sreg_cluster_nctaid : PTXReadSRegIntrinsicNB_v4i32<MAX_GR
 // ctarank is an index within cluster CTA ranks (non-negative).
 // nctarank is the number of CTA ranks in cluster (positive).
 def int_nvvm_read_ptx_sreg_cluster_ctarank
-  : PTXReadSRegIntrinsicNB_r32<[Range<RetIndex, 0, !add(MAX_GRID_SIZE_X, 1)>]>;
+  : PTXReadSRegIntrinsicNB_r32<
+      [Range<RetIndex, 0, MAX_CLUSTER_RANK>]>;
 def int_nvvm_read_ptx_sreg_cluster_nctarank
-  : PTXReadSRegIntrinsicNB_r32<[Range<RetIndex, 1, !add(MAX_GRID_SIZE_X, 1)>]>;
+  : PTXReadSRegIntrinsicNB_r32<
+      [Range<RetIndex, 1, !add(MAX_CLUSTER_RANK, 1)>]>;
 
 //
 // Reserved Shared Memory Intrinsics
diff --git a/llvm/test/CodeGen/NVPTX/intr-range.ll b/llvm/test/CodeGen/NVPTX/intr-range.ll
index b82704aefab1a..741808acbbdaf 100644
--- a/llvm/test/CodeGen/NVPTX/intr-range.ll
+++ b/llvm/test/CodeGen/NVPTX/intr-range.ll
@@ -178,8 +178,8 @@ define ptx_kernel i32 @test_cluster_dim() "nvvm.cluster_dim"="4,4,1" {
 ; DEFAULT-DAG: declare noundef range(i32 1, 65536)       i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.y()
 ; DEFAULT-DAG: declare noundef range(i32 1, 65536)       i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.z()
 ; DEFAULT-DAG: declare noundef range(i32 0, 1)           i32 @llvm.nvvm.read.ptx.sreg.cluster.nctaid.w()
-; DEFAULT-DAG: declare noundef range(i32 0, -2147483648) i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
-; DEFAULT-DAG: declare noundef range(i32 1, -2147483648) i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
+; DEFAULT-DAG: declare noundef range(i32 0, 256) i32 @llvm.nvvm.read.ptx.sreg.cluster.ctarank()
+; DEFAULT-DAG: declare noundef range(i32 1, 257) i32 @llvm.nvvm.read.ptx.sreg.cluster.nctarank()
 
 declare i32 @llvm.nvvm.read.ptx.sreg.tid.x()
 declare i32 @llvm.nvvm.read.ptx.sreg.tid.y()



More information about the llvm-commits mailing list