Skip to content

Commit c88992d

Browse files
committed
Auto merge of #50000 - michaelwoerister:cross-lang-lto, r=alexcrichton
Add some groundwork for cross-language LTO. Implements part of #49879: - Adds a `-Z cross-lang-lto` flag to rustc - Makes sure that bitcode is embedded in object files if the flag is set. This should already allow for using cross language LTO for staticlibs (where one has to invoke the linker manually anyway). However, `rustc` will not try to enable LTO for its own linker invocations yet. r? @alexcrichton
2 parents 9b97705 + 0bf26bf commit c88992d

File tree

9 files changed

+160
-17
lines changed

9 files changed

+160
-17
lines changed

src/bootstrap/test.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ test!(RunFailFullDepsPretty {
835835
host: true
836836
});
837837

838-
default_test!(RunMake {
838+
host_test!(RunMake {
839839
path: "src/test/run-make",
840840
mode: "run-make",
841841
suite: "run-make"
@@ -1022,7 +1022,7 @@ impl Step for Compiletest {
10221022

10231023
// Only pass correct values for these flags for the `run-make` suite as it
10241024
// requires that a C++ compiler was configured which isn't always the case.
1025-
if !builder.config.dry_run && suite == "run-make-fulldeps" {
1025+
if !builder.config.dry_run && mode == "run-make" {
10261026
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
10271027
let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags"));
10281028
cmd.arg("--cc").arg(builder.cc(target))
@@ -1035,13 +1035,13 @@ impl Step for Compiletest {
10351035
}
10361036
}
10371037
}
1038-
if suite == "run-make-fulldeps" && !builder.config.llvm_enabled {
1038+
if mode == "run-make" && !builder.config.llvm_enabled {
10391039
builder.info(
10401040
&format!("Ignoring run-make test suite as they generally don't work without LLVM"));
10411041
return;
10421042
}
10431043

1044-
if suite != "run-make-fulldeps" {
1044+
if mode != "run-make" {
10451045
cmd.arg("--cc").arg("")
10461046
.arg("--cxx").arg("")
10471047
.arg("--cflags").arg("")

src/bootstrap/tool.rs

+39-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use std::fs;
1212
use std::env;
13+
use std::iter;
1314
use std::path::PathBuf;
1415
use std::process::{Command, exit};
1516

@@ -593,7 +594,7 @@ impl<'a> Builder<'a> {
593594
/// right location to run `compiler`.
594595
fn prepare_tool_cmd(&self, compiler: Compiler, cmd: &mut Command) {
595596
let host = &compiler.host;
596-
let mut paths: Vec<PathBuf> = vec![
597+
let mut lib_paths: Vec<PathBuf> = vec![
597598
PathBuf::from(&self.sysroot_libdir(compiler, compiler.host)),
598599
self.cargo_out(compiler, Mode::Tool, *host).join("deps"),
599600
];
@@ -610,11 +611,46 @@ impl<'a> Builder<'a> {
610611
}
611612
for path in env::split_paths(v) {
612613
if !curpaths.contains(&path) {
613-
paths.push(path);
614+
lib_paths.push(path);
614615
}
615616
}
616617
}
617618
}
618-
add_lib_path(paths, cmd);
619+
620+
// Add the llvm/bin directory to PATH since it contains lots of
621+
// useful, platform-independent tools
622+
if let Some(llvm_bin_path) = self.llvm_bin_path() {
623+
if host.contains("windows") {
624+
// On Windows, PATH and the dynamic library path are the same,
625+
// so we just add the LLVM bin path to lib_path
626+
lib_paths.push(llvm_bin_path);
627+
} else {
628+
let old_path = env::var_os("PATH").unwrap_or_default();
629+
let new_path = env::join_paths(iter::once(llvm_bin_path)
630+
.chain(env::split_paths(&old_path)))
631+
.expect("Could not add LLVM bin path to PATH");
632+
cmd.env("PATH", new_path);
633+
}
634+
}
635+
636+
add_lib_path(lib_paths, cmd);
637+
}
638+
639+
fn llvm_bin_path(&self) -> Option<PathBuf> {
640+
if self.config.llvm_enabled && !self.config.dry_run {
641+
let llvm_config = self.ensure(native::Llvm {
642+
target: self.config.build,
643+
emscripten: false,
644+
});
645+
646+
// Add the llvm/bin directory to PATH since it contains lots of
647+
// useful, platform-independent tools
648+
let llvm_bin_path = llvm_config.parent()
649+
.expect("Expected llvm-config to be contained in directory");
650+
assert!(llvm_bin_path.is_dir());
651+
Some(llvm_bin_path.to_path_buf())
652+
} else {
653+
None
654+
}
619655
}
620656
}

src/librustc/session/config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
12951295
"make the current crate share its generic instantiations"),
12961296
chalk: bool = (false, parse_bool, [TRACKED],
12971297
"enable the experimental Chalk-based trait solving engine"),
1298+
cross_lang_lto: bool = (false, parse_bool, [TRACKED],
1299+
"generate build artifacts that are compatible with linker-based LTO."),
12981300
}
12991301

13001302
pub fn default_lib_output() -> CrateType {

src/librustc_trans/back/write.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ impl ModuleConfig {
293293
self.inline_threshold = sess.opts.cg.inline_threshold;
294294
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
295295
let embed_bitcode = sess.target.target.options.embed_bitcode ||
296-
sess.opts.debugging_opts.embed_bitcode;
296+
sess.opts.debugging_opts.embed_bitcode ||
297+
sess.opts.debugging_opts.cross_lang_lto;
297298
if embed_bitcode {
298299
match sess.opts.optimize {
299300
config::OptLevel::No |
@@ -841,13 +842,18 @@ unsafe fn embed_bitcode(cgcx: &CodegenContext,
841842
"rustc.embedded.module\0".as_ptr() as *const _,
842843
);
843844
llvm::LLVMSetInitializer(llglobal, llconst);
844-
let section = if cgcx.opts.target_triple.triple().contains("-ios") {
845+
846+
let is_apple = cgcx.opts.target_triple.triple().contains("-ios") ||
847+
cgcx.opts.target_triple.triple().contains("-darwin");
848+
849+
let section = if is_apple {
845850
"__LLVM,__bitcode\0"
846851
} else {
847852
".llvmbc\0"
848853
};
849854
llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
850855
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
856+
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
851857

852858
let llconst = C_bytes_in_context(llcx, &[]);
853859
let llglobal = llvm::LLVMAddGlobal(
@@ -856,7 +862,7 @@ unsafe fn embed_bitcode(cgcx: &CodegenContext,
856862
"rustc.embedded.cmdline\0".as_ptr() as *const _,
857863
);
858864
llvm::LLVMSetInitializer(llglobal, llconst);
859-
let section = if cgcx.opts.target_triple.triple().contains("-ios") {
865+
let section = if is_apple {
860866
"__LLVM,__cmdline\0"
861867
} else {
862868
".llvmcmd\0"
@@ -1350,6 +1356,10 @@ fn execute_work_item(cgcx: &CodegenContext,
13501356
// settings.
13511357
let needs_lto = needs_lto && mtrans.kind != ModuleKind::Metadata;
13521358

1359+
// Don't run LTO passes when cross-lang LTO is enabled. The linker
1360+
// will do that for us in this case.
1361+
let needs_lto = needs_lto && !cgcx.opts.debugging_opts.cross_lang_lto;
1362+
13531363
if needs_lto {
13541364
Ok(WorkItemResult::NeedsLTO(mtrans))
13551365
} else {
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
# min-llvm-version 4.0
3+
# ignore-mingw
4+
5+
-include ../../run-make-fulldeps/tools.mk
6+
7+
# This test makes sure that the expected .llvmbc sections for use by
8+
# linker-based LTO are available in object files when compiling with
9+
# -Z cross-lang-lto
10+
11+
LLVMBC_SECTION_NAME=\\.llvmbc
12+
13+
ifeq ($(UNAME),Darwin)
14+
LLVMBC_SECTION_NAME=__bitcode
15+
endif
16+
17+
18+
OBJDUMP=llvm-objdump
19+
SECTION_HEADERS=$(OBJDUMP) -section-headers
20+
21+
BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1
22+
23+
BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1 --emit=obj
24+
25+
all: staticlib staticlib-fat-lto staticlib-thin-lto rlib exe cdylib rdylib
26+
27+
staticlib: lib.rs
28+
$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib.a
29+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
30+
31+
staticlib-fat-lto: lib.rs
32+
$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib-fat-lto.a -Clto=fat
33+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib-fat-lto.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
34+
35+
staticlib-thin-lto: lib.rs
36+
$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib-thin-lto.a -Clto=thin
37+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib-thin-lto.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
38+
39+
rlib: lib.rs
40+
$(BUILD_LIB) --crate-type=rlib -o $(TMPDIR)/liblib.rlib
41+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib.rlib | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
42+
43+
cdylib: lib.rs
44+
$(BUILD_LIB) --crate-type=cdylib --emit=obj -o $(TMPDIR)/cdylib.o
45+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/cdylib.o | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
46+
47+
rdylib: lib.rs
48+
$(BUILD_LIB) --crate-type=dylib --emit=obj -o $(TMPDIR)/rdylib.o
49+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/rdylib.o | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
50+
51+
exe: lib.rs
52+
$(BUILD_EXE) -o $(TMPDIR)/exe.o
53+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/exe.o | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2018 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+
#[no_mangle]
12+
pub extern "C" fn foo() {
13+
println!("abc");
14+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright 2018 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+
fn main() {
12+
println!("Hello World");
13+
}

src/tools/compiletest/src/header.rs

+16-6
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,15 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
425425
if testfile.is_dir() {
426426
return;
427427
}
428+
429+
let comment = if testfile.to_string_lossy().ends_with(".rs") {
430+
"//"
431+
} else {
432+
"#"
433+
};
434+
435+
let comment_with_brace = comment.to_string() + "[";
436+
428437
let rdr = BufReader::new(File::open(testfile).unwrap());
429438
for ln in rdr.lines() {
430439
// Assume that any directives will be found before the first
@@ -434,10 +443,11 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
434443
let ln = ln.trim();
435444
if ln.starts_with("fn") || ln.starts_with("mod") {
436445
return;
437-
} else if ln.starts_with("//[") {
446+
} else if ln.starts_with(&comment_with_brace) {
438447
// A comment like `//[foo]` is specific to revision `foo`
439448
if let Some(close_brace) = ln.find(']') {
440-
let lncfg = &ln[3..close_brace];
449+
let open_brace = ln.find('[').unwrap();
450+
let lncfg = &ln[open_brace + 1 .. close_brace];
441451
let matches = match cfg {
442452
Some(s) => s == &lncfg[..],
443453
None => false,
@@ -446,11 +456,11 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
446456
it(ln[(close_brace + 1) ..].trim_left());
447457
}
448458
} else {
449-
panic!("malformed condition directive: expected `//[foo]`, found `{}`",
450-
ln)
459+
panic!("malformed condition directive: expected `{}foo]`, found `{}`",
460+
comment_with_brace, ln)
451461
}
452-
} else if ln.starts_with("//") {
453-
it(ln[2..].trim_left());
462+
} else if ln.starts_with(comment) {
463+
it(ln[comment.len() ..].trim_left());
454464
}
455465
}
456466
return;

src/tools/compiletest/src/main.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,12 @@ pub fn is_test(file_name: &OsString) -> bool {
614614
}
615615

616616
pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
617-
let early_props = EarlyProps::from_file(config, &testpaths.file);
617+
618+
let early_props = if config.mode == Mode::RunMake {
619+
EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
620+
} else {
621+
EarlyProps::from_file(config, &testpaths.file)
622+
};
618623

619624
// The `should-fail` annotation doesn't apply to pretty tests,
620625
// since we run the pretty printer across all tests by default.

0 commit comments

Comments
 (0)