diff --git a/mk/target.mk b/mk/target.mk index c398950965f54..c2de9af39c764 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -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)))))) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index cf5feabcc57e2..e0495226d9061 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -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(); diff --git a/src/librustc_trans/back/msvc/mod.rs b/src/librustc_trans/back/msvc/mod.rs new file mode 100644 index 0000000000000..0077e7eed52d0 --- /dev/null +++ b/src/librustc_trans/back/msvc/mod.rs @@ -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 or the MIT license +// , 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 { + 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 { + 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::().unwrap(); + let _minor = parts.next().unwrap().parse::().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") +} diff --git a/src/librustc_trans/back/msvc/registry.rs b/src/librustc_trans/back/msvc/registry.rs new file mode 100644 index 0000000000000..97fd7f99d197e --- /dev/null +++ b/src/librustc_trans/back/msvc/registry.rs @@ -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 or the MIT license +// , 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, + 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 { + let key = key.encode_wide().chain(Some(0)).collect::>(); + 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 { + let name: &OsStr = name.as_ref(); + let name = name.encode_wide().chain(Some(0)).collect::>(); + 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; + + fn next(&mut self) -> Option> { + 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))) + } + }) + } +} diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 267f0b6d95329..90ddba4e09c58 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -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(); } } diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index dc692b0e765dd..66881edaa8b86 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -83,7 +83,7 @@ pub mod back { pub mod link; pub mod lto; pub mod write; - + pub mod msvc; } pub mod trans; diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 0cd6bbad03aa9..079cd9c3757e5 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -751,13 +751,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. diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index 588e4cea5048b..1891320313a85 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -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, diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 3bcdcd89c47f5..be8510c8e968b 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -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 diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 2117291817754..73e45619774d7 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -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] diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 04f36d99c8eb5..031fda089c84c 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -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 diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index be674c83e2213..b7839423e5225 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -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 {} diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 5002de559884d..a2dbb0f834243 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -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, diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 0a3c350086cdd..724c0b2a8927f 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -872,7 +872,7 @@ fn run_tests(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 = 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 } } } diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 1a2917a1dd67f..76a3debef59a4 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#if !defined(_WIN32) + #include #include #include @@ -15,7 +17,6 @@ #include -#if !defined(_WIN32) #include #include #include @@ -23,12 +24,6 @@ #include #include #include -#else -#include -#include -#include -#include -#endif #ifdef __APPLE__ #include @@ -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++ diff --git a/src/rt/rust_try_msvc_32.ll b/src/rt/rust_try_msvc_32.ll new file mode 100644 index 0000000000000..bdee53b136e10 --- /dev/null +++ b/src/rt/rust_try_msvc_32.ll @@ -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 or the MIT license +; , 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 diff --git a/src/test/run-pass/extern-pass-empty.rs b/src/test/run-pass/extern-pass-empty.rs index 17b0bb580fce2..21948d2e5ad23 100644 --- a/src/test/run-pass/extern-pass-empty.rs +++ b/src/test/run-pass/extern-pass-empty.rs @@ -11,6 +11,7 @@ // Test a foreign function that accepts empty struct. // pretty-expanded FIXME #23616 +// ignore-msvc struct TwoU8s { one: u8, diff --git a/src/test/run-pass/issue-2214.rs b/src/test/run-pass/issue-2214.rs index 775cfb0ee4219..316e379e664ae 100644 --- a/src/test/run-pass/issue-2214.rs +++ b/src/test/run-pass/issue-2214.rs @@ -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; } }