Skip to content

Optimize if conditions with integer equality to matches on the integer #75144

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

Closed
oli-obk opened this issue Aug 4, 2020 · 11 comments · Fixed by #75370
Closed

Optimize if conditions with integer equality to matches on the integer #75144

oli-obk opened this issue Aug 4, 2020 · 11 comments · Fixed by #75370
Assignees
Labels
A-mir-opt Area: MIR optimizations C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@oli-obk
Copy link
Contributor

oli-obk commented Aug 4, 2020

If we generate the MIR for

fn black_box<T>(t: T) -> T { t }

fn main() {
    let x = black_box(42);
    
    let y = if x == 43 {
        "foo"
    } else {
        "bar"
    };
}

we get something like

        _3 = Eq(move _4, const 43i32);
        StorageDead(_4);
        switchInt(_3) -> [false: bb2, otherwise: bb3];

which we could also write as

        switchInt(_4) -> [43i32: bb3, otherwise: bb2];

(plus move the StorageDead to the beginning of bb2 and bb3). The same goes for != (Ne).

cc @rust-lang/wg-mir-opt

@oli-obk oli-obk added the A-mir-opt Area: MIR optimizations label Aug 4, 2020
@jonas-schievink jonas-schievink added C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 4, 2020
@simonvandel
Copy link
Contributor

I would like to work on this
@rustbot claim

@simonvandel
Copy link
Contributor

Hi @oli-obk
While working on this I see that SwitchInt values are of type u128. This would mean that this optimization could only be done if the integer value is unsigned.

Is it an oversight that SwitchInt does not allow to switch on signed values?

@bjorn3
Copy link
Member

bjorn3 commented Aug 8, 2020

The u128 is just a zero-extended version of the value to switch on. For example if i == -1i8 would be something like Switch(i) -> [0xff: bb1, otherwise: bb2].

@tmiasko
Copy link
Contributor

tmiasko commented Aug 18, 2020

FYI: Codegen does the this transformation in other direction:

let switch_llty = bx.immediate_backend_type(bx.layout_of(switch_ty));
let llval = bx.const_uint_big(switch_llty, values[0]);
let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice());
bx.cond_br(cmp, lltrue, llfalse);

@oli-obk
Copy link
Contributor Author

oli-obk commented Aug 19, 2020

Hmm... that's good to know. Maybe we should additionally have the opposite optimization as a cleanup before we go into codegen, then this codegen arm can just become an assert.

@bjorn3
Copy link
Member

bjorn3 commented Aug 19, 2020

For cg_clif Eq + SwitchInt results in suboptimal codegen over SwitchInt. The former has to materialize the flag into a register before comparing it again, while the later can just perform the comparison and directly jump on the flag. This is because bools have to be represented as integers at the abi boundary and when loading and storing memory. Keeping them as bools until these points results in much more complexity than turning them into integers and compare them in the SwitchInt impl like any other integer. Also it is likely quicker for cg_llvm to switch to icmp + br_if during codegen than it is to rewrite the mir with the associated possibility of a reallocation of the statements vec.

@oli-obk
Copy link
Contributor Author

oli-obk commented Aug 19, 2020

so... should we remove that transformation from codegen_ssa and let the backend handle it?

@bjorn3
Copy link
Member

bjorn3 commented Aug 19, 2020

I think so. It can wait though, as I don't yet use codegen_ssa anyway.

@simonvandel
Copy link
Contributor

So we still want to do the MIR optimization? I understood from @bjorn3 that switching directly on the integer would be beneficial for cranelift but not necessarily for llvm. Did I get that right?

@bjorn3
Copy link
Member

bjorn3 commented Aug 19, 2020

I think that is correct.

@oli-obk
Copy link
Contributor Author

oli-obk commented Aug 20, 2020

Yes, we still want this optimization. The llvm backend already handles this change correctly, so I think we should just keep the optimization unconditionally since it simplifies the MIR.

bors added a commit to rust-lang-ci/rust that referenced this issue Aug 29, 2020
…int-to-switch, r=oli-obk

New pass to optimize `if`conditions on integrals to switches on the integer

Fixes rust-lang#75144

 Pass to convert `if` conditions on integrals into switches on the integral.
 For an example, it turns something like

 ```
 _3 = Eq(move _4, const 43i32);
 StorageDead(_4);
 switchInt(_3) -> [false: bb2, otherwise: bb3];
 ```

 into:

 ```
 switchInt(_4) -> [43i32: bb3, otherwise: bb2];
 ```
@bors bors closed this as completed in 23dda1b Aug 29, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-mir-opt Area: MIR optimizations C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants