compiler/rustc_codegen_cranelift/src/driver/aot.rs RUST 543 lines View on github.com → Search inside
1//! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a2//! standalone executable.34use std::env;5use std::fs::File;6use std::io::BufWriter;7use std::path::PathBuf;8use std::sync::Arc;9use std::thread::JoinHandle;1011use cranelift_object::{ObjectBuilder, ObjectModule};12use rustc_codegen_ssa::assert_module_sources::CguReuse;13use rustc_codegen_ssa::back::write::produce_final_output_artifacts;14use rustc_codegen_ssa::base::determine_cgu_reuse;15use rustc_codegen_ssa::{CompiledModule, CompiledModules, ModuleKind};16use rustc_data_structures::profiling::SelfProfilerRef;17use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher};18use rustc_data_structures::sync::{IntoDynSyncSend, par_map};19use rustc_hir::attrs::Linkage as RLinkage;20use rustc_middle::dep_graph::{WorkProduct, WorkProductId};21use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;22use rustc_middle::mono::{CodegenUnit, MonoItem, MonoItemData, Visibility};23use rustc_session::Session;24use rustc_session::config::{OutputFilenames, OutputType};2526use crate::base::CodegenedFunction;27use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken};28use crate::debuginfo::TypeDebugContext;29use crate::global_asm::{GlobalAsmConfig, GlobalAsmContext};30use crate::prelude::*;31use crate::unwind_module::UnwindModule;3233fn disable_incr_cache() -> bool {34    env::var("CG_CLIF_DISABLE_INCR_CACHE").as_deref() == Ok("1")35}3637struct ModuleCodegenResult {38    module_regular: CompiledModule,39    module_global_asm: Option<CompiledModule>,40    existing_work_product: Option<(WorkProductId, WorkProduct)>,41}4243enum OngoingModuleCodegen {44    Sync(Result<ModuleCodegenResult, String>),45    Async(JoinHandle<Result<ModuleCodegenResult, String>>),46}4748impl StableHash for OngoingModuleCodegen {49    fn stable_hash<Hcx: StableHashCtxt>(&self, _: &mut Hcx, _: &mut StableHasher) {50        // do nothing51    }52}5354pub(crate) struct OngoingCodegen {55    modules: Vec<OngoingModuleCodegen>,56    allocator_module: Option<CompiledModule>,57    concurrency_limiter: ConcurrencyLimiter,58}5960impl OngoingCodegen {61    pub(crate) fn join(62        self,63        sess: &Session,64        outputs: &OutputFilenames,65    ) -> (CompiledModules, FxIndexMap<WorkProductId, WorkProduct>) {66        let mut work_products = FxIndexMap::default();67        let mut modules = vec![];68        let disable_incr_cache = disable_incr_cache();6970        for module_codegen in self.modules {71            let module_codegen_result = match module_codegen {72                OngoingModuleCodegen::Sync(module_codegen_result) => module_codegen_result,73                OngoingModuleCodegen::Async(join_handle) => match join_handle.join() {74                    Ok(module_codegen_result) => module_codegen_result,75                    Err(panic) => std::panic::resume_unwind(panic),76                },77            };7879            let module_codegen_result = match module_codegen_result {80                Ok(module_codegen_result) => module_codegen_result,81                Err(err) => sess.dcx().fatal(err),82            };83            let ModuleCodegenResult { module_regular, module_global_asm, existing_work_product } =84                module_codegen_result;8586            if let Some((work_product_id, work_product)) = existing_work_product {87                work_products.insert(work_product_id, work_product);88            } else {89                let work_product = if disable_incr_cache {90                    None91                } else if let Some(module_global_asm) = &module_global_asm {92                    rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(93                        sess,94                        &module_regular.name,95                        &[96                            ("o", module_regular.object.as_ref().unwrap()),97                            ("asm.o", module_global_asm.object.as_ref().unwrap()),98                        ],99                        &[],100                    )101                } else {102                    rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(103                        sess,104                        &module_regular.name,105                        &[("o", module_regular.object.as_ref().unwrap())],106                        &[],107                    )108                };109                if let Some((work_product_id, work_product)) = work_product {110                    work_products.insert(work_product_id, work_product);111                }112            }113114            modules.push(module_regular);115            if let Some(module_global_asm) = module_global_asm {116                modules.push(module_global_asm);117            }118        }119120        self.concurrency_limiter.finished();121122        sess.dcx().abort_if_errors();123124        let compiled_modules = CompiledModules { modules, allocator_module: self.allocator_module };125126        produce_final_output_artifacts(sess, &compiled_modules, outputs);127128        (compiled_modules, work_products)129    }130}131132fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> {133    let isa = crate::build_isa(sess, false);134135    let mut builder =136        ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap();137138    // Disable function sections by default on MSVC as it causes significant slowdowns with link.exe.139    // Maybe link.exe has exponential behavior when there are many sections with the same name? Also140    // explicitly disable it on MinGW as rustc already disables it by default on MinGW and as such141    // isn't tested. If rustc enables it in the future on MinGW, we can re-enable it too once it has142    // been on MinGW.143    let default_function_sections = sess.target.function_sections && !sess.target.is_like_windows;144    builder.per_function_section(145        sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections),146    );147148    UnwindModule::new(ObjectModule::new(builder), true)149}150151fn emit_cgu(152    output_filenames: &OutputFilenames,153    invocation_temp: Option<&str>,154    prof: &SelfProfilerRef,155    name: String,156    module: UnwindModule<ObjectModule>,157    debug: Option<DebugContext>,158    global_asm_object_file: Option<PathBuf>,159    producer: &str,160) -> Result<ModuleCodegenResult, String> {161    let mut product = module.finish();162163    if let Some(mut debug) = debug {164        debug.emit(&mut product);165    }166167    let module_regular = emit_module(168        output_filenames,169        invocation_temp,170        prof,171        product.object,172        ModuleKind::Regular,173        name.clone(),174        producer,175    )?;176177    Ok(ModuleCodegenResult {178        module_regular,179        module_global_asm: global_asm_object_file.map(|global_asm_object_file| CompiledModule {180            name: format!("{name}.asm"),181            kind: ModuleKind::Regular,182            object: Some(global_asm_object_file),183            dwarf_object: None,184            bytecode: None,185            assembly: None,186            llvm_ir: None,187            links_from_incr_cache: Vec::new(),188        }),189        existing_work_product: None,190    })191}192193fn emit_module(194    output_filenames: &OutputFilenames,195    invocation_temp: Option<&str>,196    prof: &SelfProfilerRef,197    mut object: cranelift_object::object::write::Object<'_>,198    kind: ModuleKind,199    name: String,200    producer_str: &str,201) -> Result<CompiledModule, String> {202    if object.format() == cranelift_object::object::BinaryFormat::Elf {203        let comment_section = object.add_section(204            Vec::new(),205            b".comment".to_vec(),206            cranelift_object::object::SectionKind::OtherString,207        );208        let mut producer = vec![0];209        producer.extend(producer_str.as_bytes());210        producer.push(0);211        object.set_section_data(comment_section, producer, 1);212    }213214    let tmp_file = output_filenames.temp_path_for_cgu(OutputType::Object, &name, invocation_temp);215    let file = match File::create(&tmp_file) {216        Ok(file) => file,217        Err(err) => return Err(format!("error creating object file: {}", err)),218    };219220    let mut file = BufWriter::new(file);221    if let Err(err) = object.write_stream(&mut file) {222        return Err(format!("error writing object file: {}", err));223    }224    let file = match file.into_inner() {225        Ok(file) => file,226        Err(err) => return Err(format!("error writing object file: {}", err)),227    };228229    if prof.enabled() {230        prof.artifact_size(231            "object_file",232            tmp_file.file_name().unwrap().to_string_lossy(),233            file.metadata().unwrap().len(),234        );235    }236237    Ok(CompiledModule {238        name,239        kind,240        object: Some(tmp_file),241        dwarf_object: None,242        bytecode: None,243        assembly: None,244        llvm_ir: None,245        links_from_incr_cache: Vec::new(),246    })247}248249fn reuse_workproduct_for_cgu(250    tcx: TyCtxt<'_>,251    cgu: &CodegenUnit<'_>,252) -> Result<ModuleCodegenResult, String> {253    let work_product = cgu.previous_work_product(tcx);254    let obj_out_regular = tcx.output_filenames(()).temp_path_for_cgu(255        OutputType::Object,256        cgu.name().as_str(),257        tcx.sess.invocation_temp.as_deref(),258    );259    let source_file_regular = rustc_incremental::in_incr_comp_dir_sess(260        tcx.sess,261        work_product.saved_files.get("o").expect("no saved object file in work product"),262    );263264    if let Err(err) = rustc_fs_util::link_or_copy(&source_file_regular, &obj_out_regular) {265        return Err(format!(266            "unable to copy {} to {}: {}",267            source_file_regular.display(),268            obj_out_regular.display(),269            err270        ));271    }272273    let obj_out_global_asm =274        crate::global_asm::add_file_stem_postfix(obj_out_regular.clone(), ".asm");275    let source_file_global_asm = if let Some(asm_o) = work_product.saved_files.get("asm.o") {276        let source_file_global_asm = rustc_incremental::in_incr_comp_dir_sess(tcx.sess, asm_o);277        if let Err(err) = rustc_fs_util::link_or_copy(&source_file_global_asm, &obj_out_global_asm)278        {279            return Err(format!(280                "unable to copy {} to {}: {}",281                source_file_global_asm.display(),282                obj_out_global_asm.display(),283                err284            ));285        }286        Some(source_file_global_asm)287    } else {288        None289    };290291    Ok(ModuleCodegenResult {292        module_regular: CompiledModule {293            name: cgu.name().to_string(),294            kind: ModuleKind::Regular,295            object: Some(obj_out_regular),296            dwarf_object: None,297            bytecode: None,298            assembly: None,299            llvm_ir: None,300            links_from_incr_cache: vec![source_file_regular],301        },302        module_global_asm: source_file_global_asm.map(|source_file| CompiledModule {303            name: cgu.name().to_string(),304            kind: ModuleKind::Regular,305            object: Some(obj_out_global_asm),306            dwarf_object: None,307            bytecode: None,308            assembly: None,309            llvm_ir: None,310            links_from_incr_cache: vec![source_file],311        }),312        existing_work_product: Some((cgu.work_product_id(), work_product)),313    })314}315316fn codegen_cgu_content(317    tcx: TyCtxt<'_>,318    module: &mut dyn Module,319    cgu_name: rustc_span::Symbol,320) -> (Option<DebugContext>, Vec<CodegenedFunction>, String) {321    let _timer = tcx.prof.generic_activity_with_arg("codegen cgu", cgu_name.as_str());322323    let cgu = tcx.codegen_unit(cgu_name);324    let mono_items = cgu.items_in_deterministic_order(tcx);325326    let mut debug_context = DebugContext::new(tcx, module.isa(), false, cgu_name.as_str());327    let mut global_asm = String::new();328    let mut type_dbg = TypeDebugContext::default();329    super::predefine_mono_items(tcx, module, &mono_items);330    let mut codegened_functions = vec![];331    for (mono_item, item_data) in mono_items {332        match mono_item {333            MonoItem::Fn(instance) => {334                let flags = tcx.codegen_instance_attrs(instance.def).flags;335                if flags.contains(CodegenFnAttrFlags::NAKED) {336                    rustc_codegen_ssa::mir::naked_asm::codegen_naked_asm(337                        &mut GlobalAsmContext { tcx, global_asm: &mut global_asm },338                        instance,339                        MonoItemData {340                            linkage: RLinkage::External,341                            visibility: if item_data.linkage == RLinkage::Internal {342                                Visibility::Hidden343                            } else {344                                item_data.visibility345                            },346                            ..item_data347                        },348                    );349                    continue;350                }351                let codegened_function = crate::base::codegen_fn(352                    tcx,353                    cgu_name,354                    debug_context.as_mut(),355                    &mut type_dbg,356                    Function::new(),357                    module,358                    instance,359                );360                codegened_functions.push(codegened_function);361            }362            MonoItem::Static(def_id) => {363                let data_id = crate::constant::codegen_static(tcx, module, def_id);364                if let Some(debug_context) = debug_context.as_mut() {365                    debug_context.define_static(tcx, &mut type_dbg, def_id, data_id);366                }367            }368            MonoItem::GlobalAsm(item_id) => {369                rustc_codegen_ssa::base::codegen_global_asm(370                    &mut GlobalAsmContext { tcx, global_asm: &mut global_asm },371                    item_id,372                );373            }374        }375    }376    crate::main_shim::maybe_create_entry_wrapper(tcx, module, false, cgu.is_primary());377378    (debug_context, codegened_functions, global_asm)379}380381fn module_codegen(382    tcx: TyCtxt<'_>,383    global_asm_config: Arc<GlobalAsmConfig>,384    cgu_name: rustc_span::Symbol,385    token: ConcurrencyLimiterToken,386) -> OngoingModuleCodegen {387    let mut module = make_module(tcx.sess, cgu_name.as_str().to_string());388389    let (mut debug_context, codegened_functions, mut global_asm) =390        codegen_cgu_content(tcx, &mut module, cgu_name);391392    let cgu_name = cgu_name.as_str().to_owned();393394    let producer = crate::debuginfo::producer(tcx.sess);395396    let profiler = tcx.prof.clone();397    let invocation_temp = tcx.sess.invocation_temp.clone();398    let output_filenames = tcx.output_filenames(()).clone();399    let should_write_ir = crate::pretty_clif::should_write_ir(tcx.sess);400401    OngoingModuleCodegen::Async(std::thread::spawn(move || {402        profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| {403            cranelift_codegen::timing::set_thread_profiler(Box::new(super::MeasuremeProfiler(404                profiler.clone(),405            )));406407            let mut cached_context = Context::new();408            for codegened_func in codegened_functions {409                crate::base::compile_fn(410                    &profiler,411                    &output_filenames,412                    should_write_ir,413                    &mut cached_context,414                    &mut module,415                    debug_context.as_mut(),416                    &mut global_asm,417                    codegened_func,418                );419            }420        });421422        let global_asm_object_file =423            profiler.generic_activity_with_arg("compile assembly", &*cgu_name).run(|| {424                crate::global_asm::compile_global_asm(425                    &global_asm_config,426                    &cgu_name,427                    global_asm,428                    invocation_temp.as_deref(),429                )430            })?;431432        let codegen_result =433            profiler.generic_activity_with_arg("write object file", &*cgu_name).run(|| {434                emit_cgu(435                    &global_asm_config.output_filenames,436                    invocation_temp.as_deref(),437                    &profiler,438                    cgu_name,439                    module,440                    debug_context,441                    global_asm_object_file,442                    &producer,443                )444            });445        std::mem::drop(token);446        codegen_result447    }))448}449450fn emit_allocator_module(tcx: TyCtxt<'_>) -> Option<CompiledModule> {451    let mut allocator_module = make_module(tcx.sess, "allocator_shim".to_string());452    let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module);453454    if created_alloc_shim {455        let product = allocator_module.finish();456457        match emit_module(458            tcx.output_filenames(()),459            tcx.sess.invocation_temp.as_deref(),460            &tcx.sess.prof,461            product.object,462            ModuleKind::Allocator,463            "allocator_shim".to_owned(),464            &crate::debuginfo::producer(tcx.sess),465        ) {466            Ok(allocator_module) => Some(allocator_module),467            Err(err) => tcx.dcx().fatal(err),468        }469    } else {470        None471    }472}473474pub(crate) fn run_aot(tcx: TyCtxt<'_>) -> Box<OngoingCodegen> {475    let cgus = tcx.collect_and_partition_mono_items(()).codegen_units;476477    if tcx.dep_graph.is_fully_enabled() {478        for cgu in cgus {479            tcx.ensure_ok().codegen_unit(cgu.name());480        }481    }482483    // Calculate the CGU reuse484    let cgu_reuse = tcx.sess.time("find_cgu_reuse", || {485        cgus.iter().map(|cgu| determine_cgu_reuse(tcx, cgu)).collect::<Vec<_>>()486    });487488    rustc_codegen_ssa::assert_module_sources::assert_module_sources(tcx, &|cgu_reuse_tracker| {489        for (i, cgu) in cgus.iter().enumerate() {490            let cgu_reuse = cgu_reuse[i];491            cgu_reuse_tracker.set_actual_reuse(cgu.name().as_str(), cgu_reuse);492        }493    });494495    let global_asm_config = Arc::new(crate::global_asm::GlobalAsmConfig::new(tcx));496497    let disable_incr_cache = disable_incr_cache();498    let (todo_cgus, done_cgus) =499        cgus.iter().enumerate().partition::<Vec<_>, _>(|&(i, _)| match cgu_reuse[i] {500            _ if disable_incr_cache => true,501            CguReuse::No => true,502            CguReuse::PreLto | CguReuse::PostLto => false,503        });504505    let concurrency_limiter = IntoDynSyncSend(ConcurrencyLimiter::new(todo_cgus.len()));506507    let modules: Vec<_> =508        tcx.sess.time("codegen mono items", || {509            let modules: Vec<_> = par_map(todo_cgus, |(_, cgu)| {510                let dep_node = cgu.codegen_dep_node(tcx);511                let (module, _) = tcx.dep_graph.with_task(512                    dep_node,513                    tcx,514                    || {515                        module_codegen(516                            tcx,517                            global_asm_config.clone(),518                            cgu.name(),519                            concurrency_limiter.acquire(tcx.dcx()),520                        )521                    },522                    Some(rustc_middle::dep_graph::hash_result),523                );524                IntoDynSyncSend(module)525            });526            modules527                .into_iter()528                .map(|module| module.0)529                .chain(done_cgus.into_iter().map(|(_, cgu)| {530                    OngoingModuleCodegen::Sync(reuse_workproduct_for_cgu(tcx, cgu))531                }))532                .collect()533        });534535    let allocator_module = emit_allocator_module(tcx);536537    Box::new(OngoingCodegen {538        modules,539        allocator_module,540        concurrency_limiter: concurrency_limiter.0,541    })542}

Code quality findings 17

Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
//! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
("o", module_regular.object.as_ref().unwrap()),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
("asm.o", module_global_asm.object.as_ref().unwrap()),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
&[("o", module_regular.object.as_ref().unwrap())],
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
tmp_file.file_name().unwrap().to_string_lossy(),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
file.metadata().unwrap().len(),
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
work_product.saved_files.get("o").expect("no saved object file in work product"),
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
let cgu_reuse = cgu_reuse[i];
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
cgus.iter().enumerate().partition::<Vec<_>, _>(|&(i, _)| match cgu_reuse[i] {
Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use crate::prelude::*;
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info correctness unhandled-result
let file = match File::create(&tmp_file) {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
let output_filenames = tcx.output_filenames(()).clone();
Info: Raw thread spawned using std::thread::spawn. Ensure the JoinHandle is managed (usually by calling .join()) to wait for completion and handle potential panics, unless intentionally detached.
info correctness thread-spawn-unjoined
OngoingModuleCodegen::Async(std::thread::spawn(move || {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
profiler.clone().generic_activity_with_arg("compile functions", &*cgu_name).run(|| {
Performance Info: Frequent cloning, especially of Strings, Vecs, or other heap-allocated types inside loops, can be expensive. Consider using references/borrowing where possible.
info performance clone-in-loop
profiler.clone(),
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
cgus.iter().enumerate().partition::<Vec<_>, _>(|&(i, _)| match cgu_reuse[i] {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.