Skip to content

Commit

Permalink
Merge pull request #1494 from cfallin/arm64-merge
Browse files Browse the repository at this point in the history
Add new `MachInst` backend and ARM64 support.
  • Loading branch information
cfallin authored Apr 16, 2020
2 parents c268704 + 48cf2c2 commit 7da6101
Show file tree
Hide file tree
Showing 63 changed files with 16,668 additions and 322 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cranelift/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ gimli = { version = "0.20.0", default-features = false, features = ["write"], op
smallvec = { version = "1.0.0" }
thiserror = "1.0.4"
byteorder = { version = "1.3.2", default-features = false }
regalloc = "0.0.17"
# It is a goal of the cranelift-codegen crate to have minimal external dependencies.
# Please don't add any unless they are essential to the task of creating binary
# machine code. Integration tests that need external dependencies can be
Expand Down
2 changes: 2 additions & 0 deletions cranelift/codegen/meta/src/isa/arm64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let mut a64 = CpuMode::new("A64");

// TODO refine these.
let expand_flags = shared_defs.transform_groups.by_name("expand_flags");
let narrow_flags = shared_defs.transform_groups.by_name("narrow_flags");
a64.legalize_monomorphic(expand_flags);
a64.legalize_default(narrow_flags);

let cpu_modes = vec![a64];
Expand Down
4 changes: 3 additions & 1 deletion cranelift/codegen/src/binemit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ pub enum Reloc {
X86GOTPCRel4,
/// Arm32 call target
Arm32Call,
/// Arm64 call target
/// Arm64 call target. Encoded as bottom 26 bits of instruction. This
/// value is sign-extended, multiplied by 4, and added to the PC of
/// the call instruction to form the destination address.
Arm64Call,
/// RISC-V call target
RiscvCall,
Expand Down
74 changes: 58 additions & 16 deletions cranelift/codegen/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<MachCompileResult>,

/// Flag: do we want a disassembly with the MachCompileResult?
pub want_disasm: bool,
}

impl Context {
Expand All @@ -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,
}
}

Expand All @@ -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<u8>`.
Expand Down Expand Up @@ -130,9 +148,13 @@ impl Context {
pub fn compile(&mut self, isa: &dyn TargetIsa) -> CodegenResult<CodeInfo> {
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 {
Expand All @@ -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)?;
Expand All @@ -149,23 +172,32 @@ 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 result = backend.compile_function(&mut self.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.
Expand All @@ -191,7 +223,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
}

Expand Down Expand Up @@ -279,9 +315,15 @@ impl Context {
// 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);
self.verify_if(isa)
} else {
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.
Expand Down
39 changes: 3 additions & 36 deletions cranelift/codegen/src/dce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,10 @@
use crate::cursor::{Cursor, FuncCursor};
use crate::dominator_tree::DominatorTree;
use crate::entity::EntityRef;
use crate::ir::instructions::InstructionData;
use crate::ir::{DataFlowGraph, Function, Inst, Opcode};
use crate::inst_predicates::{any_inst_results_used, has_side_effect};
use crate::ir::Function;
use crate::timing;

/// Test whether the given opcode is unsafe to even consider for DCE.
fn trivially_unsafe_for_dce(opcode: Opcode) -> bool {
opcode.is_call()
|| opcode.is_branch()
|| opcode.is_terminator()
|| opcode.is_return()
|| opcode.can_trap()
|| opcode.other_side_effects()
|| opcode.can_store()
}

/// Preserve instructions with used result values.
fn any_inst_results_used(inst: Inst, live: &[bool], dfg: &DataFlowGraph) -> bool {
dfg.inst_results(inst).iter().any(|v| live[v.index()])
}

/// Load instructions without the `notrap` flag are defined to trap when
/// operating on inaccessible memory, so we can't DCE them even if the
/// loaded value is unused.
fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool {
if !opcode.can_load() {
return false;
}
match *data {
InstructionData::StackLoad { .. } => false,
InstructionData::Load { flags, .. } => !flags.notrap(),
_ => true,
}
}

/// Perform DCE on `func`.
pub fn do_dce(func: &mut Function, domtree: &mut DominatorTree) {
let _tt = timing::dce();
Expand All @@ -50,10 +20,7 @@ pub fn do_dce(func: &mut Function, domtree: &mut DominatorTree) {
let mut pos = FuncCursor::new(func).at_bottom(block);
while let Some(inst) = pos.prev_inst() {
{
let data = &pos.func.dfg[inst];
let opcode = data.opcode();
if trivially_unsafe_for_dce(opcode)
|| is_load_with_defined_trapping(opcode, &data)
if has_side_effect(pos.func, inst)
|| any_inst_results_used(inst, &live, &pos.func.dfg)
{
for arg in pos.func.dfg.inst_args(inst) {
Expand Down
42 changes: 42 additions & 0 deletions cranelift/codegen/src/inst_predicates.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Instruction predicates/properties, shared by various analyses.

use crate::ir::{DataFlowGraph, Function, Inst, InstructionData, Opcode};
use cranelift_entity::EntityRef;

/// Preserve instructions with used result values.
pub fn any_inst_results_used(inst: Inst, live: &[bool], dfg: &DataFlowGraph) -> bool {
dfg.inst_results(inst).iter().any(|v| live[v.index()])
}

/// Test whether the given opcode is unsafe to even consider as side-effect-free.
fn trivially_has_side_effects(opcode: Opcode) -> bool {
opcode.is_call()
|| opcode.is_branch()
|| opcode.is_terminator()
|| opcode.is_return()
|| opcode.can_trap()
|| opcode.other_side_effects()
|| opcode.can_store()
}

/// Load instructions without the `notrap` flag are defined to trap when
/// operating on inaccessible memory, so we can't treat them as side-effect-free even if the loaded
/// value is unused.
fn is_load_with_defined_trapping(opcode: Opcode, data: &InstructionData) -> bool {
if !opcode.can_load() {
return false;
}
match *data {
InstructionData::StackLoad { .. } => false,
InstructionData::Load { flags, .. } => !flags.notrap(),
_ => true,
}
}

/// Does the given instruction have any side-effect that would preclude it from being removed when
/// its value is unused?
pub fn has_side_effect(func: &Function, inst: Inst) -> bool {
let data = &func.dfg[inst];
let opcode = data.opcode();
trivially_has_side_effects(opcode) || is_load_with_defined_trapping(opcode, data)
}
12 changes: 10 additions & 2 deletions cranelift/codegen/src/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,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<Encoding, Legalize> {
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.
Expand Down
5 changes: 5 additions & 0 deletions cranelift/codegen/src/ir/immediates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ impl Imm64 {
pub fn wrapping_neg(self) -> Self {
Self(self.0.wrapping_neg())
}

/// Return bits of this immediate.
pub fn bits(&self) -> i64 {
self.0
}
}

impl Into<i64> for Imm64 {
Expand Down
Loading

0 comments on commit 7da6101

Please sign in to comment.