Skip to content

Commit b09d71d

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 8830a03 + 34d58d7 commit b09d71d

File tree

8 files changed

+126
-13
lines changed

8 files changed

+126
-13
lines changed

src/bootstrap/test.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,7 @@ impl Step for Compiletest {
10011001

10021002
// Only pass correct values for these flags for the `run-make` suite as it
10031003
// requires that a C++ compiler was configured which isn't always the case.
1004-
if !builder.config.dry_run && suite == "run-make-fulldeps" {
1004+
if !builder.config.dry_run && mode == "run-make" {
10051005
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
10061006
let llvm_cxxflags = output(Command::new(&llvm_config).arg("--cxxflags"));
10071007
cmd.arg("--cc").arg(builder.cc(target))
@@ -1012,15 +1012,26 @@ impl Step for Compiletest {
10121012
if let Some(ar) = builder.ar(target) {
10131013
cmd.arg("--ar").arg(ar);
10141014
}
1015+
1016+
// Add the llvm/bin directory to PATH since it contains lots of
1017+
// useful, platform-independent tools
1018+
let llvm_bin_path = llvm_config.parent()
1019+
.expect("Expected llvm-config to be contained in directory");
1020+
assert!(llvm_bin_path.is_dir());
1021+
let old_path = env::var_os("PATH").unwrap_or_default();
1022+
let new_path = env::join_paths(iter::once(llvm_bin_path.to_path_buf())
1023+
.chain(env::split_paths(&old_path)))
1024+
.expect("");
1025+
cmd.env("PATH", new_path);
10151026
}
10161027
}
1017-
if suite == "run-make-fulldeps" && !builder.config.llvm_enabled {
1028+
if mode == "run-make" && !builder.config.llvm_enabled {
10181029
builder.info(
10191030
&format!("Ignoring run-make test suite as they generally don't work without LLVM"));
10201031
return;
10211032
}
10221033

1023-
if suite != "run-make-fulldeps" {
1034+
if mode != "run-make" {
10241035
cmd.arg("--cc").arg("")
10251036
.arg("--cxx").arg("")
10261037
.arg("--cflags").arg("")

src/librustc/session/config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
13061306
"tell the linker to strip debuginfo when building without debuginfo enabled."),
13071307
share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED],
13081308
"make the current crate share its generic instantiations"),
1309+
cross_lang_lto: bool = (false, parse_bool, [TRACKED],
1310+
"generate build artifacts that are compatible with linker-based LTO."),
13091311
}
13101312

13111313
pub fn default_lib_output() -> CrateType {

src/librustc_trans/back/write.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ impl ModuleConfig {
314314
self.inline_threshold = sess.opts.cg.inline_threshold;
315315
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
316316
let embed_bitcode = sess.target.target.options.embed_bitcode ||
317-
sess.opts.debugging_opts.embed_bitcode;
317+
sess.opts.debugging_opts.embed_bitcode ||
318+
sess.opts.debugging_opts.cross_lang_lto;
318319
if embed_bitcode {
319320
match sess.opts.optimize {
320321
config::OptLevel::No |
@@ -862,13 +863,18 @@ unsafe fn embed_bitcode(cgcx: &CodegenContext,
862863
"rustc.embedded.module\0".as_ptr() as *const _,
863864
);
864865
llvm::LLVMSetInitializer(llglobal, llconst);
865-
let section = if cgcx.opts.target_triple.triple().contains("-ios") {
866+
867+
let is_apple = cgcx.opts.target_triple.triple().contains("-ios") ||
868+
cgcx.opts.target_triple.triple().contains("-darwin");
869+
870+
let section = if is_apple {
866871
"__LLVM,__bitcode\0"
867872
} else {
868873
".llvmbc\0"
869874
};
870875
llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
871876
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
877+
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
872878

873879
let llconst = C_bytes_in_context(llcx, &[]);
874880
let llglobal = llvm::LLVMAddGlobal(
@@ -877,7 +883,7 @@ unsafe fn embed_bitcode(cgcx: &CodegenContext,
877883
"rustc.embedded.cmdline\0".as_ptr() as *const _,
878884
);
879885
llvm::LLVMSetInitializer(llglobal, llconst);
880-
let section = if cgcx.opts.target_triple.triple().contains("-ios") {
886+
let section = if is_apple {
881887
"__LLVM,__cmdline\0"
882888
} else {
883889
".llvmcmd\0"
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
# min-llvm-version 4.0
3+
4+
-include ../../run-make-fulldeps/tools.mk
5+
6+
# This test makes sure that the expected .llvmbc sections for use by
7+
# linker-based LTO are available in object files when compiling with
8+
# -Z cross-lang-lto
9+
10+
LLVMBC_SECTION_NAME=\\.llvmbc
11+
12+
ifeq ($(UNAME),Darwin)
13+
LLVMBC_SECTION_NAME=__LLVM,__bitcode
14+
endif
15+
16+
17+
OBJDUMP=llvm-objdump
18+
SECTION_HEADERS=$(OBJDUMP) -section-headers
19+
20+
BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1
21+
22+
BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Z cross-lang-lto -Ccodegen-units=1 --emit=obj
23+
24+
all: staticlib staticlib-fat-lto staticlib-thin-lto rlib exe cdylib rdylib
25+
26+
staticlib: lib.rs
27+
$(BUILD_LIB) --crate-type=staticlib
28+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
29+
30+
staticlib-fat-lto: lib.rs
31+
$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib-fat-lto.a -Clto=fat
32+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib-fat-lto.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
33+
34+
staticlib-thin-lto: lib.rs
35+
$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib-thin-lto.a -Clto=thin
36+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib-thin-lto.a | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
37+
38+
rlib: lib.rs
39+
$(BUILD_LIB) --crate-type=rlib
40+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/liblib.rlib | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
41+
42+
cdylib: lib.rs
43+
$(BUILD_LIB) --crate-type=cdylib --emit=obj -o $(TMPDIR)/cdylib.o
44+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/cdylib.o | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
45+
46+
rdylib: lib.rs
47+
$(BUILD_LIB) --crate-type=dylib --emit=obj -o $(TMPDIR)/rdylib.o
48+
[ "$$($(SECTION_HEADERS) $(TMPDIR)/rdylib.o | grep -c $(LLVMBC_SECTION_NAME))" -ne "0" ]
49+
50+
exe: lib.rs
51+
$(BUILD_EXE) -o $(TMPDIR)/exe.o
52+
[ "$$($(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
@@ -412,6 +412,15 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
412412
if testfile.is_dir() {
413413
return;
414414
}
415+
416+
let comment = if testfile.to_string_lossy().ends_with(".rs") {
417+
"//"
418+
} else {
419+
"#"
420+
};
421+
422+
let comment_with_brace = comment.to_string() + "[";
423+
415424
let rdr = BufReader::new(File::open(testfile).unwrap());
416425
for ln in rdr.lines() {
417426
// Assume that any directives will be found before the first
@@ -421,10 +430,11 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
421430
let ln = ln.trim();
422431
if ln.starts_with("fn") || ln.starts_with("mod") {
423432
return;
424-
} else if ln.starts_with("//[") {
433+
} else if ln.starts_with(&comment_with_brace) {
425434
// A comment like `//[foo]` is specific to revision `foo`
426435
if let Some(close_brace) = ln.find(']') {
427-
let lncfg = &ln[3..close_brace];
436+
let open_brace = ln.find('[').unwrap();
437+
let lncfg = &ln[open_brace + 1 .. close_brace];
428438
let matches = match cfg {
429439
Some(s) => s == &lncfg[..],
430440
None => false,
@@ -433,11 +443,11 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut FnMut(&str)) {
433443
it(ln[(close_brace + 1) ..].trim_left());
434444
}
435445
} else {
436-
panic!("malformed condition directive: expected `//[foo]`, found `{}`",
437-
ln)
446+
panic!("malformed condition directive: expected `{}foo]`, found `{}`",
447+
comment_with_brace, ln)
438448
}
439-
} else if ln.starts_with("//") {
440-
it(ln[2..].trim_left());
449+
} else if ln.starts_with(comment) {
450+
it(ln[comment.len() ..].trim_left());
441451
}
442452
}
443453
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)