diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs index ca70293c05..2c3a84509e 100644 --- a/cranelift/codegen/src/context.rs +++ b/cranelift/codegen/src/context.rs @@ -19,8 +19,10 @@ use crate::flowgraph::ControlFlowGraph; use crate::ir::Function; use crate::isa::TargetIsa; use crate::legalize_function; +use crate::legalizer::simple_legalize; use crate::licm::do_licm; use crate::loop_analysis::LoopAnalysis; +use crate::machinst::MachCompileResult; use crate::nan_canonicalization::do_nan_canonicalization; use crate::postopt::do_postopt; use crate::redundant_reload_remover::RedundantReloadRemover; @@ -55,6 +57,12 @@ pub struct Context { /// Redundant-reload remover context. pub redundant_reload_remover: RedundantReloadRemover, + + /// Result of MachBackend compilation, if computed. + pub mach_compile_result: Option, + + /// Flag: do we want a disassembly with the MachCompileResult? + pub want_disasm: bool, } impl Context { @@ -78,6 +86,8 @@ impl Context { regalloc: regalloc::Context::new(), loop_analysis: LoopAnalysis::new(), redundant_reload_remover: RedundantReloadRemover::new(), + mach_compile_result: None, + want_disasm: false, } } @@ -89,6 +99,14 @@ impl Context { self.regalloc.clear(); self.loop_analysis.clear(); self.redundant_reload_remover.clear(); + self.mach_compile_result = None; + self.want_disasm = false; + } + + /// Set the flag to request a disassembly when compiling with a + /// `MachBackend` backend. + pub fn set_disasm(&mut self, val: bool) { + self.want_disasm = val; } /// Compile the function, and emit machine code into a `Vec`. @@ -130,9 +148,13 @@ impl Context { pub fn compile(&mut self, isa: &dyn TargetIsa) -> CodegenResult { let _tt = timing::compile(); self.verify_if(isa)?; - debug!("Compiling:\n{}", self.func.display(isa)); let opt_level = isa.flags().opt_level(); + debug!( + "Compiling (opt level {:?}):\n{}", + opt_level, + self.func.display(isa) + ); self.compute_cfg(); if opt_level != OptLevel::None { @@ -141,6 +163,7 @@ impl Context { if isa.flags().enable_nan_canonicalization() { self.canonicalize_nans(isa)?; } + self.legalize(isa)?; if opt_level != OptLevel::None { self.postopt(isa)?; @@ -149,23 +172,33 @@ impl Context { self.licm(isa)?; self.simple_gvn(isa)?; } + self.compute_domtree(); self.eliminate_unreachable_code(isa)?; if opt_level != OptLevel::None { self.dce(isa)?; } - self.regalloc(isa)?; - self.prologue_epilogue(isa)?; - if opt_level == OptLevel::Speed || opt_level == OptLevel::SpeedAndSize { - self.redundant_reload_remover(isa)?; - } - if opt_level == OptLevel::SpeedAndSize { - self.shrink_instructions(isa)?; - } - let result = self.relax_branches(isa); - debug!("Compiled:\n{}", self.func.display(isa)); - result + if let Some(backend) = isa.get_mach_backend() { + let func = std::mem::replace(&mut self.func, Function::new()); + let result = backend.compile_function(func, self.want_disasm)?; + let info = result.code_info(); + self.mach_compile_result = Some(result); + Ok(info) + } else { + self.regalloc(isa)?; + self.prologue_epilogue(isa)?; + if opt_level == OptLevel::Speed || opt_level == OptLevel::SpeedAndSize { + self.redundant_reload_remover(isa)?; + } + if opt_level == OptLevel::SpeedAndSize { + self.shrink_instructions(isa)?; + } + let result = self.relax_branches(isa); + + debug!("Compiled:\n{}", self.func.display(isa)); + result + } } /// Emit machine code directly into raw memory. @@ -191,7 +224,11 @@ impl Context { ) -> CodeInfo { let _tt = timing::binemit(); let mut sink = MemoryCodeSink::new(mem, relocs, traps, stackmaps); - isa.emit_function_to_memory(&self.func, &mut sink); + if let Some(ref result) = &self.mach_compile_result { + result.sections.emit(&mut sink); + } else { + isa.emit_function_to_memory(&self.func, &mut sink); + } sink.info } @@ -275,13 +312,19 @@ impl Context { /// Run the legalizer for `isa` on the function. pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> { - // Legalization invalidates the domtree and loop_analysis by mutating the CFG. - // TODO: Avoid doing this when legalization doesn't actually mutate the CFG. - self.domtree.clear(); - self.loop_analysis.clear(); - legalize_function(&mut self.func, &mut self.cfg, isa); - debug!("Legalized:\n{}", self.func.display(isa)); - self.verify_if(isa) + if isa.get_mach_backend().is_some() { + // Run some specific legalizations only. + simple_legalize(&mut self.func, &mut self.cfg, isa); + Ok(()) + } else { + // Legalization invalidates the domtree and loop_analysis by mutating the CFG. + // TODO: Avoid doing this when legalization doesn't actually mutate the CFG. + self.domtree.clear(); + self.loop_analysis.clear(); + legalize_function(&mut self.func, &mut self.cfg, isa); + debug!("Legalized:\n{}", self.func.display(isa)); + self.verify_if(isa) + } } /// Perform post-legalization rewrites on the function. diff --git a/cranelift/codegen/src/ir/function.rs b/cranelift/codegen/src/ir/function.rs index 1e72d2bc48..7e3cf71956 100644 --- a/cranelift/codegen/src/ir/function.rs +++ b/cranelift/codegen/src/ir/function.rs @@ -3,6 +3,8 @@ //! The `Function` struct defined in this module owns all of its basic blocks and //! instructions. +#![allow(unused_imports)] + use crate::binemit::CodeOffset; use crate::entity::{PrimaryMap, SecondaryMap}; use crate::ir; @@ -17,6 +19,7 @@ use crate::isa::{CallConv, EncInfo, Encoding, Legalize, TargetIsa}; use crate::regalloc::{EntryRegDiversions, RegDiversions}; use crate::value_label::ValueLabelsRanges; use crate::write::write_function; +use alloc::boxed::Box; use core::fmt; /// A function. @@ -238,13 +241,21 @@ impl Function { /// Wrapper around `encode` which assigns `inst` the resulting encoding. pub fn update_encoding(&mut self, inst: ir::Inst, isa: &dyn TargetIsa) -> Result<(), Legalize> { - self.encode(inst, isa).map(|e| self.encodings[inst] = e) + if isa.get_mach_backend().is_some() { + Ok(()) + } else { + self.encode(inst, isa).map(|e| self.encodings[inst] = e) + } } /// Wrapper around `TargetIsa::encode` for encoding an existing instruction /// in the `Function`. pub fn encode(&self, inst: ir::Inst, isa: &dyn TargetIsa) -> Result { - isa.encode(&self, &self.dfg[inst], self.dfg.ctrl_typevar(inst)) + if isa.get_mach_backend().is_some() { + Ok(Encoding::new(0, 0)) + } else { + isa.encode(&self, &self.dfg[inst], self.dfg.ctrl_typevar(inst)) + } } /// Starts collection of debug information. diff --git a/cranelift/codegen/src/isa/mod.rs b/cranelift/codegen/src/isa/mod.rs index c94707690a..a0a2a5de87 100644 --- a/cranelift/codegen/src/isa/mod.rs +++ b/cranelift/codegen/src/isa/mod.rs @@ -48,6 +48,7 @@ pub use crate::isa::call_conv::CallConv; pub use crate::isa::constraints::{ BranchRange, ConstraintKind, OperandConstraint, RecipeConstraints, }; +pub use crate::isa::enc_tables::Encodings; pub use crate::isa::encoding::{base_size, EncInfo, Encoding}; pub use crate::isa::registers::{regs_overlap, RegClass, RegClassIndex, RegInfo, RegUnit}; pub use crate::isa::stack::{StackBase, StackBaseMask, StackRef}; @@ -55,9 +56,8 @@ pub use crate::isa::stack::{StackBase, StackBaseMask, StackRef}; use crate::binemit; use crate::flowgraph; use crate::ir; -pub use crate::isa::enc_tables::Encodings; -#[cfg(feature = "unwind")] use crate::isa::fde::RegisterMappingError; +#[cfg(feature = "unwind")] use crate::machinst::MachBackend; use crate::regalloc; use crate::result::CodegenResult; @@ -117,6 +117,7 @@ pub fn lookup(triple: Triple) -> Result { isa_builder!(x86, "x86", triple) } Architecture::Arm { .. } => isa_builder!(arm32, "arm32", triple), + Architecture::Aarch64 { .. } => isa_builder!(arm64, "arm64", triple), _ => Err(LookupError::Unsupported), } } diff --git a/cranelift/codegen/src/isa/test_utils.rs b/cranelift/codegen/src/isa/test_utils.rs new file mode 100644 index 0000000000..826fabf949 --- /dev/null +++ b/cranelift/codegen/src/isa/test_utils.rs @@ -0,0 +1,83 @@ +use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc}; +use crate::ir::Value; +use crate::ir::{ConstantOffset, ExternalName, Function, JumpTable, Opcode, SourceLoc, TrapCode}; +use crate::isa::TargetIsa; + +use alloc::vec::Vec; +use std::string::{String, ToString}; + +pub struct TestCodeSink { + bytes: Vec, +} + +impl TestCodeSink { + /// Create a new TestCodeSink. + pub fn new() -> TestCodeSink { + TestCodeSink { bytes: vec![] } + } + + /// This is pretty lame, but whatever .. + pub fn stringify(&self) -> String { + let mut s = "".to_string(); + for b in &self.bytes { + s = s + &format!("{:02X}", b).to_string(); + } + s + } +} + +impl CodeSink for TestCodeSink { + fn offset(&self) -> CodeOffset { + self.bytes.len() as CodeOffset + } + + fn put1(&mut self, x: u8) { + self.bytes.push(x); + } + + fn put2(&mut self, x: u16) { + self.bytes.push((x >> 0) as u8); + self.bytes.push((x >> 8) as u8); + } + + fn put4(&mut self, mut x: u32) { + for _ in 0..4 { + self.bytes.push(x as u8); + x >>= 8; + } + } + + fn put8(&mut self, mut x: u64) { + for _ in 0..8 { + self.bytes.push(x as u8); + x >>= 8; + } + } + + fn reloc_block(&mut self, _rel: Reloc, _block_offset: CodeOffset) {} + + fn reloc_external( + &mut self, + _srcloc: SourceLoc, + _rel: Reloc, + _name: &ExternalName, + _addend: Addend, + ) { + } + + fn reloc_constant(&mut self, _rel: Reloc, _constant_offset: ConstantOffset) {} + + fn reloc_jt(&mut self, _rel: Reloc, _jt: JumpTable) {} + + fn trap(&mut self, _code: TrapCode, _srcloc: SourceLoc) {} + + fn begin_jumptables(&mut self) {} + + fn begin_rodata(&mut self) {} + + fn end_codegen(&mut self) {} + + fn add_stackmap(&mut self, _val_list: &[Value], _func: &Function, _isa: &dyn TargetIsa) {} + + fn add_call_site(&mut self, _opcode: Opcode, _srcloc: SourceLoc) {} +} diff --git a/cranelift/codegen/src/postopt.rs b/cranelift/codegen/src/postopt.rs index 42121817d5..b6c36434a1 100644 --- a/cranelift/codegen/src/postopt.rs +++ b/cranelift/codegen/src/postopt.rs @@ -360,6 +360,7 @@ fn optimize_complex_addresses(pos: &mut EncCursor, inst: Inst, isa: &dyn TargetI pub fn do_postopt(func: &mut Function, isa: &dyn TargetIsa) { let _tt = timing::postopt(); let mut pos = EncCursor::new(func, isa); + let is_mach_backend = isa.get_mach_backend().is_some(); while let Some(_block) = pos.next_block() { let mut last_flags_clobber = None; while let Some(inst) = pos.next_inst() { @@ -367,13 +368,15 @@ pub fn do_postopt(func: &mut Function, isa: &dyn TargetIsa) { // Optimize instructions to make use of flags. optimize_cpu_flags(&mut pos, inst, last_flags_clobber, isa); - // Track the most recent seen instruction that clobbers the flags. - if let Some(constraints) = isa - .encoding_info() - .operand_constraints(pos.func.encodings[inst]) - { - if constraints.clobbers_flags { - last_flags_clobber = Some(inst) + if !is_mach_backend { + // Track the most recent seen instruction that clobbers the flags. + if let Some(constraints) = isa + .encoding_info() + .operand_constraints(pos.func.encodings[inst]) + { + if constraints.clobbers_flags { + last_flags_clobber = Some(inst) + } } } } diff --git a/cranelift/codegen/src/verifier/flags.rs b/cranelift/codegen/src/verifier/flags.rs index 1a20303d20..76e83ab88a 100644 --- a/cranelift/codegen/src/verifier/flags.rs +++ b/cranelift/codegen/src/verifier/flags.rs @@ -28,13 +28,17 @@ pub fn verify_flags( errors: &mut VerifierErrors, ) -> VerifierStepResult<()> { let _tt = timing::verify_flags(); - let mut verifier = FlagsVerifier { - func, - cfg, - encinfo: isa.map(|isa| isa.encoding_info()), - livein: SecondaryMap::new(), - }; - verifier.check(errors) + if isa.is_none() || isa.unwrap().get_mach_backend().is_none() { + let mut verifier = FlagsVerifier { + func, + cfg, + encinfo: isa.map(|isa| isa.encoding_info()), + livein: SecondaryMap::new(), + }; + verifier.check(errors) + } else { + Ok(()) + } } struct FlagsVerifier<'a> { diff --git a/cranelift/src/compile.rs b/cranelift/src/compile.rs index 7d888f3113..4d71118876 100644 --- a/cranelift/src/compile.rs +++ b/cranelift/src/compile.rs @@ -49,42 +49,42 @@ fn handle_module( // If we have an isa from the command-line, use that. Otherwise if the // file contains a unique isa, use that. - let isa = if let Some(isa) = fisa.isa { - isa - } else if let Some(isa) = test_file.isa_spec.unique_isa() { - isa - } else { + let isa = fisa.isa.or(test_file.isa_spec.unique_isa()); + + if isa.is_none() { return Err(String::from("compilation requires a target isa")); }; for (func, _) in test_file.functions { - let mut context = Context::new(); - context.func = func; - let mut relocs = PrintRelocs::new(flag_print); let mut traps = PrintTraps::new(flag_print); let mut stackmaps = PrintStackmaps::new(flag_print); - let mut mem = vec![]; - // Compile and encode the result to machine code. - let code_info = context - .compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stackmaps) - .map_err(|err| pretty_error(&context.func, Some(isa), err))?; + if let Some(isa) = isa { + let mut context = Context::new(); + context.func = func; + let mut mem = vec![]; - if flag_print { - println!("{}", context.func.display(isa)); - } + // Compile and encode the result to machine code. + let code_info = context + .compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stackmaps) + .map_err(|err| pretty_error(&context.func, Some(isa), err))?; - if flag_disasm { - print_all( - isa, - &mem, - code_info.code_size, - code_info.jumptables_size + code_info.rodata_size, - &relocs, - &traps, - &stackmaps, - )?; + if flag_print { + println!("{}", context.func.display(isa)); + } + + if flag_disasm { + print_all( + isa, + &mem, + code_info.code_size, + code_info.jumptables_size + code_info.rodata_size, + &relocs, + &traps, + &stackmaps, + )?; + } } }