Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark extern functions as nounwind + other unwinding-based improvements #21186

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
#![feature(lang_items, unsafe_destructor)]
#![feature(box_syntax)]
#![feature(optin_builtin_traits)]
#![feature(unwinding_attributes)]
#![allow(unknown_features)] #![feature(int_uint)]

#[macro_use]
Expand Down Expand Up @@ -97,6 +98,7 @@ pub mod rc;
/// Common out-of-memory routine
#[cold]
#[inline(never)]
#[unsafe_no_unwind]
pub fn oom() -> ! {
// FIXME(#14674): This really needs to do something other than just abort
// here, but any printing done must be *guaranteed* to not
Expand Down
2 changes: 1 addition & 1 deletion src/libcore/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fn panic_bounds_check(file_line: &(&'static str, uint),
pub fn panic_fmt(fmt: fmt::Arguments, file_line: &(&'static str, uint)) -> ! {
#[allow(improper_ctypes)]
extern {
#[lang = "panic_fmt"]
#[lang = "panic_fmt"] #[can_unwind]
fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: uint) -> !;
}
let (file, line) = *file_line;
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,8 @@ impl LintPass for UnusedAttributes {
"no_debug",
"omit_gdb_pretty_printer_section",
"unsafe_no_drop_flag",
"can_unwind",
"unsafe_no_unwind",

// used in resolve
"prelude_import",
Expand Down
53 changes: 43 additions & 10 deletions src/librustc_trans/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use trans::cleanup;
use trans::closure;
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_integral};
use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
use trans::common::{CrateContext, ExternMap, FunctionContext};
use trans::common::{CrateContext, ExprId, ExternMap, FunctionContext};
use trans::common::{NodeInfo, Result};
use trans::common::{node_id_type, return_type_is_void};
use trans::common::{tydesc_info, type_is_immediate};
Expand Down Expand Up @@ -431,11 +431,6 @@ pub fn set_no_inline(f: ValueRef) {
llvm::SetFunctionAttribute(f, llvm::NoInlineAttribute)
}

#[allow(dead_code)] // useful
pub fn set_no_unwind(f: ValueRef) {
llvm::SetFunctionAttribute(f, llvm::NoUnwindAttribute)
}

// Tell LLVM to emit the information necessary to unwind the stack for the
// function f.
pub fn set_uwtable(f: ValueRef) {
Expand Down Expand Up @@ -470,6 +465,7 @@ pub fn set_llvm_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: Val
llvm::FunctionIndex as c_uint,
llvm::ColdAttribute as uint64_t)
},
"unsafe_no_unwind" => llvm::SetFunctionAttribute(llfn, llvm::NoUnwindAttribute),
_ => used = false,
}
if used {
Expand Down Expand Up @@ -941,9 +937,12 @@ pub fn trans_external_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
RustIntrinsic => {
ccx.sess().bug("unexpected intrinsic in trans_external_path")
}
_ => {
foreign::register_foreign_item_fn(ccx, fn_ty.abi, t,
&name[])
abi => {
let attrs = csearch::get_item_attrs(&ccx.sess().cstore, did);
let llfn = foreign::register_foreign_item_fn(ccx, fn_ty.abi, t,
&name[]);
foreign::add_abi_attributes(ccx, llfn, abi, &attrs[]);
llfn
}
}
}
Expand Down Expand Up @@ -1011,6 +1010,37 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
}

pub fn get_eh_personality<'a, 'tcx>(ccx: &CrateContext<'a,'tcx>) -> ValueRef {
// The exception handling personality function.
//
// If our compilation unit has the `eh_personality` lang item somewhere
// within it, then we just need to translate that. Otherwise, we're
// building an rlib which will depend on some upstream implementation of
// this function, so we just codegen a generic reference to it. We don't
// specify any of the types for the function, we just make it a symbol
// that LLVM can later use.
match ccx.tcx().lang_items.eh_personality() {
Some(def_id) => {
callee::trans_fn_ref(ccx, def_id, ExprId(0), &Substs::trans_empty()).val
}
None => {
let mut personality = ccx.eh_personality().borrow_mut();
match *personality {
Some(llpersonality) => llpersonality,
None => {
let fty = Type::variadic_func(&[], &Type::i32(ccx));
let f = decl_cdecl_fn(ccx,
"rust_eh_personality",
fty,
ccx.tcx().types.i32);
*personality = Some(f);
f
}
}
}
}
}

pub fn need_invoke(bcx: Block) -> bool {
if bcx.sess().no_landing_pads() {
return false;
Expand Down Expand Up @@ -2870,7 +2900,10 @@ pub fn get_item_val(ccx: &CrateContext, id: ast::NodeId) -> ValueRef {
let abi = ccx.tcx().map.get_foreign_abi(id);
let ty = ty::node_id_to_type(ccx.tcx(), ni.id);
let name = foreign::link_name(&*ni);
foreign::register_foreign_item_fn(ccx, abi, ty, &name.get()[])
let llfn = foreign::register_foreign_item_fn(ccx, abi, ty, &name.get()[]);
// FIXME(aatch) Re-enable pending RFC
//foreign::add_abi_attributes(ccx, llfn, abi, &ni.attrs[]);
llfn
}
ast::ForeignItemStatic(..) => {
foreign::register_static(ccx, &*ni)
Expand Down
19 changes: 19 additions & 0 deletions src/librustc_trans/trans/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,25 @@ pub fn Invoke(cx: Block,
B(cx).invoke(fn_, args, then, catch, attributes)
}

pub fn InvokeWithConv(cx: Block,
fn_: ValueRef,
args: &[ValueRef],
then: BasicBlockRef,
catch: BasicBlockRef,
conv: CallConv,
attributes: Option<AttrBuilder>)
-> ValueRef {
if cx.unreachable.get() {
return C_null(Type::i8(cx.ccx()));
}
check_not_terminated(cx);
terminate(cx, "InvokeWithConv");
debug!("InvokeWithConv({} with arguments ({}))",
cx.val_to_string(fn_),
args.iter().map(|a| cx.val_to_string(*a)).collect::<Vec<String>>().connect(", "));
B(cx).invoke_with_conv(fn_, args, then, catch, conv, attributes)
}

pub fn Unreachable(cx: Block) {
if cx.unreachable.get() {
return
Expand Down
41 changes: 41 additions & 0 deletions src/librustc_trans/trans/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}

pub fn invoke_with_conv(&self,
llfn: ValueRef,
args: &[ValueRef],
then: BasicBlockRef,
catch: BasicBlockRef,
conv: CallConv,
attributes: Option<AttrBuilder>)
-> ValueRef {
self.count_insn("invokewithconv");

debug!("Invoke {} with args ({})",
self.ccx.tn().val_to_string(llfn),
args.iter()
.map(|&v| self.ccx.tn().val_to_string(v))
.collect::<Vec<String>>()
.connect(", "));

unsafe {
let v = llvm::LLVMBuildInvoke(self.llbuilder,
llfn,
args.as_ptr(),
args.len() as c_uint,
then,
catch,
noname());
match attributes {
Some(a) => a.apply_callsite(v),
None => {}
}
llvm::SetInstructionCallConv(v, conv);
v
}
}

pub fn unreachable(&self) {
self.count_insn("unreachable");
unsafe {
Expand Down Expand Up @@ -952,6 +986,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}

pub fn add_clause(&self, landing_pad: ValueRef, clause: ValueRef) {
self.count_insn("addclause");
unsafe {
llvm::LLVMAddClause(landing_pad, clause);
}
}

pub fn resume(&self, exn: ValueRef) -> ValueRef {
self.count_insn("resume");
unsafe {
Expand Down
33 changes: 2 additions & 31 deletions src/librustc_trans/trans/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ pub use self::Heap::*;
use llvm::{BasicBlockRef, ValueRef};
use trans::base;
use trans::build;
use trans::callee;
use trans::common;
use trans::common::{Block, FunctionContext, ExprId, NodeInfo};
use trans::common::{Block, FunctionContext, NodeInfo};
use trans::debuginfo;
use trans::glue;
use middle::region;
Expand Down Expand Up @@ -715,35 +714,7 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
&[Type::i8p(self.ccx), Type::i32(self.ccx)],
false);

// The exception handling personality function.
//
// If our compilation unit has the `eh_personality` lang item somewhere
// within it, then we just need to translate that. Otherwise, we're
// building an rlib which will depend on some upstream implementation of
// this function, so we just codegen a generic reference to it. We don't
// specify any of the types for the function, we just make it a symbol
// that LLVM can later use.
let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() {
Some(def_id) => {
callee::trans_fn_ref(pad_bcx.ccx(), def_id, ExprId(0),
pad_bcx.fcx.param_substs).val
}
None => {
let mut personality = self.ccx.eh_personality().borrow_mut();
match *personality {
Some(llpersonality) => llpersonality,
None => {
let fty = Type::variadic_func(&[], &Type::i32(self.ccx));
let f = base::decl_cdecl_fn(self.ccx,
"rust_eh_personality",
fty,
self.ccx.tcx().types.i32);
*personality = Some(f);
f
}
}
}
};
let llpersonality = base::get_eh_personality(pad_bcx.ccx());

// The only landing pad clause will be 'cleanup'
let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1u);
Expand Down
67 changes: 55 additions & 12 deletions src/librustc_trans/trans/foreign.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
Expand All @@ -17,6 +17,7 @@ use trans::base::{llvm_linkage_by_name, push_ctxt};
use trans::base;
use trans::build::*;
use trans::cabi;
use trans::cleanup::CleanupMethods;
use trans::common::*;
use trans::machine;
use trans::monomorphize;
Expand Down Expand Up @@ -366,11 +367,27 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
arg_idx += 1;
}

let llforeign_retval = CallWithConv(bcx,
llfn,
&llargs_foreign[],
cc,
Some(attrs));
// Foreign functions *can* unwind, albeit rarely, so use invoke instead of call.
// We shouldn't really be emitting an invoke all the time, LLVM can optimise them
// out, but we should be able avoid it most of the time
let (llforeign_retval, bcx) = if base::need_invoke(bcx) {
let normal_bcx = bcx.fcx.new_temp_block("normal-return");
let landing_pad = bcx.fcx.get_landing_pad();

let ret = InvokeWithConv(bcx, llfn, &llargs_foreign[],
normal_bcx.llbb,
landing_pad,
cc,
Some(attrs));
(ret, normal_bcx)
} else {
let ret = CallWithConv(bcx,
llfn,
&llargs_foreign[],
cc,
Some(attrs));
(ret, bcx)
};

// If the function we just called does not use an outpointer,
// store the result into the rust outpointer. Cast the outpointer
Expand Down Expand Up @@ -460,15 +477,19 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) {
match foreign_mod.abi {
Rust | RustIntrinsic => {}
abi => {

let ty = ty::node_id_to_type(ccx.tcx(), foreign_item.id);
match ty.sty {
ty::ty_bare_fn(_, bft) => gate_simd_ffi(ccx.tcx(), &**decl, bft),
_ => ccx.tcx().sess.span_bug(foreign_item.span,
"foreign fn's sty isn't a bare_fn_ty?")
}

register_foreign_item_fn(ccx, abi, ty,
&lname.get()[]);
let llfn = register_foreign_item_fn(ccx, abi, ty,
&lname.get()[]);

add_abi_attributes(ccx, llfn, abi, &foreign_item.attrs[]);

// Unlike for other items, we shouldn't call
// `base::update_linkage` here. Foreign items have
// special linkage requirements, which are handled
Expand Down Expand Up @@ -568,7 +589,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
let llrustfn = build_rust_fn(ccx, decl, body, param_substs, attrs, id, hash);

// Build up the foreign wrapper (`foo` above).
return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, mty);
return build_wrap_fn(ccx, llrustfn, llwrapfn, &tys, mty, attrs);
}

fn build_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
Expand Down Expand Up @@ -618,7 +639,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
llrustfn: ValueRef,
llwrapfn: ValueRef,
tys: &ForeignTypes<'tcx>,
t: Ty<'tcx>) {
t: Ty<'tcx>, _attrs: &[ast::Attribute]) {
let _icx = push_ctxt(
"foreign::trans_rust_fn_with_foreign_abi::build_wrap_fn");
let tcx = ccx.tcx();
Expand All @@ -635,11 +656,19 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
//
// S foo(T i) {
// S r;
// foo0(&r, NULL, i);
// try {
// foo0(&r, NULL, i);
// } catch (..) {
// abort();
// }
// return r;
// }
//
// Because we mark extern "C" functions as nounwind, we need to
// enforce this for Rust functions that have a non-rust ABI. Hence
// the try-catch we emulate below.

let ptr = "the block\0".as_ptr();
let ptr = "entry\0".as_ptr();
let the_block = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llwrapfn,
ptr as *const _);

Expand Down Expand Up @@ -787,6 +816,8 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// Perform the call itself
debug!("calling llrustfn = {}, t = {}",
ccx.tn().val_to_string(llrustfn), t.repr(ccx.tcx()));
// FIXME(aatch) Wrap the call in a try-catch and handle unwinding
// pending RFC
let attributes = base::get_fn_llvm_attributes(ccx, t);
let llrust_ret_val = builder.call(llrustfn, llrust_args.as_slice(), Some(attributes));

Expand Down Expand Up @@ -1004,3 +1035,15 @@ fn add_argument_attributes(tys: &ForeignTypes,
i += 1;
}
}

pub fn add_abi_attributes(ccx: &CrateContext, llfn: ValueRef, abi: Abi, attrs: &[ast::Attribute]) {

match ccx.sess().target.target.adjust_abi(abi) {
Rust | RustCall | RustIntrinsic => {}
_ => {
if !attr::contains_name(attrs, "can_unwind") {
llvm::SetFunctionAttribute(llfn, llvm::NoUnwindAttribute);
}
}
}
}
Loading