rust-lang / rust-lang/rust

Missed optimization: When debuginfo is on, is_power_of_two does not imply nonzero.

Open
#137,359 3 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-debuginfo A-LLVM C-optimization llvm-fixed-upstream T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I compiled the following code (Godbolt):

use std::hint::assert_unchecked;

#[no_mangle]
unsafe fn foo(x: u32) -> bool {
    assert_unchecked(x.is_power_of_two());
    x == 0
}

With -Copt-level=3 -Cdebuginfo=2, I got this assembly:

foo:
        test    edi, edi
        sete    al
        ret

In the above assembly, the compiler doesn't realize that x being a power of two implies that it is not zero.

However, with -Copt-level=3 -Cdebuginfo=0, I got this assembly:

foo:
        xor     eax, eax
        ret

That is, the compiler correctly optimizes the code to always returning false.

I expect debuginfo to not impact the amount of optimization, so I'm assuming that this is a bug.

Additional context

This was discovered while checking whether x % y is optimized when y is known to be a power of two.

I compiled the following code (Godbolt):

use std::hint::assert_unchecked;

#[no_mangle]
unsafe fn foo(x: u32, y: u32) -> u32 {
    assert_unchecked(y.is_power_of_two());
    x % y
}

#[no_mangle]
unsafe fn bar(x: u32, y: u32) -> u32 {
    assert_unchecked(y != 0 && y.is_power_of_two());
    x % y
}

With -Copt-level=3 -Cdebuginfo=2, I got this assembly:

foo:
        test    esi, esi
        je      .LBB0_2
        mov     eax, edi
        xor     edx, edx
        div     esi
        mov     eax, edx
        ret
.LBB0_2:
        push    rax
        lea     rdi, [rip + .L__unnamed_1]
        call    qword ptr [rip + core::panicking::panic_const::panic_const_rem_by_zero::h39e69d103ed030d2@GOTPCREL]

bar:
        lea     eax, [rsi - 1]
        and     eax, edi
        ret

.L__unnamed_2:
        .ascii  "/app/example.rs"

.L__unnamed_1:
        .quad   .L__unnamed_2
        .asciz  "\017\000\000\000\000\000\000\000\006\000\000\000\005\000\000"

However, with -Copt-level=3 -Cdebuginfo=0, I got this assembly:

foo:
        lea     eax, [rsi - 1]
        and     eax, edi
        ret

bar:
        lea     eax, [rsi - 1]
        and     eax, edi
        ret

This seems to imply that LLVM can make use of the fact that is_power_of_two returns true to do optimizations. However, for some reason, when debuginfo is on, it can't figure out that is_power_of_two implies that the number is not zero.

Meta

The issue is reproducible on Godbolt with version info:

rustc 1.87.0-nightly (f280acf4c 2025-02-19)
binary: rustc
commit-hash: f280acf4c743806abbbbcfe65050ac52ec4bdec0
commit-date: 2025-02-19
host: x86_64-unknown-linux-gnu
release: 1.87.0-nightly
LLVM version: 20.1.0
Internal compiler ID: nightly

@rustbot labels +C-optimization +A-debuginfo

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the Godbolt examples with rustc 1.87.0-nightly, comparing -Copt-level=3 with -Cdebuginfo=2 and -Cdebuginfo=0. Trace how assert_unchecked, is_power_of_two, and LLVM optimization interact in the compiler's debuginfo path. Done means the optimized assembly remains equivalent when debuginfo is enabled, including both foo and bar.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.