Skip to content

Rust 1.99 regression: LTO leaves short constant string comparisons as bcmp calls #163677

Closed

Description

@armano2

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
        ret
is_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

Activity

  1. added
    C-bugCategory: This is a bug.
    regression-untriagedUntriaged performance or correctness regression.
    on Oct 2, 2026
  2. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Oct 2, 2026
  3. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing such
    and removed
    C-bugCategory: This is a bug.
    on Oct 2, 2026
  4. nikic commented on Oct 2, 2026

    @nikic
    Contributor

    From a cursory look MergeICmpsPass and ExpandMemCmpPass are not scheduled in the full LTO post-link pipeline.

  5. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 3, 2026
  6. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Oct 3, 2026
  7. changed the title [-]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[/+] on Oct 4, 2026
  8. armano2 commented on Oct 4, 2026

    @armano2
    Author

    small update issue is present in fat and thin LTO, and this may be related to llvm/llvm-project#77370

  9. added
    regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.
    and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    regression-untriagedUntriaged performance or correctness regression.
    on Oct 5, 2026
  10. 5 remaining items

  11. nikic commented on Oct 9, 2026

    @nikic
    Contributor

    Turns out Rust always uses the ThinLTO pipeline pre-link, even for full LTO:

    MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel);

    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=thin though... that seems to be an artifact of what --emit=asm / --emit=llvm-ir do? If I inspect the actual object with objdump, it is properly optimized. It seems like --emit stops at the pre-link IR though. Is that ... supposed to work that way?

  12. self-assigned this
    on Oct 9, 2026
  13. added
    llvm-fixed-upstreamIssue expected to be fixed by the next major LLVM upgrade, or backported fixes
    on Oct 12, 2026
  14. added a commit that references this issue on Oct 14, 2026
  15. added a commit that references this issue on Oct 14, 2026
  16. linked a pull request that will close this issue[beta] backports #164371on Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-slowIssue: Problems and improvements with respect to performance of generated code.P-mediumMedium priorityT-compilerRelevant 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 fixesregression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions