Skip to content

Limited constant propagation on Enum tag when Box is involved  #106936

Open
@douglas-raillard-arm

Description

@douglas-raillard-arm

Experiments done on https://godbolt.org/ with the nightly toolchain and flags -C opt-level=3 --edition=2021. Also reproducible with rustc 1.66. Results seem to be the same on both x86_64 and aarch64.

  • rustc 1.66:
rustc 1.66.0 (69f9c33d7 2022-12-12)
binary: rustc
commit-hash: 69f9c33d71c871fc16ac445211281c6e7a340943
commit-date: 2022-12-12
host: x86_64-unknown-linux-gnu
release: 1.66.0
LLVM version: 15.0.2
Compiler returned: 0
  • rustc nightly:
rustc 1.68.0-nightly (0b90256ad 2023-01-13)
binary: rustc
commit-hash: 0b90256ada21c6a81b4c18f2c7a23151ab5fc232
commit-date: 2023-01-13
host: x86_64-unknown-linux-gnu
release: 1.68.0-nightly
LLVM version: 15.0.6
Compiler returned: 0

The expected generated code is something like:

example::foo:
        mov     eax, 94
        ret

In this code, rustc/LLVM fails to see through the 2 levels of match required to compute the return value of 94 at compile time when MyEnum has a MyEnum::B variant containing a Box:

pub enum MyEnum {
    A(u32),
    // Uncommenting that will wreak the generated code
    // B(Box<MyEnum>)
}

pub fn foo() -> u32 {
    let x = MyEnum::A(87);

    let y = {
        let y1 = MyEnum::A(4);
        let y2 = MyEnum::A(3);

        match (y1, y2) {
            (MyEnum::A(x), MyEnum::A(y)) => MyEnum::A(x + y),
            _ => panic!()
        }
    };
    let y = MyEnum::A(7);

    match (x, y) {
        (MyEnum::A(x), MyEnum::A(y)) => x + y,
        _ => panic!(),
    }
}

This does not break if Enum::B(Box<u32>) is used, but breaks again for Box<MyEnum> or Box<S> with S being a newtype struct:

pub struct S(u32);
impl Drop for S {
    #[inline(never)]
    fn drop(&mut self) {
        println!("hello");
    }
}

So it looks like whenever there is Box variant with T containing a non-inlineable drop (either because of side effect or because it's recursive), the optim breaks.

Also, even worse, using an alias in foo such as type MyEnum2 = MyEnum; makes the optim break as well for MyEnum::B(Box<u32>), which is a regression compared to the non-aliased use, i.e.:

pub enum MyEnum {
    A(u32),
    // Box of a !Drop type optimized fine in previous example, but breaks here when a `type MyEnum2 = MyEnum;` in `foo()`
    B(Box<u32>)
}

pub fn foo() -> u32 {
    // Rewrite the function to use `MyEnum` instead of the alias and the generated code is good. Use the alias such as in this example and the optim breaks.
    type MyEnum2 = MyEnum;

    let x = MyEnum2::A(87);

    let y = {
        let y1 = MyEnum2::A(4);
        let y2 = MyEnum2::A(3);

        match (y1, y2) {
            (MyEnum2::A(x), MyEnum2::A(y)) => MyEnum2::A(x + y),
            _ => panic!()
        }
    };
    let y = MyEnum2::A(7);

    match (x, y) {
        (MyEnum2::A(x), MyEnum2::A(y)) => x + y,
        _ => panic!(),
    }
}

Note that in all these examples, rustc/LLVM is able to "see through" the leaf level of match, i.e. it will optimize y1 + y2 into MyEnum::A(7), but then it won't do the same for the outer layer x + y.

Activity

douglas-raillard-arm

douglas-raillard-arm commented on Jan 16, 2023

@douglas-raillard-arm
Author

Also note that if the B variant is impossible to build, everything is optimized well:

enum Void {}
pub enum MyEnum {
    A(u32),
    B(Void, Box<MyEnum>)
}
clubby789

clubby789 commented on Mar 27, 2023

@clubby789
Contributor

@rustbot label +A-const-prop +I-slow +A-llvm

added
A-const-propArea: Constant propagation
A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
I-slowIssue: Problems and improvements with respect to performance of generated code.
on Mar 27, 2023
added
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
on Apr 5, 2023
added
C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing such
on Feb 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.A-const-propArea: Constant propagationC-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-slowIssue: Problems and improvements with respect to performance of generated code.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

        Participants

        @clubby789@douglas-raillard-arm@workingjubilee@rustbot@Noratrieb

        Issue actions

          Limited constant propagation on Enum tag when Box is involved · Issue #106936 · rust-lang/rust