Skip to content

Commit

Permalink
Auto merge of #26569 - alexcrichton:msvc-llvm-update, r=brson
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed Jun 27, 2015
2 parents 773052a + 759a7f1 commit d3c03d0
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 132 deletions.
58 changes: 0 additions & 58 deletions mk/cfg/x86_64-pc-windows-msvc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
69 changes: 69 additions & 0 deletions mk/platform.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 6 additions & 3 deletions mk/rt.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 4 additions & 2 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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("")));
}
}

Expand Down
18 changes: 18 additions & 0 deletions src/librustc_trans/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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));
}
Expand Down
24 changes: 21 additions & 3 deletions src/librustc_trans/trans/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit d3c03d0

Please sign in to comment.