Repository navigation
Rust 1.99 regression: LTO leaves short constant string comparisons as bcmp calls #163677
Copy link
Copy link
Closed
Closed
Copy link
Labels
A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchCategory: An issue highlighting optimization opportunities or PRs implementing suchI-slowIssue: Problems and improvements with respect to performance of generated code.Issue: Problems and improvements with respect to performance of generated code.P-mediumMedium priorityMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.llvm-fixed-upstreamIssue expected to be fixed by the next major LLVM upgrade, or backported fixesIssue expected to be fixed by the next major LLVM upgrade, or backported fixesregression-from-stable-to-stablePerformance or correctness regression from one stable version to another.Performance or correctness regression from one stable version to another.
Description
After upgrade in Oxc from Rust 1.98.1 to 1.99.0, there seem to be a performance regression.
investigation revealed a code-gen change for short constant string comparisons when using fat LTO.
comparisons that previously expanded into multiple inline loads/comparisons now call bcmp. Disabling LTO restores the inline comparisons.
Code
I tried this code:
fn is_typeof_string(s: &str) -> bool {
s == "string"
}godbolt - complex
godbolt - min repro
is_typeof_string:
cmp rsi, 6
jne .LBB0_1
mov eax, 1769108595
xor eax, dword ptr [rdi]
movzx ecx, word ptr [rdi + 4]
xor ecx, 26478
or ecx, eax
sete al
ret
.LBB0_1:
xor eax, eax
retis_typeof_string:
cmpq $6, %rsi
jne .LBB0_1
pushq %rax
leaq .Lstring(%rip), %rsi
movl $6, %edx
callq *bcmp@GOTPCREL(%rip)
testl %eax, %eax
sete %al
addq $8, %rsp
retq
.LBB0_1:
xorl %eax, %eax
retq
.Lstring:
.ascii "string"| String | Rust 1.98 | Rust 1.99 + fat LTO |
|---|---|---|
"tes" (3 bytes) |
16-bit + 8-bit loads | Replaced by bcmp |
"abcd" (4 bytes) |
One 32-bit load | Preserved |
"string" (6 bytes) |
32-bit + 16-bit loads | Replaced by bcmp |
"function" (8 bytes) |
One 64-bit load | Preserved |
"undefined" (9 bytes) |
64-bit + 8-bit loads | Replaced by bcmp |
example of original source: https://github.com/oxc-project/oxc/blob/7f65b757df7c3225320d8f7e051e64873f80f988/crates/oxc_minifier/src/peephole/fold_constants.rs#L794-L807
disassembly from source: https://gist.github.com/armano2/793316003e08389e24550fa234ab1796
Version it worked on
It most recently worked on: 1.98.1
Version with regression
1.99.0+
rustc --version --verbose:
rustc 1.99.0 (b940084d7 2026-09-28)
binary: rustc
commit-hash: b940084d7eb6a299eb4bfeb8e34901bc051e7ac4
commit-date: 2026-09-28
host: x86_64-unknown-linux-gnu
release: 1.99.0
LLVM version: 23.1.1
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchCategory: An issue highlighting optimization opportunities or PRs implementing suchI-slowIssue: Problems and improvements with respect to performance of generated code.Issue: Problems and improvements with respect to performance of generated code.P-mediumMedium priorityMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.llvm-fixed-upstreamIssue expected to be fixed by the next major LLVM upgrade, or backported fixesIssue expected to be fixed by the next major LLVM upgrade, or backported fixesregression-from-stable-to-stablePerformance or correctness regression from one stable version to another.Performance or correctness regression from one stable version to another.
Activity
nikic commentedon Oct 2, 2026
From a cursory look MergeICmpsPass and ExpandMemCmpPass are not scheduled in the full LTO post-link pipeline.
[-]Rust 1.99 regression: fat LTO leaves short constant string comparisons as bcmp calls[/-][+]Rust 1.99 regression: LTO leaves short constant string comparisons as bcmp calls[/+]armano2 commentedon Oct 4, 2026
small update issue is present in fat and thin LTO, and this may be related to llvm/llvm-project#77370
5 remaining items
nikic commentedon Oct 9, 2026
Turns out Rust always uses the ThinLTO pipeline pre-link, even for full LTO:
rust/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
Line 960 in 76c9095
This explains why this doesn't work for
-Clto=fat. The relevant passes are not scheduled in the post-link pipeline, and we don't run the the full LTO pre-link pipeline where they would be scheduled. (Ultimately I think what Rust is doing is fine though, and this should be fixed in LLVM by scheduling the pass post-linke)Not sure why this doesn't work for
-Clto=thinthough... that seems to be an artifact of what--emit=asm/--emit=llvm-irdo? If I inspect the actual object with objdump, it is properly optimized. It seems like--emitstops at the pre-link IR though. Is that ... supposed to work that way?[Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pipeline (#230562
Automerge: [Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pip…
Automerge: [Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pip…
[Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pipeline (#230562
[Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pipeline (llvm…
Auto merge of #164254 - nikic:llvm-update-4, r=cuviper
[Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pipeline (llvm…
Auto merge of #164254 - nikic:llvm-update-4, r=cuviper
[Passes] Run MergeICmps/ExpandMemCmps in LTO post-link pipeline (llvm…
Auto merge of #164254 - nikic:llvm-update-4, r=cuviper