[llvm] [SelectionDAG][AMDGPU] Fold mul24 with an AND operand whose low bits are zero (PR #224537)

Harrison Hao via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 20 19:42:00 PDT 2026


https://github.com/harrisonGPU updated https://github.com/llvm/llvm-project/pull/224537

>From be881abbaff8b461d3cd51115aa4902b66617a9a Mon Sep 17 00:00:00 2001
From: Harrison Hao <tsworld1314 at gmail.com>
Date: Fri, 18 Sep 2026 15:02:44 +0800
Subject: [PATCH 1/2] [AMDGPU] Fold 24 bit multiply with zero low bits

Fold `MUL_I24` and `MUL_U24` to zero when either operand has known zero
low 24 bits.

For example:
```
  llvm.amdgcn.mul.i24(x, 0x01000000) -> 0
```
---
 llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp |  5 ++
 .../CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll     | 54 ++++++++-----------
 2 files changed, 26 insertions(+), 33 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index aa5cfe38b50fc..362b37810e121 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -4087,6 +4087,11 @@ static SDValue simplifyMul24(SDNode *Node24,
 
   APInt Demanded = APInt::getLowBitsSet(LHS.getValueSizeInBits(), 24);
 
+  // The 24 bit multiplies only read bits [23:0] of each source.
+  if (DAG.MaskedValueIsZero(LHS, Demanded) ||
+      DAG.MaskedValueIsZero(RHS, Demanded))
+    return DAG.getConstant(0, SDLoc(Node24), Node24->getValueType(0));
+
   // First try to simplify using SimplifyMultipleUseDemandedBits which allows
   // the operands to have other uses, but will only perform simplifications that
   // involve bypassing some nodes for this user.
diff --git a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll
index 2b8d8702c59f1..b5c1eb267b6fc 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll
@@ -32,22 +32,20 @@ define amdgpu_kernel void @test_mul_i24(ptr addrspace(1) %out, i32 %src1, i32 %s
 define amdgpu_kernel void @test_mul_i24_zero(ptr addrspace(1) %out, i32 %src) #1 {
 ; GCN-LABEL: test_mul_i24_zero:
 ; GCN:       ; %bb.0:
-; GCN-NEXT:    s_load_dword s6, s[4:5], 0xb
 ; GCN-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x9
 ; GCN-NEXT:    s_mov_b32 s3, 0xf000
 ; GCN-NEXT:    s_mov_b32 s2, -1
+; GCN-NEXT:    v_mov_b32_e32 v0, 0
 ; GCN-NEXT:    s_waitcnt lgkmcnt(0)
-; GCN-NEXT:    v_mul_i32_i24_e64 v0, s6, 0
 ; GCN-NEXT:    buffer_store_dword v0, off, s[0:3], 0
 ; GCN-NEXT:    s_endpgm
 ;
 ; GFX12-LABEL: test_mul_i24_zero:
 ; GFX12:       ; %bb.0:
-; GFX12-NEXT:    s_load_b96 s[0:2], s[4:5], 0x24
+; GFX12-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
 ; GFX12-NEXT:    v_mov_b32_e32 v0, 0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_mul_i32_i24_e64 v1, s2, 0
-; GFX12-NEXT:    global_store_b32 v0, v1, s[0:1]
+; GFX12-NEXT:    global_store_b32 v0, v0, s[0:1]
 ; GFX12-NEXT:    s_endpgm
   %val = call i32 @llvm.amdgcn.mul.i24(i32 %src, i32 0) #0
   store i32 %val, ptr addrspace(1) %out
@@ -57,22 +55,20 @@ define amdgpu_kernel void @test_mul_i24_zero(ptr addrspace(1) %out, i32 %src) #1
 define amdgpu_kernel void @test_mul_i24_zero_lhs(ptr addrspace(1) %out, i32 %src) #1 {
 ; GCN-LABEL: test_mul_i24_zero_lhs:
 ; GCN:       ; %bb.0:
-; GCN-NEXT:    s_load_dword s6, s[4:5], 0xb
 ; GCN-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x9
 ; GCN-NEXT:    s_mov_b32 s3, 0xf000
 ; GCN-NEXT:    s_mov_b32 s2, -1
+; GCN-NEXT:    v_mov_b32_e32 v0, 0
 ; GCN-NEXT:    s_waitcnt lgkmcnt(0)
-; GCN-NEXT:    v_mul_i32_i24_e64 v0, 0, s6
 ; GCN-NEXT:    buffer_store_dword v0, off, s[0:3], 0
 ; GCN-NEXT:    s_endpgm
 ;
 ; GFX12-LABEL: test_mul_i24_zero_lhs:
 ; GFX12:       ; %bb.0:
-; GFX12-NEXT:    s_load_b96 s[0:2], s[4:5], 0x24
+; GFX12-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
 ; GFX12-NEXT:    v_mov_b32_e32 v0, 0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_mul_i32_i24_e64 v1, 0, s2
-; GFX12-NEXT:    global_store_b32 v0, v1, s[0:1]
+; GFX12-NEXT:    global_store_b32 v0, v0, s[0:1]
 ; GFX12-NEXT:    s_endpgm
   %val = call i32 @llvm.amdgcn.mul.i24(i32 0, i32 %src) #0
   store i32 %val, ptr addrspace(1) %out
@@ -82,23 +78,20 @@ define amdgpu_kernel void @test_mul_i24_zero_lhs(ptr addrspace(1) %out, i32 %src
 define amdgpu_kernel void @test_mul_i24_zero_low24(ptr addrspace(1) %out, i32 %src) #1 {
 ; GCN-LABEL: test_mul_i24_zero_low24:
 ; GCN:       ; %bb.0:
-; GCN-NEXT:    s_load_dword s6, s[4:5], 0xb
 ; GCN-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x9
-; GCN-NEXT:    v_mov_b32_e32 v0, 0x1000000
 ; GCN-NEXT:    s_mov_b32 s3, 0xf000
 ; GCN-NEXT:    s_mov_b32 s2, -1
+; GCN-NEXT:    v_mov_b32_e32 v0, 0
 ; GCN-NEXT:    s_waitcnt lgkmcnt(0)
-; GCN-NEXT:    v_mul_i32_i24_e32 v0, s6, v0
 ; GCN-NEXT:    buffer_store_dword v0, off, s[0:3], 0
 ; GCN-NEXT:    s_endpgm
 ;
 ; GFX12-LABEL: test_mul_i24_zero_low24:
 ; GFX12:       ; %bb.0:
-; GFX12-NEXT:    s_load_b96 s[0:2], s[4:5], 0x24
+; GFX12-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
 ; GFX12-NEXT:    v_mov_b32_e32 v0, 0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_mul_i32_i24_e64 v1, 0x1000000, s2
-; GFX12-NEXT:    global_store_b32 v0, v1, s[0:1]
+; GFX12-NEXT:    global_store_b32 v0, v0, s[0:1]
 ; GFX12-NEXT:    s_endpgm
   %val = call i32 @llvm.amdgcn.mul.i24(i32 %src, i32 16777216) #0
   store i32 %val, ptr addrspace(1) %out
@@ -108,23 +101,20 @@ define amdgpu_kernel void @test_mul_i24_zero_low24(ptr addrspace(1) %out, i32 %s
 define amdgpu_kernel void @test_mul_i24_known_zero_low24(ptr addrspace(1) %out, i32 %src) #1 {
 ; GCN-LABEL: test_mul_i24_known_zero_low24:
 ; GCN:       ; %bb.0:
-; GCN-NEXT:    s_load_dword s6, s[4:5], 0xb
 ; GCN-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x9
 ; GCN-NEXT:    s_mov_b32 s3, 0xf000
 ; GCN-NEXT:    s_mov_b32 s2, -1
+; GCN-NEXT:    v_mov_b32_e32 v0, 0
 ; GCN-NEXT:    s_waitcnt lgkmcnt(0)
-; GCN-NEXT:    v_mov_b32_e32 v0, s6
-; GCN-NEXT:    v_mul_i32_i24_e32 v0, 0xff000000, v0
 ; GCN-NEXT:    buffer_store_dword v0, off, s[0:3], 0
 ; GCN-NEXT:    s_endpgm
 ;
 ; GFX12-LABEL: test_mul_i24_known_zero_low24:
 ; GFX12:       ; %bb.0:
-; GFX12-NEXT:    s_load_b96 s[0:2], s[4:5], 0x24
+; GFX12-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
 ; GFX12-NEXT:    v_mov_b32_e32 v0, 0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_mul_i32_i24_e64 v1, 0xff000000, s2
-; GFX12-NEXT:    global_store_b32 v0, v1, s[0:1]
+; GFX12-NEXT:    global_store_b32 v0, v0, s[0:1]
 ; GFX12-NEXT:    s_endpgm
   %masked = and i32 %src, -16777216
   %val = call i32 @llvm.amdgcn.mul.i24(i32 %masked, i32 %src) #0
@@ -135,25 +125,23 @@ define amdgpu_kernel void @test_mul_i24_known_zero_low24(ptr addrspace(1) %out,
 define amdgpu_kernel void @test_imad24_zero(ptr addrspace(1) %out, i32 %a, i32 %c) #1 {
 ; GCN-LABEL: test_imad24_zero:
 ; GCN:       ; %bb.0:
-; GCN-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x9
-; GCN-NEXT:    s_waitcnt lgkmcnt(0)
-; GCN-NEXT:    s_mov_b64 s[4:5], s[2:3]
-; GCN-NEXT:    v_mul_i32_i24_e64 v0, s4, 0
+; GCN-NEXT:    s_load_dword s6, s[4:5], 0xc
+; GCN-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x9
 ; GCN-NEXT:    s_mov_b32 s3, 0xf000
 ; GCN-NEXT:    s_mov_b32 s2, -1
-; GCN-NEXT:    v_or_b32_e32 v0, s5, v0
+; GCN-NEXT:    s_waitcnt lgkmcnt(0)
+; GCN-NEXT:    v_mov_b32_e32 v0, s6
 ; GCN-NEXT:    buffer_store_dword v0, off, s[0:3], 0
 ; GCN-NEXT:    s_endpgm
 ;
 ; GFX12-LABEL: test_imad24_zero:
 ; GFX12:       ; %bb.0:
-; GFX12-NEXT:    s_load_b128 s[0:3], s[4:5], 0x24
-; GFX12-NEXT:    v_mov_b32_e32 v1, 0
+; GFX12-NEXT:    s_clause 0x1
+; GFX12-NEXT:    s_load_b32 s2, s[4:5], 0x30
+; GFX12-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    v_mul_i32_i24_e64 v0, s2, 0
-; GFX12-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX12-NEXT:    v_or_b32_e32 v0, s3, v0
-; GFX12-NEXT:    global_store_b32 v1, v0, s[0:1]
+; GFX12-NEXT:    v_dual_mov_b32 v0, 0 :: v_dual_mov_b32 v1, s2
+; GFX12-NEXT:    global_store_b32 v0, v1, s[0:1]
 ; GFX12-NEXT:    s_endpgm
   %mul = call i32 @llvm.amdgcn.mul.i24(i32 %a, i32 0) #0
   %res = add i32 %mul, %c

>From 55f80ba9bce9d0439609f2fda0487dde1d3a8d7d Mon Sep 17 00:00:00 2001
From: Harrison Hao <tsworld1314 at gmail.com>
Date: Sun, 20 Sep 2026 11:54:15 +0800
Subject: [PATCH 2/2] [SelectionDAG][AMDGPU] Fold mul24 with an AND operand
 whose low bits are zero

Use SimplifyMultipleUseDemandedBits to simplify AND operands based on the
low 24 bits consumed by mul24.

Fold the multiply to zero when the simplified operand is zero.

This folds cases such as:

  mul24(x & 0xff000000, y) -> 0
---
 llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp |  5 +++++
 llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp    | 16 ++++++++--------
 llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll  |  9 ++++++---
 3 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 7b92c903a209e..61f0c8e1ca3fe 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -826,6 +826,11 @@ SDValue TargetLowering::SimplifyMultipleUseDemandedBits(
     LHSKnown = DAG.computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1);
     RHSKnown = DAG.computeKnownBits(Op.getOperand(1), DemandedElts, Depth + 1);
 
+    // If either operand is known zero for every demanded bit, the result is
+    // zero.
+    if (DemandedBits.isSubsetOf(LHSKnown.Zero | RHSKnown.Zero))
+      return DAG.getConstant(0, SDLoc(Op), VT);
+
     // If all of the demanded bits are known 1 on one side, return the other.
     // These bits cannot contribute to the result of the 'and' in this
     // context.
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
index 362b37810e121..8e525c62d073e 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp
@@ -4087,20 +4087,20 @@ static SDValue simplifyMul24(SDNode *Node24,
 
   APInt Demanded = APInt::getLowBitsSet(LHS.getValueSizeInBits(), 24);
 
-  // The 24 bit multiplies only read bits [23:0] of each source.
-  if (DAG.MaskedValueIsZero(LHS, Demanded) ||
-      DAG.MaskedValueIsZero(RHS, Demanded))
-    return DAG.getConstant(0, SDLoc(Node24), Node24->getValueType(0));
-
   // First try to simplify using SimplifyMultipleUseDemandedBits which allows
   // the operands to have other uses, but will only perform simplifications that
   // involve bypassing some nodes for this user.
   SDValue DemandedLHS = TLI.SimplifyMultipleUseDemandedBits(LHS, Demanded, DAG);
   SDValue DemandedRHS = TLI.SimplifyMultipleUseDemandedBits(RHS, Demanded, DAG);
+
+  SDValue NewLHS = DemandedLHS ? DemandedLHS : LHS;
+  SDValue NewRHS = DemandedRHS ? DemandedRHS : RHS;
+  if (isNullConstant(NewLHS) || isNullConstant(NewRHS))
+    return DAG.getConstant(0, SDLoc(Node24), Node24->getValueType(0));
+
   if (DemandedLHS || DemandedRHS)
-    return DAG.getNode(NewOpcode, SDLoc(Node24), Node24->getVTList(),
-                       DemandedLHS ? DemandedLHS : LHS,
-                       DemandedRHS ? DemandedRHS : RHS);
+    return DAG.getNode(NewOpcode, SDLoc(Node24), Node24->getVTList(), NewLHS,
+                       NewRHS);
 
   // Now try SimplifyDemandedBits which can simplify the nodes used by our
   // operands if this node is the only user.
diff --git a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll
index b5c1eb267b6fc..380a28bf03229 100644
--- a/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll
+++ b/llvm/test/CodeGen/AMDGPU/llvm.amdgcn.mul.i24.ll
@@ -78,20 +78,23 @@ define amdgpu_kernel void @test_mul_i24_zero_lhs(ptr addrspace(1) %out, i32 %src
 define amdgpu_kernel void @test_mul_i24_zero_low24(ptr addrspace(1) %out, i32 %src) #1 {
 ; GCN-LABEL: test_mul_i24_zero_low24:
 ; GCN:       ; %bb.0:
+; GCN-NEXT:    s_load_dword s6, s[4:5], 0xb
 ; GCN-NEXT:    s_load_dwordx2 s[0:1], s[4:5], 0x9
+; GCN-NEXT:    v_mov_b32_e32 v0, 0x1000000
 ; GCN-NEXT:    s_mov_b32 s3, 0xf000
 ; GCN-NEXT:    s_mov_b32 s2, -1
-; GCN-NEXT:    v_mov_b32_e32 v0, 0
 ; GCN-NEXT:    s_waitcnt lgkmcnt(0)
+; GCN-NEXT:    v_mul_i32_i24_e32 v0, s6, v0
 ; GCN-NEXT:    buffer_store_dword v0, off, s[0:3], 0
 ; GCN-NEXT:    s_endpgm
 ;
 ; GFX12-LABEL: test_mul_i24_zero_low24:
 ; GFX12:       ; %bb.0:
-; GFX12-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
+; GFX12-NEXT:    s_load_b96 s[0:2], s[4:5], 0x24
 ; GFX12-NEXT:    v_mov_b32_e32 v0, 0
 ; GFX12-NEXT:    s_wait_kmcnt 0x0
-; GFX12-NEXT:    global_store_b32 v0, v0, s[0:1]
+; GFX12-NEXT:    v_mul_i32_i24_e64 v1, 0x1000000, s2
+; GFX12-NEXT:    global_store_b32 v0, v1, s[0:1]
 ; GFX12-NEXT:    s_endpgm
   %val = call i32 @llvm.amdgcn.mul.i24(i32 %src, i32 16777216) #0
   store i32 %val, ptr addrspace(1) %out



More information about the llvm-commits mailing list