Auto merge of #26741 - alexcrichton:noinline-destructors, r=brson

This PR was originally going to be a "let's start running tests on MSVC" PR, but it didn't quite get to that point. It instead gets us ~80% of the way there! The steps taken in this PR are:

* Landing pads are turned on by default for 64-bit MSVC. The LLVM support is "good enough" with the caveat the destructor glue is now marked noinline. This was recommended [on the associated bug](https://llvm.org/bugs/show_bug.cgi?id=23884) as a stopgap until LLVM has a better representation for exception handling in MSVC. The consequence of this is that MSVC will have a bit of a perf hit, but there are possible routes we can take if this workaround sticks around for too long.
* The linker (`link.exe`) is now looked up in the Windows Registry if it's not otherwise available in the environment. This improves using the compiler outside of a VS shell (e.g. in a MSYS shell or in a vanilla cmd.exe shell). This also makes cross compiles via Cargo "just work" when crossing between 32 and 64 bit!
* TLS destructors were fixed to start running on MSVC (they previously weren't running at all)
* A few assorted `run-pass` tests were fixed.
* The dependency on the `rust_builtin` library was removed entirely for MSVC to try to prevent any `cl.exe` compiled objects get into the standard library. This should help us later remove any dependence on the CRT by the standard library.
* I re-added `rust_try_msvc_32.ll` for 32-bit MSVC and ensured that landing pads were turned off by default there as well.

Despite landing pads being enabled, there are still *many* failing tests on MSVC. The two major classes I've identified so far are:

* Spurious aborts. It appears that when optimizations are enabled that landing pads aren't always lined up properly, and sometimes an exception being thrown can't find the catch block down the stack, causing the program to abort. I've been working to reduce this test case but haven't been met with great success just yet.
* Parallel codegen does not work on MSVC. Our current strategy is to take the N object files emitted by the N codegen threads and use `ld -r` to assemble them into *one* object file. The MSVC linker, however, does not have this ability, and this will need to be rearchitected to work on MSVC.

I will fix parallel codegen in a future PR, and I'll also be watching LLVM closely to see if the aborts... disappear!
This commit is contained in:
bors 2015-07-06 19:49:16 +00:00
commit 2ceaa77ae2
18 changed files with 554 additions and 80 deletions

View File

@ -249,11 +249,9 @@ endef
$(foreach host,$(CFG_HOST), \
$(foreach target,$(CFG_TARGET), \
$(foreach stage,$(STAGES), \
$(foreach crate,$(CRATES), \
$(eval $(call SETUP_LIB_MSVC_ENV_VARS,$(stage),$(target),$(host),$(crate)))))))
$(foreach crate,$(CRATES), \
$(eval $(call SETUP_LIB_MSVC_ENV_VARS,0,$(target),$(host),$(crate))))))
$(foreach host,$(CFG_HOST), \
$(foreach target,$(CFG_TARGET), \
$(foreach stage,$(STAGES), \
$(foreach tool,$(TOOLS), \
$(eval $(call SETUP_TOOL_MSVC_ENV_VARS,$(stage),$(target),$(host),$(tool)))))))
$(foreach tool,$(TOOLS), \
$(eval $(call SETUP_TOOL_MSVC_ENV_VARS,0,$(target),$(host),$(tool))))))

View File

@ -12,6 +12,7 @@ use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
use super::linker::{Linker, GnuLinker, MsvcLinker};
use super::rpath::RPathConfig;
use super::rpath;
use super::msvc;
use super::svh::Svh;
use session::config;
use session::config::NoDebugInfo;
@ -358,10 +359,14 @@ pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> Stri
mangle(path.chain(Some(gensym_name(flav))), None)
}
pub fn get_cc_prog(sess: &Session) -> String {
match sess.opts.cg.linker {
Some(ref linker) => return linker.to_string(),
None => sess.target.target.options.linker.clone(),
pub fn get_linker(sess: &Session) -> (String, Command) {
if let Some(ref linker) = sess.opts.cg.linker {
(linker.clone(), Command::new(linker))
} else if sess.target.target.options.is_like_msvc {
("link.exe".to_string(), msvc::link_exe_cmd(sess))
} else {
(sess.target.target.options.linker.clone(),
Command::new(&sess.target.target.options.linker))
}
}
@ -807,8 +812,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
// The invocations of cc share some flags across platforms
let pname = get_cc_prog(sess);
let mut cmd = Command::new(&pname);
let (pname, mut cmd) = get_linker(sess);
cmd.env("PATH", command_path(sess));
let root = sess.target_filesearch(PathKind::Native).get_lib_path();

View File

@ -0,0 +1,239 @@
// 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.
//! MSVC-specific logic for linkers and such.
//!
//! This module contains a cross-platform interface but has a blank unix
//! implementation. The Windows implementation builds on top of Windows native
//! libraries (reading registry keys), so it otherwise wouldn't link on unix.
//!
//! Note that we don't have much special logic for finding the system linker on
//! any other platforms, so it may seem a little odd to single out MSVC to have
//! a good deal of code just to find the linker. Unlike Unix systems, however,
//! the MSVC linker is not in the system PATH by default. It also additionally
//! needs a few environment variables or command line flags to be able to link
//! against system libraries.
//!
//! In order to have a nice smooth experience on Windows, the logic in this file
//! is here to find the MSVC linker and set it up in the default configuration
//! one would need to set up anyway. This means that the Rust compiler can be
//! run not only in the developer shells of MSVC but also the standard cmd.exe
//! shell or MSYS shells.
//!
//! As a high-level note, all logic in this module for looking up various
//! paths/files is copied over from Clang in its MSVCToolChain.cpp file, but
//! comments can also be found below leading through the various code paths.
use std::process::Command;
use session::Session;
#[cfg(windows)]
mod registry;
#[cfg(windows)]
pub fn link_exe_cmd(sess: &Session) -> Command {
use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;
use self::registry::{RegistryKey, LOCAL_MACHINE};
// When finding the link.exe binary the 32-bit version is at the top level
// but the versions to cross to other architectures are stored in
// sub-folders. Unknown architectures also just bail out early to return the
// standard `link.exe` command.
let extra = match &sess.target.target.arch[..] {
"x86" => "",
"x86_64" => "amd64",
"arm" => "arm",
_ => return Command::new("link.exe"),
};
let vs_install_dir = get_vs_install_dir();
// First up, we need to find the `link.exe` binary itself, and there's a few
// locations that we can look. First up is the standard VCINSTALLDIR
// environment variable which is normally set by the vcvarsall.bat file. If
// an environment is set up manually by whomever's driving the compiler then
// we shouldn't muck with that decision and should instead respect that.
//
// Next up is looking in PATH itself. Here we look for `cl.exe` and then
// assume that `link.exe` is next to it if we find it. Note that we look for
// `cl.exe` because MinGW ships /usr/bin/link.exe which is normally found in
// PATH but we're not interested in finding that.
//
// Finally we read the Windows registry to discover the VS install root.
// From here we probe for `link.exe` just to make sure that it exists.
let mut cmd = env::var_os("VCINSTALLDIR").and_then(|dir| {
let mut p = PathBuf::from(dir);
p.push("bin");
p.push(extra);
p.push("link.exe");
if fs::metadata(&p).is_ok() {Some(p)} else {None}
}).or_else(|| {
env::var_os("PATH").and_then(|path| {
env::split_paths(&path).find(|path| {
fs::metadata(&path.join("cl.exe")).is_ok()
}).map(|p| {
p.join("link.exe")
})
})
}).or_else(|| {
vs_install_dir.as_ref().and_then(|p| {
let mut p = p.join("VC/bin");
p.push(extra);
p.push("link.exe");
if fs::metadata(&p).is_ok() {Some(p)} else {None}
})
}).map(|linker| {
Command::new(linker)
}).unwrap_or_else(|| {
Command::new("link.exe")
});
// The MSVC linker uses the LIB environment variable as the default lookup
// path for libraries. This environment variable is normally set up by the
// VS shells, so we only want to start adding our own pieces if it's not
// set.
//
// If we're adding our own pieces, then we need to add two primary
// directories to the default search path for the linker. The first is in
// the VS install direcotry and the next is the Windows SDK directory.
if env::var_os("LIB").is_none() {
if let Some(mut vs_install_dir) = vs_install_dir {
vs_install_dir.push("VC/lib");
vs_install_dir.push(extra);
let mut arg = OsString::from("/LIBPATH:");
arg.push(&vs_install_dir);
cmd.arg(arg);
}
if let Some(path) = get_windows_sdk_lib_path(sess) {
let mut arg = OsString::from("/LIBPATH:");
arg.push(&path);
cmd.arg(arg);
}
}
return cmd;
// When looking for the Visual Studio installation directory we look in a
// number of locations in varying degrees of precedence:
//
// 1. The Visual Studio registry keys
// 2. The Visual Studio Express registry keys
// 3. A number of somewhat standard environment variables
//
// If we find a hit from any of these keys then we strip off the IDE/Tools
// folders which are typically found at the end.
//
// As a final note, when we take a look at the registry keys they're
// typically found underneath the version of what's installed, but we don't
// quite know what's installed. As a result we probe all sub-keys of the two
// keys we're looking at to find out the maximum version of what's installed
// and we use that root directory.
fn get_vs_install_dir() -> Option<PathBuf> {
LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio".as_ref()).or_else(|_| {
LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VCExpress".as_ref())
}).ok().and_then(|key| {
max_version(&key).and_then(|(_vers, key)| {
key.query_str("InstallDir").ok()
})
}).or_else(|| {
env::var_os("VS120COMNTOOLS")
}).or_else(|| {
env::var_os("VS100COMNTOOLS")
}).or_else(|| {
env::var_os("VS90COMNTOOLS")
}).or_else(|| {
env::var_os("VS80COMNTOOLS")
}).map(PathBuf::from).and_then(|mut dir| {
if dir.ends_with("Common7/IDE") || dir.ends_with("Common7/Tools") {
dir.pop();
dir.pop();
Some(dir)
} else {
None
}
})
}
// Given a registry key, look at all the sub keys and find the one which has
// the maximal numeric value.
//
// Returns the name of the maximal key as well as the opened maximal key.
fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
let mut max_vers = 0;
let mut max_key = None;
for subkey in key.iter().filter_map(|k| k.ok()) {
let val = subkey.to_str().and_then(|s| {
s.trim_left_matches("v").replace(".", "").parse().ok()
});
let val = match val {
Some(s) => s,
None => continue,
};
if val > max_vers {
if let Ok(k) = key.open(&subkey) {
max_vers = val;
max_key = Some((subkey, k));
}
}
}
return max_key
}
fn get_windows_sdk_lib_path(sess: &Session) -> Option<PathBuf> {
let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows";
let key = LOCAL_MACHINE.open(key.as_ref());
let (n, k) = match key.ok().as_ref().and_then(max_version) {
Some(p) => p,
None => return None,
};
let mut parts = n.to_str().unwrap().trim_left_matches("v").splitn(2, ".");
let major = parts.next().unwrap().parse::<usize>().unwrap();
let _minor = parts.next().unwrap().parse::<usize>().unwrap();
let path = match k.query_str("InstallationFolder") {
Ok(p) => PathBuf::from(p).join("Lib"),
Err(..) => return None,
};
if major <= 7 {
// In Windows SDK 7.x, x86 libraries are directly in the Lib folder,
// x64 libraries are inside, and it's not necessary to link agains
// the SDK 7.x when targeting ARM or other architectures.
let x86 = match &sess.target.target.arch[..] {
"x86" => true,
"x86_64" => false,
_ => return None,
};
Some(if x86 {path} else {path.join("x64")})
} else {
// Windows SDK 8.x installes libraries in a folder whose names
// depend on the version of the OS you're targeting. By default
// choose the newest, which usually corresponds to the version of
// the OS you've installed the SDK on.
let extra = match &sess.target.target.arch[..] {
"x86" => "x86",
"x86_64" => "x64",
"arm" => "arm",
_ => return None,
};
["winv6.3", "win8", "win7"].iter().map(|p| path.join(p)).find(|part| {
fs::metadata(part).is_ok()
}).map(|path| {
path.join("um").join(extra)
})
}
}
}
#[cfg(not(windows))]
pub fn link_exe_cmd(_sess: &Session) -> Command {
Command::new("link.exe")
}

View File

@ -0,0 +1,170 @@
// 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.
use std::io;
use std::ffi::{OsString, OsStr};
use std::os::windows::prelude::*;
use std::ops::RangeFrom;
use libc::{DWORD, LPCWSTR, LONG, LPDWORD, LPBYTE, ERROR_SUCCESS};
const HKEY_LOCAL_MACHINE: HKEY = 0x80000002 as HKEY;
const KEY_WOW64_32KEY: REGSAM = 0x0200;
const KEY_READ: REGSAM = (STANDARD_RIGTS_READ | KEY_QUERY_VALUE |
KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & !SYNCHRONIZE;
const STANDARD_RIGTS_READ: REGSAM = READ_CONTROL;
const READ_CONTROL: REGSAM = 0x00020000;
const KEY_QUERY_VALUE: REGSAM = 0x0001;
const KEY_ENUMERATE_SUB_KEYS: REGSAM = 0x0008;
const KEY_NOTIFY: REGSAM = 0x0010;
const SYNCHRONIZE: REGSAM = 0x00100000;
const REG_SZ: DWORD = 1;
const ERROR_NO_MORE_ITEMS: DWORD = 259;
enum __HKEY__ {}
pub type HKEY = *mut __HKEY__;
pub type PHKEY = *mut HKEY;
pub type REGSAM = DWORD;
pub type LPWSTR = *mut u16;
pub type PFILETIME = *mut ();
#[link(name = "advapi32")]
extern "system" {
fn RegOpenKeyExW(hKey: HKEY,
lpSubKey: LPCWSTR,
ulOptions: DWORD,
samDesired: REGSAM,
phkResult: PHKEY) -> LONG;
fn RegQueryValueExW(hKey: HKEY,
lpValueName: LPCWSTR,
lpReserved: LPDWORD,
lpType: LPDWORD,
lpData: LPBYTE,
lpcbData: LPDWORD) -> LONG;
fn RegEnumKeyExW(hKey: HKEY,
dwIndex: DWORD,
lpName: LPWSTR,
lpcName: LPDWORD,
lpReserved: LPDWORD,
lpClass: LPWSTR,
lpcClass: LPDWORD,
lpftLastWriteTime: PFILETIME) -> LONG;
fn RegCloseKey(hKey: HKEY) -> LONG;
}
pub struct RegistryKey(Repr);
struct OwnedKey(HKEY);
enum Repr {
Const(HKEY),
Owned(OwnedKey),
}
pub struct Iter<'a> {
idx: RangeFrom<DWORD>,
key: &'a RegistryKey,
}
unsafe impl Sync for RegistryKey {}
unsafe impl Send for RegistryKey {}
pub static LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::Const(HKEY_LOCAL_MACHINE));
impl RegistryKey {
fn raw(&self) -> HKEY {
match self.0 {
Repr::Const(val) => val,
Repr::Owned(ref val) => val.0,
}
}
pub fn open(&self, key: &OsStr) -> io::Result<RegistryKey> {
let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
let mut ret = 0 as *mut _;
let err = unsafe {
RegOpenKeyExW(self.raw(), key.as_ptr(), 0,
KEY_READ | KEY_WOW64_32KEY, &mut ret)
};
if err == ERROR_SUCCESS {
Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
} else {
Err(io::Error::from_raw_os_error(err as i32))
}
}
pub fn iter(&self) -> Iter {
Iter { idx: 0.., key: self }
}
pub fn query_str(&self, name: &str) -> io::Result<OsString> {
let name: &OsStr = name.as_ref();
let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
let mut len = 0;
let mut kind = 0;
unsafe {
let err = RegQueryValueExW(self.raw(), name.as_ptr(), 0 as *mut _,
&mut kind, 0 as *mut _, &mut len);
if err != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(err as i32))
}
if kind != REG_SZ {
return Err(io::Error::new(io::ErrorKind::Other,
"registry key wasn't a string"))
}
// The length here is the length in bytes, but we're using wide
// characters so we need to be sure to halve it for the capacity
// passed in.
let mut v = Vec::with_capacity(len as usize / 2);
let err = RegQueryValueExW(self.raw(), name.as_ptr(), 0 as *mut _,
0 as *mut _, v.as_mut_ptr() as *mut _,
&mut len);
if err != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(err as i32))
}
v.set_len(len as usize / 2);
// Some registry keys may have a terminating nul character, but
// we're not interested in that, so chop it off if it's there.
if v[v.len() - 1] == 0 {
v.pop();
}
Ok(OsString::from_wide(&v))
}
}
}
impl Drop for OwnedKey {
fn drop(&mut self) {
unsafe { RegCloseKey(self.0); }
}
}
impl<'a> Iterator for Iter<'a> {
type Item = io::Result<OsString>;
fn next(&mut self) -> Option<io::Result<OsString>> {
self.idx.next().and_then(|i| unsafe {
let mut v = Vec::with_capacity(256);
let mut len = v.capacity() as DWORD;
let ret = RegEnumKeyExW(self.key.raw(), i, v.as_mut_ptr(), &mut len,
0 as *mut _, 0 as *mut _, 0 as *mut _,
0 as *mut _);
if ret == ERROR_NO_MORE_ITEMS as LONG {
None
} else if ret != ERROR_SUCCESS {
Some(Err(io::Error::from_raw_os_error(ret as i32)))
} else {
v.set_len(len as usize);
Some(Ok(OsString::from_wide(&v)))
}
})
}
}

View File

@ -9,7 +9,7 @@
// except according to those terms.
use back::lto;
use back::link::{get_cc_prog, remove};
use back::link::{get_linker, remove};
use session::config::{OutputFilenames, Passes, SomePasses, AllPasses};
use session::Session;
use session::config;
@ -27,7 +27,7 @@ use std::ffi::{CStr, CString};
use std::fs;
use std::mem;
use std::path::Path;
use std::process::{Command, Stdio};
use std::process::Stdio;
use std::ptr;
use std::str;
use std::sync::{Arc, Mutex};
@ -737,8 +737,7 @@ pub fn run_passes(sess: &Session,
None
};
let pname = get_cc_prog(sess);
let mut cmd = Command::new(&pname[..]);
let (pname, mut cmd) = get_linker(sess);
cmd.args(&sess.target.target.options.pre_link_args);
cmd.arg("-nostdlib");
@ -767,8 +766,7 @@ pub fn run_passes(sess: &Session,
},
Err(e) => {
sess.err(&format!("could not exec the linker `{}`: {}",
pname,
e));
pname, e));
sess.abort_if_errors();
},
}
@ -986,8 +984,7 @@ fn run_work_multithreaded(sess: &Session,
}
pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
let pname = get_cc_prog(sess);
let mut cmd = Command::new(&pname[..]);
let (pname, mut cmd) = get_linker(sess);
cmd.arg("-c").arg("-o").arg(&outputs.path(config::OutputTypeObject))
.arg(&outputs.temp_path(config::OutputTypeAssembly));
@ -1007,9 +1004,7 @@ pub fn run_assembler(sess: &Session, outputs: &OutputFilenames) {
}
},
Err(e) => {
sess.err(&format!("could not exec the linker `{}`: {}",
pname,
e));
sess.err(&format!("could not exec the linker `{}`: {}", pname, e));
sess.abort_if_errors();
}
}

View File

@ -82,7 +82,7 @@ pub mod back {
pub mod link;
pub mod lto;
pub mod write;
pub mod msvc;
}
pub mod trans;

View File

@ -753,13 +753,15 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
pub fn need_invoke(bcx: Block) -> bool {
// FIXME(#25869) currently unwinding is not implemented for MSVC and our
// normal unwinding infrastructure ends up just causing linker
// errors with the current LLVM implementation, so landing
// pads are disabled entirely for MSVC targets
if bcx.sess().no_landing_pads() ||
bcx.sess().target.target.options.is_like_msvc {
return false;
if bcx.sess().no_landing_pads() {
return false
}
// Currently 32-bit MSVC unwinding is not super well implemented in LLVM, so
// we avoid it entirely.
if bcx.sess().target.target.options.is_like_msvc &&
bcx.sess().target.target.arch == "x86" {
return false
}
// Avoid using invoke if we are already inside a landing pad.

View File

@ -851,8 +851,8 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
// 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".
// CRT's custom personality function, 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) if !target.options.is_like_msvc => {
@ -864,10 +864,12 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
match *personality {
Some(llpersonality) => llpersonality,
None => {
let name = if target.options.is_like_msvc {
"__C_specific_handler"
} else {
let name = if !target.options.is_like_msvc {
"rust_eh_personality"
} else if target.arch == "x86" {
"_except_handler3"
} else {
"__C_specific_handler"
};
let fty = Type::variadic_func(&[], &Type::i32(self.ccx));
let f = declare::declare_cfn(self.ccx, name, fty,

View File

@ -22,8 +22,9 @@ use middle::lang_items::ExchangeFreeFnLangItem;
use middle::subst;
use middle::subst::{Subst, Substs};
use middle::ty::{self, Ty};
use trans::adt;
use trans::adt::GetDtorType; // for tcx.dtor_type()
use trans::adt;
use trans::attributes;
use trans::base::*;
use trans::build::*;
use trans::callee;
@ -43,6 +44,7 @@ use trans::type_::Type;
use arena::TypedArena;
use libc::c_uint;
use syntax::ast;
use syntax::attr::InlineAttr;
pub fn trans_exchange_free_dyn<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
v: ValueRef,
@ -250,6 +252,25 @@ fn get_drop_glue_core<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
update_linkage(ccx, llfn, None, OriginalTranslation);
// FIXME: Currently LLVM has a bug where if an SSA value is created in one
// landing pad and then used in another it will abort during
// compilation. The compiler never actually generates nested landing
// pads, but this often arises when destructors are inlined into
// other functions. To prevent this inlining from happening (and thus
// preventing the LLVM abort) we mark all drop glue as inline(never)
// on MSVC.
//
// For more information about the bug, see:
//
// https://llvm.org/bugs/show_bug.cgi?id=23884
//
// This is clearly not the ideal solution to the problem (due to the
// perf hits), so this should be removed once the upstream bug is
// fixed.
if ccx.sess().target.target.options.is_like_msvc {
attributes::inline(llfn, InlineAttr::Never);
}
ccx.stats().n_glues_created.set(ccx.stats().n_glues_created.get() + 1);
// All glue functions take values passed *by alias*; this is a
// requirement since in many contexts glue is invoked indirectly and

View File

@ -151,6 +151,7 @@
#![cfg_attr(windows, feature(str_utf16))]
#![cfg_attr(test, feature(float_from_str_radix, range_inclusive, float_extras))]
#![cfg_attr(test, feature(test, rustc_private, float_consts))]
#![cfg_attr(target_env = "msvc", feature(link_args))]
// Don't link to std. We are std.
#![no_std]

View File

@ -13,7 +13,6 @@ use io::prelude::*;
use env;
use fmt;
use intrinsics;
use libc::uintptr_t;
use sync::atomic::{self, Ordering};
use sys::stdio::Stderr;
@ -22,10 +21,18 @@ use sys::stdio::Stderr;
/// can't run correctly un-altered. Valgrind is there to help
/// you notice weirdness in normal, un-doctored code paths!
pub fn running_on_valgrind() -> bool {
extern {
fn rust_running_on_valgrind() -> uintptr_t;
return on_valgrind();
#[cfg(windows)]
fn on_valgrind() -> bool { false }
#[cfg(unix)]
fn on_valgrind() -> bool {
use libc::uintptr_t;
extern {
fn rust_running_on_valgrind() -> uintptr_t;
}
unsafe { rust_running_on_valgrind() != 0 }
}
unsafe { rust_running_on_valgrind() != 0 }
}
/// Valgrind has a fixed-sized array (size around 2000) of segment descriptors

View File

@ -12,8 +12,8 @@
//! the standard library This varies per-platform, but these libraries are
//! necessary for running libstd.
// All platforms need to link to rustrt
#[cfg(not(test))]
// A few small shims in C that haven't been translated to Rust yet
#[cfg(all(not(test), not(windows)))]
#[link(name = "rust_builtin", kind = "static")]
extern {}

View File

@ -221,8 +221,8 @@ unsafe fn unregister_dtor(key: Key) -> bool {
//
// # The article mentions crazy stuff about "/INCLUDE"?
//
// It sure does! This seems to work for now, so maybe we'll just run into
// that if we start linking with msvc?
// It sure does! We include it below for MSVC targets, but it look like for GNU
// targets we don't require it.
#[link_section = ".CRT$XLB"]
#[linkage = "external"]
@ -231,6 +231,10 @@ pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD,
LPVOID) =
on_tls_callback;
#[cfg(target_env = "msvc")]
#[link_args = "/INCLUDE:_tls_used"]
extern {}
#[allow(warnings)]
unsafe extern "system" fn on_tls_callback(h: LPVOID,
dwReason: DWORD,

View File

@ -872,7 +872,7 @@ fn run_tests<F>(opts: &TestOpts,
#[allow(deprecated)]
fn get_concurrency() -> usize {
match env::var("RUST_TEST_THREADS") {
return match env::var("RUST_TEST_THREADS") {
Ok(s) => {
let opt_n: Option<usize> = s.parse().ok();
match opt_n {
@ -884,10 +884,24 @@ fn get_concurrency() -> usize {
if std::rt::util::limit_thread_creation_due_to_osx_and_valgrind() {
1
} else {
extern { fn rust_get_num_cpus() -> libc::uintptr_t; }
unsafe { rust_get_num_cpus() as usize }
num_cpus()
}
}
};
#[cfg(windows)]
fn num_cpus() -> usize {
unsafe {
let mut sysinfo = std::mem::zeroed();
libc::GetSystemInfo(&mut sysinfo);
sysinfo.dwNumberOfProcessors as usize
}
}
#[cfg(unix)]
fn num_cpus() -> usize {
extern { fn rust_get_num_cpus() -> libc::uintptr_t; }
unsafe { rust_get_num_cpus() as usize }
}
}

View File

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#if !defined(_WIN32)
#include <stdint.h>
#include <time.h>
#include <string.h>
@ -15,7 +17,6 @@
#include <stdlib.h>
#if !defined(_WIN32)
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
@ -23,12 +24,6 @@
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#else
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#include <tchar.h>
#endif
#ifdef __APPLE__
#include <TargetConditionals.h>
@ -41,17 +36,8 @@
/* Foreign builtins. */
//include valgrind.h after stdint.h so that uintptr_t is defined for msys2 w64
#ifndef _WIN32
#include "valgrind/valgrind.h"
#endif
#if defined(_MSC_VER)
# define RUST_BUILTIN_API __declspec(dllexport)
#else
# define RUST_BUILTIN_API
#endif
#ifndef _WIN32
char*
rust_list_dir_val(struct dirent* entry_ptr) {
return entry_ptr->d_name;
@ -92,17 +78,8 @@ int
rust_dirent_t_size() {
return sizeof(struct dirent);
}
#endif
#if defined(_WIN32)
int
get_num_cpus() {
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return (int) sysinfo.dwNumberOfProcessors;
}
#elif defined(__BSD__)
#if defined(__BSD__)
int
get_num_cpus() {
/* swiped from http://stackoverflow.com/questions/150355/
@ -136,7 +113,6 @@ get_num_cpus() {
}
#endif
RUST_BUILTIN_API
uintptr_t
rust_get_num_cpus() {
return get_num_cpus();
@ -144,11 +120,7 @@ rust_get_num_cpus() {
uintptr_t
rust_running_on_valgrind() {
#ifdef _WIN32
return 0;
#else
return RUNNING_ON_VALGRIND;
#endif
}
#if defined(__DragonFly__)
@ -484,6 +456,8 @@ const char * rust_current_exe() {
#endif
#endif // !defined(_WIN32)
//
// Local Variables:
// mode: C++

View File

@ -0,0 +1,42 @@
; 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.
; For more comments about what's going on here see rust_try_msvc_64.ll. The only
; difference between that and this file is the personality function used as it's
; different for 32-bit MSVC than it is for 64-bit.
define i8* @rust_try(void (i8*)* %f, i8* %env)
personality i8* bitcast (i32 (...)* @_except_handler3 to i8*)
{
invoke void %f(i8* %env)
to label %normal
unwind label %catch
normal:
ret i8* null
catch:
%vals = landingpad { i8*, i32 }
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 @_except_handler3(...)
declare i32 @__rust_try_filter(i8*, i8*)
declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind

View File

@ -11,6 +11,7 @@
// Test a foreign function that accepts empty struct.
// pretty-expanded FIXME #23616
// ignore-msvc
struct TwoU8s {
one: u8,

View File

@ -37,7 +37,7 @@ mod m {
#[link_name="lgamma_r"]
pub fn lgamma(n: c_double, sign: &mut c_int) -> c_double;
#[cfg(windows)]
#[link_name="__lgamma_r"]
#[link_name="lgamma"]
pub fn lgamma(n: c_double, sign: &mut c_int) -> c_double;
}
}