Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
theemathas opened this issue Feb 21, 2025 · 2 comments
Labels
A-debuginfo Area: Debugging information in compiled programs (DWARF, PDB, etc.) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. C-optimization Category: An issue highlighting optimization opportunities or PRs implementing such needs-triage This issue may need triage. Remove it if it has been sufficiently triaged.

Comments

@theemathas
Copy link
Contributor

theemathas commented Feb 21, 2025

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

@theemathas theemathas added the C-bug Category: This is a bug. label Feb 21, 2025
@rustbot rustbot added needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. A-debuginfo Area: Debugging information in compiled programs (DWARF, PDB, etc.) C-optimization Category: An issue highlighting optimization opportunities or PRs implementing such labels Feb 21, 2025
@bjorn3
Copy link
Member

bjorn3 commented Feb 21, 2025

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

It absolutely does. Among other things we change the codegen for locals to put them on the stack by default to be able to point a debuginfo intrinsic at this stackslot. LLVM generally optimizes this back into registers and in doing so updates the debuginfo, but it absolutely can affect the output assembly. Rustc doesn't behave the same way as GCC in avoiding any codegen differences when optimizations are enabled.

@nikic nikic added the A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. label Feb 21, 2025
@nikic
Copy link
Contributor

nikic commented Feb 21, 2025

Upstream issue: llvm/llvm-project#128152

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-debuginfo Area: Debugging information in compiled programs (DWARF, PDB, etc.) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. C-optimization Category: An issue highlighting optimization opportunities or PRs implementing such needs-triage This issue may need triage. Remove it if it has been sufficiently triaged.
Projects
None yet
Development

No branches or pull requests

4 participants