[llvm] [HLSL][SPIRV] Codegen unbound array as OpTypeRuntimeArray (PR #185551)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 10 18:57:06 PDT 2026
=?utf-8?q?João?= Saffran <joaosaffranllvm at gmail.com>,Joao Saffran
<joaosaffranllvm at gmail.com>,
=?utf-8?q?João?= Saffran <joaosaffranllvm at gmail.com>,Joao Saffran
<joaosaffranllvm at gmail.com>,Joao Saffran <joaosaffranllvm at gmail.com>,
=?utf-8?q?João?= Saffran <joaosaffranllvm at gmail.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/185551 at github.com>
https://github.com/joaosaffran updated https://github.com/llvm/llvm-project/pull/185551
>From 3b597e52fe579240687db1aa72920c454bd54131 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Saffran?= <joaosaffranllvm at gmail.com>
Date: Thu, 5 Mar 2026 14:53:16 -0800
Subject: [PATCH 1/8] add 16 bit case
---
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 21 ++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 7b4c047593a3a..bfaf861747612 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3081,11 +3081,30 @@ bool SPIRVInstructionSelector::selectWaveExclusiveScan(
bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg,
SPIRVTypeInst ResType,
MachineInstr &I) const {
+ Register OpReg = I.getOperand(1).getReg();
+
+ if (GR.getScalarOrVectorBitWidth(ResType) == 16) {
+ SPIRVTypeInst IntType = GR.getOrCreateSPIRVIntegerType(32, I, TII);
+ unsigned N = GR.getScalarOrVectorComponentCount(ResType);
+ if (N > 1)
+ IntType = GR.getOrCreateSPIRVVectorType(ResType, N, I, TII);
+
+ OpReg = MRI->createVirtualRegister(GR.getRegClass(ResType));
+ unsigned ExtendOpcode =
+ sampledTypeIsSignedInteger(GR.getTypeForSPIRVType(ResType))
+ ? SPIRV::OpSConvert
+ : SPIRV::OpUConvert;
+ if (!selectOpWithSrcs(OpReg, IntType, I, {I.getOperand(1).getReg()},
+ ExtendOpcode))
+ return false;
+ ResType = IntType;
+ }
+
MachineBasicBlock &BB = *I.getParent();
BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpBitReverse))
.addDef(ResVReg)
.addUse(GR.getSPIRVTypeID(ResType))
- .addUse(I.getOperand(1).getReg())
+ .addUse(OpReg)
.constrainAllUses(TII, TRI, RBI);
return true;
}
>From edb30c0ad4938969f4f71a48a4374fef622dcbf4 Mon Sep 17 00:00:00 2001
From: Joao Saffran <joaosaffranllvm at gmail.com>
Date: Thu, 5 Mar 2026 15:22:50 -0800
Subject: [PATCH 2/8] fix incorrect function usage & update test reversebits.ll
---
llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 7 +++----
.../test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll | 11 +++++++++--
2 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index bfaf861747612..fd53bab28b4d8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3090,10 +3090,9 @@ bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg,
IntType = GR.getOrCreateSPIRVVectorType(ResType, N, I, TII);
OpReg = MRI->createVirtualRegister(GR.getRegClass(ResType));
- unsigned ExtendOpcode =
- sampledTypeIsSignedInteger(GR.getTypeForSPIRVType(ResType))
- ? SPIRV::OpSConvert
- : SPIRV::OpUConvert;
+ unsigned ExtendOpcode = GR.isScalarOrVectorSigned(ResType)
+ ? SPIRV::OpSConvert
+ : SPIRV::OpUConvert;
if (!selectOpWithSrcs(OpReg, IntType, I, {I.getOperand(1).getReg()},
ExtendOpcode))
return false;
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll
index 6571b2992fab3..77f24c72cffc6 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll
@@ -3,16 +3,23 @@
; CHECK: OpMemoryModel Logical GLSL450
+;CHECK-DAG: %[[#int_16:]] = OpTypeInt 16
+;CHECK-DAG: %[[#int_32:]] = OpTypeInt 32
+
define noundef i32 @reversebits_i32(i32 noundef %a) {
entry:
-; CHECK: %[[#]] = OpBitReverse %[[#]] %[[#]]
+; CHECK: %[[#param:]] = OpFunctionParameter %[[#int_32]]
+; CHECK-NOT: OpUConvert
+; CHECK: %[[#]] = OpBitReverse %[[#int_32]] %[[#param]]
%elt.bitreverse = call i32 @llvm.bitreverse.i32(i32 %a)
ret i32 %elt.bitreverse
}
define noundef i16 @reversebits_i16(i16 noundef %a) {
entry:
-; CHECK: %[[#]] = OpBitReverse %[[#]] %[[#]]
+; CHECK: %[[#param:]] = OpFunctionParameter %[[#int_16]]
+; CHECK: %[[#conversion:]] = OpUConvert %[[#int_32]] %[[#param]]
+; CHECK-NEXT: %[[#]] = OpBitReverse %[[#int_32]] %[[#conversion]]
%elt.bitreverse = call i16 @llvm.bitreverse.i16(i16 %a)
ret i16 %elt.bitreverse
}
>From 3c0f0e494ff1fb110df41733414c6a345a3c04cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Saffran?= <joaosaffranllvm at gmail.com>
Date: Thu, 5 Mar 2026 17:14:27 -0800
Subject: [PATCH 3/8] add composite to shift
---
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 68 ++++++++++++++-----
1 file changed, 52 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index fd53bab28b4d8..7b8dec6106142 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -188,6 +188,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
bool selectBitreverse(Register ResVReg, SPIRVTypeInst ResType,
MachineInstr &I) const;
+ bool selectBitreverse16(Register ResVReg, SPIRVTypeInst ResType,
+ MachineInstr &I) const;
+
bool selectBuildVector(Register ResVReg, SPIRVTypeInst ResType,
MachineInstr &I) const;
bool selectSplatVector(Register ResVReg, SPIRVTypeInst ResType,
@@ -3078,32 +3081,65 @@ bool SPIRVInstructionSelector::selectWaveExclusiveScan(
return true;
}
+bool SPIRVInstructionSelector::selectBitreverse16(Register ResVReg,
+ SPIRVTypeInst ResType,
+ MachineInstr &I) const {
+ SPIRVTypeInst Int32Type = GR.getOrCreateSPIRVIntegerType(32, I, TII);
+
+ unsigned N = GR.getScalarOrVectorComponentCount(ResType);
+ if (N > 1)
+ Int32Type = GR.getOrCreateSPIRVVectorType(Int32Type, N, I, TII);
+
+ Register ExtReg = MRI->createVirtualRegister(GR.getRegClass(Int32Type));
+ unsigned ExtendOpcode = GR.isScalarOrVectorSigned(ResType)
+ ? SPIRV::OpSConvert
+ : SPIRV::OpUConvert;
+ if (!selectOpWithSrcs(ExtReg, Int32Type, I, {I.getOperand(1).getReg()},
+ ExtendOpcode))
+ return false;
+
+ Register BitrevReg = MRI->createVirtualRegister(GR.getRegClass(Int32Type));
+ if (!selectOpWithSrcs(BitrevReg, Int32Type, I, {ExtReg}, SPIRV::OpBitReverse))
+ return false;
+
+ Register ScalarShiftAmount = GR.getOrCreateConstInt(16, I, Int32Type, TII);
+
+ Register ShiftConst;
+ if (N > 1) {
+ ShiftConst = MRI->createVirtualRegister(GR.getRegClass(Int32Type));
+ auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(),
+ TII.get(SPIRV::OpConstantComposite))
+ .addDef(ShiftConst)
+ .addUse(GR.getSPIRVTypeID(Int32Type));
+ for (unsigned It = I.getNumExplicitDefs(); It < I.getNumExplicitOperands();
+ ++It)
+ MIB.addUse(ScalarShiftAmount);
+ MIB.constrainAllUses(TII, TRI, RBI);
+ } else {
+ ShiftConst = ScalarShiftAmount;
+ }
+
+ Register ShiftReg = MRI->createVirtualRegister(GR.getRegClass(Int32Type));
+ if (!selectOpWithSrcs(ShiftReg, Int32Type, I, {BitrevReg, ShiftConst},
+ N > 1 ? SPIRV::OpShiftRightLogicalV
+ : SPIRV::OpShiftRightLogicalS))
+ return false;
+
+ return selectOpWithSrcs(ResVReg, ResType, I, {ShiftReg}, ExtendOpcode);
+}
+
bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg,
SPIRVTypeInst ResType,
MachineInstr &I) const {
- Register OpReg = I.getOperand(1).getReg();
-
if (GR.getScalarOrVectorBitWidth(ResType) == 16) {
- SPIRVTypeInst IntType = GR.getOrCreateSPIRVIntegerType(32, I, TII);
- unsigned N = GR.getScalarOrVectorComponentCount(ResType);
- if (N > 1)
- IntType = GR.getOrCreateSPIRVVectorType(ResType, N, I, TII);
-
- OpReg = MRI->createVirtualRegister(GR.getRegClass(ResType));
- unsigned ExtendOpcode = GR.isScalarOrVectorSigned(ResType)
- ? SPIRV::OpSConvert
- : SPIRV::OpUConvert;
- if (!selectOpWithSrcs(OpReg, IntType, I, {I.getOperand(1).getReg()},
- ExtendOpcode))
- return false;
- ResType = IntType;
+ return selectBitreverse16(ResVReg, ResType, I);
}
MachineBasicBlock &BB = *I.getParent();
BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpBitReverse))
.addDef(ResVReg)
.addUse(GR.getSPIRVTypeID(ResType))
- .addUse(OpReg)
+ .addUse(I.getOperand(1).getReg())
.constrainAllUses(TII, TRI, RBI);
return true;
}
>From 1f4c76b49e9a528c02790d84abd9aa9381f2a073 Mon Sep 17 00:00:00 2001
From: Joao Saffran <joaosaffranllvm at gmail.com>
Date: Thu, 5 Mar 2026 17:36:31 -0800
Subject: [PATCH 4/8] fix composite logic
---
llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 7b8dec6106142..41bbb10da8e74 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -3085,6 +3085,7 @@ bool SPIRVInstructionSelector::selectBitreverse16(Register ResVReg,
SPIRVTypeInst ResType,
MachineInstr &I) const {
SPIRVTypeInst Int32Type = GR.getOrCreateSPIRVIntegerType(32, I, TII);
+ Register ScalarShiftAmount = GR.getOrCreateConstInt(16, I, Int32Type, TII);
unsigned N = GR.getScalarOrVectorComponentCount(ResType);
if (N > 1)
@@ -3102,8 +3103,6 @@ bool SPIRVInstructionSelector::selectBitreverse16(Register ResVReg,
if (!selectOpWithSrcs(BitrevReg, Int32Type, I, {ExtReg}, SPIRV::OpBitReverse))
return false;
- Register ScalarShiftAmount = GR.getOrCreateConstInt(16, I, Int32Type, TII);
-
Register ShiftConst;
if (N > 1) {
ShiftConst = MRI->createVirtualRegister(GR.getRegClass(Int32Type));
@@ -3111,8 +3110,7 @@ bool SPIRVInstructionSelector::selectBitreverse16(Register ResVReg,
TII.get(SPIRV::OpConstantComposite))
.addDef(ShiftConst)
.addUse(GR.getSPIRVTypeID(Int32Type));
- for (unsigned It = I.getNumExplicitDefs(); It < I.getNumExplicitOperands();
- ++It)
+ for (unsigned It = 0; It < N; ++It)
MIB.addUse(ScalarShiftAmount);
MIB.constrainAllUses(TII, TRI, RBI);
} else {
>From b63d635555dc90b2e6d8e5eac42c2806bbff07c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Saffran?= <joaosaffranllvm at gmail.com>
Date: Thu, 5 Mar 2026 18:21:45 -0800
Subject: [PATCH 5/8] update test
---
.../SPIRV/hlsl-intrinsics/reversebits.ll | 23 ++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll
index 77f24c72cffc6..c1c7210b90f6a 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/reversebits.ll
@@ -5,12 +5,18 @@
;CHECK-DAG: %[[#int_16:]] = OpTypeInt 16
;CHECK-DAG: %[[#int_32:]] = OpTypeInt 32
+;CHECK-DAG: %[[#vec_int_32:]] = OpTypeVector %[[#int_32]] 2
+;CHECK-DAG: %[[#vec_int_16:]] = OpTypeVector %[[#int_16]] 2
+
+;CHECK-DAG: %[[#const_16:]] = OpConstant %[[#int_32]] 16
+;CHECK-DAG: %[[#composite:]] = OpConstantComposite %[[#vec_int_32]] %[[#const_16]] %[[#const_16]]
define noundef i32 @reversebits_i32(i32 noundef %a) {
entry:
; CHECK: %[[#param:]] = OpFunctionParameter %[[#int_32]]
; CHECK-NOT: OpUConvert
; CHECK: %[[#]] = OpBitReverse %[[#int_32]] %[[#param]]
+; CHECK-NOT: OpShiftRightLogical
%elt.bitreverse = call i32 @llvm.bitreverse.i32(i32 %a)
ret i32 %elt.bitreverse
}
@@ -19,10 +25,25 @@ define noundef i16 @reversebits_i16(i16 noundef %a) {
entry:
; CHECK: %[[#param:]] = OpFunctionParameter %[[#int_16]]
; CHECK: %[[#conversion:]] = OpUConvert %[[#int_32]] %[[#param]]
-; CHECK-NEXT: %[[#]] = OpBitReverse %[[#int_32]] %[[#conversion]]
+; CHECK-NEXT: %[[#bitrev:]] = OpBitReverse %[[#int_32]] %[[#conversion]]
+; CHECK-NEXT: %[[#shift:]] = OpShiftRightLogical %[[#int_32]] %[[#bitrev]] %[[#const_16]]
+; CHECK-NEXT: %[[#]] = OpUConvert %[[#int_16]] %[[#shift]]
%elt.bitreverse = call i16 @llvm.bitreverse.i16(i16 %a)
ret i16 %elt.bitreverse
}
+define noundef <2 x i16> @reversebits_veci16(<2 x i16> noundef %a) {
+entry:
+; CHECK: %[[#param:]] = OpFunctionParameter %[[#vec_int_16]]
+; CHECK: %[[#conversion:]] = OpUConvert %[[#vec_int_32]] %[[#param]]
+; CHECK-NEXT: %[[#bitrev:]] = OpBitReverse %[[#vec_int_32]] %[[#conversion]]
+; CHECK-NEXT: %[[#shift:]] = OpShiftRightLogical %[[#vec_int_32]] %[[#bitrev]] %[[#composite]]
+; CHECK-NEXT: %[[#]] = OpUConvert %[[#vec_int_16]] %[[#shift]]
+ %elt.bitreverse = call <2 x i16> @llvm.bitreverse.v2i16(<2 x i16> %a)
+ ret <2 x i16> %elt.bitreverse
+}
+
+
declare i16 @llvm.bitreverse.i16(i16)
declare i32 @llvm.bitreverse.i32(i32)
+declare <2 x i16> @llvm.bitreverse.v2i16(<2 x i16>)
>From d24d74265243710dbfa8a0f30aab8a326145744e Mon Sep 17 00:00:00 2001
From: Joao Saffran <joaosaffranllvm at gmail.com>
Date: Mon, 9 Mar 2026 17:18:40 -0700
Subject: [PATCH 6/8] modify codegen to account for unbound arrays
---
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 9a85634c82626..e2a52a2a17665 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -910,7 +910,18 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
SPIRVTypeInst ArrayType = nullptr;
const SPIRVSubtarget &ST =
cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
- if (NumElems != 0) {
+
+ // Unbounded arrays (e.g. RWBuffer Buf[]) are represented in LLVM IR
+ // with UINT32_MAX as the element count. Lower these to OpTypeRuntimeArray
+ // instead of OpTypeArray.
+ if (NumElems == std::numeric_limits<uint32_t>::max()) {
+ ArrayType = createConstOrTypeAtFunctionEntry(
+ MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray)
+ .addDef(createTypeVReg(MIRBuilder))
+ .addUse(getSPIRVTypeID(ElemType));
+ });
+ } else if (NumElems != 0) {
Register NumElementsVReg =
buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
ArrayType = createConstOrTypeAtFunctionEntry(
>From af0cc4133f5263d8813d62a903cc8275777dcda2 Mon Sep 17 00:00:00 2001
From: Joao Saffran <joaosaffranllvm at gmail.com>
Date: Mon, 9 Mar 2026 17:18:40 -0700
Subject: [PATCH 7/8] modify codegen to account for unbound arrays
---
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 9a85634c82626..e2a52a2a17665 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -910,7 +910,18 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
SPIRVTypeInst ArrayType = nullptr;
const SPIRVSubtarget &ST =
cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
- if (NumElems != 0) {
+
+ // Unbounded arrays (e.g. RWBuffer Buf[]) are represented in LLVM IR
+ // with UINT32_MAX as the element count. Lower these to OpTypeRuntimeArray
+ // instead of OpTypeArray.
+ if (NumElems == std::numeric_limits<uint32_t>::max()) {
+ ArrayType = createConstOrTypeAtFunctionEntry(
+ MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray)
+ .addDef(createTypeVReg(MIRBuilder))
+ .addUse(getSPIRVTypeID(ElemType));
+ });
+ } else if (NumElems != 0) {
Register NumElementsVReg =
buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR);
ArrayType = createConstOrTypeAtFunctionEntry(
>From 39d6b19500a130c59f13e83c56d29f6c0e546cbd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Saffran?= <joaosaffranllvm at gmail.com>
Date: Mon, 9 Mar 2026 18:21:30 -0700
Subject: [PATCH 8/8] add missing capability
---
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index e2a52a2a17665..363c7ba6911f4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -915,6 +915,8 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems,
// with UINT32_MAX as the element count. Lower these to OpTypeRuntimeArray
// instead of OpTypeArray.
if (NumElems == std::numeric_limits<uint32_t>::max()) {
+ MIRBuilder.buildInstr(SPIRV::OpCapability)
+ .addImm(SPIRV::Capability::RuntimeDescriptorArrayEXT);
ArrayType = createConstOrTypeAtFunctionEntry(
MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray)
More information about the llvm-commits
mailing list