[llvm-branch-commits] [llvm] release/23.x: [Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pipeline (#230562) (PR #231126)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 12 04:09:41 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Nikita Popov (nikic)

<details>
<summary>Changes</summary>

Backport of 17379353858fbac934f573a22e6aa5acce6884d7.

This fixes an optimization regression that was introduced in LLVM 23. (These passes moved from the backend pipeline to the middle-end pipeline in LLVM 23, but were not added in all the necessary places.)

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


3 Files Affected:

- (modified) llvm/lib/Passes/PassBuilderPipelines.cpp (+7-1) 
- (modified) llvm/test/Other/new-pm-lto-defaults.ll (+2) 
- (modified) llvm/test/Transforms/PhaseOrdering/X86/expand-memcmp-middle-end.ll (+56) 


``````````diff
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index d62828c78bfe4..5de45e5668fb9 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -1662,7 +1662,7 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   OptimizePM.addPass(DivRemPairsPass());
 
   // Merge adjacent icmps into memcmp, then expand memcmp to loads/compares.
-  // TODO: move this furter up so that it can be optimized by GVN, etc.
+  // TODO: move this further up so that it can be optimized by GVN, etc.
   if (EnableMergeICmps)
     OptimizePM.addPass(MergeICmpsPass());
   OptimizePM.addPass(ExpandMemCmpPass());
@@ -2362,6 +2362,12 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
   // flattening of blocks.
   LateFPM.addPass(DivRemPairsPass());
 
+  // Merge adjacent icmps into memcmp, then expand memcmp to loads/compares.
+  // TODO: move this further up so that it can be optimized by GVN, etc.
+  if (EnableMergeICmps)
+    LateFPM.addPass(MergeICmpsPass());
+  LateFPM.addPass(ExpandMemCmpPass());
+
   // Delete basic blocks, which optimization passes may have killed.
   LateFPM.addPass(SimplifyCFGPass(SimplifyCFGOptions()
                                       .convertSwitchRangeToICmp(true)
diff --git a/llvm/test/Other/new-pm-lto-defaults.ll b/llvm/test/Other/new-pm-lto-defaults.ll
index 14f189194d0e3..e327fff84ce57 100644
--- a/llvm/test/Other/new-pm-lto-defaults.ll
+++ b/llvm/test/Other/new-pm-lto-defaults.ll
@@ -153,6 +153,8 @@
 ; CHECK-O-NEXT: Running pass: DropTypeTestsPass
 ; CHECK-O23-NEXT: Running pass: LoopSink
 ; CHECK-O23-NEXT: Running pass: DivRemPairs
+; CHECK-O23-NEXT: Running pass: MergeICmpsPass
+; CHECK-O23-NEXT: Running pass: ExpandMemCmpPass
 ; CHECK-O23-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O23-NEXT: Running pass: EliminateAvailableExternallyPass
 ; CHECK-O23-NEXT: Running pass: GlobalDCEPass
diff --git a/llvm/test/Transforms/PhaseOrdering/X86/expand-memcmp-middle-end.ll b/llvm/test/Transforms/PhaseOrdering/X86/expand-memcmp-middle-end.ll
index 4f6e94431e496..fea7f8f4cc434 100644
--- a/llvm/test/Transforms/PhaseOrdering/X86/expand-memcmp-middle-end.ll
+++ b/llvm/test/Transforms/PhaseOrdering/X86/expand-memcmp-middle-end.ll
@@ -1,5 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt -S -O2 -mtriple=x86_64-unknown-unknown < %s | FileCheck %s
+; RUN: opt -S -passes='thinlto-pre-link<O2>,thinlto<O2>' -mtriple=x86_64-unknown-unknown < %s | FileCheck %s --check-prefixes=LTO-THIN
+; RUN: opt -S -passes='lto-pre-link<O2>,lto<O2>' -mtriple=x86_64-unknown-unknown < %s | FileCheck %s --check-prefixes=LTO-FULL
+; RUN: opt -S -passes='thinlto-pre-link<O2>,lto<O2>' -mtriple=x86_64-unknown-unknown < %s | FileCheck %s --check-prefixes=LTO-MIX
 ;
 ; Verify that expanding memcmp in the middle-end enables further IR
 ; optimizations. When two memcmp calls share a common pointer operand and
@@ -27,6 +30,52 @@ define i1 @redundant_memcmp_loads(ptr nocapture readonly %x, ptr nocapture reado
 ; CHECK-NEXT:    [[TMP11:%.*]] = or i32 [[TMP10]], [[TMP5]]
 ; CHECK-NEXT:    [[RESULT:%.*]] = icmp eq i32 [[TMP11]], 0
 ; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+; LTO-THIN-LABEL: define i1 @redundant_memcmp_loads(
+; LTO-THIN-SAME: ptr readonly captures(none) [[X:%.*]], ptr readonly captures(none) [[Y:%.*]], ptr readonly captures(none) [[Z:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !guid [[META0:![0-9]+]] {
+; LTO-THIN-NEXT:    [[TMP1:%.*]] = load i32, ptr [[X]], align 1
+; LTO-THIN-NEXT:    [[TMP2:%.*]] = load i32, ptr [[Y]], align 1
+; LTO-THIN-NEXT:    [[TMP3:%.*]] = tail call i32 @llvm.bswap.i32(i32 [[TMP1]])
+; LTO-THIN-NEXT:    [[TMP4:%.*]] = tail call i32 @llvm.bswap.i32(i32 [[TMP2]])
+; LTO-THIN-NEXT:    [[TMP5:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP3]], i32 [[TMP4]])
+; LTO-THIN-NEXT:    [[TMP6:%.*]] = load i32, ptr [[X]], align 1
+; LTO-THIN-NEXT:    [[TMP7:%.*]] = load i32, ptr [[Z]], align 1
+; LTO-THIN-NEXT:    [[TMP8:%.*]] = tail call i32 @llvm.bswap.i32(i32 [[TMP6]])
+; LTO-THIN-NEXT:    [[TMP9:%.*]] = tail call i32 @llvm.bswap.i32(i32 [[TMP7]])
+; LTO-THIN-NEXT:    [[TMP10:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP8]], i32 [[TMP9]])
+; LTO-THIN-NEXT:    [[TMP11:%.*]] = or i32 [[TMP10]], [[TMP5]]
+; LTO-THIN-NEXT:    [[RESULT:%.*]] = icmp eq i32 [[TMP11]], 0
+; LTO-THIN-NEXT:    ret i1 [[RESULT]]
+;
+; LTO-FULL-LABEL: define i1 @redundant_memcmp_loads(
+; LTO-FULL-SAME: ptr readonly captures(none) [[X:%.*]], ptr readonly captures(none) [[Y:%.*]], ptr readonly captures(none) [[Z:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !guid [[META0:![0-9]+]] {
+; LTO-FULL-NEXT:    [[TMP1:%.*]] = load i32, ptr [[X]], align 1
+; LTO-FULL-NEXT:    [[TMP2:%.*]] = load i32, ptr [[Y]], align 1
+; LTO-FULL-NEXT:    [[TMP3:%.*]] = tail call i32 @llvm.bswap.i32(i32 [[TMP1]])
+; LTO-FULL-NEXT:    [[TMP4:%.*]] = tail call i32 @llvm.bswap.i32(i32 [[TMP2]])
+; LTO-FULL-NEXT:    [[TMP5:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP3]], i32 [[TMP4]])
+; LTO-FULL-NEXT:    [[TMP6:%.*]] = load i32, ptr [[Z]], align 1
+; LTO-FULL-NEXT:    [[TMP7:%.*]] = tail call i32 @llvm.bswap.i32(i32 [[TMP6]])
+; LTO-FULL-NEXT:    [[TMP8:%.*]] = tail call i32 @llvm.ucmp.i32.i32(i32 [[TMP3]], i32 [[TMP7]])
+; LTO-FULL-NEXT:    [[TMP9:%.*]] = or i32 [[TMP8]], [[TMP5]]
+; LTO-FULL-NEXT:    [[RESULT:%.*]] = icmp eq i32 [[TMP9]], 0
+; LTO-FULL-NEXT:    ret i1 [[RESULT]]
+;
+; LTO-MIX-LABEL: define i1 @redundant_memcmp_loads(
+; LTO-MIX-SAME: ptr readonly captures(none) [[X:%.*]], ptr readonly captures(none) [[Y:%.*]], ptr readonly captures(none) [[Z:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !guid [[META0:![0-9]+]] {
+; LTO-MIX-NEXT:    [[TMP5:%.*]] = load i32, ptr [[X]], align 1
+; LTO-MIX-NEXT:    [[TMP2:%.*]] = load i32, ptr [[Y]], align 1
+; LTO-MIX-NEXT:    [[TMP3:%.*]] = call i32 @llvm.bswap.i32(i32 [[TMP5]])
+; LTO-MIX-NEXT:    [[TMP4:%.*]] = call i32 @llvm.bswap.i32(i32 [[TMP2]])
+; LTO-MIX-NEXT:    [[CMP1:%.*]] = call i32 @llvm.ucmp.i32.i32(i32 [[TMP3]], i32 [[TMP4]])
+; LTO-MIX-NEXT:    [[TMP6:%.*]] = load i32, ptr [[X]], align 1
+; LTO-MIX-NEXT:    [[TMP7:%.*]] = load i32, ptr [[Z]], align 1
+; LTO-MIX-NEXT:    [[TMP8:%.*]] = call i32 @llvm.bswap.i32(i32 [[TMP6]])
+; LTO-MIX-NEXT:    [[TMP9:%.*]] = call i32 @llvm.bswap.i32(i32 [[TMP7]])
+; LTO-MIX-NEXT:    [[CMP2:%.*]] = call i32 @llvm.ucmp.i32.i32(i32 [[TMP8]], i32 [[TMP9]])
+; LTO-MIX-NEXT:    [[TMP1:%.*]] = or i32 [[CMP2]], [[CMP1]]
+; LTO-MIX-NEXT:    [[RESULT:%.*]] = icmp eq i32 [[TMP1]], 0
+; LTO-MIX-NEXT:    ret i1 [[RESULT]]
 ;
   %cmp1 = call i32 @memcmp(ptr %x, ptr %y, i64 4)
   %eq1 = icmp eq i32 %cmp1, 0
@@ -35,3 +84,10 @@ define i1 @redundant_memcmp_loads(ptr nocapture readonly %x, ptr nocapture reado
   %result = and i1 %eq1, %eq2
   ret i1 %result
 }
+;.
+; LTO-THIN: [[META0]] = !{i64 8415969065289030232}
+;.
+; LTO-FULL: [[META0]] = !{i64 8415969065289030232}
+;.
+; LTO-MIX: [[META0]] = !{i64 8415969065289030232}
+;.

``````````

</details>


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


More information about the llvm-branch-commits mailing list