2018-04-23 22:17:07 +00:00
|
|
|
use attributes;
|
2017-10-20 01:44:33 +00:00
|
|
|
use back::bytecode::{self, RLIB_BYTECODE_EXTENSION};
|
2018-10-23 15:01:35 +00:00
|
|
|
use back::lto::ThinBuffer;
|
|
|
|
use rustc_codegen_ssa::back::write::{CodegenContext, ModuleConfig, run_assembler};
|
2018-11-16 11:45:28 +00:00
|
|
|
use rustc_codegen_ssa::traits::*;
|
2017-10-17 20:08:13 +00:00
|
|
|
use base;
|
|
|
|
use consts;
|
2018-10-23 15:01:35 +00:00
|
|
|
use rustc::session::config::{self, OutputType, Passes, Lto};
|
2017-05-27 18:48:09 +00:00
|
|
|
use rustc::session::Session;
|
2018-10-23 15:01:35 +00:00
|
|
|
use time_graph::Timeline;
|
2018-09-06 20:52:15 +00:00
|
|
|
use llvm::{self, DiagnosticInfo, PassManager, SMDiagnostic};
|
2018-08-23 18:03:22 +00:00
|
|
|
use llvm_util;
|
2018-10-23 15:01:35 +00:00
|
|
|
use ModuleLlvm;
|
|
|
|
use rustc_codegen_ssa::{ModuleCodegen, CompiledModule};
|
|
|
|
use rustc::util::common::time_ext;
|
2018-11-29 13:09:28 +00:00
|
|
|
use rustc_fs_util::{path_to_c_string, link_or_copy};
|
2018-08-07 14:04:34 +00:00
|
|
|
use rustc_data_structures::small_c_str::SmallCStr;
|
2018-10-23 15:01:35 +00:00
|
|
|
use errors::{self, Handler, FatalError};
|
2017-10-17 20:08:13 +00:00
|
|
|
use type_::Type;
|
2018-08-29 16:42:25 +00:00
|
|
|
use context::{is_pie_binary, get_reloc_model};
|
2018-08-30 12:24:41 +00:00
|
|
|
use common;
|
2018-10-23 15:01:35 +00:00
|
|
|
use LlvmCodegenBackend;
|
2017-06-29 14:52:43 +00:00
|
|
|
use rustc_demangle;
|
2014-08-11 17:33:58 +00:00
|
|
|
|
2017-10-17 20:08:13 +00:00
|
|
|
use std::ffi::{CString, CStr};
|
2018-01-10 16:58:39 +00:00
|
|
|
use std::fs;
|
|
|
|
use std::io::{self, Write};
|
2018-10-23 15:01:35 +00:00
|
|
|
use std::path::Path;
|
2014-08-11 17:33:58 +00:00
|
|
|
use std::str;
|
2017-07-24 12:21:28 +00:00
|
|
|
use std::sync::Arc;
|
2017-06-29 14:52:43 +00:00
|
|
|
use std::slice;
|
|
|
|
use libc::{c_uint, c_void, c_char, size_t};
|
2014-08-11 17:33:58 +00:00
|
|
|
|
2018-10-06 09:50:34 +00:00
|
|
|
pub const RELOC_MODEL_ARGS : [(&str, llvm::RelocMode); 7] = [
|
2016-08-06 05:50:48 +00:00
|
|
|
("pic", llvm::RelocMode::PIC),
|
|
|
|
("static", llvm::RelocMode::Static),
|
|
|
|
("default", llvm::RelocMode::Default),
|
|
|
|
("dynamic-no-pic", llvm::RelocMode::DynamicNoPic),
|
2017-04-26 00:05:51 +00:00
|
|
|
("ropi", llvm::RelocMode::ROPI),
|
|
|
|
("rwpi", llvm::RelocMode::RWPI),
|
|
|
|
("ropi-rwpi", llvm::RelocMode::ROPI_RWPI),
|
2016-07-24 07:27:23 +00:00
|
|
|
];
|
|
|
|
|
2018-01-23 01:01:36 +00:00
|
|
|
pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[
|
2016-08-06 05:50:48 +00:00
|
|
|
("small", llvm::CodeModel::Small),
|
|
|
|
("kernel", llvm::CodeModel::Kernel),
|
|
|
|
("medium", llvm::CodeModel::Medium),
|
|
|
|
("large", llvm::CodeModel::Large),
|
2016-07-24 07:27:23 +00:00
|
|
|
];
|
|
|
|
|
2018-10-06 09:50:34 +00:00
|
|
|
pub const TLS_MODEL_ARGS : [(&str, llvm::ThreadLocalMode); 4] = [
|
2017-10-31 18:24:04 +00:00
|
|
|
("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic),
|
|
|
|
("local-dynamic", llvm::ThreadLocalMode::LocalDynamic),
|
|
|
|
("initial-exec", llvm::ThreadLocalMode::InitialExec),
|
|
|
|
("local-exec", llvm::ThreadLocalMode::LocalExec),
|
|
|
|
];
|
|
|
|
|
2018-10-06 09:42:14 +00:00
|
|
|
pub fn llvm_err(handler: &errors::Handler, msg: &str) -> FatalError {
|
2015-10-23 05:07:19 +00:00
|
|
|
match llvm::last_error() {
|
2017-06-15 14:08:18 +00:00
|
|
|
Some(err) => handler.fatal(&format!("{}: {}", msg, err)),
|
|
|
|
None => handler.fatal(&msg),
|
2014-08-11 17:33:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn write_output_file(
|
2015-12-13 22:17:55 +00:00
|
|
|
handler: &errors::Handler,
|
2018-07-17 11:17:47 +00:00
|
|
|
target: &'ll llvm::TargetMachine,
|
|
|
|
pm: &llvm::PassManager<'ll>,
|
|
|
|
m: &'ll llvm::Module,
|
2014-08-11 17:33:58 +00:00
|
|
|
output: &Path,
|
2017-06-15 14:08:18 +00:00
|
|
|
file_type: llvm::FileType) -> Result<(), FatalError> {
|
2014-08-11 17:33:58 +00:00
|
|
|
unsafe {
|
2018-11-29 13:09:28 +00:00
|
|
|
let output_c = path_to_c_string(output);
|
2018-10-06 09:37:28 +00:00
|
|
|
let result = llvm::LLVMRustWriteOutputFile(target, pm, m, output_c.as_ptr(), file_type);
|
2016-08-01 21:16:16 +00:00
|
|
|
if result.into_result().is_err() {
|
2017-06-15 14:08:18 +00:00
|
|
|
let msg = format!("could not write output to {}", output.display());
|
2018-10-06 09:42:14 +00:00
|
|
|
Err(llvm_err(handler, &msg))
|
2017-06-15 14:08:18 +00:00
|
|
|
} else {
|
|
|
|
Ok(())
|
2016-07-07 18:57:09 +00:00
|
|
|
}
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
pub(crate) fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
match optimize {
|
2016-08-01 21:16:16 +00:00
|
|
|
config::OptLevel::No => llvm::CodeGenOptLevel::None,
|
|
|
|
config::OptLevel::Less => llvm::CodeGenOptLevel::Less,
|
|
|
|
config::OptLevel::Default => llvm::CodeGenOptLevel::Default,
|
|
|
|
config::OptLevel::Aggressive => llvm::CodeGenOptLevel::Aggressive,
|
|
|
|
_ => llvm::CodeGenOptLevel::Default,
|
2016-03-27 19:42:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
pub(crate) fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize {
|
2016-03-27 19:42:47 +00:00
|
|
|
match optimize {
|
|
|
|
config::OptLevel::Size => llvm::CodeGenOptSizeDefault,
|
|
|
|
config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive,
|
|
|
|
_ => llvm::CodeGenOptSizeNone,
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
}
|
|
|
|
}
|
2014-08-11 17:33:58 +00:00
|
|
|
|
2018-07-17 15:26:58 +00:00
|
|
|
pub fn create_target_machine(
|
|
|
|
sess: &Session,
|
|
|
|
find_features: bool,
|
|
|
|
) -> &'static mut llvm::TargetMachine {
|
2018-04-01 06:26:05 +00:00
|
|
|
target_machine_factory(sess, find_features)().unwrap_or_else(|err| {
|
2018-10-06 09:42:14 +00:00
|
|
|
llvm_err(sess.diagnostic(), &err).raise()
|
2017-07-23 15:14:38 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-04-01 06:26:05 +00:00
|
|
|
// If find_features is true this won't access `sess.crate_types` by assuming
|
|
|
|
// that `is_pie_binary` is false. When we discover LLVM target features
|
|
|
|
// `sess.crate_types` is uninitialized so we cannot access it.
|
|
|
|
pub fn target_machine_factory(sess: &Session, find_features: bool)
|
2018-06-27 14:57:25 +00:00
|
|
|
-> Arc<dyn Fn() -> Result<&'static mut llvm::TargetMachine, String> + Send + Sync>
|
2017-07-23 15:14:38 +00:00
|
|
|
{
|
2016-07-14 21:40:14 +00:00
|
|
|
let reloc_model = get_reloc_model(sess);
|
2014-08-11 17:33:58 +00:00
|
|
|
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
let opt_level = get_llvm_opt_level(sess.opts.optimize);
|
|
|
|
let use_softfp = sess.opts.cg.soft_float;
|
|
|
|
|
2014-07-23 18:56:36 +00:00
|
|
|
let ffunction_sections = sess.target.target.options.function_sections;
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
let fdata_sections = ffunction_sections;
|
|
|
|
|
2018-01-23 01:01:36 +00:00
|
|
|
let code_model_arg = sess.opts.cg.code_model.as_ref().or(
|
|
|
|
sess.target.target.options.code_model.as_ref(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let code_model = match code_model_arg {
|
|
|
|
Some(s) => {
|
|
|
|
match CODE_GEN_MODEL_ARGS.iter().find(|arg| arg.0 == s) {
|
|
|
|
Some(x) => x.1,
|
|
|
|
_ => {
|
|
|
|
sess.err(&format!("{:?} is not a valid code model",
|
|
|
|
code_model_arg));
|
|
|
|
sess.abort_if_errors();
|
|
|
|
bug!();
|
|
|
|
}
|
|
|
|
}
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
}
|
2018-01-23 01:01:36 +00:00
|
|
|
None => llvm::CodeModel::None,
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
};
|
|
|
|
|
2018-09-01 05:41:17 +00:00
|
|
|
let features = attributes::llvm_target_features(sess).collect::<Vec<_>>();
|
|
|
|
let mut singlethread = sess.target.target.options.singlethread;
|
|
|
|
|
|
|
|
// On the wasm target once the `atomics` feature is enabled that means that
|
|
|
|
// we're no longer single-threaded, or otherwise we don't want LLVM to
|
|
|
|
// lower atomic operations to single-threaded operations.
|
|
|
|
if singlethread &&
|
|
|
|
sess.target.target.llvm_target.contains("wasm32") &&
|
|
|
|
features.iter().any(|s| *s == "+atomics")
|
|
|
|
{
|
|
|
|
singlethread = false;
|
|
|
|
}
|
2017-10-23 03:01:00 +00:00
|
|
|
|
2018-08-07 14:04:34 +00:00
|
|
|
let triple = SmallCStr::new(&sess.target.target.llvm_target);
|
2018-08-23 18:03:22 +00:00
|
|
|
let cpu = SmallCStr::new(llvm_util::target_cpu(sess));
|
2018-09-01 05:41:17 +00:00
|
|
|
let features = features.join(",");
|
2018-04-23 22:17:07 +00:00
|
|
|
let features = CString::new(features).unwrap();
|
2018-04-01 06:26:05 +00:00
|
|
|
let is_pie_binary = !find_features && is_pie_binary(sess);
|
2017-11-11 15:08:00 +00:00
|
|
|
let trap_unreachable = sess.target.target.options.trap_unreachable;
|
2018-09-13 17:43:15 +00:00
|
|
|
let emit_stack_size_section = sess.opts.debugging_opts.emit_stack_sizes;
|
2017-07-23 15:14:38 +00:00
|
|
|
|
2018-08-12 17:59:18 +00:00
|
|
|
let asm_comments = sess.asm_comments();
|
|
|
|
|
2017-07-23 15:14:38 +00:00
|
|
|
Arc::new(move || {
|
|
|
|
let tm = unsafe {
|
|
|
|
llvm::LLVMRustCreateTargetMachine(
|
|
|
|
triple.as_ptr(), cpu.as_ptr(), features.as_ptr(),
|
|
|
|
code_model,
|
|
|
|
reloc_model,
|
|
|
|
opt_level,
|
|
|
|
use_softfp,
|
|
|
|
is_pie_binary,
|
|
|
|
ffunction_sections,
|
|
|
|
fdata_sections,
|
2017-11-11 15:08:00 +00:00
|
|
|
trap_unreachable,
|
2017-10-23 03:01:00 +00:00
|
|
|
singlethread,
|
2018-08-12 17:59:18 +00:00
|
|
|
asm_comments,
|
2018-09-13 17:43:15 +00:00
|
|
|
emit_stack_size_section,
|
2017-07-23 15:14:38 +00:00
|
|
|
)
|
|
|
|
};
|
2014-10-29 01:58:46 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
tm.ok_or_else(|| {
|
|
|
|
format!("Could not create LLVM TargetMachine for triple: {}",
|
|
|
|
triple.to_str().unwrap())
|
|
|
|
})
|
2017-07-23 15:14:38 +00:00
|
|
|
})
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
pub(crate) fn save_temp_bitcode(
|
|
|
|
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|
|
|
module: &ModuleCodegen<ModuleLlvm>,
|
|
|
|
name: &str
|
|
|
|
) {
|
|
|
|
if !cgcx.save_temps {
|
|
|
|
return
|
2017-09-14 03:26:39 +00:00
|
|
|
}
|
2018-10-23 15:01:35 +00:00
|
|
|
unsafe {
|
|
|
|
let ext = format!("{}.bc", name);
|
|
|
|
let cgu = Some(&module.name[..]);
|
|
|
|
let path = cgcx.output_filenames.temp_path_ext(&ext, cgu);
|
2018-11-29 13:09:28 +00:00
|
|
|
let cstr = path_to_c_string(&path);
|
2018-10-23 15:01:35 +00:00
|
|
|
let llmod = module.module_llvm.llmod();
|
|
|
|
llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr());
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 23:20:51 +00:00
|
|
|
pub struct DiagnosticHandlers<'a> {
|
2018-10-23 15:01:35 +00:00
|
|
|
data: *mut (&'a CodegenContext<LlvmCodegenBackend>, &'a Handler),
|
2018-06-27 14:57:25 +00:00
|
|
|
llcx: &'a llvm::Context,
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> DiagnosticHandlers<'a> {
|
2018-10-23 15:01:35 +00:00
|
|
|
pub fn new(cgcx: &'a CodegenContext<LlvmCodegenBackend>,
|
2018-07-17 23:20:51 +00:00
|
|
|
handler: &'a Handler,
|
|
|
|
llcx: &'a llvm::Context) -> Self {
|
2018-07-14 23:52:45 +00:00
|
|
|
let data = Box::into_raw(Box::new((cgcx, handler)));
|
2017-07-23 15:14:38 +00:00
|
|
|
unsafe {
|
2018-07-14 23:52:45 +00:00
|
|
|
llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, data as *mut _);
|
|
|
|
llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, data as *mut _);
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
2018-07-14 23:52:45 +00:00
|
|
|
DiagnosticHandlers { data, llcx }
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
2014-09-12 15:17:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-23 15:14:38 +00:00
|
|
|
impl<'a> Drop for DiagnosticHandlers<'a> {
|
|
|
|
fn drop(&mut self) {
|
2018-07-14 23:52:45 +00:00
|
|
|
use std::ptr::null_mut;
|
2017-07-23 15:14:38 +00:00
|
|
|
unsafe {
|
2018-07-14 23:52:45 +00:00
|
|
|
llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, null_mut());
|
|
|
|
llvm::LLVMContextSetDiagnosticHandler(self.llcx, diagnostic_handler, null_mut());
|
|
|
|
drop(Box::from_raw(self.data));
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-12 15:17:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext<LlvmCodegenBackend>,
|
2015-05-26 08:34:50 +00:00
|
|
|
msg: &'b str,
|
|
|
|
cookie: c_uint) {
|
2018-10-06 09:50:00 +00:00
|
|
|
cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_owned());
|
2014-09-27 08:33:36 +00:00
|
|
|
}
|
|
|
|
|
2018-07-13 10:59:41 +00:00
|
|
|
unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic,
|
2015-01-22 18:43:39 +00:00
|
|
|
user: *const c_void,
|
|
|
|
cookie: c_uint) {
|
2017-07-23 15:14:38 +00:00
|
|
|
if user.is_null() {
|
|
|
|
return
|
|
|
|
}
|
2018-10-23 15:01:35 +00:00
|
|
|
let (cgcx, _) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &Handler));
|
2015-01-22 18:43:39 +00:00
|
|
|
|
2016-08-01 23:35:09 +00:00
|
|
|
let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s))
|
2015-01-22 18:43:39 +00:00
|
|
|
.expect("non-UTF8 SMDiagnostic");
|
|
|
|
|
2017-03-24 08:31:26 +00:00
|
|
|
report_inline_asm(cgcx, &msg, cookie);
|
2015-01-22 18:43:39 +00:00
|
|
|
}
|
|
|
|
|
2018-07-13 10:59:41 +00:00
|
|
|
unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) {
|
2017-07-23 15:14:38 +00:00
|
|
|
if user.is_null() {
|
|
|
|
return
|
|
|
|
}
|
2018-10-23 15:01:35 +00:00
|
|
|
let (cgcx, diag_handler) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &Handler));
|
2014-09-12 15:17:58 +00:00
|
|
|
|
|
|
|
match llvm::diagnostic::Diagnostic::unpack(info) {
|
2015-01-22 18:43:39 +00:00
|
|
|
llvm::diagnostic::InlineAsm(inline) => {
|
|
|
|
report_inline_asm(cgcx,
|
2016-02-09 20:24:11 +00:00
|
|
|
&llvm::twine_to_string(inline.message),
|
2015-01-22 18:43:39 +00:00
|
|
|
inline.cookie);
|
|
|
|
}
|
|
|
|
|
2014-09-12 15:17:58 +00:00
|
|
|
llvm::diagnostic::Optimization(opt) => {
|
|
|
|
let enabled = match cgcx.remark {
|
2018-07-26 17:22:14 +00:00
|
|
|
Passes::All => true,
|
|
|
|
Passes::Some(ref v) => v.iter().any(|s| *s == opt.pass_name),
|
2014-09-12 15:17:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if enabled {
|
2017-07-24 11:54:18 +00:00
|
|
|
diag_handler.note_without_error(&format!("optimization {} for {} at {}:{}:{}: {}",
|
2015-12-20 21:00:43 +00:00
|
|
|
opt.kind.describe(),
|
2016-11-28 14:15:51 +00:00
|
|
|
opt.pass_name,
|
2017-07-21 12:29:23 +00:00
|
|
|
opt.filename,
|
|
|
|
opt.line,
|
|
|
|
opt.column,
|
2016-11-24 16:33:47 +00:00
|
|
|
opt.message));
|
2014-09-12 15:17:58 +00:00
|
|
|
}
|
2014-08-11 17:33:58 +00:00
|
|
|
}
|
2018-07-17 23:20:51 +00:00
|
|
|
llvm::diagnostic::PGO(diagnostic_ref) |
|
|
|
|
llvm::diagnostic::Linker(diagnostic_ref) => {
|
2018-03-12 17:11:59 +00:00
|
|
|
let msg = llvm::build_string(|s| {
|
|
|
|
llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s)
|
2018-07-17 23:20:51 +00:00
|
|
|
}).expect("non-UTF8 diagnostic");
|
2018-03-12 20:18:01 +00:00
|
|
|
diag_handler.warn(&msg);
|
2018-03-12 17:11:59 +00:00
|
|
|
}
|
|
|
|
llvm::diagnostic::UnknownDiagnostic(..) => {},
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unsafe due to LLVM calls.
|
2018-10-23 15:01:35 +00:00
|
|
|
pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
2017-07-23 15:14:38 +00:00
|
|
|
diag_handler: &Handler,
|
2018-09-25 15:52:03 +00:00
|
|
|
module: &ModuleCodegen<ModuleLlvm>,
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
config: &ModuleConfig,
|
|
|
|
timeline: &mut Timeline)
|
2017-07-23 15:14:38 +00:00
|
|
|
-> Result<(), FatalError>
|
2017-06-15 14:08:18 +00:00
|
|
|
{
|
2018-08-20 15:13:01 +00:00
|
|
|
let llmod = module.module_llvm.llmod();
|
|
|
|
let llcx = &*module.module_llvm.llcx;
|
|
|
|
let tm = &*module.module_llvm.tm;
|
2017-07-23 15:14:38 +00:00
|
|
|
let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
|
2014-09-12 15:17:58 +00:00
|
|
|
|
2018-05-08 13:10:16 +00:00
|
|
|
let module_name = module.name.clone();
|
2017-07-25 15:26:24 +00:00
|
|
|
let module_name = Some(&module_name[..]);
|
2016-05-14 00:48:32 +00:00
|
|
|
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
if config.emit_no_opt_bc {
|
2017-09-14 03:26:39 +00:00
|
|
|
let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name);
|
2018-11-29 13:09:28 +00:00
|
|
|
let out = path_to_c_string(&out);
|
2014-11-25 21:28:35 +00:00
|
|
|
llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr());
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 23:22:51 +00:00
|
|
|
if config.opt_level.is_some() {
|
|
|
|
// Create the two optimizing pass managers. These mirror what clang
|
|
|
|
// does, and are by populated by LLVM's default PassManagerBuilder.
|
|
|
|
// Each manager has a different set of passes, but they also share
|
|
|
|
// some common passes.
|
|
|
|
let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod);
|
|
|
|
let mpm = llvm::LLVMCreatePassManager();
|
|
|
|
|
2018-07-17 11:17:47 +00:00
|
|
|
{
|
|
|
|
// If we're verifying or linting, add them to the function pass
|
|
|
|
// manager.
|
|
|
|
let addpass = |pass_name: &str| {
|
2018-08-07 14:04:34 +00:00
|
|
|
let pass_name = SmallCStr::new(pass_name);
|
2018-07-17 11:17:47 +00:00
|
|
|
let pass = match llvm::LLVMRustFindAndCreatePass(pass_name.as_ptr()) {
|
|
|
|
Some(pass) => pass,
|
|
|
|
None => return false,
|
|
|
|
};
|
|
|
|
let pass_manager = match llvm::LLVMRustPassKind(pass) {
|
|
|
|
llvm::PassKind::Function => &*fpm,
|
|
|
|
llvm::PassKind::Module => &*mpm,
|
|
|
|
llvm::PassKind::Other => {
|
|
|
|
diag_handler.err("Encountered LLVM pass kind we can't handle");
|
|
|
|
return true
|
|
|
|
},
|
|
|
|
};
|
|
|
|
llvm::LLVMRustAddPass(pass_manager, pass);
|
|
|
|
true
|
2016-01-25 01:22:24 +00:00
|
|
|
};
|
2014-08-11 17:33:58 +00:00
|
|
|
|
2018-07-17 11:17:47 +00:00
|
|
|
if config.verify_llvm_ir { assert!(addpass("verify")); }
|
2018-05-30 20:46:56 +00:00
|
|
|
|
|
|
|
// Some options cause LLVM bitcode to be emitted, which uses ThinLTOBuffers, so we need
|
|
|
|
// to make sure we run LLVM's NameAnonGlobals pass when emitting bitcode; otherwise
|
|
|
|
// we'll get errors in LLVM.
|
2018-11-02 12:22:48 +00:00
|
|
|
let using_thin_buffers = config.bitcode_needed();
|
2018-05-30 20:46:56 +00:00
|
|
|
let mut have_name_anon_globals_pass = false;
|
2018-07-17 11:17:47 +00:00
|
|
|
if !config.no_prepopulate_passes {
|
|
|
|
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
|
|
|
|
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
|
2018-10-23 15:01:35 +00:00
|
|
|
let opt_level = config.opt_level.map(get_llvm_opt_level)
|
|
|
|
.unwrap_or(llvm::CodeGenOptLevel::None);
|
2018-08-03 15:04:53 +00:00
|
|
|
let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal ||
|
|
|
|
(cgcx.lto != Lto::Fat && cgcx.opts.debugging_opts.cross_lang_lto.enabled());
|
2018-12-02 13:41:39 +00:00
|
|
|
with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| {
|
|
|
|
llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm);
|
|
|
|
llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm);
|
|
|
|
});
|
|
|
|
|
2018-05-30 20:46:56 +00:00
|
|
|
have_name_anon_globals_pass = have_name_anon_globals_pass || prepare_for_thin_lto;
|
|
|
|
if using_thin_buffers && !prepare_for_thin_lto {
|
|
|
|
assert!(addpass("name-anon-globals"));
|
|
|
|
have_name_anon_globals_pass = true;
|
|
|
|
}
|
2018-07-17 11:17:47 +00:00
|
|
|
}
|
2014-08-11 17:33:58 +00:00
|
|
|
|
2018-07-17 11:17:47 +00:00
|
|
|
for pass in &config.passes {
|
|
|
|
if !addpass(pass) {
|
2018-10-06 09:37:28 +00:00
|
|
|
diag_handler.warn(&format!("unknown pass `{}`, ignoring", pass));
|
2018-07-17 11:17:47 +00:00
|
|
|
}
|
2018-05-30 20:46:56 +00:00
|
|
|
if pass == "name-anon-globals" {
|
|
|
|
have_name_anon_globals_pass = true;
|
|
|
|
}
|
2014-08-11 17:33:58 +00:00
|
|
|
}
|
|
|
|
|
2018-07-17 11:17:47 +00:00
|
|
|
for pass in &cgcx.plugin_passes {
|
|
|
|
if !addpass(pass) {
|
|
|
|
diag_handler.err(&format!("a plugin asked for LLVM pass \
|
2018-10-06 09:37:28 +00:00
|
|
|
`{}` but LLVM does not \
|
|
|
|
recognize it", pass));
|
2018-07-17 11:17:47 +00:00
|
|
|
}
|
2018-05-30 20:46:56 +00:00
|
|
|
if pass == "name-anon-globals" {
|
|
|
|
have_name_anon_globals_pass = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if using_thin_buffers && !have_name_anon_globals_pass {
|
|
|
|
// As described above, this will probably cause an error in LLVM
|
|
|
|
if config.no_prepopulate_passes {
|
|
|
|
diag_handler.err("The current compilation is going to use thin LTO buffers \
|
2018-10-06 09:37:28 +00:00
|
|
|
without running LLVM's NameAnonGlobals pass. \
|
|
|
|
This will likely cause errors in LLVM. Consider adding \
|
|
|
|
-C passes=name-anon-globals to the compiler command line.");
|
2018-05-30 20:46:56 +00:00
|
|
|
} else {
|
|
|
|
bug!("We are using thin LTO buffers without running the NameAnonGlobals pass. \
|
2018-10-06 09:37:28 +00:00
|
|
|
This will likely cause errors in LLVM and should never happen.");
|
2018-05-30 20:46:56 +00:00
|
|
|
}
|
2015-04-08 19:52:58 +00:00
|
|
|
}
|
2015-07-22 23:22:51 +00:00
|
|
|
}
|
2015-04-08 19:52:58 +00:00
|
|
|
|
2017-07-24 11:54:18 +00:00
|
|
|
diag_handler.abort_if_errors();
|
2015-04-08 19:52:58 +00:00
|
|
|
|
2015-07-22 23:22:51 +00:00
|
|
|
// Finally, run the actual optimization passes
|
2017-12-03 13:21:23 +00:00
|
|
|
time_ext(config.time_passes,
|
|
|
|
None,
|
|
|
|
&format!("llvm function passes [{}]", module_name.unwrap()),
|
|
|
|
|| {
|
|
|
|
llvm::LLVMRustRunFunctionPassManager(fpm, llmod)
|
|
|
|
});
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
timeline.record("fpm");
|
2017-12-03 13:21:23 +00:00
|
|
|
time_ext(config.time_passes,
|
|
|
|
None,
|
|
|
|
&format!("llvm module passes [{}]", module_name.unwrap()),
|
|
|
|
|| {
|
2018-10-23 07:59:14 +00:00
|
|
|
llvm::LLVMRunPassManager(mpm, llmod)
|
2017-12-03 13:21:23 +00:00
|
|
|
});
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
|
2015-07-22 23:22:51 +00:00
|
|
|
// Deallocate managers that we're now done with
|
|
|
|
llvm::LLVMDisposePassManager(fpm);
|
|
|
|
llvm::LLVMDisposePassManager(mpm);
|
2017-07-23 15:14:38 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
|
2018-10-23 15:01:35 +00:00
|
|
|
pub(crate) unsafe fn codegen(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
2017-07-23 15:14:38 +00:00
|
|
|
diag_handler: &Handler,
|
2018-09-25 15:52:03 +00:00
|
|
|
module: ModuleCodegen<ModuleLlvm>,
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
config: &ModuleConfig,
|
|
|
|
timeline: &mut Timeline)
|
2017-07-23 15:14:38 +00:00
|
|
|
-> Result<CompiledModule, FatalError>
|
|
|
|
{
|
rustc: Implement ThinLTO
This commit is an implementation of LLVM's ThinLTO for consumption in rustc
itself. Currently today LTO works by merging all relevant LLVM modules into one
and then running optimization passes. "Thin" LTO operates differently by having
more sharded work and allowing parallelism opportunities between optimizing
codegen units. Further down the road Thin LTO also allows *incremental* LTO
which should enable even faster release builds without compromising on the
performance we have today.
This commit uses a `-Z thinlto` flag to gate whether ThinLTO is enabled. It then
also implements two forms of ThinLTO:
* In one mode we'll *only* perform ThinLTO over the codegen units produced in a
single compilation. That is, we won't load upstream rlibs, but we'll instead
just perform ThinLTO amongst all codegen units produced by the compiler for
the local crate. This is intended to emulate a desired end point where we have
codegen units turned on by default for all crates and ThinLTO allows us to do
this without performance loss.
* In anther mode, like full LTO today, we'll optimize all upstream dependencies
in "thin" mode. Unlike today, however, this LTO step is fully parallelized so
should finish much more quickly.
There's a good bit of comments about what the implementation is doing and where
it came from, but the tl;dr; is that currently most of the support here is
copied from upstream LLVM. This code duplication is done for a number of
reasons:
* Controlling parallelism means we can use the existing jobserver support to
avoid overloading machines.
* We will likely want a slightly different form of incremental caching which
integrates with our own incremental strategy, but this is yet to be
determined.
* This buys us some flexibility about when/where we run ThinLTO, as well as
having it tailored to fit our needs for the time being.
* Finally this allows us to reuse some artifacts such as our `TargetMachine`
creation, where all our options we used today aren't necessarily supported by
upstream LLVM yet.
My hope is that we can get some experience with this copy/paste in tree and then
eventually upstream some work to LLVM itself to avoid the duplication while
still ensuring our needs are met. Otherwise I fear that maintaining these
bindings may be quite costly over the years with LLVM updates!
2017-07-23 15:14:38 +00:00
|
|
|
timeline.record("codegen");
|
2014-12-09 18:44:51 +00:00
|
|
|
{
|
2018-08-20 15:13:01 +00:00
|
|
|
let llmod = module.module_llvm.llmod();
|
|
|
|
let llcx = &*module.module_llvm.llcx;
|
|
|
|
let tm = &*module.module_llvm.tm;
|
2018-06-27 14:57:25 +00:00
|
|
|
let module_name = module.name.clone();
|
|
|
|
let module_name = Some(&module_name[..]);
|
|
|
|
let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
|
2017-10-20 01:44:33 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if cgcx.msvc_imps_needed {
|
|
|
|
create_msvc_imps(cgcx, llcx, llmod);
|
2017-10-20 01:44:33 +00:00
|
|
|
}
|
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
// A codegen-specific pass manager is used to generate object
|
|
|
|
// files for an LLVM module.
|
|
|
|
//
|
|
|
|
// Apparently each of these pass managers is a one-shot kind of
|
|
|
|
// thing, so we create a new one for each type of output. The
|
|
|
|
// pass manager passed to the closure should be ensured to not
|
|
|
|
// escape the closure itself, and the manager should only be
|
|
|
|
// used once.
|
2018-07-17 11:17:47 +00:00
|
|
|
unsafe fn with_codegen<'ll, F, R>(tm: &'ll llvm::TargetMachine,
|
2018-10-06 09:37:28 +00:00
|
|
|
llmod: &'ll llvm::Module,
|
|
|
|
no_builtins: bool,
|
|
|
|
f: F) -> R
|
2018-07-17 11:17:47 +00:00
|
|
|
where F: FnOnce(&'ll mut PassManager<'ll>) -> R,
|
2018-06-27 14:57:25 +00:00
|
|
|
{
|
|
|
|
let cpm = llvm::LLVMCreatePassManager();
|
|
|
|
llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod);
|
|
|
|
llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins);
|
|
|
|
f(cpm)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we don't have the integrated assembler, then we need to emit asm
|
|
|
|
// from LLVM and use `gcc` to create the object file.
|
|
|
|
let asm_to_obj = config.emit_obj && config.no_integrated_as;
|
|
|
|
|
|
|
|
// Change what we write and cleanup based on whether obj files are
|
|
|
|
// just llvm bitcode. In that case write bitcode, and possibly
|
|
|
|
// delete the bitcode if it wasn't requested. Don't generate the
|
|
|
|
// machine code, instead copy the .o file from the .bc
|
|
|
|
let write_bc = config.emit_bc || config.obj_is_bitcode;
|
|
|
|
let rm_bc = !config.emit_bc && config.obj_is_bitcode;
|
|
|
|
let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm_to_obj;
|
|
|
|
let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode;
|
|
|
|
|
|
|
|
let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name);
|
|
|
|
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
|
|
|
|
|
|
|
|
|
|
|
|
if write_bc || config.emit_bc_compressed || config.embed_bitcode {
|
2018-11-01 20:06:50 +00:00
|
|
|
let thin = ThinBuffer::new(llmod);
|
|
|
|
let data = thin.data();
|
2018-06-27 14:57:25 +00:00
|
|
|
timeline.record("make-bc");
|
2018-03-09 15:25:54 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if write_bc {
|
|
|
|
if let Err(e) = fs::write(&bc_out, data) {
|
|
|
|
diag_handler.err(&format!("failed to write bytecode: {}", e));
|
|
|
|
}
|
|
|
|
timeline.record("write-bc");
|
2017-10-20 01:44:33 +00:00
|
|
|
}
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if config.embed_bitcode {
|
|
|
|
embed_bitcode(cgcx, llcx, llmod, Some(data));
|
|
|
|
timeline.record("embed-bc");
|
|
|
|
}
|
2017-06-29 14:52:43 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if config.emit_bc_compressed {
|
|
|
|
let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
|
2018-08-14 15:55:22 +00:00
|
|
|
let data = bytecode::encode(&module.name, data);
|
2018-06-27 14:57:25 +00:00
|
|
|
if let Err(e) = fs::write(&dst, data) {
|
|
|
|
diag_handler.err(&format!("failed to write bytecode: {}", e));
|
|
|
|
}
|
|
|
|
timeline.record("compress-bc");
|
|
|
|
}
|
|
|
|
} else if config.embed_bitcode_marker {
|
|
|
|
embed_bitcode(cgcx, llcx, llmod, None);
|
|
|
|
}
|
|
|
|
|
|
|
|
time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()),
|
|
|
|
|| -> Result<(), FatalError> {
|
|
|
|
if config.emit_ir {
|
|
|
|
let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name);
|
2018-11-29 13:09:28 +00:00
|
|
|
let out = path_to_c_string(&out);
|
2018-06-27 14:57:25 +00:00
|
|
|
|
|
|
|
extern "C" fn demangle_callback(input_ptr: *const c_char,
|
|
|
|
input_len: size_t,
|
|
|
|
output_ptr: *mut c_char,
|
|
|
|
output_len: size_t) -> size_t {
|
|
|
|
let input = unsafe {
|
|
|
|
slice::from_raw_parts(input_ptr as *const u8, input_len as usize)
|
|
|
|
};
|
2017-06-29 14:52:43 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
let input = match str::from_utf8(input) {
|
|
|
|
Ok(s) => s,
|
|
|
|
Err(_) => return 0,
|
|
|
|
};
|
2017-06-29 14:52:43 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
let output = unsafe {
|
|
|
|
slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize)
|
|
|
|
};
|
|
|
|
let mut cursor = io::Cursor::new(output);
|
|
|
|
|
|
|
|
let demangled = match rustc_demangle::try_demangle(input) {
|
|
|
|
Ok(d) => d,
|
|
|
|
Err(_) => return 0,
|
|
|
|
};
|
2017-06-29 14:52:43 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if let Err(_) = write!(cursor, "{:#}", demangled) {
|
|
|
|
// Possible only if provided buffer is not big enough
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cursor.position() as size_t
|
2017-06-29 14:52:43 +00:00
|
|
|
}
|
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
with_codegen(tm, llmod, config.no_builtins, |cpm| {
|
|
|
|
llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback);
|
|
|
|
llvm::LLVMDisposePassManager(cpm);
|
|
|
|
});
|
|
|
|
timeline.record("ir");
|
2017-06-29 14:52:43 +00:00
|
|
|
}
|
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if config.emit_asm || asm_to_obj {
|
|
|
|
let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
|
2014-08-11 17:33:58 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
// We can't use the same module for asm and binary output, because that triggers
|
|
|
|
// various errors like invalid IR or broken binaries, so we might have to clone the
|
|
|
|
// module to produce the asm output
|
|
|
|
let llmod = if config.emit_obj {
|
|
|
|
llvm::LLVMCloneModule(llmod)
|
|
|
|
} else {
|
|
|
|
llmod
|
|
|
|
};
|
|
|
|
with_codegen(tm, llmod, config.no_builtins, |cpm| {
|
|
|
|
write_output_file(diag_handler, tm, cpm, llmod, &path,
|
2018-10-06 09:37:28 +00:00
|
|
|
llvm::FileType::AssemblyFile)
|
2018-06-27 14:57:25 +00:00
|
|
|
})?;
|
|
|
|
timeline.record("asm");
|
2015-12-18 00:24:40 +00:00
|
|
|
}
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if write_obj {
|
|
|
|
with_codegen(tm, llmod, config.no_builtins, |cpm| {
|
|
|
|
write_output_file(diag_handler, tm, cpm, llmod, &obj_out,
|
2018-10-06 09:37:28 +00:00
|
|
|
llvm::FileType::ObjectFile)
|
2018-06-27 14:57:25 +00:00
|
|
|
})?;
|
|
|
|
timeline.record("obj");
|
|
|
|
} else if asm_to_obj {
|
|
|
|
let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name);
|
|
|
|
run_assembler(cgcx, diag_handler, &assembly, &obj_out);
|
|
|
|
timeline.record("asm_to_obj");
|
|
|
|
|
|
|
|
if !config.emit_asm && !cgcx.save_temps {
|
|
|
|
drop(fs::remove_file(&assembly));
|
|
|
|
}
|
2018-01-15 13:42:44 +00:00
|
|
|
}
|
2017-06-15 14:08:18 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
Ok(())
|
|
|
|
})?;
|
run optimization and codegen on worker threads
Refactor the code in `llvm::back` that invokes LLVM optimization and codegen
passes so that it can be called from worker threads. (Previously, it used
`&Session` extensively, and `Session` is not `Share`.) The new code can handle
multiple compilation units, by compiling each unit to `crate.0.o`, `crate.1.o`,
etc., and linking together all the `crate.N.o` files into a single `crate.o`
using `ld -r`. The later linking steps can then be run unchanged.
The new code preserves the behavior of `--emit`/`-o` when building a single
compilation unit. With multiple compilation units, the `--emit=asm/ir/bc`
options produce multiple files, so combinations like `--emit=ir -o foo.ll` will
not actually produce `foo.ll` (they instead produce several `foo.N.ll` files).
The new code supports `-Z lto` only when using a single compilation unit.
Compiling with multiple compilation units and `-Z lto` will produce an error.
(I can't think of any good reason to do such a thing.) Linking with `-Z lto`
against a library that was built as multiple compilation units will also fail,
because the rlib does not contain a `crate.bytecode.deflate` file. This could
be supported in the future by linking together the `crate.N.bc` files produced
when compiling the library into a single `crate.bc`, or by making the LTO code
support multiple `crate.N.bytecode.deflate` files.
2014-07-17 17:52:52 +00:00
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if copy_bc_to_obj {
|
|
|
|
debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out);
|
|
|
|
if let Err(e) = link_or_copy(&bc_out, &obj_out) {
|
|
|
|
diag_handler.err(&format!("failed to copy bitcode to object file: {}", e));
|
|
|
|
}
|
2015-11-27 19:44:33 +00:00
|
|
|
}
|
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
if rm_bc {
|
|
|
|
debug!("removing_bitcode {:?}", bc_out);
|
|
|
|
if let Err(e) = fs::remove_file(&bc_out) {
|
|
|
|
diag_handler.err(&format!("failed to remove bitcode: {}", e));
|
|
|
|
}
|
2015-11-27 19:44:33 +00:00
|
|
|
}
|
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
drop(handlers);
|
|
|
|
}
|
2018-05-08 13:10:16 +00:00
|
|
|
Ok(module.into_compiled_module(config.emit_obj,
|
2017-07-23 15:14:38 +00:00
|
|
|
config.emit_bc,
|
2017-10-20 01:44:33 +00:00
|
|
|
config.emit_bc_compressed,
|
2017-07-23 15:14:38 +00:00
|
|
|
&cgcx.output_filenames))
|
2016-06-30 20:32:13 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:25:54 +00:00
|
|
|
/// Embed the bitcode of an LLVM module in the LLVM module itself.
|
|
|
|
///
|
|
|
|
/// This is done primarily for iOS where it appears to be standard to compile C
|
|
|
|
/// code at least with `-fembed-bitcode` which creates two sections in the
|
|
|
|
/// executable:
|
|
|
|
///
|
|
|
|
/// * __LLVM,__bitcode
|
|
|
|
/// * __LLVM,__cmdline
|
|
|
|
///
|
|
|
|
/// It appears *both* of these sections are necessary to get the linker to
|
|
|
|
/// recognize what's going on. For us though we just always throw in an empty
|
|
|
|
/// cmdline section.
|
|
|
|
///
|
|
|
|
/// Furthermore debug/O1 builds don't actually embed bitcode but rather just
|
|
|
|
/// embed an empty section.
|
|
|
|
///
|
|
|
|
/// Basically all of this is us attempting to follow in the footsteps of clang
|
|
|
|
/// on iOS. See #35968 for lots more info.
|
2018-10-23 15:01:35 +00:00
|
|
|
unsafe fn embed_bitcode(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
2018-06-27 14:57:25 +00:00
|
|
|
llcx: &llvm::Context,
|
|
|
|
llmod: &llvm::Module,
|
2018-03-09 15:25:54 +00:00
|
|
|
bitcode: Option<&[u8]>) {
|
2018-09-06 21:44:51 +00:00
|
|
|
let llconst = common::bytes_in_context(llcx, bitcode.unwrap_or(&[]));
|
2018-03-09 15:25:54 +00:00
|
|
|
let llglobal = llvm::LLVMAddGlobal(
|
|
|
|
llmod,
|
2018-09-06 21:44:51 +00:00
|
|
|
common::val_ty(llconst),
|
2018-03-09 15:25:54 +00:00
|
|
|
"rustc.embedded.module\0".as_ptr() as *const _,
|
|
|
|
);
|
|
|
|
llvm::LLVMSetInitializer(llglobal, llconst);
|
2018-04-17 11:15:27 +00:00
|
|
|
|
|
|
|
let is_apple = cgcx.opts.target_triple.triple().contains("-ios") ||
|
|
|
|
cgcx.opts.target_triple.triple().contains("-darwin");
|
|
|
|
|
|
|
|
let section = if is_apple {
|
2018-03-09 15:25:54 +00:00
|
|
|
"__LLVM,__bitcode\0"
|
|
|
|
} else {
|
|
|
|
".llvmbc\0"
|
|
|
|
};
|
|
|
|
llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
|
|
|
|
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
2018-04-16 12:27:37 +00:00
|
|
|
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
|
2018-03-09 15:25:54 +00:00
|
|
|
|
2018-09-06 21:44:51 +00:00
|
|
|
let llconst = common::bytes_in_context(llcx, &[]);
|
2018-03-09 15:25:54 +00:00
|
|
|
let llglobal = llvm::LLVMAddGlobal(
|
|
|
|
llmod,
|
2018-09-06 21:44:51 +00:00
|
|
|
common::val_ty(llconst),
|
2018-03-09 15:25:54 +00:00
|
|
|
"rustc.embedded.cmdline\0".as_ptr() as *const _,
|
|
|
|
);
|
|
|
|
llvm::LLVMSetInitializer(llglobal, llconst);
|
2018-04-17 11:15:27 +00:00
|
|
|
let section = if is_apple {
|
2018-03-09 15:25:54 +00:00
|
|
|
"__LLVM,__cmdline\0"
|
|
|
|
} else {
|
|
|
|
".llvmcmd\0"
|
|
|
|
};
|
|
|
|
llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
|
|
|
|
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
|
|
|
}
|
|
|
|
|
2018-06-27 14:57:25 +00:00
|
|
|
pub unsafe fn with_llvm_pmb(llmod: &llvm::Module,
|
2015-07-22 23:22:51 +00:00
|
|
|
config: &ModuleConfig,
|
2017-10-11 18:19:59 +00:00
|
|
|
opt_level: llvm::CodeGenOptLevel,
|
2018-05-12 12:07:20 +00:00
|
|
|
prepare_for_thin_lto: bool,
|
2018-07-12 13:24:09 +00:00
|
|
|
f: &mut dyn FnMut(&llvm::PassManagerBuilder)) {
|
2018-02-19 00:57:12 +00:00
|
|
|
use std::ptr;
|
|
|
|
|
2014-08-11 17:33:58 +00:00
|
|
|
// Create the PassManagerBuilder for LLVM. We configure it with
|
|
|
|
// reasonable defaults and prepare it to actually populate the pass
|
|
|
|
// manager.
|
|
|
|
let builder = llvm::LLVMPassManagerBuilderCreate();
|
2018-10-23 15:01:35 +00:00
|
|
|
let opt_size = config.opt_size.map(get_llvm_opt_size).unwrap_or(llvm::CodeGenOptSizeNone);
|
2015-11-20 00:07:09 +00:00
|
|
|
let inline_threshold = config.inline_threshold;
|
rustc: Update LLVM
This commit updates the LLVM submodule in use to the current HEAD of the LLVM
repository. This is primarily being done to start picking up unwinding support
for MSVC, which is currently unimplemented in the revision of LLVM we are using.
Along the way a few changes had to be made:
* As usual, lots of C++ debuginfo bindings in LLVM changed, so there were some
significant changes to our RustWrapper.cpp
* As usual, some pass management changed in LLVM, so clang was re-scrutinized to
ensure that we're doing the same thing as clang.
* Some optimization options are now passed directly into the
`PassManagerBuilder` instead of through CLI switches to LLVM.
* The `NoFramePointerElim` option was removed from LLVM, favoring instead the
`no-frame-pointer-elim` function attribute instead.
Additionally, LLVM has picked up some new optimizations which required fixing an
existing soundness hole in the IR we generate. It appears that the current LLVM
we use does not expose this hole. When an enum is moved, the previous slot in
memory is overwritten with a bit pattern corresponding to "dropped". When the
drop glue for this slot is run, however, the switch on the discriminant can
often start executing the `unreachable` block of the switch due to the
discriminant now being outside the normal range. This was patched over locally
for now by having the `unreachable` block just change to a `ret void`.
2015-05-14 19:10:43 +00:00
|
|
|
|
2018-02-19 00:57:12 +00:00
|
|
|
let pgo_gen_path = config.pgo_gen.as_ref().map(|s| {
|
|
|
|
let s = if s.is_empty() { "default_%m.profraw" } else { s };
|
|
|
|
CString::new(s.as_bytes()).unwrap()
|
|
|
|
});
|
|
|
|
|
|
|
|
let pgo_use_path = if config.pgo_use.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(CString::new(config.pgo_use.as_bytes()).unwrap())
|
|
|
|
};
|
|
|
|
|
|
|
|
llvm::LLVMRustConfigurePassManagerBuilder(
|
|
|
|
builder,
|
|
|
|
opt_level,
|
|
|
|
config.merge_functions,
|
|
|
|
config.vectorize_slp,
|
|
|
|
config.vectorize_loop,
|
2018-05-12 12:07:20 +00:00
|
|
|
prepare_for_thin_lto,
|
2018-02-19 00:57:12 +00:00
|
|
|
pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
|
|
|
|
pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
|
|
|
|
);
|
|
|
|
|
2016-03-27 19:42:47 +00:00
|
|
|
llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32);
|
|
|
|
|
|
|
|
if opt_size != llvm::CodeGenOptSizeNone {
|
|
|
|
llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1);
|
|
|
|
}
|
rustc: Update LLVM
This commit updates the LLVM submodule in use to the current HEAD of the LLVM
repository. This is primarily being done to start picking up unwinding support
for MSVC, which is currently unimplemented in the revision of LLVM we are using.
Along the way a few changes had to be made:
* As usual, lots of C++ debuginfo bindings in LLVM changed, so there were some
significant changes to our RustWrapper.cpp
* As usual, some pass management changed in LLVM, so clang was re-scrutinized to
ensure that we're doing the same thing as clang.
* Some optimization options are now passed directly into the
`PassManagerBuilder` instead of through CLI switches to LLVM.
* The `NoFramePointerElim` option was removed from LLVM, favoring instead the
`no-frame-pointer-elim` function attribute instead.
Additionally, LLVM has picked up some new optimizations which required fixing an
existing soundness hole in the IR we generate. It appears that the current LLVM
we use does not expose this hole. When an enum is moved, the previous slot in
memory is overwritten with a bit pattern corresponding to "dropped". When the
drop glue for this slot is run, however, the switch on the discriminant can
often start executing the `unreachable` block of the switch due to the
discriminant now being outside the normal range. This was patched over locally
for now by having the `unreachable` block just change to a `ret void`.
2015-05-14 19:10:43 +00:00
|
|
|
|
|
|
|
llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins);
|
|
|
|
|
|
|
|
// Here we match what clang does (kinda). For O0 we only inline
|
|
|
|
// always-inline functions (but don't add lifetime intrinsics), at O1 we
|
|
|
|
// inline with lifetime intrinsics, and O2+ we add an inliner with a
|
|
|
|
// thresholds copied from clang.
|
2016-03-27 19:42:47 +00:00
|
|
|
match (opt_level, opt_size, inline_threshold) {
|
2016-08-26 16:23:42 +00:00
|
|
|
(.., Some(t)) => {
|
2015-11-20 00:07:09 +00:00
|
|
|
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32);
|
|
|
|
}
|
2016-08-26 16:23:42 +00:00
|
|
|
(llvm::CodeGenOptLevel::Aggressive, ..) => {
|
2016-03-27 19:42:47 +00:00
|
|
|
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275);
|
|
|
|
}
|
|
|
|
(_, llvm::CodeGenOptSizeDefault, _) => {
|
|
|
|
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75);
|
|
|
|
}
|
|
|
|
(_, llvm::CodeGenOptSizeAggressive, _) => {
|
|
|
|
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25);
|
|
|
|
}
|
2016-08-26 16:23:42 +00:00
|
|
|
(llvm::CodeGenOptLevel::None, ..) => {
|
2014-08-11 17:33:58 +00:00
|
|
|
llvm::LLVMRustAddAlwaysInlinePass(builder, false);
|
|
|
|
}
|
2016-08-26 16:23:42 +00:00
|
|
|
(llvm::CodeGenOptLevel::Less, ..) => {
|
2014-08-11 17:33:58 +00:00
|
|
|
llvm::LLVMRustAddAlwaysInlinePass(builder, true);
|
|
|
|
}
|
2016-08-26 16:23:42 +00:00
|
|
|
(llvm::CodeGenOptLevel::Default, ..) => {
|
rustc: Update LLVM
This commit updates the LLVM submodule in use to the current HEAD of the LLVM
repository. This is primarily being done to start picking up unwinding support
for MSVC, which is currently unimplemented in the revision of LLVM we are using.
Along the way a few changes had to be made:
* As usual, lots of C++ debuginfo bindings in LLVM changed, so there were some
significant changes to our RustWrapper.cpp
* As usual, some pass management changed in LLVM, so clang was re-scrutinized to
ensure that we're doing the same thing as clang.
* Some optimization options are now passed directly into the
`PassManagerBuilder` instead of through CLI switches to LLVM.
* The `NoFramePointerElim` option was removed from LLVM, favoring instead the
`no-frame-pointer-elim` function attribute instead.
Additionally, LLVM has picked up some new optimizations which required fixing an
existing soundness hole in the IR we generate. It appears that the current LLVM
we use does not expose this hole. When an enum is moved, the previous slot in
memory is overwritten with a bit pattern corresponding to "dropped". When the
drop glue for this slot is run, however, the switch on the discriminant can
often start executing the `unreachable` block of the switch due to the
discriminant now being outside the normal range. This was patched over locally
for now by having the `unreachable` block just change to a `ret void`.
2015-05-14 19:10:43 +00:00
|
|
|
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225);
|
2014-08-11 17:33:58 +00:00
|
|
|
}
|
2016-08-26 16:23:42 +00:00
|
|
|
(llvm::CodeGenOptLevel::Other, ..) => {
|
2016-08-01 21:16:16 +00:00
|
|
|
bug!("CodeGenOptLevel::Other selected")
|
|
|
|
}
|
2014-08-11 17:33:58 +00:00
|
|
|
}
|
|
|
|
|
2015-07-22 23:22:51 +00:00
|
|
|
f(builder);
|
2014-08-11 17:33:58 +00:00
|
|
|
llvm::LLVMPassManagerBuilderDispose(builder);
|
|
|
|
}
|
2017-07-21 15:15:18 +00:00
|
|
|
|
2017-10-17 20:08:13 +00:00
|
|
|
// Create a `__imp_<symbol> = &symbol` global for every public static `symbol`.
|
|
|
|
// This is required to satisfy `dllimport` references to static data in .rlibs
|
|
|
|
// when using MSVC linker. We do this only for data, as linker can fix up
|
|
|
|
// code references on its own.
|
|
|
|
// See #26591, #27438
|
2018-10-23 15:01:35 +00:00
|
|
|
fn create_msvc_imps(
|
|
|
|
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|
|
|
llcx: &llvm::Context,
|
|
|
|
llmod: &llvm::Module
|
|
|
|
) {
|
2017-10-17 20:08:13 +00:00
|
|
|
if !cgcx.msvc_imps_needed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// The x86 ABI seems to require that leading underscores are added to symbol
|
|
|
|
// names, so we need an extra underscore on 32-bit. There's also a leading
|
2018-11-27 02:59:49 +00:00
|
|
|
// '\x01' here which disables LLVM's symbol mangling (e.g., no extra
|
2017-10-17 20:08:13 +00:00
|
|
|
// underscores added in front).
|
|
|
|
let prefix = if cgcx.target_pointer_width == "32" {
|
|
|
|
"\x01__imp__"
|
|
|
|
} else {
|
|
|
|
"\x01__imp_"
|
|
|
|
};
|
|
|
|
unsafe {
|
2018-09-06 21:44:51 +00:00
|
|
|
let i8p_ty = Type::i8p_llcx(llcx);
|
2017-10-17 20:08:13 +00:00
|
|
|
let globals = base::iter_globals(llmod)
|
|
|
|
.filter(|&val| {
|
|
|
|
llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage &&
|
|
|
|
llvm::LLVMIsDeclaration(val) == 0
|
|
|
|
})
|
|
|
|
.map(move |val| {
|
|
|
|
let name = CStr::from_ptr(llvm::LLVMGetValueName(val));
|
|
|
|
let mut imp_name = prefix.as_bytes().to_vec();
|
|
|
|
imp_name.extend(name.to_bytes());
|
|
|
|
let imp_name = CString::new(imp_name).unwrap();
|
|
|
|
(imp_name, val)
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
for (imp_name, val) in globals {
|
|
|
|
let imp = llvm::LLVMAddGlobal(llmod,
|
2018-07-02 14:52:53 +00:00
|
|
|
i8p_ty,
|
2017-10-17 20:08:13 +00:00
|
|
|
imp_name.as_ptr() as *const _);
|
|
|
|
llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty));
|
|
|
|
llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|