[llvm] [AMDGPU][GlobalISel] Look through readfirstlane/readlane in computeKnownBitsForTargetInstr. (PR #224275)
Vikash Gupta via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 17 04:53:43 PDT 2026
https://github.com/vg0204 created https://github.com/llvm/llvm-project/pull/224275
Currently, `computeKnownBitsForTargetInstr` treats `amdgcn.readfirstlane/readlane` as fully unknown, even though both just return the data operand's value from some lane. This patch teaches it to recurse into the data operand (operand 2) and reuse its known bits.
Thus, it unblocks known-bits combines (e.g. redundant_and) that see these intrinsics while still generic.
>From ad853c80fd4e9e981e78c8a44379cad80d7a294f Mon Sep 17 00:00:00 2001
From: vg0204 <Vikash.Gupta at amd.com>
Date: Thu, 17 Sep 2026 17:18:50 +0530
Subject: [PATCH] [AMDGPU][GlobalISel] Look through readfirstlane/readlane in
computeKnownBitsForTargetInstr
Currently, 'computeKnownBitsForTargetInstr' treats 'amdgcn.readfirstlane
/readlane' as fully unknown, even though both just return the data
operand's value from some lane. This patch teaches it to recurse into
the data operand (operand 2) and reuse its known bits.
Thus, it unblocks known-bits combines (e.g. redundant_and) that see
these intrinsics while still generic.
---
llvm/lib/Target/AMDGPU/SIISelLowering.cpp | 7 ++
.../combine-readlane-redundant-and.mir | 50 +++++++++++
.../AMDGPU/GlobalISel/known-bits-readlane.mir | 52 +++++++++++
.../AMDGPU/llvm.amdgcn.readfirstlane.ll | 1 -
llvm/test/CodeGen/AMDGPU/llvm.set.rounding.ll | 32 ++-----
.../CodeGen/AMDGPU/readlane-known-bits.ll | 89 +++++++++++++++++++
6 files changed, 206 insertions(+), 25 deletions(-)
create mode 100644 llvm/test/CodeGen/AMDGPU/GlobalISel/combine-readlane-redundant-and.mir
create mode 100644 llvm/test/CodeGen/AMDGPU/GlobalISel/known-bits-readlane.mir
create mode 100644 llvm/test/CodeGen/AMDGPU/readlane-known-bits.ll
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index b5fa732ea9418..749d80141f799 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -20462,6 +20462,13 @@ void SITargetLowering::computeKnownBitsForTargetInstr(
llvm::countl_zero(getSubtarget()->getAddressableLocalMemorySize()));
break;
}
+ case Intrinsic::amdgcn_readfirstlane:
+ case Intrinsic::amdgcn_readlane: {
+ // Result is the data operand's value from some lane.
+ VT.computeKnownBitsImpl(MI->getOperand(2).getReg(), Known, DemandedElts,
+ Depth + 1);
+ break;
+ }
}
break;
}
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-readlane-redundant-and.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-readlane-redundant-and.mir
new file mode 100644
index 0000000000000..8f03627ce4171
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-readlane-redundant-and.mir
@@ -0,0 +1,50 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=amdgcn -mcpu=gfx900 -run-pass=amdgpu-prelegalizer-combiner %s -o - | FileCheck %s
+
+# readfirstlane/readlane preserve the data operand's known bits, so the
+# trailing G_AND against the same mask is redundant and removed.
+
+---
+name: readfirstlane_redundant_and
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $vgpr0
+ ; CHECK-LABEL: name: readfirstlane_redundant_and
+ ; CHECK: liveins: $vgpr0
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %x:_(s32) = COPY $vgpr0
+ ; CHECK-NEXT: %mask:_(s32) = G_CONSTANT i32 255
+ ; CHECK-NEXT: %m:_(s32) = G_AND %x, %mask
+ ; CHECK-NEXT: %rfl:_(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.amdgcn.readfirstlane), %m(s32)
+ ; CHECK-NEXT: $sgpr0 = COPY %rfl(s32)
+ %x:_(s32) = COPY $vgpr0
+ %mask:_(s32) = G_CONSTANT i32 255
+ %m:_(s32) = G_AND %x, %mask
+ %rfl:_(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.amdgcn.readfirstlane), %m(s32)
+ %and:_(s32) = G_AND %rfl, %mask
+ $sgpr0 = COPY %and
+...
+---
+name: readlane_redundant_and
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $vgpr0, $sgpr0
+ ; CHECK-LABEL: name: readlane_redundant_and
+ ; CHECK: liveins: $vgpr0, $sgpr0
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %x:_(s32) = COPY $vgpr0
+ ; CHECK-NEXT: %lane:_(s32) = COPY $sgpr0
+ ; CHECK-NEXT: %mask:_(s32) = G_CONSTANT i32 65535
+ ; CHECK-NEXT: %m:_(s32) = G_AND %x, %mask
+ ; CHECK-NEXT: %rl:_(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.amdgcn.readlane), %m(s32), %lane(s32)
+ ; CHECK-NEXT: $sgpr1 = COPY %rl(s32)
+ %x:_(s32) = COPY $vgpr0
+ %lane:_(s32) = COPY $sgpr0
+ %mask:_(s32) = G_CONSTANT i32 65535
+ %m:_(s32) = G_AND %x, %mask
+ %rl:_(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.amdgcn.readlane), %m(s32), %lane(s32)
+ %and:_(s32) = G_AND %rl, %mask
+ $sgpr1 = COPY %and
+...
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/known-bits-readlane.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/known-bits-readlane.mir
new file mode 100644
index 0000000000000..9c6de928c9f4a
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/known-bits-readlane.mir
@@ -0,0 +1,52 @@
+# NOTE: Assertions have been autogenerated by utils/update_givaluetracking_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -mtriple=amdgcn -mcpu=gfx900 -passes='print<gisel-value-tracking>' %s -filetype=null 2>&1 | FileCheck %s
+
+# readfirstlane/readlane return the data operand's value, so their known
+# bits are those of the data source.
+
+---
+name: readfirstlane_high_zero
+body: |
+ bb.0:
+ liveins: $vgpr0
+ ; CHECK-LABEL: name: @readfirstlane_high_zero
+ ; CHECK-NEXT: %x:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+ ; CHECK-NEXT: %mask:_ KnownBits:00000000000000000000000011111111 SignBits:24 IsKnownNeverZero:1
+ ; CHECK-NEXT: %m:_ KnownBits:000000000000000000000000???????? SignBits:24 IsKnownNeverZero:0
+ ; CHECK-NEXT: %rfl:_ KnownBits:000000000000000000000000???????? SignBits:24 IsKnownNeverZero:0
+ %x:_(s32) = COPY $vgpr0
+ %mask:_(s32) = G_CONSTANT i32 255
+ %m:_(s32) = G_AND %x, %mask
+ %rfl:_(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.amdgcn.readfirstlane), %m(s32)
+ $sgpr0 = COPY %rfl
+...
+---
+name: readfirstlane_unknown
+body: |
+ bb.0:
+ liveins: $vgpr0
+ ; CHECK-LABEL: name: @readfirstlane_unknown
+ ; CHECK-NEXT: %x:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+ ; CHECK-NEXT: %rfl:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+ %x:_(s32) = COPY $vgpr0
+ %rfl:_(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.amdgcn.readfirstlane), %x(s32)
+ $sgpr0 = COPY %rfl
+...
+---
+name: readlane_high_zero
+body: |
+ bb.0:
+ liveins: $vgpr0, $sgpr0
+ ; CHECK-LABEL: name: @readlane_high_zero
+ ; CHECK-NEXT: %x:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+ ; CHECK-NEXT: %lane:_ KnownBits:???????????????????????????????? SignBits:1 IsKnownNeverZero:0
+ ; CHECK-NEXT: %mask:_ KnownBits:00000000000000001111111111111111 SignBits:16 IsKnownNeverZero:1
+ ; CHECK-NEXT: %m:_ KnownBits:0000000000000000???????????????? SignBits:16 IsKnownNeverZero:0
+ ; CHECK-NEXT: %rl:_ KnownBits:0000000000000000???????????????? SignBits:16 IsKnownNeverZero:0
+ %x:_(s32) = COPY $vgpr0
+ %lane:_(s32) = COPY $sgpr0
+ %mask:_(s32) = G_CONSTANT i32 65535
+ %m:_(s32) = G_AND %x, %mask
+ %rl:_(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.amdgcn.readlane), %m(s32), %lane(s32)
+ $sgpr1 = COPY %rl
+...
diff --git a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.readfirstlane.ll b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.readfirstlane.ll
index fcde16e7bc055..e56b7dd9f7f6e 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.readfirstlane.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.readfirstlane.ll
@@ -70,7 +70,6 @@ define void @test_readfirstlane_i1_select(ptr addrspace(1) %out, i32 %src, i32 %
; CHECK-GISEL-NEXT: v_cmp_lt_u32_e32 vcc, 42, v2
; CHECK-GISEL-NEXT: v_cndmask_b32_e64 v4, 0, 1, vcc
; CHECK-GISEL-NEXT: v_readfirstlane_b32 s4, v4
-; CHECK-GISEL-NEXT: s_and_b32 s4, s4, 1
; CHECK-GISEL-NEXT: s_cmp_lg_u32 s4, 0
; CHECK-GISEL-NEXT: s_cselect_b64 vcc, exec, 0
; CHECK-GISEL-NEXT: v_cndmask_b32_e32 v2, v3, v2, vcc
diff --git a/llvm/test/CodeGen/AMDGPU/llvm.set.rounding.ll b/llvm/test/CodeGen/AMDGPU/llvm.set.rounding.ll
index 887673cc23f10..a7e6c3ecc1f95 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.set.rounding.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.set.rounding.ll
@@ -1702,12 +1702,8 @@ define void @v_set_rounding_select_1_3(i32 %cond) {
; GFX678-GISEL-NEXT: v_cmp_eq_u32_e32 vcc, 0, v0
; GFX678-GISEL-NEXT: v_cndmask_b32_e64 v0, 3, 1, vcc
; GFX678-GISEL-NEXT: v_readfirstlane_b32 s4, v0
-; GFX678-GISEL-NEXT: s_add_i32 s5, s4, -4
-; GFX678-GISEL-NEXT: s_min_u32 s4, s4, s5
-; GFX678-GISEL-NEXT: s_lshl_b32 s6, s4, 2
-; GFX678-GISEL-NEXT: s_mov_b32 s4, 0x1c84a50f
-; GFX678-GISEL-NEXT: s_mov_b32 s5, 0xb73e62d9
-; GFX678-GISEL-NEXT: s_lshr_b64 s[4:5], s[4:5], s6
+; GFX678-GISEL-NEXT: s_lshl_b32 s4, s4, 2
+; GFX678-GISEL-NEXT: s_lshr_b32 s4, 0xa50f, s4
; GFX678-GISEL-NEXT: s_setreg_b32 hwreg(HW_REG_MODE, 0, 4), s4
; GFX678-GISEL-NEXT: s_setpc_b64 s[30:31]
;
@@ -1727,12 +1723,8 @@ define void @v_set_rounding_select_1_3(i32 %cond) {
; GFX9-GISEL-NEXT: v_cmp_eq_u32_e32 vcc, 0, v0
; GFX9-GISEL-NEXT: v_cndmask_b32_e64 v0, 3, 1, vcc
; GFX9-GISEL-NEXT: v_readfirstlane_b32 s4, v0
-; GFX9-GISEL-NEXT: s_add_i32 s5, s4, -4
-; GFX9-GISEL-NEXT: s_min_u32 s4, s4, s5
-; GFX9-GISEL-NEXT: s_lshl_b32 s6, s4, 2
-; GFX9-GISEL-NEXT: s_mov_b32 s4, 0x1c84a50f
-; GFX9-GISEL-NEXT: s_mov_b32 s5, 0xb73e62d9
-; GFX9-GISEL-NEXT: s_lshr_b64 s[4:5], s[4:5], s6
+; GFX9-GISEL-NEXT: s_lshl_b32 s4, s4, 2
+; GFX9-GISEL-NEXT: s_lshr_b32 s4, 0xa50f, s4
; GFX9-GISEL-NEXT: s_setreg_b32 hwreg(HW_REG_MODE, 0, 4), s4
; GFX9-GISEL-NEXT: s_setpc_b64 s[30:31]
;
@@ -1751,12 +1743,8 @@ define void @v_set_rounding_select_1_3(i32 %cond) {
; GFX10-GISEL-NEXT: v_cmp_eq_u32_e32 vcc_lo, 0, v0
; GFX10-GISEL-NEXT: v_cndmask_b32_e64 v0, 3, 1, vcc_lo
; GFX10-GISEL-NEXT: v_readfirstlane_b32 s4, v0
-; GFX10-GISEL-NEXT: s_add_i32 s5, s4, -4
-; GFX10-GISEL-NEXT: s_min_u32 s4, s4, s5
-; GFX10-GISEL-NEXT: s_mov_b32 s5, 0xb73e62d9
-; GFX10-GISEL-NEXT: s_lshl_b32 s6, s4, 2
-; GFX10-GISEL-NEXT: s_mov_b32 s4, 0x1c84a50f
-; GFX10-GISEL-NEXT: s_lshr_b64 s[4:5], s[4:5], s6
+; GFX10-GISEL-NEXT: s_lshl_b32 s4, s4, 2
+; GFX10-GISEL-NEXT: s_lshr_b32 s4, 0xa50f, s4
; GFX10-GISEL-NEXT: s_setreg_b32 hwreg(HW_REG_MODE, 0, 4), s4
; GFX10-GISEL-NEXT: s_setpc_b64 s[30:31]
;
@@ -1775,12 +1763,8 @@ define void @v_set_rounding_select_1_3(i32 %cond) {
; GFX11-GISEL-NEXT: v_cmp_eq_u32_e32 vcc_lo, 0, v0
; GFX11-GISEL-NEXT: v_cndmask_b32_e64 v0, 3, 1, vcc_lo
; GFX11-GISEL-NEXT: v_readfirstlane_b32 s0, v0
-; GFX11-GISEL-NEXT: s_add_i32 s1, s0, -4
-; GFX11-GISEL-NEXT: s_min_u32 s0, s0, s1
-; GFX11-GISEL-NEXT: s_mov_b32 s1, 0xb73e62d9
-; GFX11-GISEL-NEXT: s_lshl_b32 s2, s0, 2
-; GFX11-GISEL-NEXT: s_mov_b32 s0, 0x1c84a50f
-; GFX11-GISEL-NEXT: s_lshr_b64 s[0:1], s[0:1], s2
+; GFX11-GISEL-NEXT: s_lshl_b32 s0, s0, 2
+; GFX11-GISEL-NEXT: s_lshr_b32 s0, 0xa50f, s0
; GFX11-GISEL-NEXT: s_setreg_b32 hwreg(HW_REG_MODE, 0, 4), s0
; GFX11-GISEL-NEXT: s_setpc_b64 s[30:31]
%cmp = icmp eq i32 %cond, 0
diff --git a/llvm/test/CodeGen/AMDGPU/readlane-known-bits.ll b/llvm/test/CodeGen/AMDGPU/readlane-known-bits.ll
new file mode 100644
index 0000000000000..c00e85e61ea6d
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/readlane-known-bits.ll
@@ -0,0 +1,89 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=amdgpu9.00-- < %s | FileCheck -check-prefixes=SDAG,CHECK %s
+; RUN: llc -global-isel -mtriple=amdgpu9.00-- < %s | FileCheck -check-prefixes=GISEL,CHECK %s
+
+; readfirstlane/readlane preserve the data operand's bits. GISel tracks this
+; in computeKnownBitsForTargetInstr, so the trailing `and ..., 0xff` is folded
+; away; SDAG does not track these intrinsics and keeps the `s_and_b32`.
+
+declare i32 @llvm.amdgcn.readfirstlane.i32(i32)
+declare i32 @llvm.amdgcn.readlane.i32(i32, i32)
+declare i32 @llvm.amdgcn.workitem.id.x()
+
+define amdgpu_kernel void @readfirstlane_redundant_and(ptr addrspace(1) %out, ptr addrspace(1) %in) {
+; SDAG-LABEL: readfirstlane_redundant_and:
+; SDAG: ; %bb.0:
+; SDAG-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24
+; SDAG-NEXT: v_lshlrev_b32_e32 v0, 2, v0
+; SDAG-NEXT: v_mov_b32_e32 v1, 0
+; SDAG-NEXT: s_waitcnt lgkmcnt(0)
+; SDAG-NEXT: global_load_dword v0, v0, s[2:3]
+; SDAG-NEXT: s_waitcnt vmcnt(0)
+; SDAG-NEXT: v_readfirstlane_b32 s2, v0
+; SDAG-NEXT: s_and_b32 s2, s2, 0xff
+; SDAG-NEXT: v_mov_b32_e32 v0, s2
+; SDAG-NEXT: global_store_dword v1, v0, s[0:1]
+; SDAG-NEXT: s_endpgm
+;
+; GISEL-LABEL: readfirstlane_redundant_and:
+; GISEL: ; %bb.0:
+; GISEL-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24
+; GISEL-NEXT: v_lshlrev_b32_e32 v0, 2, v0
+; GISEL-NEXT: v_mov_b32_e32 v1, 0
+; GISEL-NEXT: s_waitcnt lgkmcnt(0)
+; GISEL-NEXT: global_load_ubyte v0, v0, s[2:3]
+; GISEL-NEXT: s_waitcnt vmcnt(0)
+; GISEL-NEXT: v_readfirstlane_b32 s2, v0
+; GISEL-NEXT: v_mov_b32_e32 v0, s2
+; GISEL-NEXT: global_store_dword v1, v0, s[0:1]
+; GISEL-NEXT: s_endpgm
+ %tid = call i32 @llvm.amdgcn.workitem.id.x()
+ %gep = getelementptr i32, ptr addrspace(1) %in, i32 %tid
+ %v = load i32, ptr addrspace(1) %gep
+ %m = and i32 %v, 255
+ %rfl = call i32 @llvm.amdgcn.readfirstlane.i32(i32 %m)
+ %and = and i32 %rfl, 255
+ store i32 %and, ptr addrspace(1) %out
+ ret void
+}
+
+define amdgpu_kernel void @readlane_redundant_and(ptr addrspace(1) %out, ptr addrspace(1) %in, i32 %lane) {
+; SDAG-LABEL: readlane_redundant_and:
+; SDAG: ; %bb.0:
+; SDAG-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24
+; SDAG-NEXT: s_load_dword s6, s[4:5], 0x34
+; SDAG-NEXT: v_lshlrev_b32_e32 v0, 2, v0
+; SDAG-NEXT: v_mov_b32_e32 v1, 0
+; SDAG-NEXT: s_waitcnt lgkmcnt(0)
+; SDAG-NEXT: global_load_dword v0, v0, s[2:3]
+; SDAG-NEXT: s_waitcnt vmcnt(0)
+; SDAG-NEXT: v_readlane_b32 s2, v0, s6
+; SDAG-NEXT: s_and_b32 s2, s2, 0xff
+; SDAG-NEXT: v_mov_b32_e32 v0, s2
+; SDAG-NEXT: global_store_dword v1, v0, s[0:1]
+; SDAG-NEXT: s_endpgm
+;
+; GISEL-LABEL: readlane_redundant_and:
+; GISEL: ; %bb.0:
+; GISEL-NEXT: s_load_dwordx4 s[0:3], s[4:5], 0x24
+; GISEL-NEXT: s_load_dword s6, s[4:5], 0x34
+; GISEL-NEXT: v_lshlrev_b32_e32 v0, 2, v0
+; GISEL-NEXT: v_mov_b32_e32 v1, 0
+; GISEL-NEXT: s_waitcnt lgkmcnt(0)
+; GISEL-NEXT: global_load_ubyte v0, v0, s[2:3]
+; GISEL-NEXT: s_waitcnt vmcnt(0)
+; GISEL-NEXT: v_readlane_b32 s2, v0, s6
+; GISEL-NEXT: v_mov_b32_e32 v0, s2
+; GISEL-NEXT: global_store_dword v1, v0, s[0:1]
+; GISEL-NEXT: s_endpgm
+ %tid = call i32 @llvm.amdgcn.workitem.id.x()
+ %gep = getelementptr i32, ptr addrspace(1) %in, i32 %tid
+ %v = load i32, ptr addrspace(1) %gep
+ %m = and i32 %v, 255
+ %rl = call i32 @llvm.amdgcn.readlane.i32(i32 %m, i32 %lane)
+ %and = and i32 %rl, 255
+ store i32 %and, ptr addrspace(1) %out
+ ret void
+}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK: {{.*}}
More information about the llvm-commits
mailing list