Skip to content

Commit 91a0b97

Browse files
committed
Add notail_when_called attribute and use it on panic functions.
1 parent 9fd85d2 commit 91a0b97

File tree

11 files changed

+140
-60
lines changed

11 files changed

+140
-60
lines changed

src/libcore/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
#![feature(untagged_unions)]
9292
#![feature(unwind_attributes)]
9393
#![feature(doc_spotlight)]
94+
#![cfg_attr(not(stage0), feature(notail_when_called))]
9495

9596
#[prelude_import]
9697
#[allow(unused)]

src/libcore/panicking.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub fn panic(expr_file_line_col: &(&'static str, &'static str, u32, u32)) -> ! {
4848
// Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
4949
// output binary, saving up to a few kilobytes.
5050
let (expr, file, line, col) = *expr_file_line_col;
51-
panic_dispatch(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col), &(panic as usize))
51+
panic_dispatch(fmt::Arguments::new_v1(&[expr], &[]), &(file, line, col), panic as usize);
5252
}
5353

5454
#[cold] #[inline(never)]
@@ -58,18 +58,20 @@ fn panic_bounds_check(file_line_col: &(&'static str, u32, u32),
5858
panic_dispatch(format_args!("index out of bounds: the len is {} but the index is {}",
5959
len, index),
6060
file_line_col,
61-
&(panic_bounds_check as usize))
61+
panic_bounds_check as usize);
6262
}
6363

6464
#[cold] #[inline(never)]
6565
pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
66-
panic_dispatch(fmt, file_line_col, &(panic_fmt as usize));
66+
panic_dispatch(fmt, file_line_col, panic_fmt as usize);
6767
}
6868

6969
#[cold]
70+
#[inline(never)]
71+
#[cfg_attr(not(stage0), notail_when_called)]
7072
fn panic_dispatch(fmt: fmt::Arguments,
7173
file_line_col: &(&'static str, u32, u32),
72-
entry_point: &usize) -> ! {
74+
entry_point: usize) -> ! {
7375
#[allow(improper_ctypes)]
7476
extern {
7577
#[lang = "panic_fmt"]
@@ -78,7 +80,7 @@ fn panic_dispatch(fmt: fmt::Arguments,
7880
file: &'static str,
7981
line: u32,
8082
col: u32,
81-
entry_point: &usize) -> !;
83+
entry_point: usize) -> !;
8284
}
8385
let (file, line, col) = *file_line_col;
8486
unsafe { panic_impl(fmt, file, line, col, entry_point) }

src/librustc/hir/check_attr.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,16 @@ impl<'a> CheckAttrVisitor<'a> {
5050
fn check_attribute(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) {
5151
if let Some(name) = attr.name() {
5252
match &*name.as_str() {
53-
"inline" => self.check_inline(attr, item, target),
53+
"inline" => self.check_target_function(attr, item, target),
54+
"notail_when_called" => self.check_target_function(attr, item, target),
5455
"repr" => self.check_repr(attr, item, target),
5556
_ => (),
5657
}
5758
}
5859
}
5960

60-
/// Check if an `#[inline]` is applied to a function.
61-
fn check_inline(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) {
61+
/// Check if the attribute is applied to a function.
62+
fn check_target_function(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) {
6263
if target != Target::Fn {
6364
struct_span_err!(self.sess, attr.span, E0518, "attribute should be applied to function")
6465
.span_label(item.span, "not a function")

src/librustc_llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,7 @@ extern "C" {
11501150
Bundle: OperandBundleDefRef,
11511151
Name: *const c_char)
11521152
-> ValueRef;
1153+
pub fn LLVMRustSetCallNoTail(Call: ValueRef);
11531154
pub fn LLVMBuildSelect(B: BuilderRef,
11541155
If: ValueRef,
11551156
Then: ValueRef,

src/librustc_trans/mir/block.rs

+56-42
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
118118
llargs: &[ValueRef],
119119
destination: Option<(ReturnDest<'tcx>, mir::BasicBlock)>,
120120
cleanup: Option<mir::BasicBlock>
121-
| {
121+
| -> Option<ValueRef> {
122122
if let Some(cleanup) = cleanup {
123123
let ret_bcx = if let Some((_, target)) = destination {
124124
this.blocks[target]
@@ -137,6 +137,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
137137
this.set_debug_loc(&ret_bcx, terminator.source_info);
138138
this.store_return(&ret_bcx, ret_dest, &fn_ty.ret, invokeret);
139139
}
140+
141+
None
140142
} else {
141143
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle);
142144
fn_ty.apply_attrs_callsite(llret);
@@ -154,6 +156,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
154156
} else {
155157
bcx.unreachable();
156158
}
159+
160+
Some(llret)
157161
}
158162
};
159163

@@ -209,9 +213,9 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
209213
mir::TerminatorKind::Return => {
210214
let llval = match self.fn_ty.ret.mode {
211215
PassMode::Ignore | PassMode::Indirect(_) => {
212-
bcx.ret_void();
213-
return;
214-
}
216+
bcx.ret_void();
217+
return;
218+
}
215219

216220
PassMode::Direct(_) | PassMode::Pair(..) => {
217221
let op = self.trans_consume(&bcx, &mir::Place::Local(mir::RETURN_PLACE));
@@ -224,27 +228,27 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
224228

225229
PassMode::Cast(cast_ty) => {
226230
let op = match self.locals[mir::RETURN_PLACE] {
227-
LocalRef::Operand(Some(op)) => op,
228-
LocalRef::Operand(None) => bug!("use of return before def"),
231+
LocalRef::Operand(Some(op)) => op,
232+
LocalRef::Operand(None) => bug!("use of return before def"),
229233
LocalRef::Place(tr_place) => {
230-
OperandRef {
234+
OperandRef {
231235
val: Ref(tr_place.llval, tr_place.alignment),
232236
layout: tr_place.layout
233-
}
234237
}
235-
};
236-
let llslot = match op.val {
237-
Immediate(_) | Pair(..) => {
238+
}
239+
};
240+
let llslot = match op.val {
241+
Immediate(_) | Pair(..) => {
238242
let scratch = PlaceRef::alloca(&bcx, self.fn_ty.ret.layout, "ret");
239243
op.val.store(&bcx, scratch);
240244
scratch.llval
241-
}
242-
Ref(llval, align) => {
243-
assert_eq!(align, Alignment::AbiAligned,
245+
}
246+
Ref(llval, align) => {
247+
assert_eq!(align, Alignment::AbiAligned,
244248
"return place is unaligned!");
245-
llval
246-
}
247-
};
249+
llval
250+
}
251+
};
248252
bcx.load(
249253
bcx.pointercast(llslot, cast_ty.llvm_type(bcx.ccx).ptr_to()),
250254
Some(self.fn_ty.ret.layout.align))
@@ -448,6 +452,11 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
448452
let sig = callee.layout.ty.fn_sig(bcx.tcx());
449453
let sig = bcx.tcx().erase_late_bound_regions_and_normalize(&sig);
450454
let abi = sig.abi;
455+
let notail = def.map(|def| {
456+
def.attrs(bcx.ccx.tcx())
457+
.iter()
458+
.any(|attr| attr.check_name("notail_when_called"))
459+
}).unwrap_or(false);
451460

452461
// Handle intrinsics old trans wants Expr's for, ourselves.
453462
let intrinsic = match def {
@@ -508,28 +517,28 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
508517
ReturnDest::Store(dst) => dst.llval,
509518
ReturnDest::DirectOperand(_) =>
510519
bug!("Cannot use direct operand with an intrinsic call")
511-
};
520+
};
512521

513522
let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| {
514-
// The indices passed to simd_shuffle* in the
515-
// third argument must be constant. This is
516-
// checked by const-qualification, which also
517-
// promotes any complex rvalues to constants.
523+
// The indices passed to simd_shuffle* in the
524+
// third argument must be constant. This is
525+
// checked by const-qualification, which also
526+
// promotes any complex rvalues to constants.
518527
if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") {
519-
match *arg {
528+
match *arg {
520529
mir::Operand::Copy(_) |
521530
mir::Operand::Move(_) => {
522-
span_bug!(span, "shuffle indices must be constant");
523-
}
524-
mir::Operand::Constant(ref constant) => {
525-
let val = self.trans_constant(&bcx, constant);
531+
span_bug!(span, "shuffle indices must be constant");
532+
}
533+
mir::Operand::Constant(ref constant) => {
534+
let val = self.trans_constant(&bcx, constant);
526535
return OperandRef {
527536
val: Immediate(val.llval),
528537
layout: bcx.ccx.layout_of(val.ty)
529538
};
530-
}
531539
}
532540
}
541+
}
533542

534543
self.trans_operand(&bcx, arg)
535544
}).collect();
@@ -597,9 +606,14 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
597606
_ => span_bug!(span, "no llfn for call"),
598607
};
599608

600-
do_call(self, bcx, fn_ty, fn_ptr, &llargs,
609+
let llret = do_call(self, bcx, fn_ty, fn_ptr, &llargs,
601610
destination.as_ref().map(|&(_, target)| (ret_dest, target)),
602611
cleanup);
612+
if notail {
613+
unsafe {
614+
llret.map(|llret| llvm::LLVMRustSetCallNoTail(llret));
615+
}
616+
}
603617
}
604618
mir::TerminatorKind::GeneratorDrop |
605619
mir::TerminatorKind::Yield { .. } |
@@ -615,21 +629,21 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
615629
// Fill padding with undef value, where applicable.
616630
if let Some(ty) = arg.pad {
617631
llargs.push(C_undef(ty.llvm_type(bcx.ccx)));
618-
}
632+
}
619633

620634
if arg.is_ignore() {
621635
return;
622-
}
636+
}
623637

624638
if let PassMode::Pair(..) = arg.mode {
625639
match op.val {
626640
Pair(a, b) => {
627641
llargs.push(a);
628642
llargs.push(b);
629-
return;
630-
}
631-
_ => bug!("trans_argument: {:?} invalid for pair arugment", op)
643+
return;
632644
}
645+
_ => bug!("trans_argument: {:?} invalid for pair arugment", op)
646+
}
633647
}
634648

635649
// Force by-ref if we have to load through a cast pointer.
@@ -640,10 +654,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
640654
let scratch = PlaceRef::alloca(bcx, arg.layout, "arg");
641655
op.val.store(bcx, scratch);
642656
(scratch.llval, Alignment::AbiAligned, true)
643-
}
657+
}
644658
_ => {
645659
(op.immediate_or_packed_pair(bcx), Alignment::AbiAligned, false)
646-
}
660+
}
647661
}
648662
}
649663
Ref(llval, align @ Alignment::Packed(_)) if arg.is_indirect() => {
@@ -674,8 +688,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
674688
if let layout::Abi::Scalar(ref scalar) = arg.layout.abi {
675689
if scalar.is_bool() {
676690
bcx.range_metadata(llval, 0..2);
677-
}
678-
}
691+
}
692+
}
679693
// We store bools as i8 so we need to truncate to i1.
680694
llval = base::to_immediate(bcx, llval, arg.layout);
681695
}
@@ -697,15 +711,15 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
697711
for i in 0..tuple.layout.fields.count() {
698712
let field_ptr = tuple_ptr.project_field(bcx, i);
699713
self.trans_argument(bcx, field_ptr.load(bcx), llargs, &args[i]);
700-
}
701-
} else {
714+
}
715+
} else {
702716
// If the tuple is immediate, the elements are as well.
703717
for i in 0..tuple.layout.fields.count() {
704718
let op = tuple.extract_field(bcx, i);
705719
self.trans_argument(bcx, op, llargs, &args[i]);
720+
}
721+
}
706722
}
707-
}
708-
}
709723

710724
fn get_personality_slot(&mut self, bcx: &Builder<'a, 'tcx>) -> PlaceRef<'tcx> {
711725
let ccx = bcx.ccx;

src/libstd/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@
321321
#![feature(doc_cfg)]
322322
#![feature(doc_masked)]
323323
#![feature(doc_spotlight)]
324+
#![cfg_attr(not(stage0), feature(notail_when_called))]
324325
#![cfg_attr(test, feature(update_panic_count))]
325326
#![cfg_attr(windows, feature(used))]
326327

src/libstd/panicking.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ pub extern fn rust_begin_panic(msg: fmt::Arguments,
496496
file: &'static str,
497497
line: u32,
498498
col: u32,
499-
entry_point: &usize) -> ! {
499+
entry_point: usize) -> ! {
500500
rust_panic_fmt(&msg, &(file, line, col), entry_point)
501501
}
502502

@@ -511,13 +511,15 @@ pub extern fn rust_begin_panic(msg: fmt::Arguments,
511511
issue = "0")]
512512
#[inline(never)] #[cold]
513513
pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! {
514-
rust_panic_fmt(msg, file_line_col, &(begin_panic_fmt as usize))
514+
rust_panic_fmt(msg, file_line_col, begin_panic_fmt as usize);
515515
}
516516

517517
#[cold]
518+
#[inline(never)]
519+
#[cfg_attr(not(stage0), notail_when_called)]
518520
fn rust_panic_fmt(msg: &fmt::Arguments,
519521
file_line_col: &(&'static str, u32, u32),
520-
entry_point: &usize) -> ! {
522+
entry_point: usize) -> ! {
521523
use fmt::Write;
522524

523525
// We do two allocations here, unfortunately. But (a) they're
@@ -543,7 +545,7 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
543545
// be performed in the parent of this thread instead of the thread that's
544546
// panicking.
545547

546-
rust_panic_with_hook(Box::new(msg), file_line_col, &(begin_panic::<M> as usize))
548+
rust_panic_with_hook(Box::new(msg), file_line_col, begin_panic::<M> as usize)
547549
}
548550

549551
/// Executes the primary logic for a panic, including checking for recursive
@@ -553,10 +555,11 @@ pub fn begin_panic<M: Any + Send>(msg: M, file_line_col: &(&'static str, u32, u3
553555
/// `Box<Any>` panics. Here we'll verify that we're not panicking recursively,
554556
/// run panic hooks, and then delegate to the actual implementation of panics.
555557
#[inline(never)]
558+
#[cfg_attr(not(stage0), notail_when_called)]
556559
#[cold]
557560
fn rust_panic_with_hook(msg: Box<Any + Send>,
558561
file_line_col: &(&'static str, u32, u32),
559-
entry_point: &usize) -> ! {
562+
entry_point: usize) -> ! {
560563
let (file, line, col) = *file_line_col;
561564

562565
let panics = update_panic_count(1);
@@ -580,7 +583,7 @@ fn rust_panic_with_hook(msg: Box<Any + Send>,
580583
line,
581584
col,
582585
},
583-
entry_point: *entry_point,
586+
entry_point,
584587
};
585588
HOOK_LOCK.read();
586589
match HOOK {

0 commit comments

Comments
 (0)