auto merge of #11121 : vadimcn/rust/no-c++2, r=alexcrichton
This PR removes Rust's dependency on C++ for exception handling. Instead, it will use the unwind library API directly. closes #10469
This commit is contained in:
commit
32e730f122
22
mk/rt.mk
22
mk/rt.mk
@ -72,9 +72,6 @@ RUNTIME_CXXFLAGS_$(1)_$(2) = -D_RUST_STAGE1
|
||||
endif
|
||||
endif
|
||||
|
||||
RUNTIME_CXXS_$(1)_$(2) := \
|
||||
rt/rust_cxx_glue.cpp
|
||||
|
||||
RUNTIME_CS_$(1)_$(2) := \
|
||||
rt/rust_builtin.c \
|
||||
rt/rust_upcall.c \
|
||||
@ -82,6 +79,9 @@ RUNTIME_CS_$(1)_$(2) := \
|
||||
rt/rust_android_dummy.c \
|
||||
rt/rust_test_helpers.c
|
||||
|
||||
RUNTIME_LL_$(1)_$(2) := \
|
||||
rt/rust_try.ll
|
||||
|
||||
# stage0 remove this after the next snapshot
|
||||
%.cpp:
|
||||
@touch tmp/foo.o
|
||||
@ -94,19 +94,16 @@ RT_BUILD_DIR_$(1)_$(2) := $$(RT_OUTPUT_DIR_$(1))/stage$(2)
|
||||
RUNTIME_DEF_$(1)_$(2) := $$(RT_OUTPUT_DIR_$(1))/rustrt$$(CFG_DEF_SUFFIX_$(1))
|
||||
RUNTIME_INCS_$(1)_$(2) := -I $$(S)src/rt -I $$(S)src/rt/isaac -I $$(S)src/rt/uthash \
|
||||
-I $$(S)src/rt/arch/$$(HOST_$(1))
|
||||
RUNTIME_OBJS_$(1)_$(2) := $$(RUNTIME_CXXS_$(1)_$(2):rt/%.cpp=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) \
|
||||
RUNTIME_OBJS_$(1)_$(2) := \
|
||||
$$(RUNTIME_CS_$(1)_$(2):rt/%.c=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) \
|
||||
$$(RUNTIME_S_$(1)_$(2):rt/%.S=$$(RT_BUILD_DIR_$(1)_$(2))/%.o)
|
||||
$$(RUNTIME_S_$(1)_$(2):rt/%.S=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) \
|
||||
$$(RUNTIME_LL_$(1)_$(2):rt/%.ll=$$(RT_BUILD_DIR_$(1)_$(2))/%.o)
|
||||
|
||||
ALL_OBJ_FILES += $$(RUNTIME_OBJS_$(1)_$(2))
|
||||
|
||||
MORESTACK_OBJS_$(1)_$(2) := $$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/morestack.o
|
||||
ALL_OBJ_FILES += $$(MORESTACK_OBJS_$(1)_$(2))
|
||||
|
||||
$$(RT_BUILD_DIR_$(1)_$(2))/rust_cxx_glue.o: rt/rust_cxx_glue.cpp $$(MKFILE_DEPS)
|
||||
@$$(call E, compile: $$@)
|
||||
$$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@, $$(RUNTIME_INCS_$(1)_$(2)) \
|
||||
$$(SNAP_DEFINES) $$(RUNTIME_CXXFLAGS_$(1)_$(2))) $$<
|
||||
|
||||
$$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.c $$(MKFILE_DEPS)
|
||||
@$$(call E, compile: $$@)
|
||||
$$(Q)$$(call CFG_COMPILE_C_$(1), $$@, $$(RUNTIME_INCS_$(1)_$(2)) \
|
||||
@ -117,6 +114,11 @@ $$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.S $$(MKFILE_DEPS) \
|
||||
@$$(call E, compile: $$@)
|
||||
$$(Q)$$(call CFG_ASSEMBLE_$(1),$$@,$$<)
|
||||
|
||||
$$(RT_BUILD_DIR_$(1)_$(2))/%.o: rt/%.ll $$(MKFILE_DEPS) \
|
||||
$$(LLVM_CONFIG_$$(CFG_BUILD))
|
||||
@$$(call E, compile: $$@)
|
||||
$$(Q)$(LLC_$(CFG_BUILD)) -filetype=obj -mtriple=$(1) -relocation-model=pic -o $$@ $$<
|
||||
|
||||
$$(RT_BUILD_DIR_$(1)_$(2))/arch/$$(HOST_$(1))/libmorestack.a: $$(MORESTACK_OBJS_$(1)_$(2))
|
||||
@$$(call E, link: $$@)
|
||||
$$(Q)$(AR_$(1)) rcs $$@ $$^
|
||||
|
@ -59,6 +59,7 @@ for llconfig in sys.argv[3:]:
|
||||
for lib in out.strip().split(' '):
|
||||
lib = lib[2:] # chop of the leading '-l'
|
||||
f.write("#[link(name = \"" + lib + "\", kind = \"static\")]\n")
|
||||
f.write("#[link(name = \"stdc++\")]\n")
|
||||
if os == 'win32':
|
||||
f.write("#[link(name = \"imagehlp\")]\n")
|
||||
f.write("extern {}\n")
|
||||
|
@ -708,10 +708,7 @@ pub fn get_cc_prog(sess: Session) -> ~str {
|
||||
// In the future, FreeBSD will use clang as default compiler.
|
||||
// It would be flexible to use cc (system's default C compiler)
|
||||
// instead of hard-coded gcc.
|
||||
// For win32, there is no cc command, so we add a condition to make it use
|
||||
// g++. We use g++ rather than gcc because it automatically adds linker
|
||||
// options required for generation of dll modules that correctly register
|
||||
// stack unwind tables.
|
||||
// For win32, there is no cc command, so we add a condition to make it use gcc.
|
||||
match sess.targ_cfg.os {
|
||||
abi::OsAndroid => match sess.opts.android_cross_path {
|
||||
Some(ref path) => format!("{}/bin/arm-linux-androideabi-gcc", *path),
|
||||
@ -720,7 +717,7 @@ pub fn get_cc_prog(sess: Session) -> ~str {
|
||||
(--android-cross-path)")
|
||||
}
|
||||
},
|
||||
abi::OsWin32 => ~"g++",
|
||||
abi::OsWin32 => ~"gcc",
|
||||
_ => ~"cc",
|
||||
}
|
||||
}
|
||||
@ -1032,6 +1029,13 @@ fn link_args(sess: Session,
|
||||
}
|
||||
}
|
||||
|
||||
if sess.targ_cfg.os == abi::OsWin32 {
|
||||
// Make sure that we link to the dynamic libgcc, otherwise cross-module
|
||||
// DWARF stack unwinding will not work.
|
||||
// This behavior may be overriden by --link-args "-static-libgcc"
|
||||
args.push(~"-shared-libgcc");
|
||||
}
|
||||
|
||||
add_local_native_libraries(&mut args, sess);
|
||||
add_upstream_rust_crates(&mut args, sess, dylib, tmpdir);
|
||||
add_upstream_native_libraries(&mut args, sess);
|
||||
|
@ -1,35 +0,0 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
use driver::session;
|
||||
use middle::trans::base;
|
||||
use middle::trans::type_::Type;
|
||||
use lib::llvm::{ModuleRef, ValueRef};
|
||||
|
||||
pub struct Upcalls {
|
||||
rust_personality: ValueRef,
|
||||
}
|
||||
|
||||
macro_rules! upcall (
|
||||
(nothrow fn $name:ident -> $ret:expr) => ({
|
||||
let fn_ty = Type::func([], &$ret);
|
||||
let decl = base::decl_cdecl_fn(llmod, ~"upcall_" + stringify!($name), fn_ty);
|
||||
base::set_no_unwind(decl);
|
||||
decl
|
||||
})
|
||||
)
|
||||
|
||||
pub fn declare_upcalls(_targ_cfg: @session::config,
|
||||
llmod: ModuleRef) -> @Upcalls {
|
||||
@Upcalls {
|
||||
rust_personality: upcall!(nothrow fn rust_personality -> Type::i32()),
|
||||
}
|
||||
}
|
@ -92,7 +92,6 @@ pub mod back {
|
||||
pub mod link;
|
||||
pub mod manifest;
|
||||
pub mod abi;
|
||||
pub mod upcall;
|
||||
pub mod arm;
|
||||
pub mod mips;
|
||||
pub mod x86;
|
||||
|
@ -319,7 +319,6 @@ pub mod llvm {
|
||||
// automatically updated whenever LLVM is updated to include an up-to-date
|
||||
// set of the libraries we need to link to LLVM for.
|
||||
#[link(name = "rustllvm", kind = "static")]
|
||||
#[link(name = "stdc++")]
|
||||
extern {
|
||||
/* Create and destroy contexts. */
|
||||
pub fn LLVMContextCreate() -> ContextRef;
|
||||
|
@ -208,7 +208,7 @@ pub fn collect_language_items(crate: &ast::Crate,
|
||||
}
|
||||
|
||||
lets_do_this! {
|
||||
There are 42 lang items.
|
||||
There are 43 lang items.
|
||||
|
||||
// ID, Variant name, Name, Method name;
|
||||
0, FreezeTraitLangItem, "freeze", freeze_trait;
|
||||
@ -261,5 +261,7 @@ lets_do_this! {
|
||||
40, EventLoopFactoryLangItem, "event_loop_factory", event_loop_factory;
|
||||
|
||||
41, TypeIdLangItem, "type_id", type_id;
|
||||
|
||||
42, EhPersonalityLangItem, "eh_personality", eh_personality_fn;
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ use metadata::{csearch, cstore, encoder};
|
||||
use middle::astencode;
|
||||
use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
|
||||
use middle::lang_items::{MallocFnLangItem, ClosureExchangeMallocFnLangItem};
|
||||
use middle::lang_items::{EhPersonalityLangItem};
|
||||
use middle::trans::_match;
|
||||
use middle::trans::adt;
|
||||
use middle::trans::base;
|
||||
@ -1027,10 +1028,10 @@ pub fn get_landing_pad(bcx: @mut Block) -> BasicBlockRef {
|
||||
// this represents but it's determined by the personality function and
|
||||
// this is what the EH proposal example uses.
|
||||
let llretty = Type::struct_([Type::i8p(), Type::i32()], false);
|
||||
// The exception handling personality function. This is the C++
|
||||
// personality function __gxx_personality_v0, wrapped in our naming
|
||||
// convention.
|
||||
let personality = bcx.ccx().upcalls.rust_personality;
|
||||
// The exception handling personality function.
|
||||
let personality = callee::trans_fn_ref(bcx,
|
||||
langcall(bcx, None, "", EhPersonalityLangItem),
|
||||
0).llfn;
|
||||
// The only landing pad clause will be 'cleanup'
|
||||
let llretval = LandingPad(pad_bcx, llretty, personality, 1u);
|
||||
// The landing pad block is a cleanup
|
||||
@ -3195,6 +3196,8 @@ pub fn trans_crate(sess: session::Session,
|
||||
reachable.push(ccx.crate_map_name.to_owned());
|
||||
reachable.push(~"main");
|
||||
reachable.push(~"rust_stack_exhausted");
|
||||
reachable.push(~"rust_eh_personality"); // referenced from .eh_frame section on some platforms
|
||||
reachable.push(~"rust_eh_personality_catch"); // referenced from rt/rust_try.ll
|
||||
|
||||
return CrateTranslation {
|
||||
context: llcx,
|
||||
|
@ -9,7 +9,6 @@
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
use back::{upcall};
|
||||
use driver::session;
|
||||
use lib::llvm::{ContextRef, ModuleRef, ValueRef};
|
||||
use lib::llvm::{llvm, TargetData, TypeNames};
|
||||
@ -105,7 +104,6 @@ pub struct CrateContext {
|
||||
tcx: ty::ctxt,
|
||||
maps: astencode::Maps,
|
||||
stats: @mut Stats,
|
||||
upcalls: @upcall::Upcalls,
|
||||
tydesc_type: Type,
|
||||
int_type: Type,
|
||||
opaque_vec_type: Type,
|
||||
@ -233,7 +231,6 @@ impl CrateContext {
|
||||
llvm_insns: HashMap::new(),
|
||||
fn_stats: ~[]
|
||||
},
|
||||
upcalls: upcall::declare_upcalls(targ_cfg, llmod),
|
||||
tydesc_type: tydesc_type,
|
||||
int_type: int_type,
|
||||
opaque_vec_type: opaque_vec_type,
|
||||
|
@ -173,6 +173,9 @@ mod local_ptr;
|
||||
/// Bindings to pthread/windows thread-local storage.
|
||||
mod thread_local_storage;
|
||||
|
||||
/// Stack unwinding
|
||||
pub mod unwind;
|
||||
|
||||
/// Just stuff
|
||||
mod util;
|
||||
|
||||
|
@ -18,10 +18,9 @@ use super::local_heap::LocalHeap;
|
||||
use prelude::*;
|
||||
|
||||
use borrow;
|
||||
use cast::transmute;
|
||||
use cleanup;
|
||||
use io::Writer;
|
||||
use libc::{c_void, uintptr_t, c_char, size_t};
|
||||
use libc::{c_char, size_t};
|
||||
use local_data;
|
||||
use option::{Option, Some, None};
|
||||
use rt::borrowck::BorrowRecord;
|
||||
@ -33,8 +32,8 @@ use rt::local::Local;
|
||||
use rt::logging::StdErrLogger;
|
||||
use rt::sched::{Scheduler, SchedHandle};
|
||||
use rt::stack::{StackSegment, StackPool};
|
||||
use rt::unwind::Unwinder;
|
||||
use send_str::SendStr;
|
||||
use task::TaskResult;
|
||||
use unstable::finally::Finally;
|
||||
use unstable::mutex::Mutex;
|
||||
|
||||
@ -91,21 +90,6 @@ pub enum SchedHome {
|
||||
pub struct GarbageCollector;
|
||||
pub struct LocalStorage(Option<local_data::Map>);
|
||||
|
||||
pub struct Unwinder {
|
||||
unwinding: bool,
|
||||
cause: Option<~Any>
|
||||
}
|
||||
|
||||
impl Unwinder {
|
||||
fn result(&mut self) -> TaskResult {
|
||||
if self.unwinding {
|
||||
Err(self.cause.take().unwrap())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Task {
|
||||
|
||||
// A helper to build a new task using the dynamically found
|
||||
@ -452,54 +436,6 @@ impl Coroutine {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Just a sanity check to make sure we are catching a Rust-thrown exception
|
||||
static UNWIND_TOKEN: uintptr_t = 839147;
|
||||
|
||||
impl Unwinder {
|
||||
pub fn try(&mut self, f: ||) {
|
||||
use unstable::raw::Closure;
|
||||
|
||||
unsafe {
|
||||
let closure: Closure = transmute(f);
|
||||
let code = transmute(closure.code);
|
||||
let env = transmute(closure.env);
|
||||
|
||||
let token = rust_try(try_fn, code, env);
|
||||
assert!(token == 0 || token == UNWIND_TOKEN);
|
||||
}
|
||||
|
||||
extern fn try_fn(code: *c_void, env: *c_void) {
|
||||
unsafe {
|
||||
let closure: Closure = Closure {
|
||||
code: transmute(code),
|
||||
env: transmute(env),
|
||||
};
|
||||
let closure: || = transmute(closure);
|
||||
closure();
|
||||
}
|
||||
}
|
||||
|
||||
extern {
|
||||
fn rust_try(f: extern "C" fn(*c_void, *c_void),
|
||||
code: *c_void,
|
||||
data: *c_void) -> uintptr_t;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_unwind(&mut self, cause: ~Any) -> ! {
|
||||
self.unwinding = true;
|
||||
self.cause = Some(cause);
|
||||
unsafe {
|
||||
rust_begin_unwind(UNWIND_TOKEN);
|
||||
return transmute(());
|
||||
}
|
||||
extern {
|
||||
fn rust_begin_unwind(token: uintptr_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is invoked from rust's current __morestack function. Segmented
|
||||
/// stacks are currently not enabled as segmented stacks, but rather one giant
|
||||
/// stack segment. This means that whenever we run out of stack, we want to
|
||||
|
256
src/libstd/rt/unwind.rs
Normal file
256
src/libstd/rt/unwind.rs
Normal file
@ -0,0 +1,256 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
// Implementation of Rust stack unwinding
|
||||
//
|
||||
// For background on exception handling and stack unwinding please see "Exception Handling in LLVM"
|
||||
// (llvm.org/docs/ExceptionHandling.html) and documents linked from it.
|
||||
// These are also good reads:
|
||||
// http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
|
||||
// http://monoinfinito.wordpress.com/series/exception-handling-in-c/
|
||||
// http://www.airs.com/blog/index.php?s=exception+frames
|
||||
//
|
||||
// ~~~ A brief summary ~~~
|
||||
// Exception handling happens in two phases: a search phase and a cleanup phase.
|
||||
//
|
||||
// In both phases the unwinder walks stack frames from top to bottom using information from
|
||||
// the stack frame unwind sections of the current process's modules ("module" here refers to
|
||||
// an OS module, i.e. an executable or a dynamic library).
|
||||
//
|
||||
// For each stack frame, it invokes the associated "personality routine", whose address is also
|
||||
// stored in the unwind info section.
|
||||
//
|
||||
// In the search phase, the job of a personality routine is to examine exception object being
|
||||
// thrown, and to decide whether it should be caught at that stack frame. Once the handler frame
|
||||
// has been identified, cleanup phase begins.
|
||||
//
|
||||
// In the cleanup phase, personality routines invoke cleanup code associated with their
|
||||
// stack frames (i.e. destructors). Once stack has been unwound down to the handler frame level,
|
||||
// unwinding stops and the last personality routine transfers control to its' catch block.
|
||||
//
|
||||
// ~~~ Frame unwind info registration ~~~
|
||||
// Each module has its' own frame unwind info section (usually ".eh_frame"), and unwinder needs
|
||||
// to know about all of them in order for unwinding to be able to cross module boundaries.
|
||||
//
|
||||
// On some platforms, like Linux, this is achieved by dynamically enumerating currently loaded
|
||||
// modules via the dl_iterate_phdr() API and finding all .eh_frame sections.
|
||||
//
|
||||
// Others, like Windows, require modules to actively register their unwind info sections by calling
|
||||
// __register_frame_info() API at startup.
|
||||
// In the latter case it is essential that there is only one copy of the unwinder runtime
|
||||
// in the process. This is usually achieved by linking to the dynamic version of the unwind
|
||||
// runtime.
|
||||
//
|
||||
// Currently Rust uses unwind runtime provided by libgcc.
|
||||
|
||||
use prelude::*;
|
||||
use cast::transmute;
|
||||
use task::TaskResult;
|
||||
use libc::{c_void, c_int};
|
||||
use self::libunwind::*;
|
||||
|
||||
mod libunwind {
|
||||
//! Unwind library interface
|
||||
|
||||
#[allow(non_camel_case_types)];
|
||||
|
||||
use libc::{uintptr_t, uint64_t};
|
||||
|
||||
#[repr(C)]
|
||||
pub enum _Unwind_Action
|
||||
{
|
||||
_UA_SEARCH_PHASE = 1,
|
||||
_UA_CLEANUP_PHASE = 2,
|
||||
_UA_HANDLER_FRAME = 4,
|
||||
_UA_FORCE_UNWIND = 8,
|
||||
_UA_END_OF_STACK = 16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum _Unwind_Reason_Code {
|
||||
_URC_NO_REASON = 0,
|
||||
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
|
||||
_URC_FATAL_PHASE2_ERROR = 2,
|
||||
_URC_FATAL_PHASE1_ERROR = 3,
|
||||
_URC_NORMAL_STOP = 4,
|
||||
_URC_END_OF_STACK = 5,
|
||||
_URC_HANDLER_FOUND = 6,
|
||||
_URC_INSTALL_CONTEXT = 7,
|
||||
_URC_CONTINUE_UNWIND = 8,
|
||||
}
|
||||
|
||||
pub type _Unwind_Exception_Class = uint64_t;
|
||||
|
||||
pub type _Unwind_Word = uintptr_t;
|
||||
|
||||
pub struct _Unwind_Exception {
|
||||
exception_class: _Unwind_Exception_Class,
|
||||
exception_cleanup: _Unwind_Exception_Cleanup_Fn,
|
||||
private_1: _Unwind_Word,
|
||||
private_2: _Unwind_Word,
|
||||
}
|
||||
|
||||
pub enum _Unwind_Context {}
|
||||
|
||||
pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code,
|
||||
exception: *_Unwind_Exception);
|
||||
|
||||
extern "C" {
|
||||
pub fn _Unwind_RaiseException(exception: *_Unwind_Exception) -> _Unwind_Reason_Code;
|
||||
pub fn _Unwind_DeleteException(exception: *_Unwind_Exception);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Unwinder {
|
||||
unwinding: bool,
|
||||
cause: Option<~Any>
|
||||
}
|
||||
|
||||
impl Unwinder {
|
||||
|
||||
pub fn try(&mut self, f: ||) {
|
||||
use unstable::raw::Closure;
|
||||
|
||||
unsafe {
|
||||
let closure: Closure = transmute(f);
|
||||
let code = transmute(closure.code);
|
||||
let env = transmute(closure.env);
|
||||
|
||||
let ep = rust_try(try_fn, code, env);
|
||||
if !ep.is_null() {
|
||||
rtdebug!("Caught {}", (*ep).exception_class);
|
||||
_Unwind_DeleteException(ep);
|
||||
}
|
||||
}
|
||||
|
||||
extern fn try_fn(code: *c_void, env: *c_void) {
|
||||
unsafe {
|
||||
let closure: Closure = Closure {
|
||||
code: transmute(code),
|
||||
env: transmute(env),
|
||||
};
|
||||
let closure: || = transmute(closure);
|
||||
closure();
|
||||
}
|
||||
}
|
||||
|
||||
extern {
|
||||
// Rust's try-catch
|
||||
// When f(...) returns normally, the return value is null.
|
||||
// When f(...) throws, the return value is a pointer to the caught exception object.
|
||||
fn rust_try(f: extern "C" fn(*c_void, *c_void),
|
||||
code: *c_void,
|
||||
data: *c_void) -> *_Unwind_Exception;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_unwind(&mut self, cause: ~Any) -> ! {
|
||||
rtdebug!("begin_unwind()");
|
||||
|
||||
self.unwinding = true;
|
||||
self.cause = Some(cause);
|
||||
|
||||
unsafe {
|
||||
let exception = ~_Unwind_Exception {
|
||||
exception_class: rust_exception_class(),
|
||||
exception_cleanup: exception_cleanup,
|
||||
private_1: 0,
|
||||
private_2: 0
|
||||
};
|
||||
let error = _Unwind_RaiseException(transmute(exception));
|
||||
rtabort!("Could not unwind stack, error = {}", error as int)
|
||||
}
|
||||
|
||||
extern "C" fn exception_cleanup(_unwind_code: _Unwind_Reason_Code,
|
||||
exception: *_Unwind_Exception) {
|
||||
rtdebug!("exception_cleanup()");
|
||||
unsafe {
|
||||
let _: ~_Unwind_Exception = transmute(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn result(&mut self) -> TaskResult {
|
||||
if self.unwinding {
|
||||
Err(self.cause.take().unwrap())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rust's exception class identifier. This is used by personality routines to
|
||||
// determine whether the exception was thrown by their own runtime.
|
||||
fn rust_exception_class() -> _Unwind_Exception_Class {
|
||||
let bytes = bytes!("MOZ\0RUST"); // vendor, language
|
||||
unsafe {
|
||||
let ptr: *_Unwind_Exception_Class = transmute(bytes.as_ptr());
|
||||
*ptr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We could implement our personality routine in pure Rust, however exception info decoding
|
||||
// is tedious. More importantly, personality routines have to handle various platform
|
||||
// quirks, which are not fun to maintain. For this reason, we attempt to reuse personality
|
||||
// routine of the C language: __gcc_personality_v0.
|
||||
//
|
||||
// Since C does not support exception catching, __gcc_personality_v0 simply always
|
||||
// returns _URC_CONTINUE_UNWIND in search phase, and always returns _URC_INSTALL_CONTEXT
|
||||
// (i.e. "invoke cleanup code") in cleanup phase.
|
||||
//
|
||||
// This is pretty close to Rust's exception handling approach, except that Rust does have
|
||||
// a single "catch-all" handler at the bottom of each task's stack.
|
||||
// So we have two versions:
|
||||
// - rust_eh_personality, used by all cleanup landing pads, which never catches, so
|
||||
// the behavior of __gcc_personality_v0 is perfectly adequate there, and
|
||||
// - rust_eh_personality_catch, used only by rust_try(), which always catches. This is
|
||||
// achieved by overriding the return value in search phase to always say "catch!".
|
||||
|
||||
extern "C" {
|
||||
fn __gcc_personality_v0(version: c_int,
|
||||
actions: _Unwind_Action,
|
||||
exception_class: _Unwind_Exception_Class,
|
||||
ue_header: *_Unwind_Exception,
|
||||
context: *_Unwind_Context) -> _Unwind_Reason_Code;
|
||||
}
|
||||
|
||||
#[lang="eh_personality"]
|
||||
#[no_mangle] // so we can reference it by name from middle/trans/base.rs
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(test))]
|
||||
pub extern "C" fn rust_eh_personality(version: c_int,
|
||||
actions: _Unwind_Action,
|
||||
exception_class: _Unwind_Exception_Class,
|
||||
ue_header: *_Unwind_Exception,
|
||||
context: *_Unwind_Context) -> _Unwind_Reason_Code {
|
||||
unsafe {
|
||||
__gcc_personality_v0(version, actions, exception_class, ue_header, context)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(test))]
|
||||
pub extern "C" fn rust_eh_personality_catch(version: c_int,
|
||||
actions: _Unwind_Action,
|
||||
exception_class: _Unwind_Exception_Class,
|
||||
ue_header: *_Unwind_Exception,
|
||||
context: *_Unwind_Context) -> _Unwind_Reason_Code {
|
||||
if (actions as c_int & _UA_SEARCH_PHASE as c_int) != 0 { // search phase
|
||||
_URC_HANDLER_FOUND // catch!
|
||||
}
|
||||
else { // cleanup phase
|
||||
unsafe {
|
||||
__gcc_personality_v0(version, actions, exception_class, ue_header, context)
|
||||
}
|
||||
}
|
||||
}
|
@ -26,24 +26,45 @@ extern {}
|
||||
#[link(name = "dl")]
|
||||
#[link(name = "m")]
|
||||
#[link(name = "pthread")]
|
||||
#[link(name = "stdc++")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[link(name = "dl")]
|
||||
#[link(name = "log")]
|
||||
#[link(name = "supc++")]
|
||||
#[link(name = "m")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[link(name = "execinfo")]
|
||||
#[link(name = "rt")]
|
||||
#[link(name = "stdc++")]
|
||||
#[link(name = "pthread")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[link(name = "pthread")]
|
||||
#[link(name = "stdc++")]
|
||||
extern {}
|
||||
|
||||
// NOTE: remove after snapshot
|
||||
// stage0-generated code still depends on c++
|
||||
#[cfg(stage0)]
|
||||
mod stage0 {
|
||||
#[cfg(target_os = "linux")]
|
||||
#[link(name = "stdc++")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[link(name = "supc++")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[link(name = "stdc++")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[link(name = "stdc++")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "win32")]
|
||||
#[link(name = "stdc++")]
|
||||
extern {}
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/* Foreign builtins which require C++ */
|
||||
|
||||
#include "rust_globals.h"
|
||||
|
||||
typedef void *(rust_try_fn)(void*, void*);
|
||||
|
||||
extern "C" CDECL uintptr_t
|
||||
rust_try(rust_try_fn f, void *fptr, void *env) {
|
||||
try {
|
||||
f(fptr, env);
|
||||
} catch (uintptr_t token) {
|
||||
assert(token != 0);
|
||||
return token;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" CDECL void
|
||||
rust_begin_unwind(uintptr_t token) {
|
||||
throw token;
|
||||
}
|
34
src/rt/rust_try.ll
Normal file
34
src/rt/rust_try.ll
Normal file
@ -0,0 +1,34 @@
|
||||
; Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
; file at the top-level directory of this distribution and at
|
||||
; http://rust-lang.org/COPYRIGHT.
|
||||
;
|
||||
; Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
; option. This file may not be copied, modified, or distributed
|
||||
; except according to those terms.
|
||||
|
||||
; Rust's try-catch
|
||||
; When f(...) returns normally, the return value is null.
|
||||
; When f(...) throws, the return value is a pointer to the caught exception object.
|
||||
; See also: libstd/rt/unwind.rs
|
||||
|
||||
define i8* @rust_try(void (i8*,i8*)* %f, i8* %fptr, i8* %env) {
|
||||
|
||||
invoke void %f(i8* %fptr, i8* %env)
|
||||
to label %normal
|
||||
unwind label %catch
|
||||
|
||||
normal:
|
||||
ret i8* null
|
||||
|
||||
catch:
|
||||
%1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @rust_eh_personality_catch to i8*)
|
||||
catch i8* null ; catch everything
|
||||
|
||||
; extract and return pointer to the exception object
|
||||
%2 = extractvalue { i8*, i32 } %1, 0
|
||||
ret i8* %2
|
||||
}
|
||||
|
||||
declare i32 @rust_eh_personality_catch(...)
|
@ -8,6 +8,9 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// NOTE: remove this file after snapshot
|
||||
// unwind personality routine lives now in libstd/rt/unwind.rs
|
||||
|
||||
/*
|
||||
Upcalls
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user