Skip to content

Conversation

@kper
Copy link
Contributor

@kper kper commented Sep 18, 2025

Fixes 157370

UREM General proof: https://alive2.llvm.org/ce/z/b_GQJX
SREM General proof: https://alive2.llvm.org/ce/z/Whkaxh

I have added it as rv32i and rv64i tests because they are the only architectures where I could verify that it works.

@llvmbot llvmbot added backend:RISC-V llvm:SelectionDAG SelectionDAGISel as well labels Sep 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 18, 2025

@llvm/pr-subscribers-backend-risc-v

@llvm/pr-subscribers-llvm-selectiondag

Author: None (kper)

Changes

Fixes 157370

General proof: https://alive2.llvm.org/ce/z/b_GQJX

I have added it as rv32i and rv64i tests because they are the only architectures where I could verify that it works.


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

2 Files Affected:

  • (modified) llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp (+9)
  • (added) llvm/test/CodeGen/RISCV/urem.ll (+71)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 77bc47f28fc80..8e23282aad0fd 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -54,6 +54,7 @@
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/Metadata.h"
+#include "llvm/IR/PatternMatch.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/CommandLine.h"
@@ -5405,6 +5406,14 @@ SDValue DAGCombiner::visitREM(SDNode *N) {
   if (SDValue DivRem = useDivRem(N))
     return DivRem.getValue(1);
 
+  // fold urem(urem(A, BCst), Op1Cst) -> urem(A, Op1Cst)
+  SDValue A;
+  APInt Op1Cst, BCCst;
+  if (sd_match(N0, m_URem(m_Value(A), m_ConstInt(BCCst))) &&
+      sd_match(N1, m_ConstInt(Op1Cst)) && BCCst.urem(Op1Cst).isZero()) {
+    return DAG.getNode(ISD::UREM, DL, VT, A, DAG.getConstant(Op1Cst, DL, VT));
+  }
+
   return SDValue();
 }
 
diff --git a/llvm/test/CodeGen/RISCV/urem.ll b/llvm/test/CodeGen/RISCV/urem.ll
new file mode 100644
index 0000000000000..aeec9abd0ed9f
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/urem.ll
@@ -0,0 +1,71 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefixes=CHECK,RV32I %s
+; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefixes=CHECK,RV64I %s
+
+define i32 @fold_urem_constants(i32 %v0) {
+; RV32I-LABEL: fold_urem_constants:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    li a1, 5
+; RV32I-NEXT:    tail __umodsi3
+;
+; RV64I-LABEL: fold_urem_constants:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    .cfi_def_cfa_offset 16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    .cfi_offset ra, -8
+; RV64I-NEXT:    slli a0, a0, 32
+; RV64I-NEXT:    srli a0, a0, 32
+; RV64I-NEXT:    li a1, 5
+; RV64I-NEXT:    call __umoddi3
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    .cfi_restore ra
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    .cfi_def_cfa_offset 0
+; RV64I-NEXT:    ret
+  %v1 = urem i32 %v0, 25
+  %v2 = urem i32 %v1, 5
+  ret i32 %v2
+}
+
+define i32 @dont_test_fold_urem_constants(i32 %v0) {
+; RV32I-LABEL: dont_test_fold_urem_constants:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    addi sp, sp, -16
+; RV32I-NEXT:    .cfi_def_cfa_offset 16
+; RV32I-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32I-NEXT:    .cfi_offset ra, -4
+; RV32I-NEXT:    li a1, 25
+; RV32I-NEXT:    call __umodsi3
+; RV32I-NEXT:    li a1, 3
+; RV32I-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32I-NEXT:    .cfi_restore ra
+; RV32I-NEXT:    addi sp, sp, 16
+; RV32I-NEXT:    .cfi_def_cfa_offset 0
+; RV32I-NEXT:    tail __umodsi3
+;
+; RV64I-LABEL: dont_test_fold_urem_constants:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    addi sp, sp, -16
+; RV64I-NEXT:    .cfi_def_cfa_offset 16
+; RV64I-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64I-NEXT:    .cfi_offset ra, -8
+; RV64I-NEXT:    slli a0, a0, 32
+; RV64I-NEXT:    srli a0, a0, 32
+; RV64I-NEXT:    li a1, 25
+; RV64I-NEXT:    call __umoddi3
+; RV64I-NEXT:    li a1, 3
+; RV64I-NEXT:    call __umoddi3
+; RV64I-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64I-NEXT:    .cfi_restore ra
+; RV64I-NEXT:    addi sp, sp, 16
+; RV64I-NEXT:    .cfi_def_cfa_offset 0
+; RV64I-NEXT:    ret
+  %v1 = urem i32 %v0, 25
+  %v2 = urem i32 %v1, 3
+  ret i32 %v2
+}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK: {{.*}}

@kper
Copy link
Contributor Author

kper commented Sep 18, 2025

cc @RKSimon

Copy link
Contributor

@jayfoad jayfoad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic looks correct to me. But should it have a "one use" check, like was mentioned in #157644?


// fold urem(urem(A, BCst), Op1Cst) -> urem(A, Op1Cst)
SDValue A;
APInt Op1Cst, BCCst;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: call it BCst to match the comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it was a typo :)

@RKSimon RKSimon self-requested a review September 18, 2025 08:45
@kper
Copy link
Contributor Author

kper commented Sep 18, 2025

The logic looks correct to me. But should it have a "one use" check, like was mentioned in #157644?

I wasn't really sure. We only added it because it showed that we're breaking up the div+rem patterns. Beside that there wasn't a functional reason. So should I add it again?

; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefixes=CHECK,RV64I %s

define i32 @fold_srem_constants(i32 %v0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add nounwind to get rid of all the cfi noise

#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Type.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary?

if (sd_match(N, m_SRem(m_SRem(m_Value(A), m_ConstInt(BCst)),
m_ConstInt(Op1Cst))) &&
BCst.srem(Op1Cst).isZero() &&
Op1Cst.ne(APInt::getAllOnes(Op1Cst.getBitWidth()))) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!Op1Cst.isAllOnes() ?

@kper kper requested a review from RKSimon September 19, 2025 11:39
@kper
Copy link
Contributor Author

kper commented Sep 22, 2025

Gentle ping

@RKSimon RKSimon changed the title [DAG] Fold urem(urem(A, BCst), Op1Cst) -> urem(A, Op1Cst) [DAG] Fold rem(rem(A, BCst), Op1Cst) -> rem(A, Op1Cst) Sep 22, 2025
Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@RKSimon RKSimon enabled auto-merge (squash) September 22, 2025 09:02
@RKSimon RKSimon merged commit 0b1318f into llvm:main Sep 22, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 22, 2025

LLVM Buildbot has detected a new failure on builder clang-m68k-linux-cross running on suse-gary-m68k-cross while building llvm at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/27/builds/16404

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'lit :: shtest-readfile.py' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 3
env LIT_USE_INTERNAL_SHELL=1  not env -u FILECHECK_OPTS "/usr/bin/python3.11" /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/llvm/utils/lit/lit.py -j1 --order=lexical -a -v Inputs/shtest-readfile | FileCheck -match-full-lines -DTEMP_PATH=/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/utils/lit/tests/Inputs/shtest-readfile/Output /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/utils/lit/tests/shtest-readfile.py
# executed command: env LIT_USE_INTERNAL_SHELL=1 not env -u FILECHECK_OPTS /usr/bin/python3.11 /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/llvm/utils/lit/lit.py -j1 --order=lexical -a -v Inputs/shtest-readfile
# executed command: FileCheck -match-full-lines -DTEMP_PATH=/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/utils/lit/tests/Inputs/shtest-readfile/Output /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/utils/lit/tests/shtest-readfile.py
# .---command stderr------------
# | /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/utils/lit/tests/shtest-readfile.py:8:10: error: CHECK: expected string not found in input
# | # CHECK: echo hello
# |          ^
# | <stdin>:2:53: note: scanning from here
# | FAIL: shtest-readfile :: absolute-paths.txt (1 of 4)
# |                                                     ^
# | <stdin>:10:19: note: possible intended match here
# | # executed command: echo -n hello
# |                   ^
# | 
# | Input file: <stdin>
# | Check file: /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/utils/lit/tests/shtest-readfile.py
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |            1: -- Testing: 4 tests, 1 workers -- 
# |            2: FAIL: shtest-readfile :: absolute-paths.txt (1 of 4) 
# | check:8'0                                                         X error: no match found
# |            3: ******************** TEST 'shtest-readfile :: absolute-paths.txt' FAILED ******************** 
# | check:8'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |            4: Exit Code: 1 
# | check:8'0     ~~~~~~~~~~~~~
# |            5:  
# | check:8'0     ~
# |            6: Command Output (stdout): 
# | check:8'0     ~~~~~~~~~~~~~~~~~~~~~~~~~
# |            7: -- 
# | check:8'0     ~~~
# |            8: # RUN: at line 2 
# | check:8'0     ~~~~~~~~~~~~~~~~~
# |            9: echo -n "hello" > /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/utils/lit/tests/Inputs/shtest-readfile/Output/absolute-paths.txt.tmp 
# | check:8'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           10: # executed command: echo -n hello 
# | check:8'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# | check:8'1                       ?                possible intended match
# |           11: # RUN: at line 3 
# | check:8'0     ~~~~~~~~~~~~~~~~~
# |           12: echo  
...

@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 22, 2025

LLVM Buildbot has detected a new failure on builder clang-aarch64-quick running on linaro-clang-aarch64-quick while building llvm at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/65/builds/22972

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'Clangd Unit Tests :: ./ClangdTests/156/332' FAILED ********************
Script(shard):
--
GTEST_OUTPUT=json:/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests-Clangd Unit Tests-3521050-156-332.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=332 GTEST_SHARD_INDEX=156 /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests
--

Note: This is test shard 157 of 332.
[==========] Running 4 tests from 4 test suites.
[----------] Global test environment set-up.
[----------] 1 test from CompletionTest
[ RUN      ] CompletionTest.NoCrashOnMissingNewLineAtEOF
ASTWorker building file /clangd-test/foo.cpp version null with command 
[/clangd-test]
clang -ffreestanding /clangd-test/foo.cpp
Driver produced command: cc1 -cc1 -triple aarch64-unknown-linux-gnu -fsyntax-only -disable-free -clear-ast-before-backend -main-file-name foo.cpp -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=non-leaf -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -ffreestanding -enable-tlsdesc -target-cpu generic -target-feature +v8a -target-feature +fp-armv8 -target-feature +neon -target-abi aapcs -debugger-tuning=gdb -fdebug-compilation-dir=/clangd-test -fcoverage-compilation-dir=/clangd-test -resource-dir lib/clang/22 -internal-isystem lib/clang/22/include -internal-isystem /usr/local/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -ferror-limit 19 -fno-signed-char -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -no-round-trip-args -target-feature -fmv -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -x c++ /clangd-test/foo.cpp
Building first preamble for /clangd-test/foo.cpp version null
not idle after addDocument
UNREACHABLE executed at ../llvm/clang-tools-extra/clangd/unittests/SyncAPI.cpp:22!
 #0 0x0000b730207aba68 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xc8ba68)
 #1 0x0000b730207a9530 llvm::sys::RunSignalHandlers() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xc89530)
 #2 0x0000b730207ac8c4 SignalHandler(int, siginfo_t*, void*) Signals.cpp:0:0
 #3 0x0000e22f1207c8f8 (linux-vdso.so.1+0x8f8)
 #4 0x0000e22f11b8f1f0 __pthread_kill_implementation ./nptl/./nptl/pthread_kill.c:44:76
 #5 0x0000e22f11b4a67c gsignal ./signal/../sysdeps/posix/raise.c:27:6
 #6 0x0000e22f11b37130 abort ./stdlib/./stdlib/abort.c:81:7
 #7 0x0000b730207585a4 llvm::RTTIRoot::anchor() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xc385a4)
 #8 0x0000b73020602b24 clang::clangd::runCodeComplete(clang::clangd::ClangdServer&, llvm::StringRef, clang::clangd::Position, clang::clangd::CodeCompleteOptions) (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xae2b24)
 #9 0x0000b730201c4be8 clang::clangd::(anonymous namespace)::CompletionTest_NoCrashOnMissingNewLineAtEOF_Test::TestBody() CodeCompleteTests.cpp:0:0
#10 0x0000b730208042bc testing::Test::Run() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xce42bc)
#11 0x0000b730208055e0 testing::TestInfo::Run() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xce55e0)
#12 0x0000b7302080621c testing::TestSuite::Run() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xce621c)
#13 0x0000b7302081659c testing::internal::UnitTestImpl::RunAllTests() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xcf659c)
#14 0x0000b73020815ee8 testing::UnitTest::Run() (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xcf5ee8)
#15 0x0000b730207f1004 main (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0xcd1004)
#16 0x0000e22f11b373fc __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3
#17 0x0000e22f11b374cc call_init ./csu/../csu/libc-start.c:128:20
#18 0x0000e22f11b374cc __libc_start_main ./csu/../csu/libc-start.c:379:5
#19 0x0000b7301ffa3630 _start (/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests+0x483630)

--
exit: -6
--
shard JSON output does not exist: /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/tools/clang/tools/extra/clangd/unittests/./ClangdTests-Clangd Unit Tests-3521050-156-332.json
********************


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:RISC-V llvm:SelectionDAG SelectionDAGISel as well

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missed Optimization: eliminate redundant modulus when divisor divides previous modulus

5 participants