[llvm] [AMDGPU] [SelectionDAG] Lower constant i128 UREM without a libcall (PR #210232)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 23:24:47 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Keshav Vinayak Jha (keshavvinayak01)

<details>
<summary>Changes</summary>

AMDGPU SelectionDAG can produce an i128 UREM while expanding irregular-width funnel shifts such as i65. Generic legalization declines the constant decomposition and attempts the unsupported `__umodti3` libcall.

Custom-lower constant i128 UREM in SITargetLowering using the existing half-width remainder decomposition. This covers types promoted to i128 while leaving generic SelectionDAG behavior and other targets unchanged.

Fixes #<!-- -->197949

Assisted-by: Codex

---
Full diff: https://github.com/llvm/llvm-project/pull/210232.diff


4 Files Affected:

- (modified) llvm/include/llvm/CodeGen/TargetLowering.h (+4-4) 
- (modified) llvm/lib/Target/AMDGPU/SIISelLowering.cpp (+23) 
- (added) llvm/test/CodeGen/AMDGPU/fshl-illegal-types.ll (+47) 
- (added) llvm/test/CodeGen/AMDGPU/urem-constant-i128.ll (+42) 


``````````diff
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 75b1ca2d2745f..882999a9634f1 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -6032,6 +6032,10 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
                                        SelectionDAG &DAG) const;
 
 protected:
+  bool expandUDIVREMByConstantViaUREMDecomposition(
+      SDNode *N, APInt Divisor, SmallVectorImpl<SDValue> &Result, EVT HiLoVT,
+      SelectionDAG &DAG, SDValue LL, SDValue LH) const;
+
   void setTypeIdForCallsiteInfo(const CallBase *CB, MachineFunction &MF,
                                 MachineFunction::CallSiteInfo &CSInfo) const;
 
@@ -6069,10 +6073,6 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
                           ISD::CondCode Cond, DAGCombinerInfo &DCI,
                           const SDLoc &DL) const;
 
-  bool expandUDIVREMByConstantViaUREMDecomposition(
-      SDNode *N, APInt Divisor, SmallVectorImpl<SDValue> &Result, EVT HiLoVT,
-      SelectionDAG &DAG, SDValue LL, SDValue LH) const;
-
   bool expandUDIVREMByConstantViaUMulHiMagic(SDNode *N, const APInt &Divisor,
                                              SmallVectorImpl<SDValue> &Result,
                                              EVT HiLoVT, SelectionDAG &DAG,
diff --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index 8a8a3b97e8e08..01dd4b8fecd9c 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -951,6 +951,10 @@ SITargetLowering::SITargetLowering(const TargetMachine &TM,
 
   setOperationAction({ISD::SMULO, ISD::UMULO}, MVT::i64, Custom);
 
+  // Try to decompose constant i128 remainders before falling back to an
+  // unsupported i128 libcall.
+  setOperationAction(ISD::UREM, MVT::i128, Custom);
+
   if (Subtarget->hasVMulU64Inst())
     setOperationAction(ISD::MUL, MVT::i64, Legal);
   else if (Subtarget->hasScalarSMulU64())
@@ -8229,6 +8233,25 @@ void SITargetLowering::ReplaceNodeResults(SDNode *N,
                                           SmallVectorImpl<SDValue> &Results,
                                           SelectionDAG &DAG) const {
   switch (N->getOpcode()) {
+  case ISD::UREM: {
+    if (N->getValueType(0) != MVT::i128)
+      break;
+
+    auto *Divisor = dyn_cast<ConstantSDNode>(N->getOperand(1));
+    if (!Divisor || Divisor->getAPIntValue().ule(1))
+      break;
+
+    SmallVector<SDValue, 2> Rem;
+    if (!expandUDIVREMByConstantViaUREMDecomposition(
+            N, Divisor->getAPIntValue(), Rem, MVT::i64, DAG, SDValue(),
+            SDValue()))
+      break;
+
+    assert(Rem.size() == 2 && "Expected low and high remainder parts");
+    Results.push_back(
+        DAG.getNode(ISD::BUILD_PAIR, SDLoc(N), MVT::i128, Rem[0], Rem[1]));
+    return;
+  }
   case ISD::INSERT_VECTOR_ELT: {
     if (SDValue Res = lowerINSERT_VECTOR_ELT(SDValue(N, 0), DAG))
       Results.push_back(Res);
diff --git a/llvm/test/CodeGen/AMDGPU/fshl-illegal-types.ll b/llvm/test/CodeGen/AMDGPU/fshl-illegal-types.ll
new file mode 100644
index 0000000000000..3ef063fcd7ee4
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/fshl-illegal-types.ll
@@ -0,0 +1,47 @@
+; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx600 -verify-machineinstrs < %s | FileCheck %s --implicit-check-not=__umodti3
+; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx1100 -verify-machineinstrs < %s | FileCheck %s --implicit-check-not=__umodti3
+
+; The generated remainder and funnel-shift expansions are long and subject to
+; frequent changes. Check that SelectionDAG can lower variable funnel shifts
+; whose irregular types promote to i128 without requiring an i128 urem libcall.
+
+declare i65 @llvm.fshl.i65(i65, i65, i65)
+declare i66 @llvm.fshl.i66(i66, i66, i66)
+declare i67 @llvm.fshl.i67(i67, i67, i67)
+declare i68 @llvm.fshl.i68(i68, i68, i68)
+declare i127 @llvm.fshl.i127(i127, i127, i127)
+
+define i65 @fshl_i65(i65 %amt) {
+; CHECK-LABEL: fshl_i65:
+; CHECK:       s_setpc_b64
+  %result = call i65 @llvm.fshl.i65(i65 1, i65 0, i65 %amt)
+  ret i65 %result
+}
+
+define i66 @fshl_i66(i66 %amt) {
+; CHECK-LABEL: fshl_i66:
+; CHECK:       s_setpc_b64
+  %result = call i66 @llvm.fshl.i66(i66 1, i66 0, i66 %amt)
+  ret i66 %result
+}
+
+define i67 @fshl_i67(i67 %amt) {
+; CHECK-LABEL: fshl_i67:
+; CHECK:       s_setpc_b64
+  %result = call i67 @llvm.fshl.i67(i67 1, i67 0, i67 %amt)
+  ret i67 %result
+}
+
+define i68 @fshl_i68(i68 %amt) {
+; CHECK-LABEL: fshl_i68:
+; CHECK:       s_setpc_b64
+  %result = call i68 @llvm.fshl.i68(i68 1, i68 0, i68 %amt)
+  ret i68 %result
+}
+
+define i127 @fshl_i127(i127 %amt) {
+; CHECK-LABEL: fshl_i127:
+; CHECK:       s_setpc_b64
+  %result = call i127 @llvm.fshl.i127(i127 1, i127 0, i127 %amt)
+  ret i127 %result
+}
diff --git a/llvm/test/CodeGen/AMDGPU/urem-constant-i128.ll b/llvm/test/CodeGen/AMDGPU/urem-constant-i128.ll
new file mode 100644
index 0000000000000..eab4234c6758b
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/urem-constant-i128.ll
@@ -0,0 +1,42 @@
+; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx600 -verify-machineinstrs < %s | FileCheck %s --implicit-check-not=__umodti3
+; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx1100 -verify-machineinstrs < %s | FileCheck %s --implicit-check-not=__umodti3
+
+; AMDGPU has custom i64 udivrem lowering, but no legal i64 high multiply and no
+; i128 urem libcall. Check that wide constant urem uses the existing half-width
+; urem decomposition for representative odd, even, alternate, full-half, and
+; upper-bound funnel-shift widths.
+
+define i128 @urem_i128_65(i128 %src) {
+; CHECK-LABEL: urem_i128_65:
+; CHECK:       s_setpc_b64
+  %result = urem i128 %src, 65
+  ret i128 %result
+}
+
+define i128 @urem_i128_66(i128 %src) {
+; CHECK-LABEL: urem_i128_66:
+; CHECK:       s_setpc_b64
+  %result = urem i128 %src, 66
+  ret i128 %result
+}
+
+define i128 @urem_i128_67(i128 %src) {
+; CHECK-LABEL: urem_i128_67:
+; CHECK:       s_setpc_b64
+  %result = urem i128 %src, 67
+  ret i128 %result
+}
+
+define i128 @urem_i128_68(i128 %src) {
+; CHECK-LABEL: urem_i128_68:
+; CHECK:       s_setpc_b64
+  %result = urem i128 %src, 68
+  ret i128 %result
+}
+
+define i128 @urem_i128_127(i128 %src) {
+; CHECK-LABEL: urem_i128_127:
+; CHECK:       s_setpc_b64
+  %result = urem i128 %src, 127
+  ret i128 %result
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/210232


More information about the llvm-commits mailing list