Skip to content

Wrong optimization #98568

Description

@Grobycn

I tried this code:

fn max_turbulence_size(arr: Vec<i32>) -> i32 {
    let mut prev = 0;
    let mut cnt = 0;
    let mut ret = 0;
    for w in arr.windows(2) {
        let d = w[1] - w[0];
        if d == 0 {
            cnt = 0;
        } else if d > 0 {
            if prev < 0 {
                cnt += 1;
            } else {
                cnt = 1;
            }
        } else {
            if prev > 0 {
                cnt += 1;
            } else {
                cnt = 1;
            }
        }
        // Uncomment the follow line will give the right answer
        // println!("{} {}", d, prev);
        ret = ret.max(cnt);
        // The follow line seems optimized out if we don't access `prev`, `d` simultaneously.
        prev = d;
    }
    ret + 1
}

fn main() {
    let v = vec![9,4,2,10,7,8,8,1,9];
    let ans = max_turbulence_size(v);
    // The right answer is 5. But when build with release, the output is 2
    println!("{}", ans);
}

I expected to see this happen: 5 is printed.

Instead, this happened: when build with release, the output is 2.

Meta

rustc --version --verbose:

rustc 1.58.0-nightly (2885c4748 2021-11-20)
binary: rustc
commit-hash: 2885c474823637ae69c5967327327a337aebedb2
commit-date: 2021-11-20
host: x86_64-unknown-linux-gnu
release: 1.58.0-nightly
LLVM version: 13.0.0

I also test all the following rust version at playground, and got wrong answer when build with release.

stable: 1.61.0
beta: 1.62.0-beta.6
nightly: 1.64.0-nightly (2022-06-25 20a6f3a)

Activity

  1. Uriopass commented on Jun 27, 2022

    @Uriopass

    Simplified the function a bit:

    pub fn buggy(arr: Vec<i32>) -> i32 {
        let mut prev = 0;
        let mut cnt = 0;
        // The entire loop is optimized away in release: https://godbolt.org/z/j538GxzT1
        for d in arr {
            if d > 0 {
                if prev < 0 {
                    cnt += 1;
                } else {
                    cnt = 1;
                }
            } else {
                if prev > 0 {
                    cnt += 1;
                } else {
                    cnt = 1;
                }
            }
            prev = d;
        }
        cnt
    }
    
    fn main() {
        let v = vec![-1,1];
        let ans = buggy(v);
        // The right answer is 2. But when build with release, the output is 1
        println!("{}", ans);
    }

    It seems that something assumes that d = prev in the loop. However doing it explicitely by changing the condition to if d == prev doesn't trigger the bug.

  2. SNCPlay42 commented on Jun 27, 2022

    @SNCPlay42
    Contributor

    Per godbolt this produced the correct result on 1.55 and misoptimises on 1.56 and later.

    @rustbot label A-codegen I-unsound T-compiler regression-from-stable-to-stable

  3. added
    A-codegenArea: Code generation
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jun 27, 2022
  4. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    on Jun 27, 2022
  5. self-assigned this
    on Jun 27, 2022
  6. nikic commented on Jun 27, 2022

    @nikic
    Contributor

    Works with -C no-prepopulate-passes, so presumably an LLVM miscompile. Assigning to me for investigation.

  7. nbdd0121 commented on Jun 27, 2022

    @nbdd0121
    Member

    This C version has the same issue: https://godbolt.org/z/E86n349nr, so likely a LLVM bug.

    @rustbot label: +A-LLVM

  8. nikic commented on Jun 27, 2022

    @nikic
    Contributor

    Miscompile still present on current main, and appears to be introduced during this -indvars transform: https://alive2.llvm.org/ce/z/I-4JjZ

  9. nikic commented on Jun 27, 2022

    @nikic
    Contributor

    Upstream issue: llvm/llvm-project#56242

  10. apiraino commented on Jun 27, 2022

    @apiraino
    Contributor

    WG-prioritization assigning priority (Zulip discussion).

    @rustbot label -I-prioritize +P-critical

  11. added and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jun 27, 2022
  12. anastygnome commented on Jun 29, 2022

    @anastygnome

    LLVM miscompile. The optimiser mistakenly analyses the loop as invariant.

  13. nikic commented on Jul 5, 2022

    @nikic
    Contributor
  14. pnkfelix commented on Jul 28, 2022

    @pnkfelix
    Contributor

    Closing as fixed. I believe PR #98567 fixed this in nightly, and I believe PR #99098 fixed this in beta.

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.A-codegenArea: Code generationC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-criticalCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-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