mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-30 03:57:37 +00:00

If we don't do this, some versions of LLVM (at least 17, experimentally) will double-emit some error messages, which is how I noticed this. Given that it seems to be costing some extra work, let's only request the summary bitcode production if we'll actually bother writing it down, otherwise skip it.
472 lines
14 KiB
Rust
472 lines
14 KiB
Rust
//! The Rust compiler.
|
|
//!
|
|
//! # Note
|
|
//!
|
|
//! This API is completely unstable and subject to change.
|
|
|
|
#![allow(internal_features)]
|
|
#![feature(rustdoc_internals)]
|
|
#![doc(rust_logo)]
|
|
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
|
#![feature(exact_size_is_empty)]
|
|
#![feature(extern_types)]
|
|
#![feature(hash_raw_entry)]
|
|
#![feature(iter_intersperse)]
|
|
#![feature(let_chains)]
|
|
#![feature(impl_trait_in_assoc_type)]
|
|
|
|
#[macro_use]
|
|
extern crate tracing;
|
|
|
|
use back::owned_target_machine::OwnedTargetMachine;
|
|
use back::write::{create_informational_target_machine, create_target_machine};
|
|
|
|
use errors::ParseTargetMachineConfig;
|
|
pub use llvm_util::target_features;
|
|
use rustc_ast::expand::allocator::AllocatorKind;
|
|
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
|
|
use rustc_codegen_ssa::back::write::{
|
|
CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn,
|
|
};
|
|
use rustc_codegen_ssa::traits::*;
|
|
use rustc_codegen_ssa::ModuleCodegen;
|
|
use rustc_codegen_ssa::{CodegenResults, CompiledModule};
|
|
use rustc_data_structures::fx::FxIndexMap;
|
|
use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError};
|
|
use rustc_metadata::EncodedMetadata;
|
|
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
|
|
use rustc_middle::ty::TyCtxt;
|
|
use rustc_middle::util::Providers;
|
|
use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest};
|
|
use rustc_session::Session;
|
|
use rustc_span::symbol::Symbol;
|
|
|
|
use std::any::Any;
|
|
use std::ffi::CStr;
|
|
use std::io::Write;
|
|
use std::mem::ManuallyDrop;
|
|
|
|
mod back {
|
|
pub mod archive;
|
|
pub mod lto;
|
|
pub mod owned_target_machine;
|
|
mod profiling;
|
|
pub mod write;
|
|
}
|
|
|
|
mod abi;
|
|
mod allocator;
|
|
mod asm;
|
|
mod attributes;
|
|
mod base;
|
|
mod builder;
|
|
mod callee;
|
|
mod common;
|
|
mod consts;
|
|
mod context;
|
|
mod coverageinfo;
|
|
mod debuginfo;
|
|
mod declare;
|
|
mod errors;
|
|
mod intrinsic;
|
|
|
|
// The following is a workaround that replaces `pub mod llvm;` and that fixes issue 53912.
|
|
#[path = "llvm/mod.rs"]
|
|
mod llvm_;
|
|
pub mod llvm {
|
|
pub use super::llvm_::*;
|
|
}
|
|
|
|
mod llvm_util;
|
|
mod mono_item;
|
|
mod type_;
|
|
mod type_of;
|
|
mod va_arg;
|
|
mod value;
|
|
|
|
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
|
|
|
#[derive(Clone)]
|
|
pub struct LlvmCodegenBackend(());
|
|
|
|
struct TimeTraceProfiler {
|
|
enabled: bool,
|
|
}
|
|
|
|
impl TimeTraceProfiler {
|
|
fn new(enabled: bool) -> Self {
|
|
if enabled {
|
|
unsafe { llvm::LLVMRustTimeTraceProfilerInitialize() }
|
|
}
|
|
TimeTraceProfiler { enabled }
|
|
}
|
|
}
|
|
|
|
impl Drop for TimeTraceProfiler {
|
|
fn drop(&mut self) {
|
|
if self.enabled {
|
|
unsafe { llvm::LLVMRustTimeTraceProfilerFinishThread() }
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ExtraBackendMethods for LlvmCodegenBackend {
|
|
fn codegen_allocator<'tcx>(
|
|
&self,
|
|
tcx: TyCtxt<'tcx>,
|
|
module_name: &str,
|
|
kind: AllocatorKind,
|
|
alloc_error_handler_kind: AllocatorKind,
|
|
) -> ModuleLlvm {
|
|
let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
|
|
unsafe {
|
|
allocator::codegen(tcx, &mut module_llvm, module_name, kind, alloc_error_handler_kind);
|
|
}
|
|
module_llvm
|
|
}
|
|
fn compile_codegen_unit(
|
|
&self,
|
|
tcx: TyCtxt<'_>,
|
|
cgu_name: Symbol,
|
|
) -> (ModuleCodegen<ModuleLlvm>, u64) {
|
|
base::compile_codegen_unit(tcx, cgu_name)
|
|
}
|
|
fn target_machine_factory(
|
|
&self,
|
|
sess: &Session,
|
|
optlvl: OptLevel,
|
|
target_features: &[String],
|
|
) -> TargetMachineFactoryFn<Self> {
|
|
back::write::target_machine_factory(sess, optlvl, target_features)
|
|
}
|
|
|
|
fn spawn_named_thread<F, T>(
|
|
time_trace: bool,
|
|
name: String,
|
|
f: F,
|
|
) -> std::io::Result<std::thread::JoinHandle<T>>
|
|
where
|
|
F: FnOnce() -> T,
|
|
F: Send + 'static,
|
|
T: Send + 'static,
|
|
{
|
|
std::thread::Builder::new().name(name).spawn(move || {
|
|
let _profiler = TimeTraceProfiler::new(time_trace);
|
|
f()
|
|
})
|
|
}
|
|
}
|
|
|
|
impl WriteBackendMethods for LlvmCodegenBackend {
|
|
type Module = ModuleLlvm;
|
|
type ModuleBuffer = back::lto::ModuleBuffer;
|
|
type TargetMachine = OwnedTargetMachine;
|
|
type TargetMachineError = crate::errors::LlvmError<'static>;
|
|
type ThinData = back::lto::ThinData;
|
|
type ThinBuffer = back::lto::ThinBuffer;
|
|
fn print_pass_timings(&self) {
|
|
unsafe {
|
|
let mut size = 0;
|
|
let cstr = llvm::LLVMRustPrintPassTimings(std::ptr::addr_of_mut!(size));
|
|
if cstr.is_null() {
|
|
println!("failed to get pass timings");
|
|
} else {
|
|
let timings = std::slice::from_raw_parts(cstr as *const u8, size);
|
|
std::io::stdout().write_all(timings).unwrap();
|
|
libc::free(cstr as *mut _);
|
|
}
|
|
}
|
|
}
|
|
fn print_statistics(&self) {
|
|
unsafe {
|
|
let mut size = 0;
|
|
let cstr = llvm::LLVMRustPrintStatistics(std::ptr::addr_of_mut!(size));
|
|
if cstr.is_null() {
|
|
println!("failed to get pass stats");
|
|
} else {
|
|
let stats = std::slice::from_raw_parts(cstr as *const u8, size);
|
|
std::io::stdout().write_all(stats).unwrap();
|
|
libc::free(cstr as *mut _);
|
|
}
|
|
}
|
|
}
|
|
fn run_link(
|
|
cgcx: &CodegenContext<Self>,
|
|
dcx: &DiagCtxt,
|
|
modules: Vec<ModuleCodegen<Self::Module>>,
|
|
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
|
back::write::link(cgcx, dcx, modules)
|
|
}
|
|
fn run_fat_lto(
|
|
cgcx: &CodegenContext<Self>,
|
|
modules: Vec<FatLtoInput<Self>>,
|
|
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
|
) -> Result<LtoModuleCodegen<Self>, FatalError> {
|
|
back::lto::run_fat(cgcx, modules, cached_modules)
|
|
}
|
|
fn run_thin_lto(
|
|
cgcx: &CodegenContext<Self>,
|
|
modules: Vec<(String, Self::ThinBuffer)>,
|
|
cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>,
|
|
) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
|
|
back::lto::run_thin(cgcx, modules, cached_modules)
|
|
}
|
|
unsafe fn optimize(
|
|
cgcx: &CodegenContext<Self>,
|
|
dcx: &DiagCtxt,
|
|
module: &ModuleCodegen<Self::Module>,
|
|
config: &ModuleConfig,
|
|
) -> Result<(), FatalError> {
|
|
back::write::optimize(cgcx, dcx, module, config)
|
|
}
|
|
fn optimize_fat(
|
|
cgcx: &CodegenContext<Self>,
|
|
module: &mut ModuleCodegen<Self::Module>,
|
|
) -> Result<(), FatalError> {
|
|
let dcx = cgcx.create_dcx();
|
|
back::lto::run_pass_manager(cgcx, &dcx, module, false)
|
|
}
|
|
unsafe fn optimize_thin(
|
|
cgcx: &CodegenContext<Self>,
|
|
thin: ThinModule<Self>,
|
|
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
|
|
back::lto::optimize_thin_module(thin, cgcx)
|
|
}
|
|
unsafe fn codegen(
|
|
cgcx: &CodegenContext<Self>,
|
|
dcx: &DiagCtxt,
|
|
module: ModuleCodegen<Self::Module>,
|
|
config: &ModuleConfig,
|
|
) -> Result<CompiledModule, FatalError> {
|
|
back::write::codegen(cgcx, dcx, module, config)
|
|
}
|
|
fn prepare_thin(module: ModuleCodegen<Self::Module>, emit_summary: bool) -> (String, Self::ThinBuffer) {
|
|
back::lto::prepare_thin(module, emit_summary)
|
|
}
|
|
fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
|
|
(module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod()))
|
|
}
|
|
}
|
|
|
|
unsafe impl Send for LlvmCodegenBackend {} // Llvm is on a per-thread basis
|
|
unsafe impl Sync for LlvmCodegenBackend {}
|
|
|
|
impl LlvmCodegenBackend {
|
|
pub fn new() -> Box<dyn CodegenBackend> {
|
|
Box::new(LlvmCodegenBackend(()))
|
|
}
|
|
}
|
|
|
|
impl CodegenBackend for LlvmCodegenBackend {
|
|
fn locale_resource(&self) -> &'static str {
|
|
crate::DEFAULT_LOCALE_RESOURCE
|
|
}
|
|
|
|
fn init(&self, sess: &Session) {
|
|
llvm_util::init(sess); // Make sure llvm is inited
|
|
}
|
|
|
|
fn provide(&self, providers: &mut Providers) {
|
|
providers.global_backend_features =
|
|
|tcx, ()| llvm_util::global_llvm_features(tcx.sess, true)
|
|
}
|
|
|
|
fn print(&self, req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) {
|
|
match req.kind {
|
|
PrintKind::RelocationModels => {
|
|
writeln!(out, "Available relocation models:");
|
|
for name in &[
|
|
"static",
|
|
"pic",
|
|
"pie",
|
|
"dynamic-no-pic",
|
|
"ropi",
|
|
"rwpi",
|
|
"ropi-rwpi",
|
|
"default",
|
|
] {
|
|
writeln!(out, " {name}");
|
|
}
|
|
writeln!(out);
|
|
}
|
|
PrintKind::CodeModels => {
|
|
writeln!(out, "Available code models:");
|
|
for name in &["tiny", "small", "kernel", "medium", "large"] {
|
|
writeln!(out, " {name}");
|
|
}
|
|
writeln!(out);
|
|
}
|
|
PrintKind::TlsModels => {
|
|
writeln!(out, "Available TLS models:");
|
|
for name in
|
|
&["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"]
|
|
{
|
|
writeln!(out, " {name}");
|
|
}
|
|
writeln!(out);
|
|
}
|
|
PrintKind::StackProtectorStrategies => {
|
|
writeln!(
|
|
out,
|
|
r#"Available stack protector strategies:
|
|
all
|
|
Generate stack canaries in all functions.
|
|
|
|
strong
|
|
Generate stack canaries in a function if it either:
|
|
- has a local variable of `[T; N]` type, regardless of `T` and `N`
|
|
- takes the address of a local variable.
|
|
|
|
(Note that a local variable being borrowed is not equivalent to its
|
|
address being taken: e.g. some borrows may be removed by optimization,
|
|
while by-value argument passing may be implemented with reference to a
|
|
local stack variable in the ABI.)
|
|
|
|
basic
|
|
Generate stack canaries in functions with local variables of `[T; N]`
|
|
type, where `T` is byte-sized and `N` >= 8.
|
|
|
|
none
|
|
Do not generate stack canaries.
|
|
"#
|
|
);
|
|
}
|
|
_other => llvm_util::print(req, out, sess),
|
|
}
|
|
}
|
|
|
|
fn print_passes(&self) {
|
|
llvm_util::print_passes();
|
|
}
|
|
|
|
fn print_version(&self) {
|
|
llvm_util::print_version();
|
|
}
|
|
|
|
fn target_features(&self, sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
|
|
target_features(sess, allow_unstable)
|
|
}
|
|
|
|
fn codegen_crate<'tcx>(
|
|
&self,
|
|
tcx: TyCtxt<'tcx>,
|
|
metadata: EncodedMetadata,
|
|
need_metadata_module: bool,
|
|
) -> Box<dyn Any> {
|
|
Box::new(rustc_codegen_ssa::base::codegen_crate(
|
|
LlvmCodegenBackend(()),
|
|
tcx,
|
|
crate::llvm_util::target_cpu(tcx.sess).to_string(),
|
|
metadata,
|
|
need_metadata_module,
|
|
))
|
|
}
|
|
|
|
fn join_codegen(
|
|
&self,
|
|
ongoing_codegen: Box<dyn Any>,
|
|
sess: &Session,
|
|
outputs: &OutputFilenames,
|
|
) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
|
|
let (codegen_results, work_products) = ongoing_codegen
|
|
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<LlvmCodegenBackend>>()
|
|
.expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box<Any>")
|
|
.join(sess);
|
|
|
|
if sess.opts.unstable_opts.llvm_time_trace {
|
|
sess.time("llvm_dump_timing_file", || {
|
|
let file_name = outputs.with_extension("llvm_timings.json");
|
|
llvm_util::time_trace_profiler_finish(&file_name);
|
|
});
|
|
}
|
|
|
|
(codegen_results, work_products)
|
|
}
|
|
|
|
fn link(
|
|
&self,
|
|
sess: &Session,
|
|
codegen_results: CodegenResults,
|
|
outputs: &OutputFilenames,
|
|
) -> Result<(), ErrorGuaranteed> {
|
|
use crate::back::archive::LlvmArchiveBuilderBuilder;
|
|
use rustc_codegen_ssa::back::link::link_binary;
|
|
|
|
// Run the linker on any artifacts that resulted from the LLVM run.
|
|
// This should produce either a finished executable or library.
|
|
link_binary(sess, &LlvmArchiveBuilderBuilder, &codegen_results, outputs)
|
|
}
|
|
}
|
|
|
|
pub struct ModuleLlvm {
|
|
llcx: &'static mut llvm::Context,
|
|
llmod_raw: *const llvm::Module,
|
|
|
|
// This field is `ManuallyDrop` because it is important that the `TargetMachine`
|
|
// is disposed prior to the `Context` being disposed otherwise UAFs can occur.
|
|
tm: ManuallyDrop<OwnedTargetMachine>,
|
|
}
|
|
|
|
unsafe impl Send for ModuleLlvm {}
|
|
unsafe impl Sync for ModuleLlvm {}
|
|
|
|
impl ModuleLlvm {
|
|
fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self {
|
|
unsafe {
|
|
let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names());
|
|
let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _;
|
|
ModuleLlvm {
|
|
llmod_raw,
|
|
llcx,
|
|
tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self {
|
|
unsafe {
|
|
let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names());
|
|
let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _;
|
|
ModuleLlvm {
|
|
llmod_raw,
|
|
llcx,
|
|
tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse(
|
|
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|
name: &CStr,
|
|
buffer: &[u8],
|
|
dcx: &DiagCtxt,
|
|
) -> Result<Self, FatalError> {
|
|
unsafe {
|
|
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
|
|
let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx)?;
|
|
let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap());
|
|
let tm = match (cgcx.tm_factory)(tm_factory_config) {
|
|
Ok(m) => m,
|
|
Err(e) => {
|
|
return Err(dcx.emit_almost_fatal(ParseTargetMachineConfig(e)));
|
|
}
|
|
};
|
|
|
|
Ok(ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) })
|
|
}
|
|
}
|
|
|
|
fn llmod(&self) -> &llvm::Module {
|
|
unsafe { &*self.llmod_raw }
|
|
}
|
|
}
|
|
|
|
impl Drop for ModuleLlvm {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
ManuallyDrop::drop(&mut self.tm);
|
|
llvm::LLVMContextDispose(&mut *(self.llcx as *mut _));
|
|
}
|
|
}
|
|
}
|