Skip to content

Commit 2ceaa77

Browse files
committed
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!
2 parents 943b014 + 3e26e56 commit 2ceaa77

File tree

18 files changed

+554
-80
lines changed

18 files changed

+554
-80
lines changed

mk/target.mk

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,9 @@ endef
249249

250250
$(foreach host,$(CFG_HOST), \
251251
$(foreach target,$(CFG_TARGET), \
252-
$(foreach stage,$(STAGES), \
253-
$(foreach crate,$(CRATES), \
254-
$(eval $(call SETUP_LIB_MSVC_ENV_VARS,$(stage),$(target),$(host),$(crate)))))))
252+
$(foreach crate,$(CRATES), \
253+
$(eval $(call SETUP_LIB_MSVC_ENV_VARS,0,$(target),$(host),$(crate))))))
255254
$(foreach host,$(CFG_HOST), \
256255
$(foreach target,$(CFG_TARGET), \
257-
$(foreach stage,$(STAGES), \
258-
$(foreach tool,$(TOOLS), \
259-
$(eval $(call SETUP_TOOL_MSVC_ENV_VARS,$(stage),$(target),$(host),$(tool)))))))
256+
$(foreach tool,$(TOOLS), \
257+
$(eval $(call SETUP_TOOL_MSVC_ENV_VARS,0,$(target),$(host),$(tool))))))

src/librustc_trans/back/link.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
1212
use super::linker::{Linker, GnuLinker, MsvcLinker};
1313
use super::rpath::RPathConfig;
1414
use super::rpath;
15+
use super::msvc;
1516
use super::svh::Svh;
1617
use session::config;
1718
use session::config::NoDebugInfo;
@@ -358,10 +359,14 @@ pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> Stri
358359
mangle(path.chain(Some(gensym_name(flav))), None)
359360
}
360361

361-
pub fn get_cc_prog(sess: &Session) -> String {
362-
match sess.opts.cg.linker {
363-
Some(ref linker) => return linker.to_string(),
364-
None => sess.target.target.options.linker.clone(),
362+
pub fn get_linker(sess: &Session) -> (String, Command) {
363+
if let Some(ref linker) = sess.opts.cg.linker {
364+
(linker.clone(), Command::new(linker))
365+
} else if sess.target.target.options.is_like_msvc {
366+
("link.exe".to_string(), msvc::link_exe_cmd(sess))
367+
} else {
368+
(sess.target.target.options.linker.clone(),
369+
Command::new(&sess.target.target.options.linker))
365370
}
366371
}
367372

@@ -807,8 +812,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
807812
let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
808813

809814
// The invocations of cc share some flags across platforms
810-
let pname = get_cc_prog(sess);
811-
let mut cmd = Command::new(&pname);
815+
let (pname, mut cmd) = get_linker(sess);
812816
cmd.env("PATH", command_path(sess));
813817

814818
let root = sess.target_filesearch(PathKind::Native).get_lib_path();

src/librustc_trans/back/msvc/mod.rs

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! MSVC-specific logic for linkers and such.
12+
//!
13+
//! This module contains a cross-platform interface but has a blank unix
14+
//! implementation. The Windows implementation builds on top of Windows native
15+
//! libraries (reading registry keys), so it otherwise wouldn't link on unix.
16+
//!
17+
//! Note that we don't have much special logic for finding the system linker on
18+
//! any other platforms, so it may seem a little odd to single out MSVC to have
19+
//! a good deal of code just to find the linker. Unlike Unix systems, however,
20+
//! the MSVC linker is not in the system PATH by default. It also additionally
21+
//! needs a few environment variables or command line flags to be able to link
22+
//! against system libraries.
23+
//!
24+
//! In order to have a nice smooth experience on Windows, the logic in this file
25+
//! is here to find the MSVC linker and set it up in the default configuration
26+
//! one would need to set up anyway. This means that the Rust compiler can be
27+
//! run not only in the developer shells of MSVC but also the standard cmd.exe
28+
//! shell or MSYS shells.
29+
//!
30+
//! As a high-level note, all logic in this module for looking up various
31+
//! paths/files is copied over from Clang in its MSVCToolChain.cpp file, but
32+
//! comments can also be found below leading through the various code paths.
33+
34+
use std::process::Command;
35+
use session::Session;
36+
37+
#[cfg(windows)]
38+
mod registry;
39+
40+
#[cfg(windows)]
41+
pub fn link_exe_cmd(sess: &Session) -> Command {
42+
use std::env;
43+
use std::ffi::OsString;
44+
use std::fs;
45+
use std::path::PathBuf;
46+
use self::registry::{RegistryKey, LOCAL_MACHINE};
47+
48+
// When finding the link.exe binary the 32-bit version is at the top level
49+
// but the versions to cross to other architectures are stored in
50+
// sub-folders. Unknown architectures also just bail out early to return the
51+
// standard `link.exe` command.
52+
let extra = match &sess.target.target.arch[..] {
53+
"x86" => "",
54+
"x86_64" => "amd64",
55+
"arm" => "arm",
56+
_ => return Command::new("link.exe"),
57+
};
58+
59+
let vs_install_dir = get_vs_install_dir();
60+
61+
// First up, we need to find the `link.exe` binary itself, and there's a few
62+
// locations that we can look. First up is the standard VCINSTALLDIR
63+
// environment variable which is normally set by the vcvarsall.bat file. If
64+
// an environment is set up manually by whomever's driving the compiler then
65+
// we shouldn't muck with that decision and should instead respect that.
66+
//
67+
// Next up is looking in PATH itself. Here we look for `cl.exe` and then
68+
// assume that `link.exe` is next to it if we find it. Note that we look for
69+
// `cl.exe` because MinGW ships /usr/bin/link.exe which is normally found in
70+
// PATH but we're not interested in finding that.
71+
//
72+
// Finally we read the Windows registry to discover the VS install root.
73+
// From here we probe for `link.exe` just to make sure that it exists.
74+
let mut cmd = env::var_os("VCINSTALLDIR").and_then(|dir| {
75+
let mut p = PathBuf::from(dir);
76+
p.push("bin");
77+
p.push(extra);
78+
p.push("link.exe");
79+
if fs::metadata(&p).is_ok() {Some(p)} else {None}
80+
}).or_else(|| {
81+
env::var_os("PATH").and_then(|path| {
82+
env::split_paths(&path).find(|path| {
83+
fs::metadata(&path.join("cl.exe")).is_ok()
84+
}).map(|p| {
85+
p.join("link.exe")
86+
})
87+
})
88+
}).or_else(|| {
89+
vs_install_dir.as_ref().and_then(|p| {
90+
let mut p = p.join("VC/bin");
91+
p.push(extra);
92+
p.push("link.exe");
93+
if fs::metadata(&p).is_ok() {Some(p)} else {None}
94+
})
95+
}).map(|linker| {
96+
Command::new(linker)
97+
}).unwrap_or_else(|| {
98+
Command::new("link.exe")
99+
});
100+
101+
// The MSVC linker uses the LIB environment variable as the default lookup
102+
// path for libraries. This environment variable is normally set up by the
103+
// VS shells, so we only want to start adding our own pieces if it's not
104+
// set.
105+
//
106+
// If we're adding our own pieces, then we need to add two primary
107+
// directories to the default search path for the linker. The first is in
108+
// the VS install direcotry and the next is the Windows SDK directory.
109+
if env::var_os("LIB").is_none() {
110+
if let Some(mut vs_install_dir) = vs_install_dir {
111+
vs_install_dir.push("VC/lib");
112+
vs_install_dir.push(extra);
113+
let mut arg = OsString::from("/LIBPATH:");
114+
arg.push(&vs_install_dir);
115+
cmd.arg(arg);
116+
}
117+
if let Some(path) = get_windows_sdk_lib_path(sess) {
118+
let mut arg = OsString::from("/LIBPATH:");
119+
arg.push(&path);
120+
cmd.arg(arg);
121+
}
122+
}
123+
124+
return cmd;
125+
126+
// When looking for the Visual Studio installation directory we look in a
127+
// number of locations in varying degrees of precedence:
128+
//
129+
// 1. The Visual Studio registry keys
130+
// 2. The Visual Studio Express registry keys
131+
// 3. A number of somewhat standard environment variables
132+
//
133+
// If we find a hit from any of these keys then we strip off the IDE/Tools
134+
// folders which are typically found at the end.
135+
//
136+
// As a final note, when we take a look at the registry keys they're
137+
// typically found underneath the version of what's installed, but we don't
138+
// quite know what's installed. As a result we probe all sub-keys of the two
139+
// keys we're looking at to find out the maximum version of what's installed
140+
// and we use that root directory.
141+
fn get_vs_install_dir() -> Option<PathBuf> {
142+
LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VisualStudio".as_ref()).or_else(|_| {
143+
LOCAL_MACHINE.open(r"SOFTWARE\Microsoft\VCExpress".as_ref())
144+
}).ok().and_then(|key| {
145+
max_version(&key).and_then(|(_vers, key)| {
146+
key.query_str("InstallDir").ok()
147+
})
148+
}).or_else(|| {
149+
env::var_os("VS120COMNTOOLS")
150+
}).or_else(|| {
151+
env::var_os("VS100COMNTOOLS")
152+
}).or_else(|| {
153+
env::var_os("VS90COMNTOOLS")
154+
}).or_else(|| {
155+
env::var_os("VS80COMNTOOLS")
156+
}).map(PathBuf::from).and_then(|mut dir| {
157+
if dir.ends_with("Common7/IDE") || dir.ends_with("Common7/Tools") {
158+
dir.pop();
159+
dir.pop();
160+
Some(dir)
161+
} else {
162+
None
163+
}
164+
})
165+
}
166+
167+
// Given a registry key, look at all the sub keys and find the one which has
168+
// the maximal numeric value.
169+
//
170+
// Returns the name of the maximal key as well as the opened maximal key.
171+
fn max_version(key: &RegistryKey) -> Option<(OsString, RegistryKey)> {
172+
let mut max_vers = 0;
173+
let mut max_key = None;
174+
for subkey in key.iter().filter_map(|k| k.ok()) {
175+
let val = subkey.to_str().and_then(|s| {
176+
s.trim_left_matches("v").replace(".", "").parse().ok()
177+
});
178+
let val = match val {
179+
Some(s) => s,
180+
None => continue,
181+
};
182+
if val > max_vers {
183+
if let Ok(k) = key.open(&subkey) {
184+
max_vers = val;
185+
max_key = Some((subkey, k));
186+
}
187+
}
188+
}
189+
return max_key
190+
}
191+
192+
fn get_windows_sdk_lib_path(sess: &Session) -> Option<PathBuf> {
193+
let key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows";
194+
let key = LOCAL_MACHINE.open(key.as_ref());
195+
let (n, k) = match key.ok().as_ref().and_then(max_version) {
196+
Some(p) => p,
197+
None => return None,
198+
};
199+
let mut parts = n.to_str().unwrap().trim_left_matches("v").splitn(2, ".");
200+
let major = parts.next().unwrap().parse::<usize>().unwrap();
201+
let _minor = parts.next().unwrap().parse::<usize>().unwrap();
202+
let path = match k.query_str("InstallationFolder") {
203+
Ok(p) => PathBuf::from(p).join("Lib"),
204+
Err(..) => return None,
205+
};
206+
if major <= 7 {
207+
// In Windows SDK 7.x, x86 libraries are directly in the Lib folder,
208+
// x64 libraries are inside, and it's not necessary to link agains
209+
// the SDK 7.x when targeting ARM or other architectures.
210+
let x86 = match &sess.target.target.arch[..] {
211+
"x86" => true,
212+
"x86_64" => false,
213+
_ => return None,
214+
};
215+
Some(if x86 {path} else {path.join("x64")})
216+
} else {
217+
// Windows SDK 8.x installes libraries in a folder whose names
218+
// depend on the version of the OS you're targeting. By default
219+
// choose the newest, which usually corresponds to the version of
220+
// the OS you've installed the SDK on.
221+
let extra = match &sess.target.target.arch[..] {
222+
"x86" => "x86",
223+
"x86_64" => "x64",
224+
"arm" => "arm",
225+
_ => return None,
226+
};
227+
["winv6.3", "win8", "win7"].iter().map(|p| path.join(p)).find(|part| {
228+
fs::metadata(part).is_ok()
229+
}).map(|path| {
230+
path.join("um").join(extra)
231+
})
232+
}
233+
}
234+
}
235+
236+
#[cfg(not(windows))]
237+
pub fn link_exe_cmd(_sess: &Session) -> Command {
238+
Command::new("link.exe")
239+
}

0 commit comments

Comments
 (0)