Skip to content

Missing optimization when transmuting enums #109958

@Jarcho

Description

@Jarcho
Contributor

Transmuting a fieldless enum to it's integer type causes the optimizer to lose track of it's possible values. Note that as casts are optimized correctly.

Example:

#[repr(u32)]
enum E { X = 0 }
fn f(x: E) {
    assert_eq!(unsafe { core::mem::transmute::<E, i32>(x) }, 0); // Not optimized out
    assert_eq!(x as i32, 0); // optimized out
}

cc Lokathor/bytemuck#175

Activity

added
I-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.
on Apr 5, 2023
nikic

nikic commented on Apr 5, 2023

@nikic
Contributor

Godbolt: https://rust.godbolt.org/z/ExbzPGf7s

cc @scottmcm in case your scalar transmute changes are relevant.

Not an LLVM problem because the necessary information is never provided to LLVM.

clubby789

clubby789 commented on Apr 5, 2023

@clubby789
Contributor

assumer(range.end, BinOp::Ge);
assumer(range.start, BinOp::Le);

We emit a couple of assume's when casting an enum with as but no extra info is emitted for a transmute

erikdesjardins

erikdesjardins commented on Apr 5, 2023

@erikdesjardins
Contributor

If we could just put range metadata on arguments, we could emit

define noundef i32 @f(i32 noundef range(!0) %x)
...
!0 = !{i32 0, i32 1}

and then this and other cases (e.g. NonZeroX::get) would work without us having to add assumes for every conversion.

I actually have a partial implementation of this here. Though I assume allowing attributes to depend on metadata nodes would be untenable, they're probably separate intentionally? (@nikic thoughts?)

(My implementation also doesn't fully work because of this problem with RAUW on forward-declared metadata: erikdesjardins/llvm-project@c1342ac#diff-100c1b4a714c7d260fc41ac27cc8d2d5857d54021f16d73debc71125d110e7daR2809-R2831)

The alternative would be something like range(0, 1, 4, 5), where it doesn't use metadata to represent the ranges, and stores it in a bespoke attribute type, but I think this would also be a bit awkward. All range metadata handling code depends on the metadata representation (this could be refactored for sure), and attributes aren't equipped to hold variable-size arguments except strings, so I think some significant changes to Attribute would be required.

(Of course the other alternative would be to just add the assumes, but it feels a bit unsatisfying)

scottmcm

scottmcm commented on Apr 5, 2023

@scottmcm
Member

Transmuting a fieldless enum to it's integer type causes the optimizer to lose track of it's possible values.

If you do this directly to the repr type, it should no longer be a problem, since we MIR-optimize a transmute like that to a discriminant read: https://github.com/rust-lang/rust/pull/109612/files#diff-4f93c014037e17032815fe49c647952b4faf26aff2007afd9a36106f8d0f39f6R86-R87.

Hmm, playground is only on the 2023-04-02 nightly, so I can't look there to see if my change improved things.

scottmcm

scottmcm commented on Apr 5, 2023

@scottmcm
Member

With #109843, this:

// CHECK-LABEL: @check_to_enum(
#[no_mangle]
pub unsafe fn check_to_enum(x: i8) -> SmallEnum {
    transmute(x)
}

// CHECK-LABEL: @check_from_enum(
#[no_mangle]
pub unsafe fn check_from_enum(x: SmallEnum) -> i8 {
    transmute(x)
}

just emits

; Function Attrs: uwtable
define noundef i8 @check_to_enum(i8 noundef %x) unnamed_addr #0 {
start:
  ret i8 %x
}

; Function Attrs: uwtable
define noundef i8 @check_from_enum(i8 noundef %x) unnamed_addr #0 {
start:
  ret i8 %x
}

since it's passed as an immediate.

Loading an enum puts !range metadata on the load, but since there's no load here the OperandValue stuff I'm doing in the new code doesn't do anything with the niches.

added a commit that references this issue on Apr 20, 2023
7e23d18
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

    C-bugCategory: This is a bug.I-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

      Participants

      @nikic@erikdesjardins@Jarcho@clubby789@scottmcm

      Issue actions

        Missing optimization when transmuting enums · Issue #109958 · rust-lang/rust