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

Optimize away BitAnd and BitOr when possible #74491

Merged
merged 1 commit into from
Jul 24, 2020

Conversation

xldenis
Copy link
Contributor

@xldenis xldenis commented Jul 18, 2020

This PR lets const_prop optimize away a | true == true , a & false == false and a * 0 = 0. While I was writing this I've realized that constant propagation misses a lot of opportunities. For example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2a4b45e772f214210a36749b27223bb0

Constant propagation doesn't seem to... propagate constants, additionally the way constant propagation is currently setup makes it tricky to add cases like a | false == a.

I tried to organize eval_rvalue_with_identities to make the pattern of the optimizations easier to see but it still obscurs what should be a simple peephole optmization.

cc @oli-obk

@rust-highfive
Copy link
Collaborator

r? @estebank

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 18, 2020
self.eval_rvalue_with_identities(rvalue, place)
}

// Attempts to use algebraic identities to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you a word.

@estebank
Copy link
Contributor

The code looks fine but I can't take a good look right now. r? @oli-obk for final review.

@xldenis xldenis force-pushed the constant-binop-opt branch 2 times, most recently from f52e125 to 7894f9f Compare July 20, 2020 17:30
@xldenis
Copy link
Contributor Author

xldenis commented Jul 20, 2020

I don't understand why CI is failing:

error: failed to run custom build command for `crossbeam-utils v0.7.2`

Caused by:
  process didn't exit successfully: `/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-rustc/release/build/crossbeam-utils-a40e83968eeae062/build-script-build` (exit code: 101)
  --- stderr
  thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: Other("missing patch version") }', /cargo/registry/src/github.com-1ecc6299db9ec823/autocfg-1.0.0/src/lib.rs:128:20

I have no idea what to do about this? It doesn't seem related to my changes but it's also not flakiness from what I can tell

@estebank
Copy link
Contributor

@xldenis can you try rebasing on top of a recent master? This doesn't seem like something caused by your PR and doing that might be enough to get CI to pass. Otherwise you might be introducing a bug in this PR and it's triggered in crossbeam-utils (this is happening in stage1, not stage0, so it's using the new code).

@xldenis
Copy link
Contributor Author

xldenis commented Jul 20, 2020

I just rebased it from master, so how would I reproduce this locally? do I just run a full stage1 build? reproduced the error just need to figure out why now

@oli-obk
Copy link
Contributor

oli-obk commented Jul 21, 2020

it seems to work now on CI, you just need to ./x.py fmt

@xldenis
Copy link
Contributor Author

xldenis commented Jul 21, 2020

this optimization causes a failure for a testcase, that I am unsure how to handle:

error: this operation will panic at runtime
  --> /Users/xavier/Code/rust/rust/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs:10:61
   |
LL |     const VOID: ! = { let x = 0 * std::mem::size_of::<T>(); [][x] };
   |                                                             ^^^^^ index out of bounds: the len is 0 but the index is 0
   |
   = note: `#[deny(unconditional_panic)]` on by default

previously it seems like this code would compile. What is the correct behavior here? Remove the optimization for multiplication? Or should this code not compile because it was a ! type anyways?

EDIT: that test was supposed to fail, my opt just changes where the error is raised. I fixed the test output.

@xldenis xldenis force-pushed the constant-binop-opt branch 2 times, most recently from aa4b013 to 060c9ee Compare July 21, 2020 11:59
@@ -9,11 +9,12 @@ struct PrintName<T>(T);
impl<T> PrintName<T> {
const VOID: ! = { let x = 0 * std::mem::size_of::<T>(); [][x] };
//~^ WARN any use of this value will cause an error
//~| ERROR this operation will panic at runtime [unconditional_panic]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add #![warn(unconditional_panic)] to this file so we test the original thing again. This line can the be changed to

Suggested change
//~| ERROR this operation will panic at runtime [unconditional_panic]
//~| WARN this operation will panic at runtime [unconditional_panic]

@oli-obk
Copy link
Contributor

oli-obk commented Jul 21, 2020

cc @lcnr this will make things worse for "some functions that depend on CTFE will evaluate successfully if that generic thing is not needed/used". We already have this situation elsewhere, but we may need to be more aggressive in putting things into the "unevaluated const" list. Right now it's just about generic constants, but after this PR we may need to put function calls of generic functions into it, too.

@oli-obk
Copy link
Contributor

oli-obk commented Jul 21, 2020

@xldenis this is not a blocker for this PR in general, We may should probably just move your optimization under mir-opt-level 3 until we have the scheme for function calls. What do you think?

@lcnr
Copy link
Contributor

lcnr commented Jul 21, 2020

@oli-obk because now 0 * std::mem::size_of::<T>() evaluates without requiring std::mem::size_of::<T>()?

So the main problem here is probably something like

fn test<T>() {
    let _ = [0; SOME_MONOMORPHIC_CONST * std::mem::size_of::<T>()];
}

That means we can't really fix this by just not looking into std::mem::size_of::<T>() as std::mem::size_of::<T>() won't exist in the optimized mir afaict.

It's probably cleaner to add a flag to mir which indicates if it depends on type parameters (during mir_built), so we don't have to care about this while optimizing 🤔

@bors
Copy link
Contributor

bors commented Jul 23, 2020

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jul 23, 2020
@rust-log-analyzer
Copy link
Collaborator

The job dist-i686-msvc of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@ehuss
Copy link
Contributor

ehuss commented Jul 23, 2020

@bors retry
rust-lang/cargo#8517

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 23, 2020
@Manishearth
Copy link
Member

@bors rollup=iffy

may have perf implications

@xldenis
Copy link
Contributor Author

xldenis commented Jul 23, 2020

it doesn't do anything unless mir-opt-level=3 so it shouldn't affect normal code.

@Manishearth
Copy link
Member

@bors rollup-

Okay, thanks.

@nnethercote we don't perf-check mir-opt=3, yes?

@nnethercote
Copy link
Contributor

@nnethercote we don't perf-check mir-opt=3, yes?

rustc-perf runs cargo rustc --release (opt builds) and cargo rustc (debug builds) and cargo rustc --profile=check (check builds) for each benchmark. No options affecting optimization or codegen are given, i.e. it uses the default mir-opt value, which appears to be 1.

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Jul 24, 2020
Optimize away BitAnd and BitOr when possible

This PR lets `const_prop` optimize away `a | true == true` , `a & false == false` and `a * 0 = 0`. While I was writing this I've realized that constant propagation misses a lot of opportunities. For example:  https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2a4b45e772f214210a36749b27223bb0

Constant propagation doesn't seem to... propagate constants, additionally the way constant propagation is currently setup makes it tricky to add cases like `a | false == a`.

I tried to organize `eval_rvalue_with_identities` to make the pattern of the optimizations easier to see but it still obscurs what should be a simple peephole optmization.

cc @oli-obk
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 24, 2020
…arth

Rollup of 8 pull requests

Successful merges:

 - rust-lang#72954 (revise RwLock for HermitCore)
 - rust-lang#74367 (Rearrange the pipeline of `pow` to gain efficiency)
 - rust-lang#74491 (Optimize away BitAnd and BitOr when possible)
 - rust-lang#74639 (Downgrade glibc to 2.11.1 for ppc, ppc64 and s390x)
 - rust-lang#74661 (Refactor `region_name`: add `RegionNameHighlight`)
 - rust-lang#74692 (delay_span_bug instead of silent ignore)
 - rust-lang#74698 (fixed error reporting for mismatched traits)
 - rust-lang#74715 (Add a system for creating diffs across multiple mir optimizations.)

Failed merges:

r? @ghost
@xldenis
Copy link
Contributor Author

xldenis commented Jul 24, 2020

@nnethercote is there anyway to specify flags for a perf run? I'm curious how much mir-opt-level=2/3 impacts it.

@Manishearth
Copy link
Member

Perf runs just do regular try builds, so I don't think that's possible.

You could, however, edit the CI files to use a different set of flags, you just have to do it twice to get the "before" test results.

@bors bors merged commit e59effe into rust-lang:master Jul 24, 2020
@xldenis xldenis deleted the constant-binop-opt branch July 25, 2020 00:52
bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 9, 2020
…li-obk

make `ConstEvaluatable` more strict

relevant zulip discussion: https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/topic/.60ConstEvaluatable.60.20generic.20functions/near/204125452

Let's see how much this impacts. Depending on how this goes this should probably be a future compat warning.

Short explanation: we currently forbid anonymous constants which depend on generic types, e.g. `[0; std::mem::size_of::<T>]` currently errors.

We previously checked this by evaluating the constant and returned an error if that failed. This however allows things like
```rust
const fn foo<T>() -> usize {
    if std::mem::size_of::<*mut T>() < 8 { // size of *mut T does not depend on T
        std::mem::size_of::<T>()
    } else {
        8
    }
}

fn test<T>() {
    let _ = [0; foo::<T>()];
}
```
which is a backwards compatibility hazard. This also has worrying interactions with mir optimizations (rust-lang#74491 (comment)) and intrinsics (rust-lang#74538).

r? `@oli-obk` `@eddyb`
bors added a commit to rust-lang-ci/rust that referenced this pull request Jan 9, 2023
Always permit ConstProp to exploit arithmetic identities

Fixes rust-lang#72751

Initially, I thought I would need to enable operand propagation then do something else, but actually rust-lang#74491 already has the fix for the issue in question! It looks like this optimization was put under MIR opt level 3 due to possible soundness/stability implications, then demoted further to MIR opt level 4 when MIR opt level 2 became associated with `--release`.

Perhaps in the past we were doing CTFE on optimized MIR? We aren't anymore, so this optimization has no stability implications.

r? `@oli-obk`
RalfJung pushed a commit to RalfJung/miri that referenced this pull request Jan 13, 2023
Always permit ConstProp to exploit arithmetic identities

Fixes rust-lang/rust#72751

Initially, I thought I would need to enable operand propagation then do something else, but actually rust-lang/rust#74491 already has the fix for the issue in question! It looks like this optimization was put under MIR opt level 3 due to possible soundness/stability implications, then demoted further to MIR opt level 4 when MIR opt level 2 became associated with `--release`.

Perhaps in the past we were doing CTFE on optimized MIR? We aren't anymore, so this optimization has no stability implications.

r? `@oli-obk`
@cuviper cuviper added this to the 1.47.0 milestone May 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.