Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c6884b1

Browse files
committedSep 30, 2017
Auto merge of #44783 - alexcrichton:lto-codegen-units, r=michaelwoerister
rustc: Enable LTO and multiple codegen units This commit is a refactoring of the LTO backend in Rust to support compilations with multiple codegen units. The immediate result of this PR is to remove the artificial error emitted by rustc about `-C lto -C codegen-units-8`, but longer term this is intended to lay the groundwork for LTO with incremental compilation and ultimately be the underpinning of ThinLTO support. The problem here that needed solving is that when rustc is producing multiple codegen units in one compilation LTO needs to merge them all together. Previously only upstream dependencies were merged and it was inherently relied on that there was only one local codegen unit. Supporting this involved refactoring the optimization backend architecture for rustc, namely splitting the `optimize_and_codegen` function into `optimize` and `codegen`. After an LLVM module has been optimized it may be blocked and queued up for LTO, and only after LTO are modules code generated. Non-LTO compilations should look the same as they do today backend-wise, we'll spin up a thread for each codegen unit and optimize/codegen in that thread. LTO compilations will, however, send the LLVM module back to the coordinator thread once optimizations have finished. When all LLVM modules have finished optimizing the coordinator will invoke the LTO backend, producing a further list of LLVM modules. Currently this is always a list of one LLVM module. The coordinator then spawns further work to run LTO and code generation passes over each module. In the course of this refactoring a number of other pieces were refactored: * Management of the bytecode encoding in rlibs was centralized into one module instead of being scattered across LTO and linking. * Some internal refactorings on the link stage of the compiler was done to work directly from `CompiledModule` structures instead of lists of paths. * The trans time-graph output was tweaked a little to include a name on each bar and inflate the size of the bars a little
2 parents c4cca3a + ded38db commit c6884b1

File tree

13 files changed

+803
-430
lines changed

13 files changed

+803
-430
lines changed
 

‎src/librustc/session/config.rs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,24 +1552,6 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
15521552
early_error(error_format, "Value for codegen units must be a positive nonzero integer");
15531553
}
15541554

1555-
// It's possible that we have `codegen_units > 1` but only one item in
1556-
// `trans.modules`. We could theoretically proceed and do LTO in that
1557-
// case, but it would be confusing to have the validity of
1558-
// `-Z lto -C codegen-units=2` depend on details of the crate being
1559-
// compiled, so we complain regardless.
1560-
if cg.lto {
1561-
if let Some(n) = codegen_units {
1562-
if n > 1 {
1563-
// This case is impossible to handle because LTO expects to be able
1564-
// to combine the entire crate and all its dependencies into a
1565-
// single compilation unit, but each codegen unit is in a separate
1566-
// LLVM context, so they can't easily be combined.
1567-
early_error(error_format, "can't perform LTO when using multiple codegen units");
1568-
}
1569-
}
1570-
codegen_units = Some(1);
1571-
}
1572-
15731555
if cg.lto && debugging_opts.incremental.is_some() {
15741556
early_error(error_format, "can't perform LTO when compiling incrementally");
15751557
}

‎src/librustc_llvm/ffi.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ pub mod debuginfo {
478478
}
479479
}
480480

481+
pub enum ModuleBuffer {}
481482

482483
// Link to our native llvm bindings (things that we need to use the C++ api
483484
// for) and because llvm is written in C++ we need to link against libstdc++
@@ -1609,6 +1610,7 @@ extern "C" {
16091610
pub fn LLVMRustSetNormalizedTarget(M: ModuleRef, triple: *const c_char);
16101611
pub fn LLVMRustAddAlwaysInlinePass(P: PassManagerBuilderRef, AddLifetimes: bool);
16111612
pub fn LLVMRustLinkInExternalBitcode(M: ModuleRef, bc: *const c_char, len: size_t) -> bool;
1613+
pub fn LLVMRustLinkInParsedExternalBitcode(M: ModuleRef, M: ModuleRef) -> bool;
16121614
pub fn LLVMRustRunRestrictionPass(M: ModuleRef, syms: *const *const c_char, len: size_t);
16131615
pub fn LLVMRustMarkAllFunctionsNounwind(M: ModuleRef);
16141616

@@ -1678,4 +1680,9 @@ extern "C" {
16781680
pub fn LLVMRustSetComdat(M: ModuleRef, V: ValueRef, Name: *const c_char);
16791681
pub fn LLVMRustUnsetComdat(V: ValueRef);
16801682
pub fn LLVMRustSetModulePIELevel(M: ModuleRef);
1683+
pub fn LLVMRustModuleBufferCreate(M: ModuleRef) -> *mut ModuleBuffer;
1684+
pub fn LLVMRustModuleBufferPtr(p: *const ModuleBuffer) -> *const u8;
1685+
pub fn LLVMRustModuleBufferLen(p: *const ModuleBuffer) -> usize;
1686+
pub fn LLVMRustModuleBufferFree(p: *mut ModuleBuffer);
1687+
pub fn LLVMRustModuleCost(M: ModuleRef) -> u64;
16811688
}

‎src/librustc_trans/back/archive.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::path::{Path, PathBuf};
1717
use std::ptr;
1818
use std::str;
1919

20+
use back::bytecode::RLIB_BYTECODE_EXTENSION;
2021
use libc;
2122
use llvm::archive_ro::{ArchiveRO, Child};
2223
use llvm::{self, ArchiveKind};
@@ -154,12 +155,9 @@ impl<'a> ArchiveBuilder<'a> {
154155
// might be also an extra name suffix
155156
let obj_start = format!("{}", name);
156157

157-
// Ignoring all bytecode files, no matter of
158-
// name
159-
let bc_ext = ".bytecode.deflate";
160-
161158
self.add_archive(rlib, move |fname: &str| {
162-
if fname.ends_with(bc_ext) || fname == METADATA_FILENAME {
159+
// Ignore bytecode/metadata files, no matter the name.
160+
if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME {
163161
return true
164162
}
165163

‎src/librustc_trans/back/bytecode.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright 2017 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+
//! Management of the encoding of LLVM bytecode into rlibs
12+
//!
13+
//! This module contains the management of encoding LLVM bytecode into rlibs,
14+
//! primarily for the usage in LTO situations. Currently the compiler will
15+
//! unconditionally encode LLVM-IR into rlibs regardless of what's happening
16+
//! elsewhere, so we currently compress the bytecode via deflate to avoid taking
17+
//! up too much space on disk.
18+
//!
19+
//! After compressing the bytecode we then have the rest of the format to
20+
//! basically deal with various bugs in various archive implementations. The
21+
//! format currently is:
22+
//!
23+
//! RLIB LLVM-BYTECODE OBJECT LAYOUT
24+
//! Version 2
25+
//! Bytes Data
26+
//! 0..10 "RUST_OBJECT" encoded in ASCII
27+
//! 11..14 format version as little-endian u32
28+
//! 15..19 the length of the module identifier string
29+
//! 20..n the module identifier string
30+
//! n..n+8 size in bytes of deflate compressed LLVM bitcode as
31+
//! little-endian u64
32+
//! n+9.. compressed LLVM bitcode
33+
//! ? maybe a byte to make this whole thing even length
34+
35+
use std::io::{Read, Write};
36+
use std::ptr;
37+
use std::str;
38+
39+
use flate2::Compression;
40+
use flate2::read::DeflateDecoder;
41+
use flate2::write::DeflateEncoder;
42+
43+
// This is the "magic number" expected at the beginning of a LLVM bytecode
44+
// object in an rlib.
45+
pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT";
46+
47+
// The version number this compiler will write to bytecode objects in rlibs
48+
pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2;
49+
50+
pub const RLIB_BYTECODE_EXTENSION: &str = "bytecode.encoded";
51+
52+
pub fn encode(identifier: &str, bytecode: &[u8]) -> Vec<u8> {
53+
let mut encoded = Vec::new();
54+
55+
// Start off with the magic string
56+
encoded.extend_from_slice(RLIB_BYTECODE_OBJECT_MAGIC);
57+
58+
// Next up is the version
59+
encoded.extend_from_slice(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]);
60+
61+
// Next is the LLVM module identifier length + contents
62+
let identifier_len = identifier.len();
63+
encoded.extend_from_slice(&[
64+
(identifier_len >> 0) as u8,
65+
(identifier_len >> 8) as u8,
66+
(identifier_len >> 16) as u8,
67+
(identifier_len >> 24) as u8,
68+
]);
69+
encoded.extend_from_slice(identifier.as_bytes());
70+
71+
// Next is the LLVM module deflate compressed, prefixed with its length. We
72+
// don't know its length yet, so fill in 0s
73+
let deflated_size_pos = encoded.len();
74+
encoded.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]);
75+
76+
let before = encoded.len();
77+
DeflateEncoder::new(&mut encoded, Compression::Fast)
78+
.write_all(bytecode)
79+
.unwrap();
80+
let after = encoded.len();
81+
82+
// Fill in the length we reserved space for before
83+
let bytecode_len = (after - before) as u64;
84+
encoded[deflated_size_pos + 0] = (bytecode_len >> 0) as u8;
85+
encoded[deflated_size_pos + 1] = (bytecode_len >> 8) as u8;
86+
encoded[deflated_size_pos + 2] = (bytecode_len >> 16) as u8;
87+
encoded[deflated_size_pos + 3] = (bytecode_len >> 24) as u8;
88+
encoded[deflated_size_pos + 4] = (bytecode_len >> 32) as u8;
89+
encoded[deflated_size_pos + 5] = (bytecode_len >> 40) as u8;
90+
encoded[deflated_size_pos + 6] = (bytecode_len >> 48) as u8;
91+
encoded[deflated_size_pos + 7] = (bytecode_len >> 56) as u8;
92+
93+
// If the number of bytes written to the object so far is odd, add a
94+
// padding byte to make it even. This works around a crash bug in LLDB
95+
// (see issue #15950)
96+
if encoded.len() % 2 == 1 {
97+
encoded.push(0);
98+
}
99+
100+
return encoded
101+
}
102+
103+
pub struct DecodedBytecode<'a> {
104+
identifier: &'a str,
105+
encoded_bytecode: &'a [u8],
106+
}
107+
108+
impl<'a> DecodedBytecode<'a> {
109+
pub fn new(data: &'a [u8]) -> Result<DecodedBytecode<'a>, String> {
110+
if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) {
111+
return Err(format!("magic bytecode prefix not found"))
112+
}
113+
let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..];
114+
if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) {
115+
return Err(format!("wrong version prefix found in bytecode"))
116+
}
117+
let data = &data[4..];
118+
if data.len() < 4 {
119+
return Err(format!("bytecode corrupted"))
120+
}
121+
let identifier_len = unsafe {
122+
u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize
123+
};
124+
let data = &data[4..];
125+
if data.len() < identifier_len {
126+
return Err(format!("bytecode corrupted"))
127+
}
128+
let identifier = match str::from_utf8(&data[..identifier_len]) {
129+
Ok(s) => s,
130+
Err(_) => return Err(format!("bytecode corrupted"))
131+
};
132+
let data = &data[identifier_len..];
133+
if data.len() < 8 {
134+
return Err(format!("bytecode corrupted"))
135+
}
136+
let bytecode_len = unsafe {
137+
u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize
138+
};
139+
let data = &data[8..];
140+
if data.len() < bytecode_len {
141+
return Err(format!("bytecode corrupted"))
142+
}
143+
let encoded_bytecode = &data[..bytecode_len];
144+
145+
Ok(DecodedBytecode {
146+
identifier,
147+
encoded_bytecode,
148+
})
149+
}
150+
151+
pub fn bytecode(&self) -> Vec<u8> {
152+
let mut data = Vec::new();
153+
DeflateDecoder::new(self.encoded_bytecode).read_to_end(&mut data).unwrap();
154+
return data
155+
}
156+
157+
pub fn identifier(&self) -> &'a str {
158+
self.identifier
159+
}
160+
}

‎src/librustc_trans/back/link.rs

Lines changed: 23 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
use super::archive::{ArchiveBuilder, ArchiveConfig};
12+
use super::bytecode::{self, RLIB_BYTECODE_EXTENSION};
1213
use super::linker::Linker;
1314
use super::command::Command;
1415
use super::rpath::RPathConfig;
@@ -36,12 +37,9 @@ use std::ffi::OsString;
3637
use std::fmt;
3738
use std::fs::{self, File};
3839
use std::io::{self, Read, Write, BufWriter};
39-
use std::mem;
4040
use std::path::{Path, PathBuf};
4141
use std::process::{Output, Stdio};
4242
use std::str;
43-
use flate2::Compression;
44-
use flate2::write::DeflateEncoder;
4543
use syntax::attr;
4644

4745
/// The LLVM module name containing crate-metadata. This includes a `.` on
@@ -55,35 +53,6 @@ pub const METADATA_OBJ_NAME: &'static str = "crate.metadata.o";
5553
pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator";
5654
pub const ALLOCATOR_OBJ_NAME: &'static str = "crate.allocator.o";
5755

58-
// RLIB LLVM-BYTECODE OBJECT LAYOUT
59-
// Version 1
60-
// Bytes Data
61-
// 0..10 "RUST_OBJECT" encoded in ASCII
62-
// 11..14 format version as little-endian u32
63-
// 15..22 size in bytes of deflate compressed LLVM bitcode as
64-
// little-endian u64
65-
// 23.. compressed LLVM bitcode
66-
67-
// This is the "magic number" expected at the beginning of a LLVM bytecode
68-
// object in an rlib.
69-
pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT";
70-
71-
// The version number this compiler will write to bytecode objects in rlibs
72-
pub const RLIB_BYTECODE_OBJECT_VERSION: u32 = 1;
73-
74-
// The offset in bytes the bytecode object format version number can be found at
75-
pub const RLIB_BYTECODE_OBJECT_VERSION_OFFSET: usize = 11;
76-
77-
// The offset in bytes the size of the compressed bytecode can be found at in
78-
// format version 1
79-
pub const RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET: usize =
80-
RLIB_BYTECODE_OBJECT_VERSION_OFFSET + 4;
81-
82-
// The offset in bytes the compressed LLVM bytecode can be found at in format
83-
// version 1
84-
pub const RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: usize =
85-
RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8;
86-
8756
pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_output_for_target,
8857
invalid_output_for_target, build_link_meta, out_filename,
8958
check_file_is_writeable};
@@ -201,8 +170,8 @@ pub fn link_binary(sess: &Session,
201170
// Remove the temporary object file and metadata if we aren't saving temps
202171
if !sess.opts.cg.save_temps {
203172
if sess.opts.output_types.should_trans() {
204-
for obj in object_filenames(trans, outputs) {
205-
remove(sess, &obj);
173+
for obj in trans.modules.iter() {
174+
remove(sess, &obj.object);
206175
}
207176
}
208177
remove(sess, &outputs.with_extension(METADATA_OBJ_NAME));
@@ -282,10 +251,8 @@ fn link_binary_output(sess: &Session,
282251
crate_type: config::CrateType,
283252
outputs: &OutputFilenames,
284253
crate_name: &str) -> Vec<PathBuf> {
285-
let objects = object_filenames(trans, outputs);
286-
287-
for file in &objects {
288-
check_file_is_writeable(file, sess);
254+
for module in trans.modules.iter() {
255+
check_file_is_writeable(&module.object, sess);
289256
}
290257

291258
let tmpdir = match TempDir::new("rustc") {
@@ -308,7 +275,6 @@ fn link_binary_output(sess: &Session,
308275
link_rlib(sess,
309276
trans,
310277
RlibFlavor::Normal,
311-
&objects,
312278
outputs,
313279
&out_filename,
314280
tmpdir.path()).build();
@@ -317,12 +283,11 @@ fn link_binary_output(sess: &Session,
317283
link_staticlib(sess,
318284
trans,
319285
outputs,
320-
&objects,
321286
&out_filename,
322287
tmpdir.path());
323288
}
324289
_ => {
325-
link_natively(sess, crate_type, &objects, &out_filename,
290+
link_natively(sess, crate_type, &out_filename,
326291
trans, outputs, tmpdir.path());
327292
}
328293
}
@@ -336,14 +301,6 @@ fn link_binary_output(sess: &Session,
336301
out_filenames
337302
}
338303

339-
fn object_filenames(trans: &CrateTranslation,
340-
outputs: &OutputFilenames)
341-
-> Vec<PathBuf> {
342-
trans.modules.iter().map(|module| {
343-
outputs.temp_path(OutputType::Object, Some(&module.name))
344-
}).collect()
345-
}
346-
347304
fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {
348305
let mut search = Vec::new();
349306
sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| {
@@ -387,15 +344,14 @@ enum RlibFlavor {
387344
fn link_rlib<'a>(sess: &'a Session,
388345
trans: &CrateTranslation,
389346
flavor: RlibFlavor,
390-
objects: &[PathBuf],
391347
outputs: &OutputFilenames,
392348
out_filename: &Path,
393349
tmpdir: &Path) -> ArchiveBuilder<'a> {
394-
info!("preparing rlib from {:?} to {:?}", objects, out_filename);
350+
info!("preparing rlib to {:?}", out_filename);
395351
let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None));
396352

397-
for obj in objects {
398-
ab.add_file(obj);
353+
for module in trans.modules.iter() {
354+
ab.add_file(&module.object);
399355
}
400356

401357
// Note that in this loop we are ignoring the value of `lib.cfg`. That is,
@@ -462,15 +418,15 @@ fn link_rlib<'a>(sess: &'a Session,
462418
// For LTO purposes, the bytecode of this library is also inserted
463419
// into the archive. If codegen_units > 1, we insert each of the
464420
// bitcode files.
465-
for obj in objects {
421+
for module in trans.modules.iter() {
466422
// Note that we make sure that the bytecode filename in the
467423
// archive is never exactly 16 bytes long by adding a 16 byte
468424
// extension to it. This is to work around a bug in LLDB that
469425
// would cause it to crash if the name of a file in an archive
470426
// was exactly 16 bytes.
471-
let bc_filename = obj.with_extension("bc");
472-
let bc_deflated_filename = tmpdir.join({
473-
obj.with_extension("bytecode.deflate").file_name().unwrap()
427+
let bc_filename = module.object.with_extension("bc");
428+
let bc_encoded_filename = tmpdir.join({
429+
module.object.with_extension(RLIB_BYTECODE_EXTENSION).file_name().unwrap()
474430
});
475431

476432
let mut bc_data = Vec::new();
@@ -482,28 +438,25 @@ fn link_rlib<'a>(sess: &'a Session,
482438
e))
483439
}
484440

485-
let mut bc_data_deflated = Vec::new();
486-
DeflateEncoder::new(&mut bc_data_deflated, Compression::Fast)
487-
.write_all(&bc_data).unwrap();
441+
let encoded = bytecode::encode(&module.llmod_id, &bc_data);
488442

489-
let mut bc_file_deflated = match fs::File::create(&bc_deflated_filename) {
443+
let mut bc_file_deflated = match fs::File::create(&bc_encoded_filename) {
490444
Ok(file) => file,
491445
Err(e) => {
492446
sess.fatal(&format!("failed to create compressed \
493447
bytecode file: {}", e))
494448
}
495449
};
496450

497-
match write_rlib_bytecode_object_v1(&mut bc_file_deflated,
498-
&bc_data_deflated) {
451+
match bc_file_deflated.write_all(&encoded) {
499452
Ok(()) => {}
500453
Err(e) => {
501454
sess.fatal(&format!("failed to write compressed \
502455
bytecode: {}", e));
503456
}
504457
};
505458

506-
ab.add_file(&bc_deflated_filename);
459+
ab.add_file(&bc_encoded_filename);
507460

508461
// See the bottom of back::write::run_passes for an explanation
509462
// of when we do and don't keep .#module-name#.bc files around.
@@ -533,40 +486,6 @@ fn link_rlib<'a>(sess: &'a Session,
533486
ab
534487
}
535488

536-
fn write_rlib_bytecode_object_v1(writer: &mut Write,
537-
bc_data_deflated: &[u8]) -> io::Result<()> {
538-
let bc_data_deflated_size: u64 = bc_data_deflated.len() as u64;
539-
540-
writer.write_all(RLIB_BYTECODE_OBJECT_MAGIC)?;
541-
writer.write_all(&[1, 0, 0, 0])?;
542-
writer.write_all(&[
543-
(bc_data_deflated_size >> 0) as u8,
544-
(bc_data_deflated_size >> 8) as u8,
545-
(bc_data_deflated_size >> 16) as u8,
546-
(bc_data_deflated_size >> 24) as u8,
547-
(bc_data_deflated_size >> 32) as u8,
548-
(bc_data_deflated_size >> 40) as u8,
549-
(bc_data_deflated_size >> 48) as u8,
550-
(bc_data_deflated_size >> 56) as u8,
551-
])?;
552-
writer.write_all(&bc_data_deflated)?;
553-
554-
let number_of_bytes_written_so_far =
555-
RLIB_BYTECODE_OBJECT_MAGIC.len() + // magic id
556-
mem::size_of_val(&RLIB_BYTECODE_OBJECT_VERSION) + // version
557-
mem::size_of_val(&bc_data_deflated_size) + // data size field
558-
bc_data_deflated_size as usize; // actual data
559-
560-
// If the number of bytes written to the object so far is odd, add a
561-
// padding byte to make it even. This works around a crash bug in LLDB
562-
// (see issue #15950)
563-
if number_of_bytes_written_so_far % 2 == 1 {
564-
writer.write_all(&[0])?;
565-
}
566-
567-
return Ok(());
568-
}
569-
570489
// Create a static archive
571490
//
572491
// This is essentially the same thing as an rlib, but it also involves adding
@@ -582,13 +501,11 @@ fn write_rlib_bytecode_object_v1(writer: &mut Write,
582501
fn link_staticlib(sess: &Session,
583502
trans: &CrateTranslation,
584503
outputs: &OutputFilenames,
585-
objects: &[PathBuf],
586504
out_filename: &Path,
587505
tempdir: &Path) {
588506
let mut ab = link_rlib(sess,
589507
trans,
590508
RlibFlavor::StaticlibBase,
591-
objects,
592509
outputs,
593510
out_filename,
594511
tempdir);
@@ -692,12 +609,11 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) {
692609
// links to all upstream files as well.
693610
fn link_natively(sess: &Session,
694611
crate_type: config::CrateType,
695-
objects: &[PathBuf],
696612
out_filename: &Path,
697613
trans: &CrateTranslation,
698614
outputs: &OutputFilenames,
699615
tmpdir: &Path) {
700-
info!("preparing {:?} from {:?} to {:?}", crate_type, objects, out_filename);
616+
info!("preparing {:?} to {:?}", crate_type, out_filename);
701617
let flavor = sess.linker_flavor();
702618

703619
// The invocations of cc share some flags across platforms
@@ -735,7 +651,7 @@ fn link_natively(sess: &Session,
735651
{
736652
let mut linker = trans.linker_info.to_linker(cmd, &sess);
737653
link_args(&mut *linker, sess, crate_type, tmpdir,
738-
objects, out_filename, outputs, trans);
654+
out_filename, outputs, trans);
739655
cmd = linker.finalize();
740656
}
741657
if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) {
@@ -956,7 +872,6 @@ fn link_args(cmd: &mut Linker,
956872
sess: &Session,
957873
crate_type: config::CrateType,
958874
tmpdir: &Path,
959-
objects: &[PathBuf],
960875
out_filename: &Path,
961876
outputs: &OutputFilenames,
962877
trans: &CrateTranslation) {
@@ -969,8 +884,8 @@ fn link_args(cmd: &mut Linker,
969884
let t = &sess.target.target;
970885

971886
cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path));
972-
for obj in objects {
973-
cmd.add_object(obj);
887+
for module in trans.modules.iter() {
888+
cmd.add_object(&module.object);
974889
}
975890
cmd.output_filename(out_filename);
976891

@@ -1264,7 +1179,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
12641179
archive.update_symbols();
12651180

12661181
for f in archive.src_files() {
1267-
if f.ends_with("bytecode.deflate") || f == METADATA_FILENAME {
1182+
if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME {
12681183
archive.remove_file(&f);
12691184
continue
12701185
}
@@ -1342,7 +1257,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
13421257

13431258
let mut any_objects = false;
13441259
for f in archive.src_files() {
1345-
if f.ends_with("bytecode.deflate") || f == METADATA_FILENAME {
1260+
if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME {
13461261
archive.remove_file(&f);
13471262
continue
13481263
}

‎src/librustc_trans/back/lto.rs

Lines changed: 188 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use back::link;
11+
use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION};
1212
use back::write;
1313
use back::symbol_export;
1414
use rustc::session::config;
@@ -18,17 +18,14 @@ use llvm::archive_ro::ArchiveRO;
1818
use llvm::{ModuleRef, TargetMachineRef, True, False};
1919
use rustc::middle::exported_symbols::SymbolExportLevel;
2020
use rustc::util::common::time;
21-
use rustc::util::common::path2cstr;
2221
use rustc::hir::def_id::LOCAL_CRATE;
2322
use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext};
23+
use {ModuleTranslation, ModuleKind};
2424

2525
use libc;
26-
use flate2::read::DeflateDecoder;
2726

28-
use std::io::Read;
2927
use std::ffi::CString;
30-
use std::path::Path;
31-
use std::ptr::read_unaligned;
28+
use std::slice;
3229

3330
pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
3431
match crate_type {
@@ -42,12 +39,58 @@ pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
4239
}
4340
}
4441

45-
pub fn run(cgcx: &CodegenContext,
46-
diag_handler: &Handler,
47-
llmod: ModuleRef,
48-
tm: TargetMachineRef,
49-
config: &ModuleConfig,
50-
temp_no_opt_bc_filename: &Path) -> Result<(), FatalError> {
42+
pub enum LtoModuleTranslation {
43+
Fat {
44+
module: Option<ModuleTranslation>,
45+
_serialized_bitcode: Vec<SerializedModule>,
46+
},
47+
48+
// Note the lack of other entries in this enum! Ideally one day this gap is
49+
// intended to be filled with a "Thin" LTO variant.
50+
}
51+
52+
impl LtoModuleTranslation {
53+
pub fn name(&self) -> &str {
54+
match *self {
55+
LtoModuleTranslation::Fat { .. } => "everything",
56+
}
57+
}
58+
59+
/// Optimize this module within the given codegen context.
60+
///
61+
/// This function is unsafe as it'll return a `ModuleTranslation` still
62+
/// points to LLVM data structures owned by this `LtoModuleTranslation`.
63+
/// It's intended that the module returned is immediately code generated and
64+
/// dropped, and then this LTO module is dropped.
65+
pub unsafe fn optimize(&mut self, cgcx: &CodegenContext)
66+
-> Result<ModuleTranslation, FatalError>
67+
{
68+
match *self {
69+
LtoModuleTranslation::Fat { ref mut module, .. } => {
70+
let trans = module.take().unwrap();
71+
let config = cgcx.config(trans.kind);
72+
let llmod = trans.llvm().unwrap().llmod;
73+
let tm = trans.llvm().unwrap().tm;
74+
run_pass_manager(cgcx, tm, llmod, config);
75+
Ok(trans)
76+
}
77+
}
78+
}
79+
80+
/// A "guage" of how costly it is to optimize this module, used to sort
81+
/// biggest modules first.
82+
pub fn cost(&self) -> u64 {
83+
match *self {
84+
// Only one module with fat LTO, so the cost doesn't matter.
85+
LtoModuleTranslation::Fat { .. } => 0,
86+
}
87+
}
88+
}
89+
90+
pub fn run(cgcx: &CodegenContext, modules: Vec<ModuleTranslation>)
91+
-> Result<Vec<LtoModuleTranslation>, FatalError>
92+
{
93+
let diag_handler = cgcx.create_diag_handler();
5194
if cgcx.opts.cg.prefer_dynamic {
5295
diag_handler.struct_err("cannot prefer dynamic linking when performing LTO")
5396
.note("only 'staticlib', 'bin', and 'cdylib' outputs are \
@@ -82,106 +125,140 @@ pub fn run(cgcx: &CodegenContext,
82125
.iter()
83126
.filter_map(symbol_filter)
84127
.collect();
128+
info!("{} symbols in whitelist", symbol_white_list.len());
85129

86130
// For each of our upstream dependencies, find the corresponding rlib and
87131
// load the bitcode from the archive. Then merge it into the current LLVM
88132
// module that we've got.
133+
let mut upstream_modules = Vec::new();
89134
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
90135
symbol_white_list.extend(
91136
cgcx.exported_symbols[&cnum]
92137
.iter()
93138
.filter_map(symbol_filter));
139+
info!("{} symbols in whitelist after {}", symbol_white_list.len(), cnum);
94140

95141
let archive = ArchiveRO::open(&path).expect("wanted an rlib");
96142
let bytecodes = archive.iter().filter_map(|child| {
97143
child.ok().and_then(|c| c.name().map(|name| (name, c)))
98-
}).filter(|&(name, _)| name.ends_with("bytecode.deflate"));
144+
}).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION));
99145
for (name, data) in bytecodes {
146+
info!("adding bytecode {}", name);
100147
let bc_encoded = data.data();
101148

102-
let bc_decoded = if is_versioned_bytecode_format(bc_encoded) {
103-
time(cgcx.time_passes, &format!("decode {}", name), || {
104-
// Read the version
105-
let version = extract_bytecode_format_version(bc_encoded);
106-
107-
if version == 1 {
108-
// The only version existing so far
109-
let data_size = extract_compressed_bytecode_size_v1(bc_encoded);
110-
let compressed_data = &bc_encoded[
111-
link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET..
112-
(link::RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET + data_size as usize)];
113-
114-
let mut inflated = Vec::new();
115-
let res = DeflateDecoder::new(compressed_data)
116-
.read_to_end(&mut inflated);
117-
if res.is_err() {
118-
let msg = format!("failed to decompress bc of `{}`",
119-
name);
120-
Err(diag_handler.fatal(&msg))
121-
} else {
122-
Ok(inflated)
123-
}
124-
} else {
125-
Err(diag_handler.fatal(&format!("Unsupported bytecode format version {}",
126-
version)))
127-
}
128-
})?
129-
} else {
130-
time(cgcx.time_passes, &format!("decode {}", name), || {
131-
// the object must be in the old, pre-versioning format, so
132-
// simply inflate everything and let LLVM decide if it can
133-
// make sense of it
134-
let mut inflated = Vec::new();
135-
let res = DeflateDecoder::new(bc_encoded)
136-
.read_to_end(&mut inflated);
137-
if res.is_err() {
138-
let msg = format!("failed to decompress bc of `{}`",
139-
name);
140-
Err(diag_handler.fatal(&msg))
141-
} else {
142-
Ok(inflated)
143-
}
144-
})?
145-
};
146-
147-
let ptr = bc_decoded.as_ptr();
148-
debug!("linking {}", name);
149-
time(cgcx.time_passes, &format!("ll link {}", name), || unsafe {
150-
if llvm::LLVMRustLinkInExternalBitcode(llmod,
151-
ptr as *const libc::c_char,
152-
bc_decoded.len() as libc::size_t) {
153-
Ok(())
154-
} else {
155-
let msg = format!("failed to load bc of `{}`", name);
156-
Err(write::llvm_err(&diag_handler, msg))
149+
let (bc, id) = time(cgcx.time_passes, &format!("decode {}", name), || {
150+
match DecodedBytecode::new(bc_encoded) {
151+
Ok(b) => Ok((b.bytecode(), b.identifier().to_string())),
152+
Err(e) => Err(diag_handler.fatal(&e)),
157153
}
158154
})?;
155+
let bc = SerializedModule::FromRlib(bc);
156+
upstream_modules.push((bc, CString::new(id).unwrap()));
159157
}
160158
}
161159

162160
// Internalize everything but the exported symbols of the current module
163161
let arr: Vec<*const libc::c_char> = symbol_white_list.iter()
164162
.map(|c| c.as_ptr())
165163
.collect();
166-
let ptr = arr.as_ptr();
164+
165+
fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr)
166+
}
167+
168+
fn fat_lto(cgcx: &CodegenContext,
169+
diag_handler: &Handler,
170+
mut modules: Vec<ModuleTranslation>,
171+
mut serialized_modules: Vec<(SerializedModule, CString)>,
172+
symbol_white_list: &[*const libc::c_char])
173+
-> Result<Vec<LtoModuleTranslation>, FatalError>
174+
{
175+
info!("going for a fat lto");
176+
177+
// Find the "costliest" module and merge everything into that codegen unit.
178+
// All the other modules will be serialized and reparsed into the new
179+
// context, so this hopefully avoids serializing and parsing the largest
180+
// codegen unit.
181+
//
182+
// Additionally use a regular module as the base here to ensure that various
183+
// file copy operations in the backend work correctly. The only other kind
184+
// of module here should be an allocator one, and if your crate is smaller
185+
// than the allocator module then the size doesn't really matter anyway.
186+
let (_, costliest_module) = modules.iter()
187+
.enumerate()
188+
.filter(|&(_, module)| module.kind == ModuleKind::Regular)
189+
.map(|(i, module)| {
190+
let cost = unsafe {
191+
llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod)
192+
};
193+
(cost, i)
194+
})
195+
.max()
196+
.expect("must be trans'ing at least one module");
197+
let module = modules.remove(costliest_module);
198+
let llmod = module.llvm().expect("can't lto pre-translated modules").llmod;
199+
info!("using {:?} as a base module", module.llmod_id);
200+
201+
// For all other modules we translated we'll need to link them into our own
202+
// bitcode. All modules were translated in their own LLVM context, however,
203+
// and we want to move everything to the same LLVM context. Currently the
204+
// way we know of to do that is to serialize them to a string and them parse
205+
// them later. Not great but hey, that's why it's "fat" LTO, right?
206+
for module in modules {
207+
let llvm = module.llvm().expect("can't lto pre-translated modules");
208+
let buffer = ModuleBuffer::new(llvm.llmod);
209+
let llmod_id = CString::new(&module.llmod_id[..]).unwrap();
210+
serialized_modules.push((SerializedModule::Local(buffer), llmod_id));
211+
}
212+
213+
// For all serialized bitcode files we parse them and link them in as we did
214+
// above, this is all mostly handled in C++. Like above, though, we don't
215+
// know much about the memory management here so we err on the side of being
216+
// save and persist everything with the original module.
217+
let mut serialized_bitcode = Vec::new();
218+
for (bc_decoded, name) in serialized_modules {
219+
info!("linking {:?}", name);
220+
time(cgcx.time_passes, &format!("ll link {:?}", name), || unsafe {
221+
let data = bc_decoded.data();
222+
if llvm::LLVMRustLinkInExternalBitcode(llmod,
223+
data.as_ptr() as *const libc::c_char,
224+
data.len() as libc::size_t) {
225+
Ok(())
226+
} else {
227+
let msg = format!("failed to load bc of {:?}", name);
228+
Err(write::llvm_err(&diag_handler, msg))
229+
}
230+
})?;
231+
serialized_bitcode.push(bc_decoded);
232+
}
233+
cgcx.save_temp_bitcode(&module, "lto.input");
234+
235+
// Internalize everything that *isn't* in our whitelist to help strip out
236+
// more modules and such
167237
unsafe {
238+
let ptr = symbol_white_list.as_ptr();
168239
llvm::LLVMRustRunRestrictionPass(llmod,
169240
ptr as *const *const libc::c_char,
170-
arr.len() as libc::size_t);
241+
symbol_white_list.len() as libc::size_t);
242+
cgcx.save_temp_bitcode(&module, "lto.after-restriction");
171243
}
172244

173245
if cgcx.no_landing_pads {
174246
unsafe {
175247
llvm::LLVMRustMarkAllFunctionsNounwind(llmod);
176248
}
249+
cgcx.save_temp_bitcode(&module, "lto.after-nounwind");
177250
}
178251

179-
if cgcx.opts.cg.save_temps {
180-
let cstr = path2cstr(temp_no_opt_bc_filename);
181-
unsafe {
182-
llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
183-
}
184-
}
252+
Ok(vec![LtoModuleTranslation::Fat {
253+
module: Some(module),
254+
_serialized_bitcode: serialized_bitcode,
255+
}])
256+
}
257+
258+
fn run_pass_manager(cgcx: &CodegenContext,
259+
tm: TargetMachineRef,
260+
llmod: ModuleRef,
261+
config: &ModuleConfig) {
185262

186263
// Now we have one massive module inside of llmod. Time to run the
187264
// LTO-specific optimization passes that LLVM provides.
@@ -212,25 +289,45 @@ pub fn run(cgcx: &CodegenContext,
212289
llvm::LLVMDisposePassManager(pm);
213290
}
214291
debug!("lto done");
215-
Ok(())
216292
}
217293

218-
fn is_versioned_bytecode_format(bc: &[u8]) -> bool {
219-
let magic_id_byte_count = link::RLIB_BYTECODE_OBJECT_MAGIC.len();
220-
return bc.len() > magic_id_byte_count &&
221-
&bc[..magic_id_byte_count] == link::RLIB_BYTECODE_OBJECT_MAGIC;
294+
pub enum SerializedModule {
295+
Local(ModuleBuffer),
296+
FromRlib(Vec<u8>),
222297
}
223298

224-
fn extract_bytecode_format_version(bc: &[u8]) -> u32 {
225-
let pos = link::RLIB_BYTECODE_OBJECT_VERSION_OFFSET;
226-
let byte_data = &bc[pos..pos + 4];
227-
let data = unsafe { read_unaligned(byte_data.as_ptr() as *const u32) };
228-
u32::from_le(data)
299+
impl SerializedModule {
300+
fn data(&self) -> &[u8] {
301+
match *self {
302+
SerializedModule::Local(ref m) => m.data(),
303+
SerializedModule::FromRlib(ref m) => m,
304+
}
305+
}
229306
}
230307

231-
fn extract_compressed_bytecode_size_v1(bc: &[u8]) -> u64 {
232-
let pos = link::RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET;
233-
let byte_data = &bc[pos..pos + 8];
234-
let data = unsafe { read_unaligned(byte_data.as_ptr() as *const u64) };
235-
u64::from_le(data)
308+
pub struct ModuleBuffer(*mut llvm::ModuleBuffer);
309+
310+
unsafe impl Send for ModuleBuffer {}
311+
unsafe impl Sync for ModuleBuffer {}
312+
313+
impl ModuleBuffer {
314+
fn new(m: ModuleRef) -> ModuleBuffer {
315+
ModuleBuffer(unsafe {
316+
llvm::LLVMRustModuleBufferCreate(m)
317+
})
318+
}
319+
320+
fn data(&self) -> &[u8] {
321+
unsafe {
322+
let ptr = llvm::LLVMRustModuleBufferPtr(self.0);
323+
let len = llvm::LLVMRustModuleBufferLen(self.0);
324+
slice::from_raw_parts(ptr, len)
325+
}
326+
}
327+
}
328+
329+
impl Drop for ModuleBuffer {
330+
fn drop(&mut self) {
331+
unsafe { llvm::LLVMRustModuleBufferFree(self.0); }
332+
}
236333
}

‎src/librustc_trans/back/write.rs

Lines changed: 258 additions & 122 deletions
Large diffs are not rendered by default.

‎src/librustc_trans/base.rs

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use super::ModuleKind;
3131
use assert_module_sources::{self, Disposition};
3232
use back::link;
3333
use back::symbol_export;
34-
use back::write::{self, OngoingCrateTranslation};
34+
use back::write::{self, OngoingCrateTranslation, create_target_machine};
3535
use llvm::{ContextRef, ModuleRef, ValueRef, Vector, get_param};
3636
use llvm;
3737
use metadata;
@@ -732,6 +732,7 @@ fn contains_null(s: &str) -> bool {
732732
}
733733

734734
fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
735+
llmod_id: &str,
735736
link_meta: &LinkMeta,
736737
exported_symbols: &NodeSet)
737738
-> (ContextRef, ModuleRef,
@@ -741,7 +742,7 @@ fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
741742
use flate2::write::DeflateEncoder;
742743

743744
let (metadata_llcx, metadata_llmod) = unsafe {
744-
context::create_context_and_module(tcx.sess, "metadata")
745+
context::create_context_and_module(tcx.sess, llmod_id)
745746
};
746747

747748
#[derive(PartialEq, Eq, PartialOrd, Ord)]
@@ -886,17 +887,20 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
886887

887888
let shared_ccx = SharedCrateContext::new(tcx);
888889
// Translate the metadata.
890+
let llmod_id = "metadata";
889891
let (metadata_llcx, metadata_llmod, metadata, metadata_incr_hashes) =
890892
time(tcx.sess.time_passes(), "write metadata", || {
891-
write_metadata(tcx, &link_meta, &exported_symbol_node_ids)
893+
write_metadata(tcx, llmod_id, &link_meta, &exported_symbol_node_ids)
892894
});
893895

894896
let metadata_module = ModuleTranslation {
895897
name: link::METADATA_MODULE_NAME.to_string(),
898+
llmod_id: llmod_id.to_string(),
896899
symbol_name_hash: 0, // we always rebuild metadata, at least for now
897900
source: ModuleSource::Translated(ModuleLlvm {
898901
llcx: metadata_llcx,
899902
llmod: metadata_llmod,
903+
tm: create_target_machine(tcx.sess),
900904
}),
901905
kind: ModuleKind::Metadata,
902906
};
@@ -935,8 +939,6 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
935939
shared_ccx.tcx().collect_and_partition_translation_items(LOCAL_CRATE).1;
936940
let codegen_units = (*codegen_units).clone();
937941

938-
assert!(codegen_units.len() <= 1 || !tcx.sess.lto());
939-
940942
let ongoing_translation = write::start_async_translation(
941943
tcx,
942944
time_graph.clone(),
@@ -945,31 +947,23 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
945947
rx);
946948

947949
// Translate an allocator shim, if any
948-
//
949-
// If LTO is enabled and we've got some previous LLVM module we translated
950-
// above, then we can just translate directly into that LLVM module. If not,
951-
// however, we need to create a separate module and trans into that. Note
952-
// that the separate translation is critical for the standard library where
953-
// the rlib's object file doesn't have allocator functions but the dylib
954-
// links in an object file that has allocator functions. When we're
955-
// compiling a final LTO artifact, though, there's no need to worry about
956-
// this as we're not working with this dual "rlib/dylib" functionality.
957-
let allocator_module = if tcx.sess.lto() {
958-
None
959-
} else if let Some(kind) = tcx.sess.allocator_kind.get() {
950+
let allocator_module = if let Some(kind) = tcx.sess.allocator_kind.get() {
960951
unsafe {
952+
let llmod_id = "allocator";
961953
let (llcx, llmod) =
962-
context::create_context_and_module(tcx.sess, "allocator");
954+
context::create_context_and_module(tcx.sess, llmod_id);
963955
let modules = ModuleLlvm {
964956
llmod,
965957
llcx,
958+
tm: create_target_machine(tcx.sess),
966959
};
967960
time(tcx.sess.time_passes(), "write allocator module", || {
968961
allocator::trans(tcx, &modules, kind)
969962
});
970963

971964
Some(ModuleTranslation {
972965
name: link::ALLOCATOR_MODULE_NAME.to_string(),
966+
llmod_id: llmod_id.to_string(),
973967
symbol_name_hash: 0, // we always rebuild allocator shims
974968
source: ModuleSource::Translated(modules),
975969
kind: ModuleKind::Allocator,
@@ -1002,10 +996,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1002996
ongoing_translation.wait_for_signal_to_translate_item();
1003997
ongoing_translation.check_for_errors(tcx.sess);
1004998

1005-
let _timing_guard = time_graph
1006-
.as_ref()
1007-
.map(|time_graph| time_graph.start(write::TRANS_WORKER_TIMELINE,
1008-
write::TRANS_WORK_PACKAGE_KIND));
999+
let _timing_guard = time_graph.as_ref().map(|time_graph| {
1000+
time_graph.start(write::TRANS_WORKER_TIMELINE,
1001+
write::TRANS_WORK_PACKAGE_KIND,
1002+
&format!("codegen {}", cgu.name()))
1003+
});
10091004
let start_time = Instant::now();
10101005
all_stats.extend(tcx.compile_codegen_unit(*cgu.name()));
10111006
total_trans_time += start_time.elapsed();
@@ -1336,6 +1331,16 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13361331
let cgu_id = cgu.work_product_id();
13371332
let symbol_name_hash = cgu.compute_symbol_name_hash(tcx);
13381333

1334+
// Append ".rs" to LLVM module identifier.
1335+
//
1336+
// LLVM code generator emits a ".file filename" directive
1337+
// for ELF backends. Value of the "filename" is set as the
1338+
// LLVM module identifier. Due to a LLVM MC bug[1], LLVM
1339+
// crashes if the module identifier is same as other symbols
1340+
// such as a function name in the module.
1341+
// 1. http://llvm.org/bugs/show_bug.cgi?id=11479
1342+
let llmod_id = format!("{}.rs", cgu.name());
1343+
13391344
// Check whether there is a previous work-product we can
13401345
// re-use. Not only must the file exist, and the inputs not
13411346
// be dirty, but the hash of the symbols we will generate must
@@ -1361,6 +1366,7 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13611366
if let Some(buf) = previous_work_product {
13621367
// Don't need to translate this module.
13631368
let module = ModuleTranslation {
1369+
llmod_id: llmod_id,
13641370
name: cgu_name,
13651371
symbol_name_hash,
13661372
source: ModuleSource::Preexisting(buf.clone()),
@@ -1371,7 +1377,7 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13711377

13721378
// Instantiate translation items without filling out definitions yet...
13731379
let scx = SharedCrateContext::new(tcx);
1374-
let lcx = LocalCrateContext::new(&scx, cgu);
1380+
let lcx = LocalCrateContext::new(&scx, cgu, &llmod_id);
13751381
let module = {
13761382
let ccx = CrateContext::new(&scx, &lcx);
13771383
let trans_items = ccx.codegen_unit()
@@ -1423,20 +1429,9 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
14231429
let llvm_module = ModuleLlvm {
14241430
llcx: ccx.llcx(),
14251431
llmod: ccx.llmod(),
1432+
tm: create_target_machine(ccx.sess()),
14261433
};
14271434

1428-
// In LTO mode we inject the allocator shim into the existing
1429-
// module.
1430-
if ccx.sess().lto() {
1431-
if let Some(kind) = ccx.sess().allocator_kind.get() {
1432-
time(ccx.sess().time_passes(), "write allocator module", || {
1433-
unsafe {
1434-
allocator::trans(ccx.tcx(), &llvm_module, kind);
1435-
}
1436-
});
1437-
}
1438-
}
1439-
14401435
// Adjust exported symbols for MSVC dllimport
14411436
if ccx.sess().target.target.options.is_like_msvc &&
14421437
ccx.sess().crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) {
@@ -1448,6 +1443,7 @@ fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
14481443
symbol_name_hash,
14491444
source: ModuleSource::Translated(llvm_module),
14501445
kind: ModuleKind::Regular,
1446+
llmod_id,
14511447
}
14521448
};
14531449

‎src/librustc_trans/context.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -320,19 +320,10 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
320320

321321
impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> {
322322
pub fn new(shared: &SharedCrateContext<'a, 'tcx>,
323-
codegen_unit: Arc<CodegenUnit<'tcx>>)
323+
codegen_unit: Arc<CodegenUnit<'tcx>>,
324+
llmod_id: &str)
324325
-> LocalCrateContext<'a, 'tcx> {
325326
unsafe {
326-
// Append ".rs" to LLVM module identifier.
327-
//
328-
// LLVM code generator emits a ".file filename" directive
329-
// for ELF backends. Value of the "filename" is set as the
330-
// LLVM module identifier. Due to a LLVM MC bug[1], LLVM
331-
// crashes if the module identifier is same as other symbols
332-
// such as a function name in the module.
333-
// 1. http://llvm.org/bugs/show_bug.cgi?id=11479
334-
let llmod_id = format!("{}.rs", codegen_unit.name());
335-
336327
let (llcx, llmod) = create_context_and_module(&shared.tcx.sess,
337328
&llmod_id[..]);
338329

‎src/librustc_trans/lib.rs

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,26 @@ pub use base::trans_crate;
6868
pub use metadata::LlvmMetadataLoader;
6969
pub use llvm_util::{init, target_features, print_version, print_passes, print, enable_llvm_debug};
7070

71+
use std::any::Any;
72+
use std::path::PathBuf;
7173
use std::rc::Rc;
74+
use std::sync::mpsc;
7275

76+
use rustc::dep_graph::DepGraph;
7377
use rustc::hir::def_id::CrateNum;
78+
use rustc::middle::cstore::MetadataLoader;
7479
use rustc::middle::cstore::{NativeLibrary, CrateSource, LibSource};
80+
use rustc::session::Session;
81+
use rustc::session::config::{OutputFilenames, OutputType};
7582
use rustc::ty::maps::Providers;
83+
use rustc::ty::{self, TyCtxt};
7684
use rustc::util::nodemap::{FxHashSet, FxHashMap};
7785

7886
mod diagnostics;
7987

8088
pub mod back {
8189
mod archive;
90+
mod bytecode;
8291
mod command;
8392
pub(crate) mod linker;
8493
pub mod link;
@@ -138,14 +147,6 @@ mod type_;
138147
mod type_of;
139148
mod value;
140149

141-
use std::sync::mpsc;
142-
use std::any::Any;
143-
use rustc::ty::{self, TyCtxt};
144-
use rustc::session::Session;
145-
use rustc::session::config::OutputFilenames;
146-
use rustc::middle::cstore::MetadataLoader;
147-
use rustc::dep_graph::DepGraph;
148-
149150
pub struct LlvmTransCrate(());
150151

151152
impl LlvmTransCrate {
@@ -202,63 +203,62 @@ pub struct ModuleTranslation {
202203
/// something unique to this crate (e.g., a module path) as well
203204
/// as the crate name and disambiguator.
204205
name: String,
206+
llmod_id: String,
205207
symbol_name_hash: u64,
206208
pub source: ModuleSource,
207209
pub kind: ModuleKind,
208210
}
209211

210-
#[derive(Copy, Clone, Debug)]
212+
#[derive(Copy, Clone, Debug, PartialEq)]
211213
pub enum ModuleKind {
212214
Regular,
213215
Metadata,
214216
Allocator,
215217
}
216218

217219
impl ModuleTranslation {
218-
pub fn into_compiled_module(self, emit_obj: bool, emit_bc: bool) -> CompiledModule {
220+
pub fn llvm(&self) -> Option<&ModuleLlvm> {
221+
match self.source {
222+
ModuleSource::Translated(ref llvm) => Some(llvm),
223+
ModuleSource::Preexisting(_) => None,
224+
}
225+
}
226+
227+
pub fn into_compiled_module(self,
228+
emit_obj: bool,
229+
emit_bc: bool,
230+
outputs: &OutputFilenames) -> CompiledModule {
219231
let pre_existing = match self.source {
220232
ModuleSource::Preexisting(_) => true,
221233
ModuleSource::Translated(_) => false,
222234
};
235+
let object = outputs.temp_path(OutputType::Object, Some(&self.name));
223236

224237
CompiledModule {
238+
llmod_id: self.llmod_id,
225239
name: self.name.clone(),
226240
kind: self.kind,
227241
symbol_name_hash: self.symbol_name_hash,
228242
pre_existing,
229243
emit_obj,
230244
emit_bc,
231-
}
232-
}
233-
}
234-
235-
impl Drop for ModuleTranslation {
236-
fn drop(&mut self) {
237-
match self.source {
238-
ModuleSource::Preexisting(_) => {
239-
// Nothing to dispose.
240-
},
241-
ModuleSource::Translated(llvm) => {
242-
unsafe {
243-
llvm::LLVMDisposeModule(llvm.llmod);
244-
llvm::LLVMContextDispose(llvm.llcx);
245-
}
246-
},
245+
object,
247246
}
248247
}
249248
}
250249

251250
#[derive(Debug)]
252251
pub struct CompiledModule {
253252
pub name: String,
253+
pub llmod_id: String,
254+
pub object: PathBuf,
254255
pub kind: ModuleKind,
255256
pub symbol_name_hash: u64,
256257
pub pre_existing: bool,
257258
pub emit_obj: bool,
258259
pub emit_bc: bool,
259260
}
260261

261-
#[derive(Clone)]
262262
pub enum ModuleSource {
263263
/// Copy the `.o` files or whatever from the incr. comp. directory.
264264
Preexisting(WorkProduct),
@@ -267,14 +267,25 @@ pub enum ModuleSource {
267267
Translated(ModuleLlvm),
268268
}
269269

270-
#[derive(Copy, Clone, Debug)]
270+
#[derive(Debug)]
271271
pub struct ModuleLlvm {
272272
llcx: llvm::ContextRef,
273273
pub llmod: llvm::ModuleRef,
274+
tm: llvm::TargetMachineRef,
274275
}
275276

276-
unsafe impl Send for ModuleTranslation { }
277-
unsafe impl Sync for ModuleTranslation { }
277+
unsafe impl Send for ModuleLlvm { }
278+
unsafe impl Sync for ModuleLlvm { }
279+
280+
impl Drop for ModuleLlvm {
281+
fn drop(&mut self) {
282+
unsafe {
283+
llvm::LLVMDisposeModule(self.llmod);
284+
llvm::LLVMContextDispose(self.llcx);
285+
llvm::LLVMRustDisposeTargetMachine(self.tm);
286+
}
287+
}
288+
}
278289

279290
pub struct CrateTranslation {
280291
pub crate_name: Symbol,

‎src/librustc_trans/time_graph.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ use std::io::prelude::*;
1616
use std::fs::File;
1717

1818
const OUTPUT_WIDTH_IN_PX: u64 = 1000;
19-
const TIME_LINE_HEIGHT_IN_PX: u64 = 7;
20-
const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 10;
19+
const TIME_LINE_HEIGHT_IN_PX: u64 = 20;
20+
const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 30;
2121

2222
#[derive(Clone)]
2323
struct Timing {
2424
start: Instant,
2525
end: Instant,
2626
work_package_kind: WorkPackageKind,
27+
name: String,
2728
}
2829

2930
#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
@@ -32,7 +33,7 @@ pub struct TimelineId(pub usize);
3233
#[derive(Clone)]
3334
struct PerThread {
3435
timings: Vec<Timing>,
35-
open_work_package: Option<(Instant, WorkPackageKind)>,
36+
open_work_package: Option<(Instant, WorkPackageKind, String)>,
3637
}
3738

3839
#[derive(Clone)]
@@ -66,7 +67,8 @@ impl TimeGraph {
6667

6768
pub fn start(&self,
6869
timeline: TimelineId,
69-
work_package_kind: WorkPackageKind) -> RaiiToken {
70+
work_package_kind: WorkPackageKind,
71+
name: &str) -> RaiiToken {
7072
{
7173
let mut table = self.data.lock().unwrap();
7274

@@ -76,7 +78,7 @@ impl TimeGraph {
7678
});
7779

7880
assert!(data.open_work_package.is_none());
79-
data.open_work_package = Some((Instant::now(), work_package_kind));
81+
data.open_work_package = Some((Instant::now(), work_package_kind, name.to_string()));
8082
}
8183

8284
RaiiToken {
@@ -92,17 +94,16 @@ impl TimeGraph {
9294
let mut table = self.data.lock().unwrap();
9395
let data = table.get_mut(&timeline).unwrap();
9496

95-
if let Some((start, work_package_kind)) = data.open_work_package {
97+
if let Some((start, work_package_kind, name)) = data.open_work_package.take() {
9698
data.timings.push(Timing {
9799
start,
98100
end,
99101
work_package_kind,
102+
name,
100103
});
101104
} else {
102105
bug!("end timing without start?")
103106
}
104-
105-
data.open_work_package = None;
106107
}
107108

108109
pub fn dump(&self, output_filename: &str) {
@@ -148,16 +149,18 @@ impl TimeGraph {
148149
let colors = span.work_package_kind.0;
149150

150151
writeln!(file, "<div style='position:absolute; \
152+
overflow:hidden; \
151153
top:{}px; \
152154
left:{}px; \
153155
width:{}px; \
154156
height:{}px; \
155-
background:{};'></div>",
157+
background:{};'>{}</div>",
156158
line_top,
157159
start,
158160
end - start,
159161
TIME_LINE_HEIGHT_IN_PX,
160-
colors[color % colors.len()]
162+
colors[color % colors.len()],
163+
span.name,
161164
).unwrap();
162165

163166
color += 1;

‎src/rustllvm/RustWrapper.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/IR/Instructions.h"
1616
#include "llvm/Object/Archive.h"
1717
#include "llvm/Object/ObjectFile.h"
18+
#include "llvm/Bitcode/BitcodeWriterPass.h"
1819

1920
#include "llvm/IR/CallSite.h"
2021

@@ -891,6 +892,23 @@ extern "C" bool LLVMRustLinkInExternalBitcode(LLVMModuleRef DstRef, char *BC,
891892
return true;
892893
}
893894

895+
extern "C" bool LLVMRustLinkInParsedExternalBitcode(
896+
LLVMModuleRef DstRef, LLVMModuleRef SrcRef) {
897+
#if LLVM_VERSION_GE(4, 0)
898+
Module *Dst = unwrap(DstRef);
899+
std::unique_ptr<Module> Src(unwrap(SrcRef));
900+
901+
if (Linker::linkModules(*Dst, std::move(Src))) {
902+
LLVMRustSetLastError("failed to link modules");
903+
return false;
904+
}
905+
return true;
906+
#else
907+
LLVMRustSetLastError("can't link parsed modules on this LLVM");
908+
return false;
909+
#endif
910+
}
911+
894912
// Note that the two following functions look quite similar to the
895913
// LLVMGetSectionName function. Sadly, it appears that this function only
896914
// returns a char* pointer, which isn't guaranteed to be null-terminated. The
@@ -1403,3 +1421,47 @@ extern "C" void LLVMRustSetVisibility(LLVMValueRef V,
14031421
LLVMRustVisibility RustVisibility) {
14041422
LLVMSetVisibility(V, fromRust(RustVisibility));
14051423
}
1424+
1425+
struct LLVMRustModuleBuffer {
1426+
std::string data;
1427+
};
1428+
1429+
extern "C" LLVMRustModuleBuffer*
1430+
LLVMRustModuleBufferCreate(LLVMModuleRef M) {
1431+
auto Ret = llvm::make_unique<LLVMRustModuleBuffer>();
1432+
{
1433+
raw_string_ostream OS(Ret->data);
1434+
{
1435+
legacy::PassManager PM;
1436+
PM.add(createBitcodeWriterPass(OS));
1437+
PM.run(*unwrap(M));
1438+
}
1439+
}
1440+
return Ret.release();
1441+
}
1442+
1443+
extern "C" void
1444+
LLVMRustModuleBufferFree(LLVMRustModuleBuffer *Buffer) {
1445+
delete Buffer;
1446+
}
1447+
1448+
extern "C" const void*
1449+
LLVMRustModuleBufferPtr(const LLVMRustModuleBuffer *Buffer) {
1450+
return Buffer->data.data();
1451+
}
1452+
1453+
extern "C" size_t
1454+
LLVMRustModuleBufferLen(const LLVMRustModuleBuffer *Buffer) {
1455+
return Buffer->data.length();
1456+
}
1457+
1458+
extern "C" uint64_t
1459+
LLVMRustModuleCost(LLVMModuleRef M) {
1460+
Module &Mod = *unwrap(M);
1461+
uint64_t cost = 0;
1462+
for (auto &F : Mod.functions()) {
1463+
(void)F;
1464+
cost += 1;
1465+
}
1466+
return cost;
1467+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 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+
// compile-flags: -C lto -C codegen-units=8
12+
// no-prefer-dynamic
13+
14+
fn main() {
15+
}

0 commit comments

Comments
 (0)
Please sign in to comment.