summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_codegen_llvm/src/back
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_codegen_llvm/src/back/archive.rs116
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs121
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs287
3 files changed, 145 insertions, 379 deletions
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index 27039cda2..082665bba 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -2,16 +2,21 @@
use std::env;
use std::ffi::{CStr, CString, OsString};
-use std::io;
+use std::fs;
+use std::io::{self, Write};
use std::mem;
use std::path::{Path, PathBuf};
use std::ptr;
use std::str;
+use object::read::macho::FatArch;
+
+use crate::common;
use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
-use rustc_session::cstore::{DllCallingConvention, DllImport};
+use rustc_data_structures::memmap::Mmap;
+use rustc_session::cstore::DllImport;
use rustc_session::Session;
/// Helper for adding many files to an archive.
@@ -52,13 +57,70 @@ fn llvm_machine_type(cpu: &str) -> LLVMMachineType {
}
}
+fn try_filter_fat_archs(
+ archs: object::read::Result<&[impl FatArch]>,
+ target_arch: object::Architecture,
+ archive_path: &Path,
+ archive_map_data: &[u8],
+) -> io::Result<Option<PathBuf>> {
+ let archs = archs.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
+
+ let desired = match archs.iter().filter(|a| a.architecture() == target_arch).next() {
+ Some(a) => a,
+ None => return Ok(None),
+ };
+
+ let (mut new_f, extracted_path) = tempfile::Builder::new()
+ .suffix(archive_path.file_name().unwrap())
+ .tempfile()?
+ .keep()
+ .unwrap();
+
+ new_f.write_all(
+ desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
+ )?;
+
+ Ok(Some(extracted_path))
+}
+
+fn try_extract_macho_fat_archive(
+ sess: &Session,
+ archive_path: &Path,
+) -> io::Result<Option<PathBuf>> {
+ let archive_map = unsafe { Mmap::map(fs::File::open(&archive_path)?)? };
+ let target_arch = match sess.target.arch.as_ref() {
+ "aarch64" => object::Architecture::Aarch64,
+ "x86_64" => object::Architecture::X86_64,
+ _ => return Ok(None),
+ };
+
+ match object::macho::FatHeader::parse(&*archive_map) {
+ Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC => {
+ let archs = object::macho::FatHeader::parse_arch32(&*archive_map);
+ try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
+ }
+ Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC_64 => {
+ let archs = object::macho::FatHeader::parse_arch64(&*archive_map);
+ try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
+ }
+ // Not a FatHeader at all, just return None.
+ _ => Ok(None),
+ }
+}
+
impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
fn add_archive(
&mut self,
archive: &Path,
skip: Box<dyn FnMut(&str) -> bool + 'static>,
) -> io::Result<()> {
- let archive_ro = match ArchiveRO::open(archive) {
+ let mut archive = archive.to_path_buf();
+ if self.sess.target.llvm_target.contains("-apple-macosx") {
+ if let Some(new_archive) = try_extract_macho_fat_archive(&self.sess, &archive)? {
+ archive = new_archive
+ }
+ }
+ let archive_ro = match ArchiveRO::open(&archive) {
Ok(ar) => ar,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
};
@@ -66,7 +128,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
return Ok(());
}
self.additions.push(Addition::Archive {
- path: archive.to_path_buf(),
+ path: archive,
archive: archive_ro,
skip: Box::new(skip),
});
@@ -103,29 +165,28 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
lib_name: &str,
dll_imports: &[DllImport],
tmpdir: &Path,
+ is_direct_dependency: bool,
) -> PathBuf {
+ let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" };
let output_path = {
let mut output_path: PathBuf = tmpdir.to_path_buf();
- output_path.push(format!("{}_imports", lib_name));
+ output_path.push(format!("{}{}", lib_name, name_suffix));
output_path.with_extension("lib")
};
let target = &sess.target;
- let mingw_gnu_toolchain = target.vendor == "pc"
- && target.os == "windows"
- && target.env == "gnu"
- && target.abi.is_empty();
+ let mingw_gnu_toolchain = common::is_mingw_gnu_toolchain(target);
let import_name_and_ordinal_vector: Vec<(String, Option<u16>)> = dll_imports
.iter()
.map(|import: &DllImport| {
if sess.target.arch == "x86" {
(
- LlvmArchiveBuilder::i686_decorated_name(import, mingw_gnu_toolchain),
- import.ordinal,
+ common::i686_decorated_name(import, mingw_gnu_toolchain, false),
+ import.ordinal(),
)
} else {
- (import.name.to_string(), import.ordinal)
+ (import.name.to_string(), import.ordinal())
}
})
.collect();
@@ -136,7 +197,8 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
// that loaded but crashed with an AV upon calling one of the imported
// functions. Therefore, use binutils to create the import library instead,
// by writing a .DEF file to the temp dir and calling binutils's dlltool.
- let def_file_path = tmpdir.join(format!("{}_imports", lib_name)).with_extension("def");
+ let def_file_path =
+ tmpdir.join(format!("{}{}", lib_name, name_suffix)).with_extension("def");
let def_file_content = format!(
"EXPORTS\n{}",
@@ -159,6 +221,9 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
}
};
+ // --no-leading-underscore: For the `import_name_type` feature to work, we need to be
+ // able to control the *exact* spelling of each of the symbols that are being imported:
+ // hence we don't want `dlltool` adding leading underscores automatically.
let dlltool = find_binutils_dlltool(sess);
let result = std::process::Command::new(dlltool)
.args([
@@ -168,6 +233,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
lib_name,
"-l",
output_path.to_str().unwrap(),
+ "--no-leading-underscore",
])
.output();
@@ -188,10 +254,10 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
let output_path_z = rustc_fs_util::path_to_c_string(&output_path);
- tracing::trace!("invoking LLVMRustWriteImportLibrary");
- tracing::trace!(" dll_name {:#?}", dll_name_z);
- tracing::trace!(" output_path {}", output_path.display());
- tracing::trace!(
+ trace!("invoking LLVMRustWriteImportLibrary");
+ trace!(" dll_name {:#?}", dll_name_z);
+ trace!(" output_path {}", output_path.display());
+ trace!(
" import names: {}",
dll_imports
.iter()
@@ -322,22 +388,6 @@ impl<'a> LlvmArchiveBuilder<'a> {
ret
}
}
-
- fn i686_decorated_name(import: &DllImport, mingw: bool) -> String {
- let name = import.name;
- let prefix = if mingw { "" } else { "_" };
-
- match import.calling_convention {
- DllCallingConvention::C => format!("{}{}", prefix, name),
- DllCallingConvention::Stdcall(arg_list_size) => {
- format!("{}{}@{}", prefix, name, arg_list_size)
- }
- DllCallingConvention::Fastcall(arg_list_size) => format!("@{}@{}", name, arg_list_size),
- DllCallingConvention::Vectorcall(arg_list_size) => {
- format!("{}@@{}", name, arg_list_size)
- }
- }
- }
}
fn string_to_io_error(s: String) -> io::Error {
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 3731c6bcf..a49cc7f8d 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -1,15 +1,14 @@
-use crate::back::write::{
- self, save_temp_bitcode, to_llvm_opt_settings, with_llvm_pmb, DiagnosticHandlers,
-};
-use crate::llvm::archive_ro::ArchiveRO;
-use crate::llvm::{self, build_string, False, True};
-use crate::{llvm_util, LlvmCodegenBackend, ModuleLlvm};
+use crate::back::write::{self, save_temp_bitcode, DiagnosticHandlers};
+use crate::llvm::{self, build_string};
+use crate::{LlvmCodegenBackend, ModuleLlvm};
+use object::read::archive::ArchiveFile;
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::symbol_export;
use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, TargetMachineFactoryConfig};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::memmap::Mmap;
use rustc_errors::{FatalError, Handler};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
@@ -17,7 +16,6 @@ use rustc_middle::dep_graph::WorkProduct;
use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
use rustc_session::cgu_reuse_tracker::CguReuse;
use rustc_session::config::{self, CrateType, Lto};
-use tracing::{debug, info};
use std::ffi::{CStr, CString};
use std::fs::File;
@@ -34,8 +32,8 @@ pub const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin";
pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
match crate_type {
- CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => true,
- CrateType::Dylib | CrateType::Rlib | CrateType::ProcMacro => false,
+ CrateType::Executable | CrateType::Dylib | CrateType::Staticlib | CrateType::Cdylib => true,
+ CrateType::Rlib | CrateType::ProcMacro => false,
}
}
@@ -75,17 +73,6 @@ fn prepare_lto(
// with either fat or thin LTO
let mut upstream_modules = Vec::new();
if cgcx.lto != Lto::ThinLocal {
- if cgcx.opts.cg.prefer_dynamic {
- diag_handler
- .struct_err("cannot prefer dynamic linking when performing LTO")
- .note(
- "only 'staticlib', 'bin', and 'cdylib' outputs are \
- supported with LTO",
- )
- .emit();
- return Err(FatalError);
- }
-
// Make sure we actually can run LTO
for crate_type in cgcx.crate_types.iter() {
if !crate_type_allows_lto(*crate_type) {
@@ -94,9 +81,25 @@ fn prepare_lto(
static library outputs",
);
return Err(e);
+ } else if *crate_type == CrateType::Dylib {
+ if !cgcx.opts.unstable_opts.dylib_lto {
+ return Err(diag_handler
+ .fatal("lto cannot be used for `dylib` crate type without `-Zdylib-lto`"));
+ }
}
}
+ if cgcx.opts.cg.prefer_dynamic && !cgcx.opts.unstable_opts.dylib_lto {
+ diag_handler
+ .struct_err("cannot prefer dynamic linking when performing LTO")
+ .note(
+ "only 'staticlib', 'bin', and 'cdylib' outputs are \
+ supported with LTO",
+ )
+ .emit();
+ return Err(FatalError);
+ }
+
for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() {
let exported_symbols =
cgcx.exported_symbols.as_ref().expect("needs exported symbols for LTO");
@@ -107,14 +110,24 @@ fn prepare_lto(
.extend(exported_symbols[&cnum].iter().filter_map(symbol_filter));
}
- let archive = ArchiveRO::open(path).expect("wanted an rlib");
+ let archive_data = unsafe {
+ Mmap::map(std::fs::File::open(&path).expect("couldn't open rlib"))
+ .expect("couldn't map rlib")
+ };
+ let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
let obj_files = archive
- .iter()
- .filter_map(|child| child.ok().and_then(|c| c.name().map(|name| (name, c))))
+ .members()
+ .filter_map(|child| {
+ child.ok().and_then(|c| {
+ std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))
+ })
+ })
.filter(|&(name, _)| looks_like_rust_object_file(name));
for (name, child) in obj_files {
info!("adding bitcode from {}", name);
- match get_bitcode_slice_from_object_data(child.data()) {
+ match get_bitcode_slice_from_object_data(
+ child.data(&*archive_data).expect("corrupt rlib"),
+ ) {
Ok(data) => {
let module = SerializedModule::FromRlib(data.to_vec());
upstream_modules.push((module, CString::new(name).unwrap()));
@@ -565,7 +578,7 @@ pub(crate) fn run_pass_manager(
module: &mut ModuleCodegen<ModuleLlvm>,
thin: bool,
) -> Result<(), FatalError> {
- let _timer = cgcx.prof.extra_verbose_generic_activity("LLVM_lto_optimize", &*module.name);
+ let _timer = cgcx.prof.verbose_generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
let config = cgcx.config(module.kind);
// Now we have one massive module inside of llmod. Time to run the
@@ -587,61 +600,9 @@ pub(crate) fn run_pass_manager(
1,
);
}
- if llvm_util::should_use_new_llvm_pass_manager(
- &config.new_llvm_pass_manager,
- &cgcx.target_arch,
- ) {
- let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
- let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
- write::optimize_with_new_llvm_pass_manager(
- cgcx,
- diag_handler,
- module,
- config,
- opt_level,
- opt_stage,
- )?;
- debug!("lto done");
- return Ok(());
- }
-
- let pm = llvm::LLVMCreatePassManager();
- llvm::LLVMAddAnalysisPasses(module.module_llvm.tm, pm);
-
- if config.verify_llvm_ir {
- let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr().cast());
- llvm::LLVMRustAddPass(pm, pass.unwrap());
- }
-
- let opt_level = config
- .opt_level
- .map(|x| to_llvm_opt_settings(x).0)
- .unwrap_or(llvm::CodeGenOptLevel::None);
- with_llvm_pmb(module.module_llvm.llmod(), config, opt_level, false, &mut |b| {
- if thin {
- llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm);
- } else {
- llvm::LLVMRustPassManagerBuilderPopulateLTOPassManager(
- b, pm, /* Internalize = */ False, /* RunInliner = */ True,
- );
- }
- });
-
- // We always generate bitcode through ThinLTOBuffers,
- // which do not support anonymous globals
- if config.bitcode_needed() {
- let pass = llvm::LLVMRustFindAndCreatePass("name-anon-globals\0".as_ptr().cast());
- llvm::LLVMRustAddPass(pm, pass.unwrap());
- }
-
- if config.verify_llvm_ir {
- let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr().cast());
- llvm::LLVMRustAddPass(pm, pass.unwrap());
- }
-
- llvm::LLVMRunPassManager(pm, module.module_llvm.llmod());
-
- llvm::LLVMDisposePassManager(pm);
+ let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
+ let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
+ write::llvm_optimize(cgcx, diag_handler, module, config, opt_level, opt_stage)?;
}
debug!("lto done");
Ok(())
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 534d32e8a..11053a8f6 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -5,7 +5,7 @@ use crate::back::profiling::{
use crate::base;
use crate::common;
use crate::consts;
-use crate::llvm::{self, DiagnosticInfo, PassManager, SMDiagnostic};
+use crate::llvm::{self, DiagnosticInfo, PassManager};
use crate::llvm_util;
use crate::type_::Type;
use crate::LlvmCodegenBackend;
@@ -21,14 +21,12 @@ use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_errors::{FatalError, Handler, Level};
use rustc_fs_util::{link_or_copy, path_to_c_string};
-use rustc_middle::bug;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath};
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::InnerSpan;
use rustc_target::spec::{CodeModel, RelocModel, SanitizerSet, SplitDebuginfo};
-use tracing::debug;
use libc::{c_char, c_int, c_uint, c_void, size_t};
use std::ffi::CString;
@@ -304,7 +302,6 @@ impl<'a> DiagnosticHandlers<'a> {
remark_passes.as_ptr(),
remark_passes.len(),
);
- llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, data.cast());
DiagnosticHandlers { data, llcx, old_handler }
}
}
@@ -312,9 +309,7 @@ impl<'a> DiagnosticHandlers<'a> {
impl<'a> Drop for DiagnosticHandlers<'a> {
fn drop(&mut self) {
- use std::ptr::null_mut;
unsafe {
- llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, null_mut());
llvm::LLVMRustContextSetDiagnosticHandler(self.llcx, self.old_handler);
drop(Box::from_raw(self.data));
}
@@ -342,16 +337,6 @@ fn report_inline_asm(
cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, level, source);
}
-unsafe extern "C" fn inline_asm_handler(diag: &SMDiagnostic, user: *const c_void, cookie: c_uint) {
- if user.is_null() {
- return;
- }
- let (cgcx, _) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &Handler));
-
- let smdiag = llvm::diagnostic::SrcMgrDiagnostic::unpack(diag);
- report_inline_asm(cgcx, smdiag.message, smdiag.level, cookie, smdiag.source);
-}
-
unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) {
if user.is_null() {
return;
@@ -423,7 +408,15 @@ fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option<CString> {
.map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap())
}
-pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
+fn get_instr_profile_output_path(config: &ModuleConfig) -> Option<CString> {
+ if config.instrument_coverage {
+ Some(CString::new("default_%m_%p.profraw").unwrap())
+ } else {
+ None
+ }
+}
+
+pub(crate) unsafe fn llvm_optimize(
cgcx: &CodegenContext<LlvmCodegenBackend>,
diag_handler: &Handler,
module: &ModuleCodegen<ModuleLlvm>,
@@ -438,6 +431,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
let pgo_use_path = get_pgo_use_path(config);
let pgo_sample_use_path = get_pgo_sample_use_path(config);
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
+ let instr_profile_output_path = get_instr_profile_output_path(config);
// Sanitizer instrumentation is only inserted during the pre-link optimization stage.
let sanitizer_options = if !is_lto {
Some(llvm::SanitizerOptions {
@@ -470,7 +464,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
// FIXME: NewPM doesn't provide a facility to pass custom InlineParams.
// We would have to add upstream support for this first, before we can support
// config.inline_threshold and our more aggressive default thresholds.
- let result = llvm::LLVMRustOptimizeWithNewPassManager(
+ let result = llvm::LLVMRustOptimize(
module.module_llvm.llmod(),
&*module.module_llvm.tm,
to_pass_builder_opt_level(opt_level),
@@ -488,6 +482,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
config.instrument_coverage,
+ instr_profile_output_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
config.instrument_gcov,
pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
config.debug_info_for_profiling,
@@ -513,18 +508,11 @@ pub(crate) unsafe fn optimize(
let llmod = module.module_llvm.llmod();
let llcx = &*module.module_llvm.llcx;
- let tm = &*module.module_llvm.tm;
let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
let module_name = module.name.clone();
let module_name = Some(&module_name[..]);
- if let Some(false) = config.new_llvm_pass_manager && llvm_util::get_version() >= (15, 0, 0) {
- diag_handler.warn(
- "ignoring `-Z new-llvm-pass-manager=no`, which is no longer supported with LLVM 15",
- );
- }
-
if config.emit_no_opt_bc {
let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name);
let out = path_to_c_string(&out);
@@ -532,191 +520,24 @@ pub(crate) unsafe fn optimize(
}
if let Some(opt_level) = config.opt_level {
- if llvm_util::should_use_new_llvm_pass_manager(
- &config.new_llvm_pass_manager,
- &cgcx.target_arch,
- ) {
- let opt_stage = match cgcx.lto {
- Lto::Fat => llvm::OptStage::PreLinkFatLTO,
- Lto::Thin | Lto::ThinLocal => llvm::OptStage::PreLinkThinLTO,
- _ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO,
- _ => llvm::OptStage::PreLinkNoLTO,
- };
- return optimize_with_new_llvm_pass_manager(
- cgcx,
- diag_handler,
- module,
- config,
- opt_level,
- opt_stage,
- );
- }
-
- if cgcx.prof.llvm_recording_enabled() {
- diag_handler
- .warn("`-Z self-profile-events = llvm` requires `-Z new-llvm-pass-manager`");
- }
-
- // 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();
-
- {
- let find_pass = |pass_name: &str| {
- let pass_name = SmallCStr::new(pass_name);
- llvm::LLVMRustFindAndCreatePass(pass_name.as_ptr())
- };
-
- if config.verify_llvm_ir {
- // Verification should run as the very first pass.
- llvm::LLVMRustAddPass(fpm, find_pass("verify").unwrap());
- }
-
- let mut extra_passes = Vec::new();
- let mut have_name_anon_globals_pass = false;
-
- for pass_name in &config.passes {
- if pass_name == "lint" {
- // Linting should also be performed early, directly on the generated IR.
- llvm::LLVMRustAddPass(fpm, find_pass("lint").unwrap());
- continue;
- }
-
- if let Some(pass) = find_pass(pass_name) {
- extra_passes.push(pass);
- } else {
- diag_handler.warn(&format!("unknown pass `{}`, ignoring", pass_name));
- }
-
- if pass_name == "name-anon-globals" {
- have_name_anon_globals_pass = true;
- }
- }
-
- // Instrumentation must be inserted before optimization,
- // otherwise LLVM may optimize some functions away which
- // breaks llvm-cov.
- //
- // This mirrors what Clang does in lib/CodeGen/BackendUtil.cpp.
- if config.instrument_gcov {
- llvm::LLVMRustAddPass(mpm, find_pass("insert-gcov-profiling").unwrap());
- }
- if config.instrument_coverage {
- llvm::LLVMRustAddPass(mpm, find_pass("instrprof").unwrap());
- }
- if config.debug_info_for_profiling {
- llvm::LLVMRustAddPass(mpm, find_pass("add-discriminators").unwrap());
- }
-
- add_sanitizer_passes(config, &mut extra_passes);
-
- // 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.
- let using_thin_buffers = config.bitcode_needed();
- if !config.no_prepopulate_passes {
- llvm::LLVMAddAnalysisPasses(tm, fpm);
- llvm::LLVMAddAnalysisPasses(tm, mpm);
- let opt_level = to_llvm_opt_settings(opt_level).0;
- let prepare_for_thin_lto = cgcx.lto == Lto::Thin
- || cgcx.lto == Lto::ThinLocal
- || (cgcx.lto != Lto::Fat && cgcx.opts.cg.linker_plugin_lto.enabled());
- with_llvm_pmb(llmod, config, opt_level, prepare_for_thin_lto, &mut |b| {
- llvm::LLVMRustAddLastExtensionPasses(
- b,
- extra_passes.as_ptr(),
- extra_passes.len() as size_t,
- );
- llvm::LLVMRustPassManagerBuilderPopulateFunctionPassManager(b, fpm);
- llvm::LLVMRustPassManagerBuilderPopulateModulePassManager(b, mpm);
- });
-
- have_name_anon_globals_pass = have_name_anon_globals_pass || prepare_for_thin_lto;
- if using_thin_buffers && !prepare_for_thin_lto {
- llvm::LLVMRustAddPass(mpm, find_pass("name-anon-globals").unwrap());
- have_name_anon_globals_pass = true;
- }
- } else {
- // If we don't use the standard pipeline, directly populate the MPM
- // with the extra passes.
- for pass in extra_passes {
- llvm::LLVMRustAddPass(mpm, pass);
- }
- }
-
- 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 \
- 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.",
- );
- } else {
- bug!(
- "We are using thin LTO buffers without running the NameAnonGlobals pass. \
- This will likely cause errors in LLVM and should never happen."
- );
- }
- }
- }
-
- diag_handler.abort_if_errors();
-
- // Finally, run the actual optimization passes
- {
- let _timer = cgcx.prof.extra_verbose_generic_activity(
- "LLVM_module_optimize_function_passes",
- &*module.name,
- );
- llvm::LLVMRustRunFunctionPassManager(fpm, llmod);
- }
- {
- let _timer = cgcx.prof.extra_verbose_generic_activity(
- "LLVM_module_optimize_module_passes",
- &*module.name,
- );
- llvm::LLVMRunPassManager(mpm, llmod);
- }
-
- // Deallocate managers that we're now done with
- llvm::LLVMDisposePassManager(fpm);
- llvm::LLVMDisposePassManager(mpm);
+ let opt_stage = match cgcx.lto {
+ Lto::Fat => llvm::OptStage::PreLinkFatLTO,
+ Lto::Thin | Lto::ThinLocal => llvm::OptStage::PreLinkThinLTO,
+ _ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO,
+ _ => llvm::OptStage::PreLinkNoLTO,
+ };
+ return llvm_optimize(cgcx, diag_handler, module, config, opt_level, opt_stage);
}
Ok(())
}
-unsafe fn add_sanitizer_passes(config: &ModuleConfig, passes: &mut Vec<&'static mut llvm::Pass>) {
- if config.sanitizer.contains(SanitizerSet::ADDRESS) {
- let recover = config.sanitizer_recover.contains(SanitizerSet::ADDRESS);
- passes.push(llvm::LLVMRustCreateAddressSanitizerFunctionPass(recover));
- passes.push(llvm::LLVMRustCreateModuleAddressSanitizerPass(recover));
- }
- if config.sanitizer.contains(SanitizerSet::MEMORY) {
- let track_origins = config.sanitizer_memory_track_origins as c_int;
- let recover = config.sanitizer_recover.contains(SanitizerSet::MEMORY);
- passes.push(llvm::LLVMRustCreateMemorySanitizerPass(track_origins, recover));
- }
- if config.sanitizer.contains(SanitizerSet::THREAD) {
- passes.push(llvm::LLVMRustCreateThreadSanitizerPass());
- }
- if config.sanitizer.contains(SanitizerSet::HWADDRESS) {
- let recover = config.sanitizer_recover.contains(SanitizerSet::HWADDRESS);
- passes.push(llvm::LLVMRustCreateHWAddressSanitizerPass(recover));
- }
-}
-
pub(crate) fn link(
cgcx: &CodegenContext<LlvmCodegenBackend>,
diag_handler: &Handler,
mut modules: Vec<ModuleCodegen<ModuleLlvm>>,
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
use super::lto::{Linker, ModuleBuffer};
- // Sort the modules by name to ensure to ensure deterministic behavior.
+ // Sort the modules by name to ensure deterministic behavior.
modules.sort_by(|a, b| a.name.cmp(&b.name));
let (first, elements) =
modules.split_first().expect("Bug! modules must contain at least one module.");
@@ -1076,72 +897,6 @@ unsafe fn embed_bitcode(
}
}
-pub unsafe fn with_llvm_pmb(
- llmod: &llvm::Module,
- config: &ModuleConfig,
- opt_level: llvm::CodeGenOptLevel,
- prepare_for_thin_lto: bool,
- f: &mut dyn FnMut(&llvm::PassManagerBuilder),
-) {
- use std::ptr;
-
- // Create the PassManagerBuilder for LLVM. We configure it with
- // reasonable defaults and prepare it to actually populate the pass
- // manager.
- let builder = llvm::LLVMRustPassManagerBuilderCreate();
- let opt_size = config.opt_size.map_or(llvm::CodeGenOptSizeNone, |x| to_llvm_opt_settings(x).1);
- let inline_threshold = config.inline_threshold;
- let pgo_gen_path = get_pgo_gen_path(config);
- let pgo_use_path = get_pgo_use_path(config);
- let pgo_sample_use_path = get_pgo_sample_use_path(config);
-
- llvm::LLVMRustConfigurePassManagerBuilder(
- builder,
- opt_level,
- config.merge_functions,
- config.vectorize_slp,
- config.vectorize_loop,
- prepare_for_thin_lto,
- 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()),
- pgo_sample_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
- opt_size as c_int,
- );
-
- 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.
- match (opt_level, opt_size, inline_threshold) {
- (.., Some(t)) => {
- llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, t);
- }
- (llvm::CodeGenOptLevel::Aggressive, ..) => {
- llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 275);
- }
- (_, llvm::CodeGenOptSizeDefault, _) => {
- llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 75);
- }
- (_, llvm::CodeGenOptSizeAggressive, _) => {
- llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 25);
- }
- (llvm::CodeGenOptLevel::None, ..) => {
- llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers);
- }
- (llvm::CodeGenOptLevel::Less, ..) => {
- llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers);
- }
- (llvm::CodeGenOptLevel::Default, ..) => {
- llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 225);
- }
- }
-
- f(builder);
- llvm::LLVMRustPassManagerBuilderDispose(builder);
-}
-
// 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