[llvm] 65d651b - [AMDGPU] Look through readfirstlane/readlane in computeKnownBitsForTargetInstr. (#224275)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 24 02:03:41 PDT 2026
Author: Vikash Gupta
Date: 2026-09-24T14:33:34+05:30
New Revision: 65d651bc8fd6d83ae4989d93dc89cb287231afd2
URL: https://github.com/llvm/llvm-project/commit/65d651bc8fd6d83ae4989d93dc89cb287231afd2
DIFF: https://github.com/llvm/llvm-project/commit/65d651bc8fd6d83ae4989d93dc89cb287231afd2.diff
LOG: [AMDGPU] Look through readfirstlane/readlane in computeKnownBitsForTargetInstr. (#224275)
Currently, `computeKnownBitsForTargetInstr` in GISel and
`computeKnownBitsForTargetNode` in SDAG 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.
Added:
llvm/test/CodeGen/AMDGPU/GlobalISel/combine-readlane-redundant-and.mir
llvm/test/CodeGen/AMDGPU/GlobalISel/known-bits-readlane.mir
llvm/test/CodeGen/AMDGPU/readlane-known-bits.ll
Modified:
llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
llvm/lib/Target/AMDGPU/SIISelLowering.cpp
llvm/test/CodeGen/AMDGPU/llvm.amdgcn.readfirstlane.ll
llvm/test/CodeGen/AMDGPU/llvm.set.rounding.ll
Removed:
################################################################################
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index aa5cfe38b50fc..40e87a701c5f3 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -6222,6 +6222,11 @@ void AMDGPUTargetLowering::computeKnownBitsForTargetNode(
Known.Zero.setHighBits(llvm::countl_zero(MaxValue));
break;
}
+ case Intrinsic::amdgcn_readfirstlane:
+ case Intrinsic::amdgcn_readlane:
+ // Result is the data operand's value from some lane.
+ Known = DAG.computeKnownBits(Op.getOperand(1), DemandedElts, Depth + 1);
+ break;
default:
break;
}
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 9b93d48235126..e700adfaa14a3 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -20482,6 +20482,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..c0222d945ae03
--- /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=amdgpu9.00 -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..607e8a29d39fa
--- /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=amdgpu9.00 -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..54d11bc1e9f44
--- /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 6
+; RUN: llc -global-isel=0 -mtriple=amdgpu9.00 < %s | FileCheck -check-prefixes=SDAG,CHECK %s
+; RUN: llc -global-isel=1 -mtriple=amdgpu9.00 < %s | FileCheck -check-prefixes=GISEL,CHECK %s
+
+; readfirstlane/readlane preserve the data operand's bits. Both SDAG's
+; computeKnownBitsForTargetNode and GISel's computeKnownBitsForTargetInstr look
+; through these intrinsics, so the trailing `and ..., 0xff` is folded away.
+
+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_and_b32_e32 v0, 0xff, v0
+; SDAG-NEXT: v_readfirstlane_b32 s2, v0
+; 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_and_b32_e32 v0, 0xff, v0
+; SDAG-NEXT: v_readlane_b32 s2, v0, s6
+; 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