1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
//! Compilation backend pipeline: optimized IR to VCode / binemit.

use crate::ir::Function;
use crate::machinst::*;
use crate::settings;
use crate::timing;

use log::debug;
use regalloc::{allocate_registers_with_opts, Algorithm, Options, PrettyPrint};

/// Compile the given function down to VCode with allocated registers, ready
/// for binary emission.
pub fn compile<B: LowerBackend + MachBackend>(
    f: &Function,
    b: &B,
    abi: Box<dyn ABICallee<I = B::MInst>>,
    emit_info: <B::MInst as MachInstEmit>::Info,
) -> CodegenResult<VCode<B::MInst>>
where
    B::MInst: PrettyPrint,
{
    // Compute lowered block order.
    let block_order = BlockLoweringOrder::new(f);
    // Build the lowering context.
    let lower = Lower::new(f, abi, emit_info, block_order)?;
    // Lower the IR.
    let (mut vcode, stack_map_request_info) = {
        let _tt = timing::vcode_lower();
        lower.lower(b)?
    };

    debug!(
        "vcode from lowering: \n{}",
        vcode.show_rru(Some(b.reg_universe()))
    );

    // Perform register allocation.
    let (run_checker, algorithm) = match vcode.flags().regalloc() {
        settings::Regalloc::Backtracking => (false, Algorithm::Backtracking(Default::default())),
        settings::Regalloc::BacktrackingChecked => {
            (true, Algorithm::Backtracking(Default::default()))
        }
        settings::Regalloc::ExperimentalLinearScan => {
            (false, Algorithm::LinearScan(Default::default()))
        }
        settings::Regalloc::ExperimentalLinearScanChecked => {
            (true, Algorithm::LinearScan(Default::default()))
        }
    };

    #[cfg(feature = "regalloc-snapshot")]
    {
        use std::fs;
        use std::path::Path;
        if let Some(path) = std::env::var("SERIALIZE_REGALLOC").ok() {
            let snapshot = regalloc::IRSnapshot::from_function(&vcode, b.reg_universe());
            let serialized = bincode::serialize(&snapshot).expect("couldn't serialize snapshot");

            let file_path = Path::new(&path).join(Path::new(&format!("ir{}.bin", f.name)));
            fs::write(file_path, &serialized).expect("couldn't write IR snapshot file");
        }
    }

    // If either there are no reference-typed values, or else there are
    // but there are no safepoints at which we need to know about them,
    // then we don't need stack maps.
    let sri = if stack_map_request_info.reftyped_vregs.len() > 0
        && stack_map_request_info.safepoint_insns.len() > 0
    {
        Some(&stack_map_request_info)
    } else {
        None
    };

    let result = {
        let _tt = timing::regalloc();
        allocate_registers_with_opts(
            &mut vcode,
            b.reg_universe(),
            sri,
            Options {
                run_checker,
                algorithm,
            },
        )
        .map_err(|err| {
            debug!(
                "Register allocation error for vcode\n{}\nError: {:?}",
                vcode.show_rru(Some(b.reg_universe())),
                err
            );
            err
        })
        .expect("register allocation")
    };

    // Reorder vcode into final order and copy out final instruction sequence
    // all at once. This also inserts prologues/epilogues.
    {
        let _tt = timing::vcode_post_ra();
        vcode.replace_insns_from_regalloc(result);
    }

    debug!(
        "vcode after regalloc: final version:\n{}",
        vcode.show_rru(Some(b.reg_universe()))
    );

    Ok(vcode)
}