Skip to content

MIR building: Place should be moved if not used later #75993

Closed
@simonvandel

Description

@simonvandel

The following MIR can be improved by converting a copy to a move: https://godbolt.org/z/jnKK4f .
Observed

fn opt_char(_1: char) -> u32 {
    debug x => _1;                       // in scope 0 at ./example.rs:1:17: 1:18
    let mut _0: u32;                     // return place in scope 0 at ./example.rs:1:29: 1:32
    let mut _2: bool;                    // in scope 0 at ./example.rs:2:8: 2:28
    let mut _3: char;                    // in scope 0 at ./example.rs:2:8: 2:9
    let mut _4: bool;                    // in scope 0 at ./example.rs:2:20: 2:28
    let mut _5: char;                    // in scope 0 at ./example.rs:2:20: 2:21

    bb0: {
        StorageLive(_2);                 // scope 0 at ./example.rs:2:8: 2:28
        StorageLive(_3);                 // scope 0 at ./example.rs:2:8: 2:9
        _3 = _1;                         // scope 0 at ./example.rs:2:8: 2:9
        switchInt(move _3) -> ['b': bb1, otherwise: bb2]; // scope 0 at ./example.rs:2:8: 2:28
    }

    bb1: {
        StorageDead(_3);                 // scope 0 at ./example.rs:2:8: 2:28
        _2 = const true;                 // scope 0 at ./example.rs:2:8: 2:28
        goto -> bb3;                     // scope 0 at ./example.rs:2:8: 2:28
    }

    bb2: {
        StorageDead(_3);                 // scope 0 at ./example.rs:2:8: 2:28
        StorageLive(_4);                 // scope 0 at ./example.rs:2:20: 2:28
        StorageLive(_5);                 // scope 0 at ./example.rs:2:20: 2:21
        _5 = _1;                         // scope 0 at ./example.rs:2:20: 2:21
        _4 = Eq(move _5, const 'a');     // scope 0 at ./example.rs:2:20: 2:28
        StorageDead(_5);                 // scope 0 at ./example.rs:2:27: 2:28
        _2 = Ne(_4, const false);        // scope 0 at ./example.rs:2:8: 2:28
        goto -> bb3;                     // scope 0 at ./example.rs:2:8: 2:28
    }

    bb3: {
        StorageDead(_4);                 // scope 0 at ./example.rs:2:27: 2:28
        switchInt(_2) -> [false: bb4, otherwise: bb5]; // scope 0 at ./example.rs:2:5: 2:45
    }

    bb4: {
        _0 = const 1_u32;                // scope 0 at ./example.rs:2:42: 2:43
        goto -> bb6;                     // scope 0 at ./example.rs:2:5: 2:45
    }

    bb5: {
        _0 = const 0_u32;                // scope 0 at ./example.rs:2:31: 2:32
        goto -> bb6;                     // scope 0 at ./example.rs:2:5: 2:45
    }

    bb6: {
        StorageDead(_2);                 // scope 0 at ./example.rs:3:1: 3:2
        return;                          // scope 0 at ./example.rs:3:2: 3:2
    }
}

Expected: _2 is moved into the switchInt as it has no uses later. _4 could similarly be moved into the Ne comparison

fn opt_char(_1: char) -> u32 {
    debug x => _1;                       // in scope 0 at ./example.rs:1:17: 1:18
    let mut _0: u32;                     // return place in scope 0 at ./example.rs:1:29: 1:32
    let mut _2: bool;                    // in scope 0 at ./example.rs:2:8: 2:28
    let mut _3: char;                    // in scope 0 at ./example.rs:2:8: 2:9
    let mut _4: bool;                    // in scope 0 at ./example.rs:2:20: 2:28
    let mut _5: char;                    // in scope 0 at ./example.rs:2:20: 2:21

    bb0: {
        StorageLive(_2);                 // scope 0 at ./example.rs:2:8: 2:28
        StorageLive(_3);                 // scope 0 at ./example.rs:2:8: 2:9
        _3 = _1;                         // scope 0 at ./example.rs:2:8: 2:9
        switchInt(move _3) -> ['b': bb1, otherwise: bb2]; // scope 0 at ./example.rs:2:8: 2:28
    }

    bb1: {
        StorageDead(_3);                 // scope 0 at ./example.rs:2:8: 2:28
        _2 = const true;                 // scope 0 at ./example.rs:2:8: 2:28
        goto -> bb3;                     // scope 0 at ./example.rs:2:8: 2:28
    }

    bb2: {
        StorageDead(_3);                 // scope 0 at ./example.rs:2:8: 2:28
        StorageLive(_4);                 // scope 0 at ./example.rs:2:20: 2:28
        StorageLive(_5);                 // scope 0 at ./example.rs:2:20: 2:21
        _5 = _1;                         // scope 0 at ./example.rs:2:20: 2:21
        _4 = Eq(move _5, const 'a');     // scope 0 at ./example.rs:2:20: 2:28
        StorageDead(_5);                 // scope 0 at ./example.rs:2:27: 2:28
        _2 = Ne(move _4, const false);        // scope 0 at ./example.rs:2:8: 2:28
        goto -> bb3;                     // scope 0 at ./example.rs:2:8: 2:28
    }

    bb3: {
        StorageDead(_4);                 // scope 0 at ./example.rs:2:27: 2:28
        switchInt(move _2) -> [false: bb4, otherwise: bb5]; // scope 0 at ./example.rs:2:5: 2:45
    }

    bb4: {
        _0 = const 1_u32;                // scope 0 at ./example.rs:2:42: 2:43
        goto -> bb6;                     // scope 0 at ./example.rs:2:5: 2:45
    }

    bb5: {
        _0 = const 0_u32;                // scope 0 at ./example.rs:2:31: 2:32
        goto -> bb6;                     // scope 0 at ./example.rs:2:5: 2:45
    }

    bb6: {
        StorageDead(_2);                 // scope 0 at ./example.rs:3:1: 3:2
        return;                          // scope 0 at ./example.rs:3:2: 3:2
    }
}
Original issue - no longer relevant

In the following snippet https://godbolt.org/z/6f3xs3 , I would have expected _2 to be moved into the switchInt since it is not used afterwards.

This was noticed in #75370 (comment) where the proposed MIR-optimization currently fails to remove a comparison..

Observed:

fn f(_1: i8) -> i8 {
    debug x => _1;                       // in scope 0 at ./example.rs:1:6: 1:7
    let mut _0: i8;                      // return place in scope 0 at ./example.rs:1:16: 1:18
    let mut _2: bool;                    // in scope 0 at ./example.rs:2:8: 2:15

    bb0: {
        StorageLive(_2);                 // scope 0 at ./example.rs:2:8: 2:15
        _2 = Eq(move _1, const 42_i8);   // scope 0 at ./example.rs:2:8: 2:15
        switchInt(_2) -> [false: bb1, otherwise: bb2]; // scope 0 at ./example.rs:2:5: 2:32
    }

    bb1: {
        _0 = const 1_i8;                 // scope 0 at ./example.rs:2:29: 2:30
        goto -> bb3;                     // scope 0 at ./example.rs:2:5: 2:32
    }

    bb2: {
        _0 = const 0_i8;                 // scope 0 at ./example.rs:2:18: 2:19
        goto -> bb3;                     // scope 0 at ./example.rs:2:5: 2:32
    }

    bb3: {
        StorageDead(_2);                 // scope 0 at ./example.rs:3:1: 3:2
        return;                          // scope 0 at ./example.rs:3:2: 3:2
    }
}

Expected:

fn f(_1: i8) -> i8 {
    debug x => _1;                       // in scope 0 at ./example.rs:1:6: 1:7
    let mut _0: i8;                      // return place in scope 0 at ./example.rs:1:16: 1:18
    let mut _2: bool;                    // in scope 0 at ./example.rs:2:8: 2:15

    bb0: {
        StorageLive(_2);                 // scope 0 at ./example.rs:2:8: 2:15
        _2 = Eq(move _1, const 42_i8);   // scope 0 at ./example.rs:2:8: 2:15
        switchInt(move _2) -> [false: bb1, otherwise: bb2]; // scope 0 at ./example.rs:2:5: 2:32
    }

    bb1: {
        _0 = const 1_i8;                 // scope 0 at ./example.rs:2:29: 2:30
        goto -> bb3;                     // scope 0 at ./example.rs:2:5: 2:32
    }

    bb2: {
        _0 = const 0_i8;                 // scope 0 at ./example.rs:2:18: 2:19
        goto -> bb3;                     // scope 0 at ./example.rs:2:5: 2:32
    }

    bb3: {
        StorageDead(_2);                 // scope 0 at ./example.rs:3:1: 3:2
        return;                          // scope 0 at ./example.rs:3:2: 3:2
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-mir-optArea: MIR optimizationsC-enhancementCategory: An issue proposing an enhancement or a PR with one.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions