Skip to content

Commit 46d16b6

Browse files
committed
std: Avoid allocating panic message unless needed
This commit removes allocation of the panic message in instances like `panic!("foo: {}", "bar")` if we don't actually end up needing the message. We don't need it in the case of wasm32 right now, and in general it's not needed for panic=abort instances that use the default panic hook. For now this commit only solves the wasm use case where with LTO the allocation is entirely removed, but the panic=abort use case can be implemented at a later date if needed.
1 parent 2bb5b5c commit 46d16b6

File tree

4 files changed

+103
-50
lines changed

4 files changed

+103
-50
lines changed

src/libcore/panic.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,17 @@ impl<'a> PanicInfo<'a> {
4949
and related macros",
5050
issue = "0")]
5151
#[doc(hidden)]
52-
pub fn internal_constructor(payload: &'a (Any + Send),
53-
message: Option<&'a fmt::Arguments<'a>>,
52+
#[inline]
53+
pub fn internal_constructor(message: Option<&'a fmt::Arguments<'a>>,
5454
location: Location<'a>)
5555
-> Self {
56-
PanicInfo { payload, location, message }
56+
PanicInfo { payload: &(), location, message }
57+
}
58+
59+
#[doc(hidden)]
60+
#[inline]
61+
pub fn set_payload(&mut self, info: &'a (Any + Send)) {
62+
self.payload = info;
5763
}
5864

5965
/// Returns the payload associated with the panic.
@@ -259,5 +265,5 @@ impl<'a> fmt::Display for Location<'a> {
259265
#[doc(hidden)]
260266
pub unsafe trait BoxMeUp {
261267
fn box_me_up(&mut self) -> *mut (Any + Send);
262-
fn get(&self) -> &(Any + Send);
268+
fn get(&mut self) -> &(Any + Send);
263269
}

src/libstd/panicking.rs

+73-45
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,6 @@ fn default_hook(info: &PanicInfo) {
165165
#[cfg(feature = "backtrace")]
166166
use sys_common::backtrace;
167167

168-
// Some platforms know that printing to stderr won't ever actually print
169-
// anything, and if that's the case we can skip everything below.
170-
if stderr_prints_nothing() {
171-
return
172-
}
173-
174168
// If this is a double panic, make sure that we print a backtrace
175169
// for this panic. Otherwise only print it if logging is enabled.
176170
#[cfg(feature = "backtrace")]
@@ -185,9 +179,6 @@ fn default_hook(info: &PanicInfo) {
185179
};
186180

187181
let location = info.location().unwrap(); // The current implementation always returns Some
188-
let file = location.file();
189-
let line = location.line();
190-
let col = location.column();
191182

192183
let msg = match info.payload().downcast_ref::<&'static str>() {
193184
Some(s) => *s,
@@ -201,8 +192,8 @@ fn default_hook(info: &PanicInfo) {
201192
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
202193

203194
let write = |err: &mut ::io::Write| {
204-
let _ = writeln!(err, "thread '{}' panicked at '{}', {}:{}:{}",
205-
name, msg, file, line, col);
195+
let _ = writeln!(err, "thread '{}' panicked at '{}', {}",
196+
name, msg, location);
206197

207198
#[cfg(feature = "backtrace")]
208199
{
@@ -350,9 +341,38 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments,
350341
// panic + OOM properly anyway (see comment in begin_panic
351342
// below).
352343

353-
let mut s = String::new();
354-
let _ = s.write_fmt(*msg);
355-
rust_panic_with_hook(&mut PanicPayload::new(s), Some(msg), file_line_col)
344+
rust_panic_with_hook(&mut PanicPayload::new(msg), Some(msg), file_line_col);
345+
346+
struct PanicPayload<'a> {
347+
inner: &'a fmt::Arguments<'a>,
348+
string: Option<String>,
349+
}
350+
351+
impl<'a> PanicPayload<'a> {
352+
fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
353+
PanicPayload { inner, string: None }
354+
}
355+
356+
fn fill(&mut self) -> &mut String {
357+
let inner = self.inner;
358+
self.string.get_or_insert_with(|| {
359+
let mut s = String::new();
360+
drop(s.write_fmt(*inner));
361+
s
362+
})
363+
}
364+
}
365+
366+
unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
367+
fn box_me_up(&mut self) -> *mut (Any + Send) {
368+
let contents = mem::replace(self.fill(), String::new());
369+
Box::into_raw(Box::new(contents))
370+
}
371+
372+
fn get(&mut self) -> &(Any + Send) {
373+
self.fill()
374+
}
375+
}
356376
}
357377

358378
/// This is the entry point of panicking for panic!() and assert!().
@@ -368,42 +388,41 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
368388
// be performed in the parent of this thread instead of the thread that's
369389
// panicking.
370390

371-
rust_panic_with_hook(&mut PanicPayload::new(msg), None, file_line_col)
372-
}
373-
374-
struct PanicPayload<A> {
375-
inner: Option<A>,
376-
}
391+
rust_panic_with_hook(&mut PanicPayload::new(msg), None, file_line_col);
377392

378-
impl<A: Send + 'static> PanicPayload<A> {
379-
fn new(inner: A) -> PanicPayload<A> {
380-
PanicPayload { inner: Some(inner) }
393+
struct PanicPayload<A> {
394+
inner: Option<A>,
381395
}
382-
}
383396

384-
unsafe impl<A: Send + 'static> BoxMeUp for PanicPayload<A> {
385-
fn box_me_up(&mut self) -> *mut (Any + Send) {
386-
let data = match self.inner.take() {
387-
Some(a) => Box::new(a) as Box<Any + Send>,
388-
None => Box::new(()),
389-
};
390-
Box::into_raw(data)
397+
impl<A: Send + 'static> PanicPayload<A> {
398+
fn new(inner: A) -> PanicPayload<A> {
399+
PanicPayload { inner: Some(inner) }
400+
}
391401
}
392402

393-
fn get(&self) -> &(Any + Send) {
394-
match self.inner {
395-
Some(ref a) => a,
396-
None => &(),
403+
unsafe impl<A: Send + 'static> BoxMeUp for PanicPayload<A> {
404+
fn box_me_up(&mut self) -> *mut (Any + Send) {
405+
let data = match self.inner.take() {
406+
Some(a) => Box::new(a) as Box<Any + Send>,
407+
None => Box::new(()),
408+
};
409+
Box::into_raw(data)
410+
}
411+
412+
fn get(&mut self) -> &(Any + Send) {
413+
match self.inner {
414+
Some(ref a) => a,
415+
None => &(),
416+
}
397417
}
398418
}
399419
}
400420

401-
/// Executes the primary logic for a panic, including checking for recursive
402-
/// panics and panic hooks.
421+
/// Central point for dispatching panics.
403422
///
404-
/// This is the entry point or panics from libcore, formatted panics, and
405-
/// `Box<Any>` panics. Here we'll verify that we're not panicking recursively,
406-
/// run panic hooks, and then delegate to the actual implementation of panics.
423+
/// Executes the primary logic for a panic, including checking for recursive
424+
/// panics, panic hooks, and finally dispatching to the panic runtime to either
425+
/// abort or unwind.
407426
fn rust_panic_with_hook(payload: &mut BoxMeUp,
408427
message: Option<&fmt::Arguments>,
409428
file_line_col: &(&'static str, u32, u32)) -> ! {
@@ -423,15 +442,24 @@ fn rust_panic_with_hook(payload: &mut BoxMeUp,
423442
}
424443

425444
unsafe {
426-
let info = PanicInfo::internal_constructor(
427-
payload.get(),
445+
let mut info = PanicInfo::internal_constructor(
428446
message,
429447
Location::internal_constructor(file, line, col),
430448
);
431449
HOOK_LOCK.read();
432450
match HOOK {
433-
Hook::Default => default_hook(&info),
434-
Hook::Custom(ptr) => (*ptr)(&info),
451+
// Some platforms know that printing to stderr won't ever actually
452+
// print anything, and if that's the case we can skip the default
453+
// hook.
454+
Hook::Default if stderr_prints_nothing() => {}
455+
Hook::Default => {
456+
info.set_payload(payload.get());
457+
default_hook(&info);
458+
}
459+
Hook::Custom(ptr) => {
460+
info.set_payload(payload.get());
461+
(*ptr)(&info);
462+
}
435463
}
436464
HOOK_LOCK.read_unlock();
437465
}
@@ -460,7 +488,7 @@ pub fn update_count_then_panic(msg: Box<Any + Send>) -> ! {
460488
Box::into_raw(mem::replace(&mut self.0, Box::new(())))
461489
}
462490

463-
fn get(&self) -> &(Any + Send) {
491+
fn get(&mut self) -> &(Any + Send) {
464492
&*self.0
465493
}
466494
}

src/test/run-make/wasm-panic-small/Makefile

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22

33
ifeq ($(TARGET),wasm32-unknown-unknown)
44
all:
5-
$(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown
5+
$(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown --cfg a
66
wc -c < $(TMPDIR)/foo.wasm
77
[ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "1024" ]
8+
$(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown --cfg b
9+
wc -c < $(TMPDIR)/foo.wasm
10+
[ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "5120" ]
11+
$(RUSTC) foo.rs -C lto -O --target wasm32-unknown-unknown --cfg c
12+
wc -c < $(TMPDIR)/foo.wasm
13+
[ "`wc -c < $(TMPDIR)/foo.wasm`" -lt "5120" ]
814
else
915
all:
1016
endif

src/test/run-make/wasm-panic-small/foo.rs

+13
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
#![crate_type = "cdylib"]
1212

1313
#[no_mangle]
14+
#[cfg(a)]
1415
pub fn foo() {
1516
panic!("test");
1617
}
18+
19+
#[no_mangle]
20+
#[cfg(b)]
21+
pub fn foo() {
22+
panic!("{}", 1);
23+
}
24+
25+
#[no_mangle]
26+
#[cfg(c)]
27+
pub fn foo() {
28+
panic!("{}", "a");
29+
}

0 commit comments

Comments
 (0)