[llvm] [AMDGPU][GlobalISel] Fix BITOP3 selecting B16 opcode for 32-bit vector types (PR #217048)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 18 10:54:28 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/217048

>From 44b1f03a386514c3cb930f36fdc41bf742710a36 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 18 Aug 2026 17:13:25 +0200
Subject: [PATCH 1/3] [AMDGPU][GlobalISel] Fix BITOP3 selecting B16 opcode for
 32-bit vector types

Key the opcode choice off bit width instead of an exact i32 match, since `<2 x i16>` is also 32 bits wide.
---
 .../AMDGPU/AMDGPUInstructionSelector.cpp      |  5 +-
 .../AMDGPU/GlobalISel/inst-select-bitop3.mir  | 50 +++++++++++++++++++
 llvm/test/CodeGen/AMDGPU/bitop3.ll            | 47 +++++++++++++++++
 3 files changed, 101 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp b/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
index 3f0f3e115d906..529a3dd706b26 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUInstructionSelector.cpp
@@ -4462,7 +4462,10 @@ bool AMDGPUInstructionSelector::selectBITOP3(MachineInstr &MI) const {
   if (NumOpcodes < 2 || Src.empty())
     return false;
 
-  const bool IsB32 = MRI->getType(DstReg) == LLT::scalar(32);
+  unsigned Size = MRI->getType(DstReg).getSizeInBits();
+  if (Size != 16 && Size != 32)
+    return false;
+  const bool IsB32 = Size == 32;
   if (NumOpcodes == 2 && IsB32) {
     // Avoid using BITOP3 for OR3, XOR3, AND_OR. This is not faster but makes
     // asm more readable. This cannot be modeled with AddedComplexity because
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir
new file mode 100644
index 0000000000000..730f14a71f774
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir
@@ -0,0 +1,50 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx950 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 -mattr=-real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 -mattr=+real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+
+# A <2 x i16> logic tree is 32 bits wide and must select the B32 opcode:
+# V_BITOP3_B16 only computes 16 bits.
+
+---
+name: bitop3_v2i16
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0
+    ; GCN-LABEL: name: bitop3_v2i16
+    ; GCN: liveins: $vgpr0
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: [[COPY:%[0-9]+]]:vgpr_32 = COPY $vgpr0
+    ; GCN-NEXT: [[V_OR_B32_e64_:%[0-9]+]]:vgpr_32 = V_OR_B32_e64 [[COPY]], [[COPY]], implicit $exec
+    ; GCN-NEXT: [[V_BITOP3_B32_e64_:%[0-9]+]]:vgpr_32 = V_BITOP3_B32_e64 [[COPY]], [[V_OR_B32_e64_]], [[COPY]], 240, implicit $exec
+    ; GCN-NEXT: $vgpr0 = COPY [[V_BITOP3_B32_e64_]]
+    %0:vgpr(<2 x i16>) = COPY $vgpr0
+    %1:vgpr(<2 x i16>) = G_OR %0, %0
+    %2:vgpr(<2 x i16>) = G_OR %0, %1
+    %3:vgpr(<2 x i16>) = G_OR %2, %2
+    $vgpr0 = COPY %3(<2 x i16>)
+...
+---
+name: bitop3_i32
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0
+    ; GCN-LABEL: name: bitop3_i32
+    ; GCN: liveins: $vgpr0
+    ; GCN-NEXT: {{  $}}
+    ; GCN-NEXT: [[COPY:%[0-9]+]]:vgpr_32 = COPY $vgpr0
+    ; GCN-NEXT: [[V_OR_B32_e64_:%[0-9]+]]:vgpr_32 = V_OR_B32_e64 [[COPY]], [[COPY]], implicit $exec
+    ; GCN-NEXT: [[V_BITOP3_B32_e64_:%[0-9]+]]:vgpr_32 = V_BITOP3_B32_e64 [[COPY]], [[V_OR_B32_e64_]], [[COPY]], 240, implicit $exec
+    ; GCN-NEXT: $vgpr0 = COPY [[V_BITOP3_B32_e64_]]
+    %0:vgpr(i32) = COPY $vgpr0
+    %1:vgpr(i32) = G_OR %0, %0
+    %2:vgpr(i32) = G_OR %0, %1
+    %3:vgpr(i32) = G_OR %2, %2
+    $vgpr0 = COPY %3(i32)
+...
diff --git a/llvm/test/CodeGen/AMDGPU/bitop3.ll b/llvm/test/CodeGen/AMDGPU/bitop3.ll
index c64ae5ec1a31c..479c89c38e605 100644
--- a/llvm/test/CodeGen/AMDGPU/bitop3.ll
+++ b/llvm/test/CodeGen/AMDGPU/bitop3.ll
@@ -885,6 +885,53 @@ define amdgpu_ps half @test_and_or_b16(i16 %a, i16 %b, i16 %c) {
   %ret_cast = bitcast i16 %or1 to half
   ret half %ret_cast
 }
+
+; ========= Packed 16 bit tests =========
+
+; A <2 x i16> tree is 32 bits wide and must use the b32 opcode.
+
+define amdgpu_ps float @test_and_or_xor_v2i16(<2 x i16> %a, <2 x i16> %b, <2 x i16> %c) {
+; GFX950-SDAG-LABEL: test_and_or_xor_v2i16:
+; GFX950-SDAG:       ; %bb.0:
+; GFX950-SDAG-NEXT:    v_bitop3_b32 v0, v0, v2, v1 bitop3:0x5c
+; GFX950-SDAG-NEXT:    v_and_or_b32 v0, v0, v1, v2
+; GFX950-SDAG-NEXT:    ; return to shader part epilog
+;
+; GFX950-GISEL-LABEL: test_and_or_xor_v2i16:
+; GFX950-GISEL:       ; %bb.0:
+; GFX950-GISEL-NEXT:    v_bitop3_b32 v0, v0, v1, v2 bitop3:8
+; GFX950-GISEL-NEXT:    v_or_b32_e32 v0, v0, v2
+; GFX950-GISEL-NEXT:    ; return to shader part epilog
+;
+; GFX1250-SDAG-LABEL: test_and_or_xor_v2i16:
+; GFX1250-SDAG:       ; %bb.0:
+; GFX1250-SDAG-NEXT:    s_mov_b64 s[64:65], 0
+; GFX1250-SDAG-NEXT:    v_nop
+; GFX1250-SDAG-NEXT:    global_prefetch_b8 v0, s[64:65] scope:SCOPE_SE
+; GFX1250-SDAG-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; GFX1250-SDAG-NEXT:    v_bitop3_b32 v0, v0, v2, v1 bitop3:0x5c
+; GFX1250-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-SDAG-NEXT:    v_and_or_b32 v0, v0, v1, v2
+; GFX1250-SDAG-NEXT:    ; return to shader part epilog
+;
+; GFX1250-GISEL-LABEL: test_and_or_xor_v2i16:
+; GFX1250-GISEL:       ; %bb.0:
+; GFX1250-GISEL-NEXT:    s_mov_b64 s[64:65], 0
+; GFX1250-GISEL-NEXT:    v_nop
+; GFX1250-GISEL-NEXT:    global_prefetch_b8 v0, s[64:65] scope:SCOPE_SE
+; GFX1250-GISEL-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; GFX1250-GISEL-NEXT:    v_bitop3_b32 v0, v0, v1, v2 bitop3:8
+; GFX1250-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-GISEL-NEXT:    v_or_b32_e32 v0, v0, v2
+; GFX1250-GISEL-NEXT:    ; return to shader part epilog
+  %and1 = and <2 x i16> %a, %b
+  %or1 = or <2 x i16> %a, %c
+  %xor1 = xor <2 x i16> %and1, %or1
+  %and2 = and <2 x i16> %xor1, %b
+  %or2 = or <2 x i16> %and2, %c
+  %ret_cast = bitcast <2 x i16> %or2 to float
+  ret float %ret_cast
+}
 ;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
 ; GCN: {{.*}}
 ; GFX1250-FAKE16: {{.*}}

>From 3139d35a7d0c45a804636b3a82d4e94633b20dcc Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 18 Aug 2026 17:34:23 +0200
Subject: [PATCH 2/3] triples

---
 llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir
index 730f14a71f774..2dffd71ee13f2 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir
@@ -1,7 +1,7 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx950 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
-# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 -mattr=-real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
-# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 -mattr=+real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+# RUN: llc -mtriple=amdgpu9.50-- -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+# RUN: llc -mtriple=amdgpu12.50-- -mattr=-real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+# RUN: llc -mtriple=amdgpu12.50-- -mattr=+real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
 
 # A <2 x i16> logic tree is 32 bits wide and must select the B32 opcode:
 # V_BITOP3_B16 only computes 16 bits.

>From 1a9f9c97943e7fd3771b44132b67be15c5259833 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 18 Aug 2026 19:54:14 +0200
Subject: [PATCH 3/3] triples 2

---
 llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir
index 2dffd71ee13f2..5e7d7a0be1971 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-bitop3.mir
@@ -1,7 +1,7 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -mtriple=amdgpu9.50-- -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
-# RUN: llc -mtriple=amdgpu12.50-- -mattr=-real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
-# RUN: llc -mtriple=amdgpu12.50-- -mattr=+real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+# RUN: llc -mtriple=amdgpu9.50 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+# RUN: llc -mtriple=amdgpu12.50 -mattr=-real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
+# RUN: llc -mtriple=amdgpu12.50 -mattr=+real-true16 -run-pass=instruction-select %s -o - | FileCheck %s -check-prefix=GCN
 
 # A <2 x i16> logic tree is 32 bits wide and must select the B32 opcode:
 # V_BITOP3_B16 only computes 16 bits.



More information about the llvm-commits mailing list