Skip to content

Commit 836c35b

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 91db9dc + d0253ad commit 836c35b

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
@@ -832,7 +832,7 @@ test!(RunFailFullDepsPretty {
832832
host: true
833833
});
834834

835-
default_test!(RunMake {
835+
host_test!(RunMake {
836836
path: "src/test/run-make",
837837
mode: "run-make",
838838
suite: "run-make"
@@ -1019,7 +1019,7 @@ impl Step for Compiletest {
10191019

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

1041-
if suite != "run-make-fulldeps" {
1041+
if mode != "run-make" {
10421042
cmd.arg("--cc").arg("")
10431043
.arg("--cxx").arg("")
10441044
.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
@@ -1293,6 +1293,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
12931293
"make the current crate share its generic instantiations"),
12941294
chalk: bool = (false, parse_bool, [TRACKED],
12951295
"enable the experimental Chalk-based trait solving engine"),
1296+
cross_lang_lto: bool = (false, parse_bool, [TRACKED],
1297+
"generate build artifacts that are compatible with linker-based LTO."),
12961298
}
12971299

12981300
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=__LLVM,__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
@@ -419,6 +419,15 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
419419
if testfile.is_dir() {
420420
return;
421421
}
422+
423+
let comment = if testfile.to_string_lossy().ends_with(".rs") {
424+
"//"
425+
} else {
426+
"#"
427+
};
428+
429+
let comment_with_brace = comment.to_string() + "[";
430+
422431
let rdr = BufReader::new(File::open(testfile).unwrap());
423432
for ln in rdr.lines() {
424433
// Assume that any directives will be found before the first
@@ -428,10 +437,11 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
428437
let ln = ln.trim();
429438
if ln.starts_with("fn") || ln.starts_with("mod") {
430439
return;
431-
} else if ln.starts_with("//[") {
440+
} else if ln.starts_with(&comment_with_brace) {
432441
// A comment like `//[foo]` is specific to revision `foo`
433442
if let Some(close_brace) = ln.find(']') {
434-
let lncfg = &ln[3..close_brace];
443+
let open_brace = ln.find('[').unwrap();
444+
let lncfg = &ln[open_brace + 1 .. close_brace];
435445
let matches = match cfg {
436446
Some(s) => s == &lncfg[..],
437447
None => false,
@@ -440,11 +450,11 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
440450
it(ln[(close_brace + 1) ..].trim_left());
441451
}
442452
} else {
443-
panic!("malformed condition directive: expected `//[foo]`, found `{}`",
444-
ln)
453+
panic!("malformed condition directive: expected `{}foo]`, found `{}`",
454+
comment_with_brace, ln)
445455
}
446-
} else if ln.starts_with("//") {
447-
it(ln[2..].trim_left());
456+
} else if ln.starts_with(comment) {
457+
it(ln[comment.len() ..].trim_left());
448458
}
449459
}
450460
return;

src/tools/compiletest/src/main.rs

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

612612
pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
613-
let early_props = EarlyProps::from_file(config, &testpaths.file);
613+
614+
let early_props = if config.mode == Mode::RunMake {
615+
EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
616+
} else {
617+
EarlyProps::from_file(config, &testpaths.file)
618+
};
614619

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

0 commit comments

Comments
 (0)