[llvm-branch-commits] [llvm] 94ae4f8 - [Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pipeline (#230562)
Tobias Hieta via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 13 00:54:23 PDT 2026
Author: Nikita Popov
Date: 2026-10-13T09:54:01+02:00
New Revision: 94ae4f86b8f317328d3fea74f1a305fba54e1a68
URL: https://github.com/llvm/llvm-project/commit/94ae4f86b8f317328d3fea74f1a305fba54e1a68
DIFF: https://github.com/llvm/llvm-project/commit/94ae4f86b8f317328d3fea74f1a305fba54e1a68.diff
LOG: [Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pipeline (#230562)
Otherwise memcmp will never get expanded if running the combination of
ThinLTO pre-link and LTO post-link, which is what Rust does, and what
unified LTO does.
The pipeline position is chosen to roughly match what the module
optimization pipeline does. There is no compile-time impact.
For https://github.com/rust-lang/rust/issues/163677.
(cherry picked from commit 17379353858fbac934f573a22e6aa5acce6884d7)
Added:
Modified:
llvm/lib/Passes/PassBuilderPipelines.cpp
llvm/test/Other/new-pm-lto-defaults.ll
llvm/test/Transforms/PhaseOrdering/X86/expand-memcmp-middle-end.ll
Removed:
################################################################################
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}
+;.
More information about the llvm-branch-commits
mailing list