Auto merge of #26569 - alexcrichton:msvc-llvm-update, r=brson
Now that LLVM has been updated, the only remaining roadblock to implementing unwinding for MSVC is to fill out the runtime support in `std::rt::unwind::seh`. This commit does precisely that, fixing up some other bits and pieces along the way: * The `seh` unwinding module now uses `RaiseException` to initiate a panic. * The `rust_try.ll` file was rewritten for MSVC (as it's quite different) and is located at `rust_try_msvc_64.ll`, only included on MSVC builds for now. * The personality function for all landing pads generated by LLVM is hard-wired to `__C_specific_handler` instead of the standard `rust_eh_personality` lang item. This is required to get LLVM to emit SEH unwinding information instead of DWARF unwinding information. This also means that on MSVC the `rust_eh_personality` function is entirely unused (but is defined as it's a lang item). More details about how panicking works on SEH can be found in the `rust_try_msvc_64.ll` or `seh.rs` files, but I'm always open to adding more comments! A key aspect of this PR is missing, however, which is that **unwinding is still turned off by default for MSVC**. There is a [bug in llvm][llvm-bug] which causes optimizations to inline enough landing pads that LLVM chokes. If the compiler is optimized at `-O1` (where inlining isn't enabled) then it can bootstrap with unwinding enabled, but when optimized at `-O2` (inlining is enabled) then it hits a fatal LLVM error. [llvm-bug]: https://llvm.org/bugs/show_bug.cgi?id=23884
This commit is contained in:
commit
d3c03d0987
@ -23,64 +23,6 @@ CFG_RUN_x86_64-pc-windows-msvc=$(2)
|
||||
CFG_RUN_TARG_x86_64-pc-windows-msvc=$(call CFG_RUN_x86_64-pc-windows-msvc,,$(2))
|
||||
CFG_GNU_TRIPLE_x86_64-pc-windows-msvc := x86_64-pc-win32
|
||||
|
||||
# These two environment variables are scraped by the `./configure` script and
|
||||
# are necessary for `cl.exe` to find standard headers (the INCLUDE variable) and
|
||||
# for `link.exe` to find standard libraries (the LIB variable).
|
||||
ifdef CFG_MSVC_INCLUDE_PATH
|
||||
export INCLUDE := $(CFG_MSVC_INCLUDE_PATH)
|
||||
endif
|
||||
ifdef CFG_MSVC_LIB_PATH
|
||||
export LIB := $(CFG_MSVC_LIB_PATH)
|
||||
endif
|
||||
|
||||
# Unfortunately `link.exe` is also a program in `/usr/bin` on MinGW installs,
|
||||
# but it's not the one that we want. As a result we make sure that our detected
|
||||
# `link.exe` shows up in PATH first.
|
||||
ifdef CFG_MSVC_LINK
|
||||
export PATH := $(CFG_MSVC_ROOT)/VC/bin/amd64:$(PATH)
|
||||
endif
|
||||
|
||||
# There are more comments about this available in the target specification for
|
||||
# Windows MSVC in the compiler, but the gist of it is that we use `llvm-ar.exe`
|
||||
# instead of `lib.exe` for assembling archives, so we need to inject this custom
|
||||
# dependency here.
|
||||
NATIVE_TOOL_DEPS_core_T_x86_64-pc-windows-msvc += llvm-ar.exe
|
||||
INSTALLED_BINS_x86_64-pc-windows-msvc += llvm-ar.exe
|
||||
|
||||
# When working with MSVC on windows, each DLL needs to explicitly declare its
|
||||
# interface to the outside world through some means. The options for doing so
|
||||
# include:
|
||||
#
|
||||
# 1. A custom attribute on each function itself
|
||||
# 2. A linker argument saying what to export
|
||||
# 3. A file which lists all symbols that need to be exported
|
||||
#
|
||||
# The Rust compiler takes care (1) for us for all Rust code by annotating all
|
||||
# public-facing functions with dllexport, but we have a few native dependencies
|
||||
# which need to cross the DLL boundary. The most important of these dependencies
|
||||
# is LLVM which is linked into `rustc_llvm.dll` but primarily used from
|
||||
# `rustc_trans.dll`. This means that many of LLVM's C API functions need to be
|
||||
# exposed from `rustc_llvm.dll` to be forwarded over the boundary.
|
||||
#
|
||||
# Unfortunately, at this time, LLVM does not handle this sort of exportation on
|
||||
# Windows for us, so we're forced to do it ourselves if we want it (which seems
|
||||
# like the path of least resistance right now). To do this we generate a `.DEF`
|
||||
# file [1] which we then custom-pass to the linker when building the rustc_llvm
|
||||
# crate. This DEF file list all symbols that are exported from
|
||||
# `src/librustc_llvm/lib.rs` and is generated by a small python script.
|
||||
#
|
||||
# Fun times!
|
||||
#
|
||||
# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
|
||||
RUSTFLAGS_rustc_llvm_T_x86_64-pc-windows-msvc += \
|
||||
-C link-args="-DEF:x86_64-pc-windows-msvc/rt/rustc_llvm.def"
|
||||
CUSTOM_DEPS_rustc_llvm_T_x86_64-pc-windows-msvc += \
|
||||
x86_64-pc-windows-msvc/rt/rustc_llvm.def
|
||||
|
||||
x86_64-pc-windows-msvc/rt/rustc_llvm.def: $(S)src/etc/mklldef.py \
|
||||
$(S)src/librustc_llvm/lib.rs
|
||||
$(CFG_PYTHON) $^ $@ rustc_llvm-$(CFG_FILENAME_EXTRA)
|
||||
|
||||
# All windows nightiles are currently a GNU triple, so this MSVC triple is not
|
||||
# bootstrapping from itself. This is relevant during stage0, and other parts of
|
||||
# the build system take this into account.
|
||||
|
@ -238,3 +238,72 @@ endef
|
||||
|
||||
$(foreach target,$(CFG_TARGET), \
|
||||
$(eval $(call CFG_MAKE_TOOLCHAIN,$(target))))
|
||||
|
||||
# These two environment variables are scraped by the `./configure` script and
|
||||
# are necessary for `cl.exe` to find standard headers (the INCLUDE variable) and
|
||||
# for `link.exe` to find standard libraries (the LIB variable).
|
||||
ifdef CFG_MSVC_INCLUDE_PATH
|
||||
export INCLUDE := $(CFG_MSVC_INCLUDE_PATH)
|
||||
endif
|
||||
ifdef CFG_MSVC_LIB_PATH
|
||||
export LIB := $(CFG_MSVC_LIB_PATH)
|
||||
endif
|
||||
|
||||
# Unfortunately `link.exe` is also a program in `/usr/bin` on MinGW installs,
|
||||
# but it's not the one that we want. As a result we make sure that our detected
|
||||
# `link.exe` shows up in PATH first.
|
||||
ifdef CFG_MSVC_LINK
|
||||
export PATH := $(CFG_MSVC_ROOT)/VC/bin/amd64:$(PATH)
|
||||
endif
|
||||
|
||||
# There are more comments about this available in the target specification for
|
||||
# Windows MSVC in the compiler, but the gist of it is that we use `llvm-ar.exe`
|
||||
# instead of `lib.exe` for assembling archives, so we need to inject this custom
|
||||
# dependency here.
|
||||
define ADD_LLVM_AR_TO_MSVC_DEPS
|
||||
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||
NATIVE_TOOL_DEPS_core_T_$(1) += llvm-ar.exe
|
||||
INSTALLED_BINS_$(1) += llvm-ar.exe
|
||||
endif
|
||||
endef
|
||||
|
||||
$(foreach target,$(CFG_TARGET), \
|
||||
$(eval $(call ADD_LLVM_AR_TO_MSVC_DEPS,$(target))))
|
||||
|
||||
# When working with MSVC on windows, each DLL needs to explicitly declare its
|
||||
# interface to the outside world through some means. The options for doing so
|
||||
# include:
|
||||
#
|
||||
# 1. A custom attribute on each function itself
|
||||
# 2. A linker argument saying what to export
|
||||
# 3. A file which lists all symbols that need to be exported
|
||||
#
|
||||
# The Rust compiler takes care (1) for us for all Rust code by annotating all
|
||||
# public-facing functions with dllexport, but we have a few native dependencies
|
||||
# which need to cross the DLL boundary. The most important of these dependencies
|
||||
# is LLVM which is linked into `rustc_llvm.dll` but primarily used from
|
||||
# `rustc_trans.dll`. This means that many of LLVM's C API functions need to be
|
||||
# exposed from `rustc_llvm.dll` to be forwarded over the boundary.
|
||||
#
|
||||
# Unfortunately, at this time, LLVM does not handle this sort of exportation on
|
||||
# Windows for us, so we're forced to do it ourselves if we want it (which seems
|
||||
# like the path of least resistance right now). To do this we generate a `.DEF`
|
||||
# file [1] which we then custom-pass to the linker when building the rustc_llvm
|
||||
# crate. This DEF file list all symbols that are exported from
|
||||
# `src/librustc_llvm/lib.rs` and is generated by a small python script.
|
||||
#
|
||||
# Fun times!
|
||||
#
|
||||
# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
|
||||
define ADD_RUSTC_LLVM_DEF_TO_MSVC
|
||||
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||
RUSTFLAGS_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
|
||||
CUSTOM_DEPS_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
|
||||
|
||||
$(1)/rt/rustc_llvm.def: $$(S)src/etc/mklldef.py $$(S)src/librustc_llvm/lib.rs
|
||||
$$(CFG_PYTHON) $$^ $$@ rustc_llvm-$$(CFG_FILENAME_EXTRA)
|
||||
endif
|
||||
endef
|
||||
|
||||
$(foreach target,$(CFG_TARGET), \
|
||||
$(eval $(call ADD_RUSTC_LLVM_DEF_TO_MSVC,$(target))))
|
||||
|
9
mk/rt.mk
9
mk/rt.mk
@ -53,9 +53,12 @@ NATIVE_DEPS_hoedown_$(1) := hoedown/src/autolink.c \
|
||||
NATIVE_DEPS_miniz_$(1) = miniz.c
|
||||
NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \
|
||||
rust_android_dummy.c
|
||||
NATIVE_DEPS_rustrt_native_$(1) := \
|
||||
rust_try.ll \
|
||||
arch/$$(HOST_$(1))/record_sp.S
|
||||
NATIVE_DEPS_rustrt_native_$(1) := arch/$$(HOST_$(1))/record_sp.S
|
||||
ifeq ($$(findstring msvc,$(1)),msvc)
|
||||
NATIVE_DEPS_rustrt_native_$(1) += rust_try_msvc_64.ll
|
||||
else
|
||||
NATIVE_DEPS_rustrt_native_$(1) += rust_try.ll
|
||||
endif
|
||||
NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c
|
||||
NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S
|
||||
|
||||
|
@ -1214,11 +1214,13 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
|
||||
|
||||
// Just need to tell the linker about where the library lives and
|
||||
// what its name is
|
||||
if let Some(dir) = cratepath.parent() {
|
||||
let parent = cratepath.parent();
|
||||
if let Some(dir) = parent {
|
||||
cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
|
||||
}
|
||||
let filestem = cratepath.file_stem().unwrap().to_str().unwrap();
|
||||
cmd.link_dylib(&unlib(&sess.target, filestem));
|
||||
cmd.link_rust_dylib(&unlib(&sess.target, filestem),
|
||||
parent.unwrap_or(Path::new("")));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::fs;
|
||||
|
||||
use rustc_back::archive;
|
||||
use session::Session;
|
||||
@ -25,6 +26,7 @@ use session::config;
|
||||
/// MSVC linker (e.g. `link.exe`) is being used.
|
||||
pub trait Linker {
|
||||
fn link_dylib(&mut self, lib: &str);
|
||||
fn link_rust_dylib(&mut self, lib: &str, path: &Path);
|
||||
fn link_framework(&mut self, framework: &str);
|
||||
fn link_staticlib(&mut self, lib: &str);
|
||||
fn link_rlib(&mut self, lib: &Path);
|
||||
@ -67,6 +69,10 @@ impl<'a> Linker for GnuLinker<'a> {
|
||||
fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
|
||||
fn args(&mut self, args: &[String]) { self.cmd.args(args); }
|
||||
|
||||
fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
|
||||
self.cmd.arg("-l").arg(lib);
|
||||
}
|
||||
|
||||
fn link_framework(&mut self, framework: &str) {
|
||||
self.cmd.arg("-framework").arg(framework);
|
||||
}
|
||||
@ -189,6 +195,18 @@ impl<'a> Linker for MsvcLinker<'a> {
|
||||
fn link_dylib(&mut self, lib: &str) {
|
||||
self.cmd.arg(&format!("{}.lib", lib));
|
||||
}
|
||||
|
||||
fn link_rust_dylib(&mut self, lib: &str, path: &Path) {
|
||||
// When producing a dll, the MSVC linker may not actually emit a
|
||||
// `foo.lib` file if the dll doesn't actually export any symbols, so we
|
||||
// check to see if the file is there and just omit linking to it if it's
|
||||
// not present.
|
||||
let name = format!("{}.lib", lib);
|
||||
if fs::metadata(&path.join(&name)).is_ok() {
|
||||
self.cmd.arg(name);
|
||||
}
|
||||
}
|
||||
|
||||
fn link_staticlib(&mut self, lib: &str) {
|
||||
self.cmd.arg(&format!("{}.lib", lib));
|
||||
}
|
||||
|
@ -856,18 +856,36 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
|
||||
// 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.
|
||||
//
|
||||
// Note that MSVC is a little special here in that we don't use the
|
||||
// `eh_personality` lang item at all. Currently LLVM has support for
|
||||
// both Dwarf and SEH unwind mechanisms for MSVC targets and uses the
|
||||
// *name of the personality function* to decide what kind of unwind side
|
||||
// tables/landing pads to emit. It looks like Dwarf is used by default,
|
||||
// injecting a dependency on the `_Unwind_Resume` symbol for resuming
|
||||
// an "exception", but for MSVC we want to force SEH. This means that we
|
||||
// can't actually have the personality function be our standard
|
||||
// `rust_eh_personality` function, but rather we wired it up to the
|
||||
// CRT's custom `__C_specific_handler` personality funciton, which
|
||||
// forces LLVM to consider landing pads as "landing pads for SEH".
|
||||
let target = &self.ccx.sess().target.target;
|
||||
let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() {
|
||||
Some(def_id) => {
|
||||
Some(def_id) if !target.options.is_like_msvc => {
|
||||
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 name = if target.options.is_like_msvc {
|
||||
"__C_specific_handler"
|
||||
} else {
|
||||
"rust_eh_personality"
|
||||
};
|
||||
let fty = Type::variadic_func(&[], &Type::i32(self.ccx));
|
||||
let f = declare::declare_cfn(self.ccx, "rust_eh_personality", fty,
|
||||
let f = declare::declare_cfn(self.ccx, name, fty,
|
||||
self.ccx.tcx().types.i32);
|
||||
*personality = Some(f);
|
||||
f
|
||||
|
@ -8,23 +8,136 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
|
||||
//!
|
||||
//! On Windows (currently only on MSVC), the default exception handling
|
||||
//! mechanism is Structured Exception Handling (SEH). This is quite different
|
||||
//! than Dwarf-based exception handling (e.g. what other unix platforms use) in
|
||||
//! terms of compiler internals, so LLVM is required to have a good deal of
|
||||
//! extra support for SEH. Currently this support is somewhat lacking, so what's
|
||||
//! here is the bare bones of SEH support.
|
||||
//!
|
||||
//! In a nutshell, what happens here is:
|
||||
//!
|
||||
//! 1. The `panic` function calls the standard Windows function `RaiseException`
|
||||
//! with a Rust-specific code, triggering the unwinding process.
|
||||
//! 2. All landing pads generated by the compiler (just "cleanup" landing pads)
|
||||
//! use the personality function `__C_specific_handler`, a function in the
|
||||
//! CRT, and the unwinding code in Windows will use this personality function
|
||||
//! to execute all cleanup code on the stack.
|
||||
//! 3. Eventually the "catch" code in `rust_try` (located in
|
||||
//! src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the
|
||||
//! exception being caught is indeed a Rust exception, returning control back
|
||||
//! into Rust.
|
||||
//!
|
||||
//! Some specific differences from the gcc-based exception handling are:
|
||||
//!
|
||||
//! * Rust has no custom personality function, it is instead *always*
|
||||
//! __C_specific_handler, so the filtering is done in a C++-like manner
|
||||
//! instead of in the personality function itself. Note that the specific
|
||||
//! syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM
|
||||
//! test case for SEH.
|
||||
//! * We've got some data to transmit across the unwinding boundary,
|
||||
//! specifically a `Box<Any + Send + 'static>`. In Dwarf-based unwinding this
|
||||
//! data is part of the payload of the exception, but I have not currently
|
||||
//! figured out how to do this with LLVM's bindings. Judging by some comments
|
||||
//! in the LLVM test cases this may not even be possible currently with LLVM,
|
||||
//! so this is just abandoned entirely. Instead the data is stored in a
|
||||
//! thread-local in `panic` and retrieved during `cleanup`.
|
||||
//!
|
||||
//! So given all that, the bindings here are pretty small,
|
||||
|
||||
#![allow(bad_style)]
|
||||
|
||||
use prelude::v1::*;
|
||||
|
||||
use any::Any;
|
||||
use intrinsics;
|
||||
use libc::c_void;
|
||||
use libc::{c_ulong, DWORD, c_void};
|
||||
use sys_common::thread_local::StaticKey;
|
||||
|
||||
pub unsafe fn panic(_data: Box<Any + Send + 'static>) -> ! {
|
||||
intrinsics::abort();
|
||||
// 0x R U S T
|
||||
const RUST_PANIC: DWORD = 0x52555354;
|
||||
static PANIC_DATA: StaticKey = StaticKey::new(None);
|
||||
|
||||
// This function is provided by kernel32.dll
|
||||
extern "system" {
|
||||
fn RaiseException(dwExceptionCode: DWORD,
|
||||
dwExceptionFlags: DWORD,
|
||||
nNumberOfArguments: DWORD,
|
||||
lpArguments: *const c_ulong);
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(_ptr: *mut c_void) -> Box<Any + Send + 'static> {
|
||||
intrinsics::abort();
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_POINTERS {
|
||||
ExceptionRecord: *mut EXCEPTION_RECORD,
|
||||
ContextRecord: *mut CONTEXT,
|
||||
}
|
||||
|
||||
enum CONTEXT {}
|
||||
|
||||
#[repr(C)]
|
||||
struct EXCEPTION_RECORD {
|
||||
ExceptionCode: DWORD,
|
||||
ExceptionFlags: DWORD,
|
||||
ExceptionRecord: *mut _EXCEPTION_RECORD,
|
||||
ExceptionAddress: *mut c_void,
|
||||
NumberParameters: DWORD,
|
||||
ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS],
|
||||
}
|
||||
|
||||
enum _EXCEPTION_RECORD {}
|
||||
|
||||
const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
|
||||
|
||||
pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
|
||||
// See module docs above for an explanation of why `data` is stored in a
|
||||
// thread local instead of being passed as an argument to the
|
||||
// `RaiseException` function (which can in theory carry along arbitrary
|
||||
// data).
|
||||
let exception = Box::new(data);
|
||||
rtassert!(PANIC_DATA.get().is_null());
|
||||
PANIC_DATA.set(Box::into_raw(exception) as *mut u8);
|
||||
|
||||
RaiseException(RUST_PANIC, 0, 0, 0 as *const _);
|
||||
rtabort!("could not unwind stack");
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(ptr: *mut c_void) -> Box<Any + Send + 'static> {
|
||||
// The `ptr` here actually corresponds to the code of the exception, and our
|
||||
// real data is stored in our thread local.
|
||||
rtassert!(ptr as DWORD == RUST_PANIC);
|
||||
|
||||
let data = PANIC_DATA.get() as *mut Box<Any + Send + 'static>;
|
||||
PANIC_DATA.set(0 as *mut u8);
|
||||
rtassert!(!data.is_null());
|
||||
|
||||
*Box::from_raw(data)
|
||||
}
|
||||
|
||||
// This is required by the compiler to exist (e.g. it's a lang item), but it's
|
||||
// never actually called by the compiler because __C_specific_handler is the
|
||||
// personality function that is always used. Hence this is just an aborting
|
||||
// stub.
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_eh_personality() {}
|
||||
fn rust_eh_personality() {
|
||||
unsafe { ::intrinsics::abort() }
|
||||
}
|
||||
|
||||
// This is a function referenced from `rust_try_msvc_64.ll` which is used to
|
||||
// filter the exceptions being caught by that function.
|
||||
//
|
||||
// In theory local variables can be accessed through the `rbp` parameter of this
|
||||
// function, but a comment in an LLVM test case indicates that this is not
|
||||
// implemented in LLVM, so this is just an idempotent function which doesn't
|
||||
// ferry along any other information.
|
||||
//
|
||||
// This function just takes a look at the current EXCEPTION_RECORD being thrown
|
||||
// to ensure that it's code is RUST_PANIC, which was set by the call to
|
||||
// `RaiseException` above in the `panic` function.
|
||||
#[no_mangle]
|
||||
pub extern fn rust_eh_personality_catch() {}
|
||||
pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS,
|
||||
_rbp: *mut c_void) -> i32 {
|
||||
unsafe {
|
||||
((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32
|
||||
}
|
||||
}
|
||||
|
78
src/rt/rust_try_msvc_64.ll
Normal file
78
src/rt/rust_try_msvc_64.ll
Normal file
@ -0,0 +1,78 @@
|
||||
; Copyright 2015 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.
|
||||
|
||||
; 64-bit MSVC's definition of the `rust_try` function. This function can't be
|
||||
; defined in Rust as it's a "try-catch" block that's not expressible in Rust's
|
||||
; syntax, so we're using LLVM to produce an object file with the associated
|
||||
; handler.
|
||||
;
|
||||
; To use the correct system implementation details, this file is separate from
|
||||
; the standard rust_try.ll as we need specifically use the __C_specific_handler
|
||||
; personality function or otherwise LLVM doesn't emit SEH handling tables.
|
||||
; There's also a few fiddly bits about SEH right now in LLVM that require us to
|
||||
; structure this a fairly particular way!
|
||||
;
|
||||
; See also: src/libstd/rt/unwind/seh.rs
|
||||
|
||||
define i8* @rust_try(void (i8*)* %f, i8* %env) {
|
||||
invoke void %f(i8* %env)
|
||||
to label %normal
|
||||
unwind label %catch
|
||||
|
||||
normal:
|
||||
ret i8* null
|
||||
|
||||
; Here's where most of the magic happens, this is the only landing pad in rust
|
||||
; tagged with "catch" to indicate that we're catching an exception. The other
|
||||
; catch handlers in rust_try.ll just catch *all* exceptions, but that's because
|
||||
; most exceptions are already filtered out by their personality function.
|
||||
;
|
||||
; For MSVC we're just using a standard personality function that we can't
|
||||
; customize, so we need to do the exception filtering ourselves, and this is
|
||||
; currently performed by the `__rust_try_filter` function. This function,
|
||||
; specified in the landingpad instruction, will be invoked by Windows SEH
|
||||
; routines and will return whether the exception in question can be caught (aka
|
||||
; the Rust runtime is the one that threw the exception).
|
||||
;
|
||||
; To get this to compile (currently LLVM segfaults if it's not in this
|
||||
; particular structure), when the landingpad is executing we test to make sure
|
||||
; that the ID of the exception being thrown is indeed the one that we were
|
||||
; expecting. If it's not, we resume the exception, and otherwise we return the
|
||||
; pointer that we got
|
||||
;
|
||||
; Full disclosure: It's not clear to me what this `llvm.eh.typeid` stuff is
|
||||
; doing *other* then just allowing LLVM to compile this file without
|
||||
; segfaulting. I would expect the entire landing pad to just be:
|
||||
;
|
||||
; %vals = landingpad ...
|
||||
; %ehptr = extractvalue { i8*, i32 } %vals, 0
|
||||
; ret i8* %ehptr
|
||||
;
|
||||
; but apparently LLVM chokes on this, so we do the more complicated thing to
|
||||
; placate it.
|
||||
catch:
|
||||
%vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
|
||||
catch i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*)
|
||||
%ehptr = extractvalue { i8*, i32 } %vals, 0
|
||||
%sel = extractvalue { i8*, i32 } %vals, 1
|
||||
%filter_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*))
|
||||
%is_filter = icmp eq i32 %sel, %filter_sel
|
||||
br i1 %is_filter, label %catch-return, label %catch-resume
|
||||
|
||||
catch-return:
|
||||
ret i8* %ehptr
|
||||
|
||||
catch-resume:
|
||||
resume { i8*, i32 } %vals
|
||||
}
|
||||
|
||||
declare i32 @__C_specific_handler(...)
|
||||
declare i32 @__rust_try_filter(i8*, i8*)
|
||||
declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind
|
@ -8,15 +8,12 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(no_std)]
|
||||
#![feature(no_std, core, libc)]
|
||||
#![no_std]
|
||||
#![feature(lang_items)]
|
||||
|
||||
#[lang="sized"]
|
||||
pub trait Sized { }
|
||||
|
||||
#[lang="panic"]
|
||||
fn panic(_: &(&'static str, &'static str, usize)) -> ! { loop {} }
|
||||
extern crate core;
|
||||
extern crate libc;
|
||||
|
||||
#[lang = "stack_exhausted"]
|
||||
extern fn stack_exhausted() {}
|
||||
@ -24,26 +21,8 @@ extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"]
|
||||
extern fn eh_personality() {}
|
||||
|
||||
#[lang="copy"]
|
||||
pub trait Copy {
|
||||
// Empty.
|
||||
}
|
||||
|
||||
#[lang="rem"]
|
||||
pub trait Rem<RHS=Self> {
|
||||
type Output = Self;
|
||||
fn rem(self, rhs: RHS) -> Self::Output;
|
||||
}
|
||||
|
||||
impl Rem for isize {
|
||||
type Output = isize;
|
||||
|
||||
#[inline]
|
||||
fn rem(self, other: isize) -> isize {
|
||||
// if you use `self % other` here, as one would expect, you
|
||||
// get back an error because of potential failure/overflow,
|
||||
// which tries to invoke error fns that don't have the
|
||||
// appropriate signatures anymore. So...just return 0.
|
||||
0
|
||||
}
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn rust_begin_unwind(msg: core::fmt::Arguments, file: &'static str,
|
||||
line: u32) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
@ -11,39 +11,11 @@
|
||||
// aux-build:lang-item-public.rs
|
||||
// ignore-android
|
||||
|
||||
#![feature(lang_items, start, no_std)]
|
||||
#![feature(start, no_std)]
|
||||
#![no_std]
|
||||
|
||||
extern crate lang_item_public as lang_lib;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[link(name = "c")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[link(name = "c")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[link(name = "execinfo")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "freebsd")]
|
||||
#[link(name = "c")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "dragonfly")]
|
||||
#[link(name = "c")]
|
||||
extern {}
|
||||
|
||||
#[cfg(any(target_os = "bitrig", target_os = "openbsd"))]
|
||||
#[link(name = "c")]
|
||||
extern {}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[link(name = "System")]
|
||||
extern {}
|
||||
|
||||
#[start]
|
||||
fn main(_: isize, _: *const *const u8) -> isize {
|
||||
1_isize % 1_isize
|
||||
|
Loading…
x
Reference in New Issue
Block a user