Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
rollup merge of rust-lang#21418: Aatch/assume-refcount
The reference count can never be 0, unless we're about to drop the data
completely. Using the `assume` intrinsic allows us to inform LLVM about
that invariant, meaning it can avoid unnecessary drops.

---

Before and after IR: https://gist.github.com/Aatch/3786d20df2edaad6a0e8

Generated from the example in rust-lang#13018

Fixes rust-lang#13018
  • Loading branch information
alexcrichton committed Jan 21, 2015
2 parents a6780d8 + a7525bc commit 229243c
Showing 1 changed file with 37 additions and 4 deletions.
41 changes: 37 additions & 4 deletions src/liballoc/rc.rs
Expand Up @@ -160,6 +160,7 @@ use core::option::Option::{Some, None};
use core::ptr::{self, PtrExt};
use core::result::Result;
use core::result::Result::{Ok, Err};
use core::intrinsics::assume;

use heap::deallocate;

Expand Down Expand Up @@ -905,10 +906,24 @@ trait RcBoxPtr<T> {
fn strong(&self) -> uint { self.inner().strong.get() }

#[inline]
fn inc_strong(&self) { self.inner().strong.set(self.strong() + 1); }
fn inc_strong(&self) {
let strong = self.strong();
// The reference count is always at least one unless we're about to drop the type
// This allows the bulk of the destructor to be omitted in cases where we know that
// the reference count must be > 0.
unsafe { assume(strong > 0); }
self.inner().strong.set(strong + 1);
}

#[inline]
fn dec_strong(&self) { self.inner().strong.set(self.strong() - 1); }
fn dec_strong(&self) {
let strong = self.strong();
// The reference count is always at least one unless we're about to drop the type
// This allows the bulk of the destructor to be omitted in cases where we know that
// the reference count must be > 0
unsafe { assume(strong > 0); }
self.inner().strong.set(strong - 1);
}

#[inline]
fn weak(&self) -> uint { self.inner().weak.get() }
Expand All @@ -922,12 +937,30 @@ trait RcBoxPtr<T> {

impl<T> RcBoxPtr<T> for Rc<T> {
#[inline(always)]
fn inner(&self) -> &RcBox<T> { unsafe { &(**self._ptr) } }
fn inner(&self) -> &RcBox<T> {
unsafe {
// Safe to assume this here, as if it weren't true, we'd be breaking
// the contract anyway.
// This allows the null check to be elided in the destructor if we
// manipulated the reference count in the same function.
assume(!self._ptr.is_null());
&(**self._ptr)
}
}
}

impl<T> RcBoxPtr<T> for Weak<T> {
#[inline(always)]
fn inner(&self) -> &RcBox<T> { unsafe { &(**self._ptr) } }
fn inner(&self) -> &RcBox<T> {
unsafe {
// Safe to assume this here, as if it weren't true, we'd be breaking
// the contract anyway
// This allows the null check to be elided in the destructor if we
// manipulated the reference count in the same function.
assume(!self._ptr.is_null());
&(**self._ptr)
}
}
}

#[cfg(test)]
Expand Down

0 comments on commit 229243c

Please sign in to comment.