[llvm] [NVPTX] Widen sub-byte and non-power-of-two integers to a legal PTX type (PR #226113)
Samarth Narang via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 24 03:51:46 PDT 2026
https://github.com/snarang181 created https://github.com/llvm/llvm-project/pull/226113
`getPTXFundamentalTypeStr()` prints integers as `.u<N>` for their exact bit width, but PTX has no sub-byte or non-power-of-two integer types. An `i4` kernel parameter becomes `.param .u4`, which ptxas rejects (`Parsing error near '.u4': syntax error`); every non-power-of-two width up to 64 is affected, as are globals printed through the same path. The output is also self-inconsistent: ISel already loads such a parameter with `max(8, PowerOf2Ceil(N))` bits, so the same kernel declares `.param .u4` and reads it with `ld.param.b8`. Round the printed type up to that width. `i1` keeps its existing `pred`/`.u8` handling; the `i65`–`i127` `llvm_unreachable` is a separate pre-existing issue and is left alone.
Tested with `LLVM_PTXAS_EXECUTABLE` set (ptxas 13.4): NVPTX suite 614/614, and the new test's `%ptxas-verify` fails before this change.
Fixes #225261
>From dedde00883f9049000b97133232355cf759ddec2 Mon Sep 17 00:00:00 2001
From: Samarth Narang <sn2910 at nyu.edu>
Date: Thu, 24 Sep 2026 00:22:31 +0000
Subject: [PATCH] [NVPTX] Widen sub-byte and non-power-of-two integers to a
legal PTX type
getPTXFundamentalTypeStr() printed an integer as ".u<N>" for its exact bit
width, but PTX has no sub-byte or non-power-of-two integer types. An i4 kernel
parameter was emitted as ".param .u4", which ptxas rejects with a parse error,
and every non-power-of-two width up to 64 was affected (i2, i9, i12, i24, i48,
...). Global variables print through the same function and were equally
unparseable.
The emitted PTX was already internally inconsistent: ISel selects the load for
such a parameter with max(8, PowerOf2Ceil(N)) bits, so the i4 kernel above
declared ".param .u4" and then read it back with "ld.param.b8". The alignment
printed next to a global was likewise derived from the widened alloc size.
Round the printed type up to the same width, so the declaration agrees with
both. i1 keeps its existing "pred" spelling and its .u8 special cases.
Fixes #225261
---
llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp | 5 +-
.../NVPTX/non-power-of-two-int-param.ll | 46 +++++++++++++++++++
2 files changed, 49 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/NVPTX/non-power-of-two-int-param.ll
diff --git a/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp b/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
index a7d7b55c81fff..292e0b7537a2a 100644
--- a/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXAsmPrinter.cpp
@@ -91,6 +91,7 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
#include "llvm/Support/NativeFormatting.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
@@ -1799,8 +1800,8 @@ NVPTXAsmPrinter::getPTXFundamentalTypeStr(Type *Ty, bool useB4PTR) const {
if (NumBits == 1)
return "pred";
if (NumBits <= 64) {
- std::string name = "u";
- return name + utostr(NumBits);
+ // PTX has no sub-byte or non-power-of-two integer types.
+ return "u" + utostr(std::max<unsigned>(8, PowerOf2Ceil(NumBits)));
}
llvm_unreachable("Integer too large");
break;
diff --git a/llvm/test/CodeGen/NVPTX/non-power-of-two-int-param.ll b/llvm/test/CodeGen/NVPTX/non-power-of-two-int-param.ll
new file mode 100644
index 0000000000000..dff02742d7cb1
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/non-power-of-two-int-param.ll
@@ -0,0 +1,46 @@
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_20 | FileCheck %s
+; RUN: %if ptxas %{ llc < %s -mtriple=nvptx64 -mcpu=sm_20 | %ptxas-verify %}
+
+; Sub-byte and non-power-of-two integers must be widened to a legal PTX type.
+
+; CHECK: .visible .global .align 1 .u8 g4;
+; CHECK: .visible .global .align 4 .u32 g24;
+ at g4 = addrspace(1) global i4 0
+ at g24 = addrspace(1) global i24 0
+
+; CHECK: .entry sub_byte
+; CHECK: .param .u8 sub_byte_param_0
+; CHECK: .param .u64 .ptr .align 1 sub_byte_param_1
+; CHECK-DAG: ld.param.b8 {{%r[0-9]+}}, [sub_byte_param_0];
+define ptx_kernel void @sub_byte(i4 %p, ptr %out) {
+ %val = zext i4 %p to i32
+ store i32 %val, ptr %out
+ ret void
+}
+
+; CHECK: .entry not_byte_multiple
+; CHECK: .param .u16 not_byte_multiple_param_0
+; CHECK-DAG: ld.param.b16 {{%r[0-9]+}}, [not_byte_multiple_param_0];
+define ptx_kernel void @not_byte_multiple(i12 %p, ptr %out) {
+ %val = zext i12 %p to i32
+ store i32 %val, ptr %out
+ ret void
+}
+
+; CHECK: .entry not_power_of_two
+; CHECK: .param .u32 not_power_of_two_param_0
+; CHECK-DAG: ld.param.b32 {{%r[0-9]+}}, [not_power_of_two_param_0];
+define ptx_kernel void @not_power_of_two(i24 %p, ptr %out) {
+ %val = zext i24 %p to i32
+ store i32 %val, ptr %out
+ ret void
+}
+
+; CHECK: .entry wide
+; CHECK: .param .u64 wide_param_0
+; CHECK-DAG: ld.param.b64 {{%rd[0-9]+}}, [wide_param_0];
+define ptx_kernel void @wide(i48 %p, ptr %out) {
+ %val = zext i48 %p to i64
+ store i64 %val, ptr %out
+ ret void
+}
More information about the llvm-commits
mailing list