Skip to content

Missing optimization around __rust_alloc and unknown functions if panic=unwind #46515

Description

@RalfJung

The following code gets optimized as expected:

pub fn test() -> bool {
    let x = &*Box::new(0);
    let y = &*Box::new(0);
    
    if x as *const _ == y as *const _ {
        return true;
    }
    return false;
}

becomes

; example::test
; Function Attrs: nounwind uwtable
define zeroext i1 @example::test() unnamed_addr #0 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality !dbg !18 {
  ret i1 false, !dbg !22
}

However, if I let the code call an unknown function instead of return, the optimization disappears:

pub fn test(f: fn()) {
    let x = &*Box::new(0);
    let y = &*Box::new(0);
    
    if x as *const _ == y as *const _ {
        f();
    }
}

becomes

; example::test
; Function Attrs: uwtable
define void @example::test(void ()* nocapture nonnull %f) unnamed_addr #1 personality i32 (i32, i32, i64, %"unwind::libunwind::_Unwind_Exception"*, %"unwind::libunwind::_Unwind_Context"*)* @rust_eh_personality !dbg !36 {
  %err.i.i.i10 = alloca %"core::mem::ManuallyDrop<alloc::allocator::AllocErr>", align 8
  %_5.i.i.i.i11 = alloca %"alloc::allocator::AllocErr", align 8
  %err.i.i.i = alloca %"core::mem::ManuallyDrop<alloc::allocator::AllocErr>", align 8
  %_5.i.i.i.i = alloca %"alloc::allocator::AllocErr", align 8
  %_9.sroa.9.i.i = alloca [16 x i8], align 8
  %_9.sroa.9.0.sroa_idx9.i.i = getelementptr inbounds [16 x i8], [16 x i8]* %_9.sroa.9.i.i, i64 0, i64 0, !dbg !38
  call void @llvm.lifetime.start(i64 16, i8* nonnull %_9.sroa.9.0.sroa_idx9.i.i), !dbg !38
  %0 = getelementptr inbounds %"core::mem::ManuallyDrop<alloc::allocator::AllocErr>", %"core::mem::ManuallyDrop<alloc::allocator::AllocErr>"* %err.i.i.i, i64 0, i32 0, i64 0, !dbg !48
  call void @llvm.lifetime.start(i64 24, i8* nonnull %0) #6, !dbg !48, !noalias !52
  %1 = call i8* @__rust_alloc(i64 4, i64 4, i8* nonnull %0) #6, !dbg !55, !noalias !52
  %2 = icmp eq i8* %1, null, !dbg !57
  br i1 %2, label %bb3.i.i.i, label %"_ZN35_$LT$alloc..boxed..Box$LT$T$GT$$GT$3new17he6f20be442f24a38E.exit", !dbg !61

[snip]

That seems wrong, why should the optimization stop kicking in? The corresponding C++ code does not have any problem:

#include <memory>

void test(void (*f)()) {
    auto x = std::make_unique<int>(0);
    auto y = std::make_unique<int>(0);

    if (&*x == &*y) {
        f();
    }
}

becomes

; Function Attrs: uwtable
define void @test(void (*)())(void ()* nocapture) local_unnamed_addr #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) !dbg !1204 {
  tail call void @llvm.dbg.value(metadata void ()* %0, i64 0, metadata !1208, metadata !1211), !dbg !1212
  tail call void @llvm.dbg.value(metadata %"class.std::unique_ptr"* undef, i64 0, metadata !1209, metadata !1213), !dbg !1214
  tail call void @llvm.dbg.value(metadata %"class.std::unique_ptr"* undef, i64 0, metadata !1209, metadata !1213), !dbg !1214
  tail call void @llvm.dbg.value(metadata %"class.std::unique_ptr"* undef, i64 0, metadata !1210, metadata !1213), !dbg !1215
  tail call void @llvm.dbg.value(metadata %"class.std::unique_ptr"* undef, i64 0, metadata !1210, metadata !1213), !dbg !1215
  tail call void @llvm.dbg.value(metadata %"class.std::unique_ptr"* undef, i64 0, metadata !1209, metadata !1213), !dbg !1214
  ret void, !dbg !1216
}

(I briefly thought maybe unwinding is the problem, but C++ should have the same kind of unwinding here.)

Activity

  1. added
    A-allocatorsArea: Custom and system allocators
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    I-slowIssue: Problems and improvements with respect to performance of generated code.
    on Dec 5, 2017
  2. RalfJung commented on Dec 5, 2017

    @RalfJung
    MemberAuthor

    @eddyb suggested I try -C panic=abort, and indeed, that makes all the difference. However, the optimization is sound even with unwinding panics, so it should happen either way.

  3. changed the title [-]Missing optimization around __rust_alloc and unknown functions[/-] [+]Missing optimization around __rust_alloc and unknown functions if panic=unwind[/+] on Dec 5, 2017
  4. RalfJung commented on Dec 10, 2017

    @RalfJung
    MemberAuthor

    Aha! I think I got it. In the IR for the Rust program, the unwinding branch calls @_ZN4core3ptr13drop_in_place17h91409ca23b53ad07E with the noinline attribute. So LLVM does not look through this call at all, and (I guess) it considers the pointer passed to it escaped. The pointer equality folding only happens if the pointer does not get escaped, so LLVM can guarantee that all comparisons of this pointer are folded consistently.

    So, to fix this, we'd have to remove this noinline -- at least for Box<T> where T does not itself implement Drop.

  5. added a commit that references this issue on Dec 10, 2017
    34cc5c8
  6. SimonSapin commented on Apr 1, 2018

    @SimonSapin
    Contributor

    CC @nox, @rust-lang/wg-codegen

  7. nox commented on Apr 1, 2018

    @nox
    Contributor

    Could this be preventing other optimisations than pointer equality folding? Cc @rust-lang/compiler

  8. nagisa commented on Apr 1, 2018

    @nagisa
    Member

    @nox definitely. My guess is that alias analysis is being overly conservative here and is declaring pointers to be MayAlias, rather than NoAlias, which what they definitely are. This will defeat many optimisations that check for aliasing (pretty much everything).

  9. RalfJung commented on Apr 2, 2018

    @RalfJung
    MemberAuthor

    However, pointer equality optimizations and aliasing information are (AFAIK) mostly unrelated -- in particular, optimizing equalities based on noalias is incorrect:

    int foo(restrict int *x, restrict int *y) {
      // The restrict will compile to noalias in LLVM. However, C does not let you
      // optimize the following unless both x and y are actually dereferenced.
      (x == y) ? 1 : 0
    }
  10. steveklabnik commented on Jan 20, 2020

    @steveklabnik
    Contributor

    Triage: this optimization still does not happen.

  11. 11 remaining items

  12. added a commit that references this issue on Jun 27, 2023
  13. added 2 commits that reference this issue on Oct 2, 2023
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-allocatorsArea: Custom and system allocatorsC-enhancementCategory: An issue proposing an enhancement or a PR with one.I-slowIssue: Problems and improvements with respect to performance of generated code.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions