Repository: rust
Issue: Flaky constant propagation results in loss of SIMD optimization
Labels: A-LLVM,T-compiler,C-optimization

<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
along with any information you feel relevant to replicating the bug.
-->

We have observed unexpected loss of vectorization effort by the compiler when compiling the code below. Target was amd64 machine with AVX2 instruction set supported.

```rust
type T = u32;
#[inline(never)]
fn check_dumb(n:u32, max_val:T)->Option<(T,T,T,T)>{                
    for x in 1..max_val{
        for y in 1..max_val{        
            for z in 0..max_val{
                if x.pow(n) + y.pow(n) == z.pow(n){
                    return Some((x,y,z,n as T));
                }                                
                
            }
        }
    }
    return None
}

#[inline(never)]
fn check_smart(n:u32, max_val:T)->Option<(T,T,T,T)>{
    
    const STRIDE:usize=24;

    let zarr_base = {
        let mut t = [0;STRIDE];
        for i in 0..STRIDE{
            t[i] = 1 + i as T;
        }
        t
    };
                
    for x in 1..max_val{
        //let xpow = x.pow(n); // manually "lifting" this operation from inner loop "fixes" the optimizer
        for y in 1..max_val{
            //let ypow = y.pow(n);  // manually "lifting" this operation from inner loop "fixes" the optimizer
            let mut zarr = zarr_base;

            for z_i in 0..max_val/STRIDE as T{

                for i in 0..STRIDE{
                    zarr[i] += STRIDE as T;
                }

                let mut zarr3 = [0;STRIDE];
                for i in 0..STRIDE{
                    zarr3[i] = zarr[i] *zarr[i]*zarr[i] ;
                }     

                let found = zarr3.iter().any(|&i|{
                    x.pow(n) + y.pow(n) == i           
                    // xpow + ypow == i           // see comments above for why this might work
                });

                if found {    
                    return Some((x,y,z_i * STRIDE as T,n as T));
                }                                
                
            }
        }
    }
    return None
}


fn main() {
    
    let n = 3; //this const value should apply for the entire main() function
    {
        let s = std::time::Instant::now();
        println!("Check_dumb n={} returned {:?}",n, check_dumb(n, 1000));
        println!("Time taken {:?}", s.elapsed());
    }
    {
        //Commenting out the line below results in loss of vectorization in check_smart!!!
        let n = 3;
        let s = std::time::Instant::now();
        println!("Check with simd n={} returned {:?}",n, check_smart(n, 1000));
        println!("Time taken {:?}", s.elapsed());
    }
}
```

I expected to see this happen: the function check_smart should leverage vectorization and SIMD instructions, while function check_dumb should be unable to do so. I should not have to specify n=3 twice in main(), as value of n from outer scope should be propagated into function check_smart. The vectorization in check_smart should happen for any value of n (even when n is not known ahead of time).

Instead, this happened: the ability of the compiler to actually perform SIMD vectorization of check_smart seems to depend on whether x.pow(n) can panic. When value of n is not propagated into the function, the optimizer fails to perform vectorization resulting in massive loss of performance.

### Meta


`rustc --version --verbose`:
```
rustc 1.76.0 (07dca489a 2024-02-04)
binary: rustc
commit-hash: 07dca489ac2d933c78d3c5158e3f43beefeb02ce
commit-date: 2024-02-04
host: x86_64-unknown-linux-gnu
release: 1.76.0
LLVM version: 17.0.6
```
Nightly compiler exhibits identical behavior.

CPU is Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz
<!--
Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
environment. E.g. `RUST_BACKTRACE=1 cargo build`.
-->