about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-01-27 12:06:47 -0800
committerbors <bors@rust-lang.org>2014-01-27 12:06:47 -0800
commitfeacb594669f505d2f7ba0805c7fd5c5d7d28bad (patch)
tree345b03546629c7fc63d39da3f2ec433b6d168f61 /src/libstd
parentd6d7812da841ddedf6c765eebb655be9866956ce (diff)
parentb4bb8c0f4ee1234e9d84aa7cb1a9ad691a509212 (diff)
downloadrust-feacb594669f505d2f7ba0805c7fd5c5d7d28bad.tar.gz
rust-feacb594669f505d2f7ba0805c7fd5c5d7d28bad.zip
auto merge of #11841 : huonw/rust/noinline-fail, r=alexcrichton
In two ways:
- for a plain `fail!(a)` we make the generic part of `begin_unwind` as small as possible (makes `fn main() { fail!() }` compile 2-3x faster, due to less monomorphisation bloat)
- for `fail!("format {}", "string")`, we avoid touching the generics completely by doing the formatting in a specialised function, which (with optimisations) saves a function call at the call-site of `fail!`. (This one has significantly less benefit than the first.)
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/macros.rs14
-rw-r--r--src/libstd/rt/mod.rs2
-rw-r--r--src/libstd/rt/unwind.rs33
3 files changed, 43 insertions, 6 deletions
diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs
index e7844101c5f..4032b63790b 100644
--- a/src/libstd/macros.rs
+++ b/src/libstd/macros.rs
@@ -48,8 +48,18 @@ macro_rules! fail(
         ::std::rt::begin_unwind($msg, file!(), line!())
     );
     ($fmt:expr, $($arg:tt)*) => (
-        ::std::rt::begin_unwind(format!($fmt, $($arg)*), file!(), line!())
-    )
+        {
+            // a closure can't have return type !, so we need a full
+            // function to pass to format_args!, *and* we need the
+            // file and line numbers right here; so an inner bare fn
+            // is our only choice.
+            #[inline]
+            fn run_fmt(fmt: &::std::fmt::Arguments) -> ! {
+                ::std::rt::begin_unwind_fmt(fmt, file!(), line!())
+            }
+            format_args!(run_fmt, $fmt, $($arg)*)
+        }
+        )
 )
 
 #[macro_export]
diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs
index 56f6c8f8e6c..0e30f3e2efd 100644
--- a/src/libstd/rt/mod.rs
+++ b/src/libstd/rt/mod.rs
@@ -69,7 +69,7 @@ use self::task::{Task, BlockedTask};
 pub use self::util::default_sched_threads;
 
 // Export unwinding facilities used by the failure macros
-pub use self::unwind::{begin_unwind, begin_unwind_raw};
+pub use self::unwind::{begin_unwind, begin_unwind_raw, begin_unwind_fmt};
 
 // FIXME: these probably shouldn't be public...
 #[doc(hidden)]
diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs
index 6f3aa4c4fd0..25a92148e96 100644
--- a/src/libstd/rt/unwind.rs
+++ b/src/libstd/rt/unwind.rs
@@ -58,6 +58,7 @@
 use any::{Any, AnyRefExt};
 use c_str::CString;
 use cast;
+use fmt;
 use kinds::Send;
 use option::{Some, None, Option};
 use prelude::drop;
@@ -382,17 +383,43 @@ pub fn begin_unwind_raw(msg: *u8, file: *u8, line: uint) -> ! {
     begin_unwind(msg, file, line as uint)
 }
 
+/// The entry point for unwinding with a formatted message.
+///
+/// This is designed to reduce the amount of code required at the call
+/// site as much as possible (so that `fail!()` has as low an implact
+/// on (e.g.) the inlining of other functions as possible), by moving
+/// the actual formatting into this shared place.
+#[inline(never)] #[cold]
+pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
+    begin_unwind_inner(~fmt::format(msg), file, line)
+}
+
 /// This is the entry point of unwinding for fail!() and assert!().
-#[inline(never)] #[cold] // this is the slow path, please never inline this
+#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
 pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
-    // Note that this should be the only allocation performed in this block.
+    // Note that this should be the only allocation performed in this code path.
     // Currently this means that fail!() on OOM will invoke this code path,
     // but then again we're not really ready for failing on OOM anyway. If
     // we do start doing this, then we should propagate this allocation to
     // be performed in the parent of this task instead of the task that's
     // failing.
-    let msg = ~msg as ~Any;
 
+    // see below for why we do the `Any` coercion here.
+    begin_unwind_inner(~msg, file, line)
+}
+
+
+/// The core of the unwinding.
+///
+/// This is non-generic to avoid instantiation bloat in other crates
+/// (which makes compilation of small crates noticably slower). (Note:
+/// we need the `Any` object anyway, we're not just creating it to
+/// avoid being generic.)
+///
+/// Do this split took the LLVM IR line counts of `fn main() { fail!()
+/// }` from ~1900/3700 (-O/no opts) to 180/590.
+#[inline(never)] #[cold] // this is the slow path, please never inline this
+fn begin_unwind_inner(msg: ~Any, file: &'static str, line: uint) -> ! {
     let mut task;
     {
         let msg_s = match msg.as_ref::<&'static str>() {