Address review comments:
- Undo temporary changes to default features (`all-arch`) and a
signal-handler test.
- Remove `SIGTRAP` handler: no longer needed now that we've found an
"undefined opcode" option on ARM64.
- Rename pp.rs to pretty_print.rs in machinst/.
- Only use empty stack-probe on non-x86. As per a comment in
rust-lang/compiler-builtins [1], LLVM only supports stack probes on
x86 and x86-64. Thus, on any other CPU architecture, we cannot refer
to `__rust_probestack`, because it does not exist.
- Rename arm64 to aarch64.
- Use `target` directive in vcode filetests.
- Run the flags verifier, but without encinfo, when using new backends.
- Clean up warning overrides.
- Fix up use of casts: use u32::from(x) and siblings when possible,
u32::try_from(x).unwrap() when not, to avoid silent truncation.
- Take immutable `Function` borrows as input; we don't actually
mutate the input IR.
- Lots of other miscellaneous cleanups.
[1] cae3e6ea23/src/probestack.rs (L39)
This commit is contained in:
@@ -34,7 +34,7 @@ regalloc = "0.0.17"
|
|||||||
cranelift-codegen-meta = { path = "meta", version = "0.62.0" }
|
cranelift-codegen-meta = { path = "meta", version = "0.62.0" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "unwind", "all-arch"]
|
default = ["std", "unwind"]
|
||||||
|
|
||||||
# The "std" feature enables use of libstd. The "core" feature enables use
|
# The "std" feature enables use of libstd. The "core" feature enables use
|
||||||
# of some minimal std-like replacement libraries. At least one of these two
|
# of some minimal std-like replacement libraries. At least one of these two
|
||||||
|
|||||||
@@ -180,8 +180,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(backend) = isa.get_mach_backend() {
|
if let Some(backend) = isa.get_mach_backend() {
|
||||||
let func = std::mem::replace(&mut self.func, Function::new());
|
let result = backend.compile_function(&mut self.func, self.want_disasm)?;
|
||||||
let result = backend.compile_function(func, self.want_disasm)?;
|
|
||||||
let info = result.code_info();
|
let info = result.code_info();
|
||||||
self.mach_compile_result = Some(result);
|
self.mach_compile_result = Some(result);
|
||||||
Ok(info)
|
Ok(info)
|
||||||
@@ -312,15 +311,15 @@ impl Context {
|
|||||||
|
|
||||||
/// Run the legalizer for `isa` on the function.
|
/// Run the legalizer for `isa` on the function.
|
||||||
pub fn legalize(&mut self, isa: &dyn TargetIsa) -> CodegenResult<()> {
|
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();
|
||||||
if isa.get_mach_backend().is_some() {
|
if isa.get_mach_backend().is_some() {
|
||||||
// Run some specific legalizations only.
|
// Run some specific legalizations only.
|
||||||
simple_legalize(&mut self.func, &mut self.cfg, isa);
|
simple_legalize(&mut self.func, &mut self.cfg, isa);
|
||||||
Ok(())
|
self.verify_if(isa)
|
||||||
} else {
|
} 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);
|
legalize_function(&mut self.func, &mut self.cfg, isa);
|
||||||
debug!("Legalized:\n{}", self.func.display(isa));
|
debug!("Legalized:\n{}", self.func.display(isa));
|
||||||
self.verify_if(isa)
|
self.verify_if(isa)
|
||||||
|
|||||||
@@ -6,48 +6,10 @@
|
|||||||
use crate::cursor::{Cursor, FuncCursor};
|
use crate::cursor::{Cursor, FuncCursor};
|
||||||
use crate::dominator_tree::DominatorTree;
|
use crate::dominator_tree::DominatorTree;
|
||||||
use crate::entity::EntityRef;
|
use crate::entity::EntityRef;
|
||||||
use crate::ir::instructions::InstructionData;
|
use crate::inst_predicates::{any_inst_results_used, has_side_effect};
|
||||||
use crate::ir::{DataFlowGraph, Function, Inst, Opcode};
|
use crate::ir::Function;
|
||||||
use crate::timing;
|
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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_unsafe_for_dce(opcode) || is_load_with_defined_trapping(opcode, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform DCE on `func`.
|
/// Perform DCE on `func`.
|
||||||
pub fn do_dce(func: &mut Function, domtree: &mut DominatorTree) {
|
pub fn do_dce(func: &mut Function, domtree: &mut DominatorTree) {
|
||||||
let _tt = timing::dce();
|
let _tt = timing::dce();
|
||||||
|
|||||||
42
cranelift/codegen/src/inst_predicates.rs
Normal file
42
cranelift/codegen/src/inst_predicates.rs
Normal file
@@ -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)
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@
|
|||||||
//! The `Function` struct defined in this module owns all of its basic blocks and
|
//! The `Function` struct defined in this module owns all of its basic blocks and
|
||||||
//! instructions.
|
//! instructions.
|
||||||
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
|
|
||||||
use crate::binemit::CodeOffset;
|
use crate::binemit::CodeOffset;
|
||||||
use crate::entity::{PrimaryMap, SecondaryMap};
|
use crate::entity::{PrimaryMap, SecondaryMap};
|
||||||
use crate::ir;
|
use crate::ir;
|
||||||
@@ -19,7 +17,6 @@ use crate::isa::{CallConv, EncInfo, Encoding, Legalize, TargetIsa};
|
|||||||
use crate::regalloc::{EntryRegDiversions, RegDiversions};
|
use crate::regalloc::{EntryRegDiversions, RegDiversions};
|
||||||
use crate::value_label::ValueLabelsRanges;
|
use crate::value_label::ValueLabelsRanges;
|
||||||
use crate::write::write_function;
|
use crate::write::write_function;
|
||||||
use alloc::boxed::Box;
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
/// A function.
|
/// A function.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
//! Implementation of the standard ARM64 ABI.
|
//! Implementation of the standard AArch64 ABI.
|
||||||
|
|
||||||
use crate::ir;
|
use crate::ir;
|
||||||
use crate::ir::types;
|
use crate::ir::types;
|
||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::StackSlot;
|
use crate::ir::StackSlot;
|
||||||
use crate::isa;
|
use crate::isa;
|
||||||
use crate::isa::arm64::inst::*;
|
use crate::isa::aarch64::inst::*;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
|
|
||||||
@@ -15,19 +15,16 @@ use regalloc::{RealReg, Reg, RegClass, Set, SpillSlot, Writable};
|
|||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
// A location for an argument or return value.
|
/// A location for an argument or return value.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum ABIArg {
|
enum ABIArg {
|
||||||
// In a real register.
|
/// In a real register.
|
||||||
Reg(RealReg, ir::Type),
|
Reg(RealReg, ir::Type),
|
||||||
// Arguments only: on stack, at given offset from SP at entry.
|
/// Arguments only: on stack, at given offset from SP at entry.
|
||||||
Stack(i64, ir::Type),
|
Stack(i64, ir::Type),
|
||||||
// (first and only) return value only: in memory pointed to by x8 on entry.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
RetMem(ir::Type),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ARM64 ABI information shared between body (callee) and caller.
|
/// AArch64 ABI information shared between body (callee) and caller.
|
||||||
struct ABISig {
|
struct ABISig {
|
||||||
args: Vec<ABIArg>,
|
args: Vec<ABIArg>,
|
||||||
rets: Vec<ABIArg>,
|
rets: Vec<ABIArg>,
|
||||||
@@ -161,11 +158,6 @@ impl ABISig {
|
|||||||
let (args, stack_arg_space) = compute_arg_locs(sig.call_conv, &sig.params);
|
let (args, stack_arg_space) = compute_arg_locs(sig.call_conv, &sig.params);
|
||||||
let (rets, _) = compute_arg_locs(sig.call_conv, &sig.returns);
|
let (rets, _) = compute_arg_locs(sig.call_conv, &sig.returns);
|
||||||
|
|
||||||
// Verify that there are no arguments in return-memory area.
|
|
||||||
assert!(args.iter().all(|a| match a {
|
|
||||||
&ABIArg::RetMem(..) => false,
|
|
||||||
_ => true,
|
|
||||||
}));
|
|
||||||
// Verify that there are no return values on the stack.
|
// Verify that there are no return values on the stack.
|
||||||
assert!(rets.iter().all(|a| match a {
|
assert!(rets.iter().all(|a| match a {
|
||||||
&ABIArg::Stack(..) => false,
|
&ABIArg::Stack(..) => false,
|
||||||
@@ -181,14 +173,21 @@ impl ABISig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ARM64 ABI object for a function body.
|
/// AArch64 ABI object for a function body.
|
||||||
pub struct ARM64ABIBody {
|
pub struct AArch64ABIBody {
|
||||||
sig: ABISig, // signature: arg and retval regs
|
/// signature: arg and retval regs
|
||||||
stackslots: Vec<usize>, // offsets to each stackslot
|
sig: ABISig,
|
||||||
stackslots_size: usize, // total stack size of all stackslots
|
/// offsets to each stackslot
|
||||||
clobbered: Set<Writable<RealReg>>, // clobbered registers, from regalloc.
|
stackslots: Vec<u32>,
|
||||||
spillslots: Option<usize>, // total number of spillslots, from regalloc.
|
/// total stack size of all stackslots
|
||||||
frame_size: Option<usize>,
|
stackslots_size: u32,
|
||||||
|
/// clobbered registers, from regalloc.
|
||||||
|
clobbered: Set<Writable<RealReg>>,
|
||||||
|
/// total number of spillslots, from regalloc.
|
||||||
|
spillslots: Option<usize>,
|
||||||
|
/// Total frame size.
|
||||||
|
frame_size: Option<u32>,
|
||||||
|
/// Calling convention this function expects.
|
||||||
call_conv: isa::CallConv,
|
call_conv: isa::CallConv,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,20 +206,31 @@ fn in_vec_reg(ty: ir::Type) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ARM64ABIBody {
|
impl AArch64ABIBody {
|
||||||
/// Create a new body ABI instance.
|
/// Create a new body ABI instance.
|
||||||
pub fn new(f: &ir::Function) -> Self {
|
pub fn new(f: &ir::Function) -> Self {
|
||||||
debug!("ARM64 ABI: func signature {:?}", f.signature);
|
debug!("AArch64 ABI: func signature {:?}", f.signature);
|
||||||
|
|
||||||
let sig = ABISig::from_func_sig(&f.signature);
|
let sig = ABISig::from_func_sig(&f.signature);
|
||||||
|
|
||||||
|
let call_conv = f.signature.call_conv;
|
||||||
|
// Only these calling conventions are supported.
|
||||||
|
assert!(
|
||||||
|
call_conv == isa::CallConv::SystemV
|
||||||
|
|| call_conv == isa::CallConv::Fast
|
||||||
|
|| call_conv == isa::CallConv::Cold
|
||||||
|
|| call_conv.extends_baldrdash(),
|
||||||
|
"Unsupported calling convention: {:?}",
|
||||||
|
call_conv
|
||||||
|
);
|
||||||
|
|
||||||
// Compute stackslot locations and total stackslot size.
|
// Compute stackslot locations and total stackslot size.
|
||||||
let mut stack_offset: usize = 0;
|
let mut stack_offset: u32 = 0;
|
||||||
let mut stackslots = vec![];
|
let mut stackslots = vec![];
|
||||||
for (stackslot, data) in f.stack_slots.iter() {
|
for (stackslot, data) in f.stack_slots.iter() {
|
||||||
let off = stack_offset;
|
let off = stack_offset;
|
||||||
stack_offset += data.size as usize;
|
stack_offset += data.size;
|
||||||
stack_offset = (stack_offset + 7) & !7usize;
|
stack_offset = (stack_offset + 7) & !7;
|
||||||
assert_eq!(stackslot.as_u32() as usize, stackslots.len());
|
assert_eq!(stackslot.as_u32() as usize, stackslots.len());
|
||||||
stackslots.push(off);
|
stackslots.push(off);
|
||||||
}
|
}
|
||||||
@@ -232,7 +242,7 @@ impl ARM64ABIBody {
|
|||||||
clobbered: Set::empty(),
|
clobbered: Set::empty(),
|
||||||
spillslots: None,
|
spillslots: None,
|
||||||
frame_size: None,
|
frame_size: None,
|
||||||
call_conv: f.signature.call_conv,
|
call_conv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,7 +274,7 @@ fn load_stack(fp_offset: i64, into_reg: Writable<Reg>, ty: Type) -> Inst {
|
|||||||
mem,
|
mem,
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
},
|
},
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!("load_stack({})", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +305,7 @@ fn store_stack(fp_offset: i64, from_reg: Reg, ty: Type) -> Inst {
|
|||||||
mem,
|
mem,
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
},
|
},
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!("store_stack({})", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,11 +412,13 @@ fn get_caller_saves_set(call_conv: isa::CallConv) -> Set<Writable<Reg>> {
|
|||||||
set
|
set
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ABIBody<Inst> for ARM64ABIBody {
|
impl ABIBody for AArch64ABIBody {
|
||||||
|
type I = Inst;
|
||||||
|
|
||||||
fn liveins(&self) -> Set<RealReg> {
|
fn liveins(&self) -> Set<RealReg> {
|
||||||
let mut set: Set<RealReg> = Set::empty();
|
let mut set: Set<RealReg> = Set::empty();
|
||||||
for arg in &self.sig.args {
|
for &arg in &self.sig.args {
|
||||||
if let &ABIArg::Reg(r, _) = arg {
|
if let ABIArg::Reg(r, _) = arg {
|
||||||
set.insert(r);
|
set.insert(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -415,8 +427,8 @@ impl ABIBody<Inst> for ARM64ABIBody {
|
|||||||
|
|
||||||
fn liveouts(&self) -> Set<RealReg> {
|
fn liveouts(&self) -> Set<RealReg> {
|
||||||
let mut set: Set<RealReg> = Set::empty();
|
let mut set: Set<RealReg> = Set::empty();
|
||||||
for ret in &self.sig.rets {
|
for &ret in &self.sig.rets {
|
||||||
if let &ABIArg::Reg(r, _) = ret {
|
if let ABIArg::Reg(r, _) = ret {
|
||||||
set.insert(r);
|
set.insert(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,7 +451,6 @@ impl ABIBody<Inst> for ARM64ABIBody {
|
|||||||
match &self.sig.args[idx] {
|
match &self.sig.args[idx] {
|
||||||
&ABIArg::Reg(r, ty) => Inst::gen_move(into_reg, r.to_reg(), ty),
|
&ABIArg::Reg(r, ty) => Inst::gen_move(into_reg, r.to_reg(), ty),
|
||||||
&ABIArg::Stack(off, ty) => load_stack(off + 16, into_reg, ty),
|
&ABIArg::Stack(off, ty) => load_stack(off + 16, into_reg, ty),
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +458,6 @@ impl ABIBody<Inst> for ARM64ABIBody {
|
|||||||
match &self.sig.rets[idx] {
|
match &self.sig.rets[idx] {
|
||||||
&ABIArg::Reg(r, ty) => Inst::gen_move(Writable::from_reg(r.to_reg()), from_reg, ty),
|
&ABIArg::Reg(r, ty) => Inst::gen_move(Writable::from_reg(r.to_reg()), from_reg, ty),
|
||||||
&ABIArg::Stack(off, ty) => store_stack(off + 16, from_reg, ty),
|
&ABIArg::Stack(off, ty) => store_stack(off + 16, from_reg, ty),
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,7 +480,7 @@ impl ABIBody<Inst> for ARM64ABIBody {
|
|||||||
fn load_stackslot(
|
fn load_stackslot(
|
||||||
&self,
|
&self,
|
||||||
slot: StackSlot,
|
slot: StackSlot,
|
||||||
offset: usize,
|
offset: u32,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
into_reg: Writable<Reg>,
|
into_reg: Writable<Reg>,
|
||||||
) -> Inst {
|
) -> Inst {
|
||||||
@@ -480,7 +490,7 @@ impl ABIBody<Inst> for ARM64ABIBody {
|
|||||||
load_stack(fp_off, into_reg, ty)
|
load_stack(fp_off, into_reg, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_stackslot(&self, slot: StackSlot, offset: usize, ty: Type, from_reg: Reg) -> Inst {
|
fn store_stackslot(&self, slot: StackSlot, offset: u32, ty: Type, from_reg: Reg) -> Inst {
|
||||||
// Offset from beginning of stackslot area, which is at FP - stackslots_size.
|
// Offset from beginning of stackslot area, which is at FP - stackslots_size.
|
||||||
let stack_off = self.stackslots[slot.as_u32() as usize] as i64;
|
let stack_off = self.stackslots[slot.as_u32() as usize] as i64;
|
||||||
let fp_off: i64 = -(self.stackslots_size as i64) + stack_off + (offset as i64);
|
let fp_off: i64 = -(self.stackslots_size as i64) + stack_off + (offset as i64);
|
||||||
@@ -532,13 +542,13 @@ impl ABIBody<Inst> for ARM64ABIBody {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut total_stacksize = self.stackslots_size + 8 * self.spillslots.unwrap();
|
let mut total_stacksize = self.stackslots_size + 8 * self.spillslots.unwrap() as u32;
|
||||||
if self.call_conv.extends_baldrdash() {
|
if self.call_conv.extends_baldrdash() {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!flags.enable_probestack(),
|
!flags.enable_probestack(),
|
||||||
"baldrdash does not expect cranelift to emit stack probes"
|
"baldrdash does not expect cranelift to emit stack probes"
|
||||||
);
|
);
|
||||||
total_stacksize += flags.baldrdash_prologue_words() as usize * 8;
|
total_stacksize += flags.baldrdash_prologue_words() as u32 * 8;
|
||||||
}
|
}
|
||||||
let total_stacksize = (total_stacksize + 15) & !15; // 16-align the stack.
|
let total_stacksize = (total_stacksize + 15) & !15; // 16-align the stack.
|
||||||
|
|
||||||
@@ -692,7 +702,7 @@ impl ABIBody<Inst> for ARM64ABIBody {
|
|||||||
|
|
||||||
fn frame_size(&self) -> u32 {
|
fn frame_size(&self) -> u32 {
|
||||||
self.frame_size
|
self.frame_size
|
||||||
.expect("frame size not computed before prologue generation") as u32
|
.expect("frame size not computed before prologue generation")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
|
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32 {
|
||||||
@@ -719,8 +729,8 @@ enum CallDest {
|
|||||||
Reg(Reg),
|
Reg(Reg),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ARM64 ABI object for a function call.
|
/// AArch64 ABI object for a function call.
|
||||||
pub struct ARM64ABICall {
|
pub struct AArch64ABICall {
|
||||||
sig: ABISig,
|
sig: ABISig,
|
||||||
uses: Set<Reg>,
|
uses: Set<Reg>,
|
||||||
defs: Set<Writable<Reg>>,
|
defs: Set<Writable<Reg>>,
|
||||||
@@ -751,16 +761,16 @@ fn abisig_to_uses_and_defs(sig: &ABISig) -> (Set<Reg>, Set<Writable<Reg>>) {
|
|||||||
(uses, defs)
|
(uses, defs)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ARM64ABICall {
|
impl AArch64ABICall {
|
||||||
/// Create a callsite ABI object for a call directly to the specified function.
|
/// Create a callsite ABI object for a call directly to the specified function.
|
||||||
pub fn from_func(
|
pub fn from_func(
|
||||||
sig: &ir::Signature,
|
sig: &ir::Signature,
|
||||||
extname: &ir::ExternalName,
|
extname: &ir::ExternalName,
|
||||||
loc: ir::SourceLoc,
|
loc: ir::SourceLoc,
|
||||||
) -> ARM64ABICall {
|
) -> AArch64ABICall {
|
||||||
let sig = ABISig::from_func_sig(sig);
|
let sig = ABISig::from_func_sig(sig);
|
||||||
let (uses, defs) = abisig_to_uses_and_defs(&sig);
|
let (uses, defs) = abisig_to_uses_and_defs(&sig);
|
||||||
ARM64ABICall {
|
AArch64ABICall {
|
||||||
sig,
|
sig,
|
||||||
uses,
|
uses,
|
||||||
defs,
|
defs,
|
||||||
@@ -777,10 +787,10 @@ impl ARM64ABICall {
|
|||||||
ptr: Reg,
|
ptr: Reg,
|
||||||
loc: ir::SourceLoc,
|
loc: ir::SourceLoc,
|
||||||
opcode: ir::Opcode,
|
opcode: ir::Opcode,
|
||||||
) -> ARM64ABICall {
|
) -> AArch64ABICall {
|
||||||
let sig = ABISig::from_func_sig(sig);
|
let sig = ABISig::from_func_sig(sig);
|
||||||
let (uses, defs) = abisig_to_uses_and_defs(&sig);
|
let (uses, defs) = abisig_to_uses_and_defs(&sig);
|
||||||
ARM64ABICall {
|
AArch64ABICall {
|
||||||
sig,
|
sig,
|
||||||
uses,
|
uses,
|
||||||
defs,
|
defs,
|
||||||
@@ -820,7 +830,9 @@ fn adjust_stack(amt: u64, is_sub: bool) -> Vec<Inst> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ABICall<Inst> for ARM64ABICall {
|
impl ABICall for AArch64ABICall {
|
||||||
|
type I = Inst;
|
||||||
|
|
||||||
fn num_args(&self) -> usize {
|
fn num_args(&self) -> usize {
|
||||||
self.sig.args.len()
|
self.sig.args.len()
|
||||||
}
|
}
|
||||||
@@ -841,14 +853,12 @@ impl ABICall<Inst> for ARM64ABICall {
|
|||||||
mem: MemArg::SPOffset(off),
|
mem: MemArg::SPOffset(off),
|
||||||
srcloc: None,
|
srcloc: None,
|
||||||
},
|
},
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_copy_retval_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> Inst {
|
fn gen_copy_retval_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> Inst {
|
||||||
match &self.sig.rets[idx] {
|
match &self.sig.rets[idx] {
|
||||||
&ABIArg::Reg(reg, ty) => Inst::gen_move(into_reg, reg.to_reg(), ty),
|
&ABIArg::Reg(reg, ty) => Inst::gen_move(into_reg, reg.to_reg(), ty),
|
||||||
&ABIArg::RetMem(..) => panic!("Return-memory area not yet supported"),
|
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,49 +1,34 @@
|
|||||||
//! ARM64 ISA definitions: instruction arguments.
|
//! AArch64 ISA definitions: instruction arguments.
|
||||||
|
|
||||||
|
// Some variants are never constructed, but we still want them as options in the future.
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use crate::binemit::{CodeOffset, CodeSink};
|
use crate::binemit::CodeOffset;
|
||||||
use crate::ir::constant::{ConstantData, ConstantOffset};
|
|
||||||
use crate::ir::Type;
|
use crate::ir::Type;
|
||||||
use crate::isa::arm64::inst::*;
|
use crate::isa::aarch64::inst::*;
|
||||||
use crate::machinst::*;
|
|
||||||
|
|
||||||
use regalloc::{
|
use regalloc::{RealRegUniverse, Reg, Writable};
|
||||||
RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, SpillSlot, VirtualReg, Writable,
|
|
||||||
NUM_REG_CLASSES,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::string::{String, ToString};
|
use core::convert::{Into, TryFrom};
|
||||||
|
use std::string::String;
|
||||||
|
|
||||||
/// A shift operator for a register or immediate.
|
/// A shift operator for a register or immediate.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum ShiftOp {
|
pub enum ShiftOp {
|
||||||
ASR,
|
LSL = 0b00,
|
||||||
LSR,
|
LSR = 0b01,
|
||||||
LSL,
|
ASR = 0b10,
|
||||||
ROR,
|
ROR = 0b11,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShiftOp {
|
impl ShiftOp {
|
||||||
/// Get the encoding of this shift op.
|
/// Get the encoding of this shift op.
|
||||||
pub fn bits(&self) -> u8 {
|
pub fn bits(self) -> u8 {
|
||||||
match self {
|
self as u8
|
||||||
&ShiftOp::LSL => 0b00,
|
|
||||||
&ShiftOp::LSR => 0b01,
|
|
||||||
&ShiftOp::ASR => 0b10,
|
|
||||||
&ShiftOp::ROR => 0b11,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A shift operator with an amount, guaranteed to be within range.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct ShiftOpAndAmt {
|
|
||||||
op: ShiftOp,
|
|
||||||
shift: ShiftOpShiftImm,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A shift operator amount.
|
/// A shift operator amount.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct ShiftOpShiftImm(u8);
|
pub struct ShiftOpShiftImm(u8);
|
||||||
@@ -62,11 +47,18 @@ impl ShiftOpShiftImm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the shift amount.
|
/// Return the shift amount.
|
||||||
pub fn value(&self) -> u8 {
|
pub fn value(self) -> u8 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A shift operator with an amount, guaranteed to be within range.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ShiftOpAndAmt {
|
||||||
|
op: ShiftOp,
|
||||||
|
shift: ShiftOpShiftImm,
|
||||||
|
}
|
||||||
|
|
||||||
impl ShiftOpAndAmt {
|
impl ShiftOpAndAmt {
|
||||||
pub fn new(op: ShiftOp, shift: ShiftOpShiftImm) -> ShiftOpAndAmt {
|
pub fn new(op: ShiftOp, shift: ShiftOpShiftImm) -> ShiftOpAndAmt {
|
||||||
ShiftOpAndAmt { op, shift }
|
ShiftOpAndAmt { op, shift }
|
||||||
@@ -74,7 +66,7 @@ impl ShiftOpAndAmt {
|
|||||||
|
|
||||||
/// Get the shift op.
|
/// Get the shift op.
|
||||||
pub fn op(&self) -> ShiftOp {
|
pub fn op(&self) -> ShiftOp {
|
||||||
self.op.clone()
|
self.op
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the shift amount.
|
/// Get the shift amount.
|
||||||
@@ -85,30 +77,22 @@ impl ShiftOpAndAmt {
|
|||||||
|
|
||||||
/// An extend operator for a register.
|
/// An extend operator for a register.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum ExtendOp {
|
pub enum ExtendOp {
|
||||||
SXTB,
|
UXTB = 0b000,
|
||||||
SXTH,
|
UXTH = 0b001,
|
||||||
SXTW,
|
UXTW = 0b010,
|
||||||
SXTX,
|
UXTX = 0b011,
|
||||||
UXTB,
|
SXTB = 0b100,
|
||||||
UXTH,
|
SXTH = 0b101,
|
||||||
UXTW,
|
SXTW = 0b110,
|
||||||
UXTX,
|
SXTX = 0b111,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtendOp {
|
impl ExtendOp {
|
||||||
/// Encoding of this op.
|
/// Encoding of this op.
|
||||||
pub fn bits(&self) -> u8 {
|
pub fn bits(self) -> u8 {
|
||||||
match self {
|
self as u8
|
||||||
&ExtendOp::UXTB => 0b000,
|
|
||||||
&ExtendOp::UXTH => 0b001,
|
|
||||||
&ExtendOp::UXTW => 0b010,
|
|
||||||
&ExtendOp::UXTX => 0b011,
|
|
||||||
&ExtendOp::SXTB => 0b100,
|
|
||||||
&ExtendOp::SXTH => 0b101,
|
|
||||||
&ExtendOp::SXTW => 0b110,
|
|
||||||
&ExtendOp::SXTX => 0b111,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,18 +112,34 @@ pub enum MemLabel {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum MemArg {
|
pub enum MemArg {
|
||||||
Label(MemLabel),
|
Label(MemLabel),
|
||||||
|
/// "post-indexed" mode as per AArch64 docs: postincrement reg after address computation.
|
||||||
PostIndexed(Writable<Reg>, SImm9),
|
PostIndexed(Writable<Reg>, SImm9),
|
||||||
|
/// "pre-indexed" mode as per AArch64 docs: preincrement reg before address computation.
|
||||||
PreIndexed(Writable<Reg>, SImm9),
|
PreIndexed(Writable<Reg>, SImm9),
|
||||||
|
|
||||||
// N.B.: RegReg, RegScaled, and RegScaledExtended all correspond to
|
// N.B.: RegReg, RegScaled, and RegScaledExtended all correspond to
|
||||||
// what the ISA calls the "register offset" addressing mode. We split out
|
// what the ISA calls the "register offset" addressing mode. We split out
|
||||||
// several options here for more ergonomic codegen.
|
// several options here for more ergonomic codegen.
|
||||||
|
/// Register plus register offset.
|
||||||
RegReg(Reg, Reg),
|
RegReg(Reg, Reg),
|
||||||
|
|
||||||
|
/// Register plus register offset, scaled by type's size.
|
||||||
RegScaled(Reg, Reg, Type),
|
RegScaled(Reg, Reg, Type),
|
||||||
|
|
||||||
|
/// Register plus register offset, scaled by type's size, with index sign- or zero-extended
|
||||||
|
/// first.
|
||||||
RegScaledExtended(Reg, Reg, Type, ExtendOp),
|
RegScaledExtended(Reg, Reg, Type, ExtendOp),
|
||||||
|
|
||||||
|
/// Unscaled signed 9-bit immediate offset from reg.
|
||||||
Unscaled(Reg, SImm9),
|
Unscaled(Reg, SImm9),
|
||||||
|
|
||||||
|
/// Scaled (by size of a type) unsigned 12-bit immediate offset from reg.
|
||||||
UnsignedOffset(Reg, UImm12Scaled),
|
UnsignedOffset(Reg, UImm12Scaled),
|
||||||
/// Offset from the stack pointer or frame pointer.
|
|
||||||
|
/// Offset from the stack pointer. Lowered into a real amode at emission.
|
||||||
SPOffset(i64),
|
SPOffset(i64),
|
||||||
|
|
||||||
|
/// Offset from the frame pointer. Lowered into a real amode at emission.
|
||||||
FPOffset(i64),
|
FPOffset(i64),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,9 +153,7 @@ impl MemArg {
|
|||||||
|
|
||||||
/// Memory reference using an address in a register and an offset, if possible.
|
/// Memory reference using an address in a register and an offset, if possible.
|
||||||
pub fn reg_maybe_offset(reg: Reg, offset: i64, value_type: Type) -> Option<MemArg> {
|
pub fn reg_maybe_offset(reg: Reg, offset: i64, value_type: Type) -> Option<MemArg> {
|
||||||
if offset == 0 {
|
if let Some(simm9) = SImm9::maybe_from_i64(offset) {
|
||||||
Some(MemArg::Unscaled(reg, SImm9::zero()))
|
|
||||||
} else if let Some(simm9) = SImm9::maybe_from_i64(offset) {
|
|
||||||
Some(MemArg::Unscaled(reg, simm9))
|
Some(MemArg::Unscaled(reg, simm9))
|
||||||
} else if let Some(uimm12s) = UImm12Scaled::maybe_from_i64(offset, value_type) {
|
} else if let Some(uimm12s) = UImm12Scaled::maybe_from_i64(offset, value_type) {
|
||||||
Some(MemArg::UnsignedOffset(reg, uimm12s))
|
Some(MemArg::UnsignedOffset(reg, uimm12s))
|
||||||
@@ -165,17 +163,18 @@ impl MemArg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Memory reference using the sum of two registers as an address.
|
/// Memory reference using the sum of two registers as an address.
|
||||||
pub fn reg_reg(reg1: Reg, reg2: Reg) -> MemArg {
|
pub fn reg_plus_reg(reg1: Reg, reg2: Reg) -> MemArg {
|
||||||
MemArg::RegReg(reg1, reg2)
|
MemArg::RegReg(reg1, reg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Memory reference using `reg1 + sizeof(ty) * reg2` as an address.
|
/// Memory reference using `reg1 + sizeof(ty) * reg2` as an address.
|
||||||
pub fn reg_reg_scaled(reg1: Reg, reg2: Reg, ty: Type) -> MemArg {
|
pub fn reg_plus_reg_scaled(reg1: Reg, reg2: Reg, ty: Type) -> MemArg {
|
||||||
MemArg::RegScaled(reg1, reg2, ty)
|
MemArg::RegScaled(reg1, reg2, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Memory reference using `reg1 + sizeof(ty) * reg2` as an address.
|
/// Memory reference using `reg1 + sizeof(ty) * reg2` as an address, with `reg2` sign- or
|
||||||
pub fn reg_reg_scaled_extended(reg1: Reg, reg2: Reg, ty: Type, op: ExtendOp) -> MemArg {
|
/// zero-extended as per `op`.
|
||||||
|
pub fn reg_plus_reg_scaled_extended(reg1: Reg, reg2: Reg, ty: Type, op: ExtendOp) -> MemArg {
|
||||||
MemArg::RegScaledExtended(reg1, reg2, ty, op)
|
MemArg::RegScaledExtended(reg1, reg2, ty, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,23 +198,24 @@ pub enum PairMemArg {
|
|||||||
|
|
||||||
/// Condition for conditional branches.
|
/// Condition for conditional branches.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum Cond {
|
pub enum Cond {
|
||||||
Eq,
|
Eq = 0,
|
||||||
Ne,
|
Ne = 1,
|
||||||
Hs,
|
Hs = 2,
|
||||||
Lo,
|
Lo = 3,
|
||||||
Mi,
|
Mi = 4,
|
||||||
Pl,
|
Pl = 5,
|
||||||
Vs,
|
Vs = 6,
|
||||||
Vc,
|
Vc = 7,
|
||||||
Hi,
|
Hi = 8,
|
||||||
Ls,
|
Ls = 9,
|
||||||
Ge,
|
Ge = 10,
|
||||||
Lt,
|
Lt = 11,
|
||||||
Gt,
|
Gt = 12,
|
||||||
Le,
|
Le = 13,
|
||||||
Al,
|
Al = 14,
|
||||||
Nv,
|
Nv = 15,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cond {
|
impl Cond {
|
||||||
@@ -224,18 +224,25 @@ impl Cond {
|
|||||||
match self {
|
match self {
|
||||||
Cond::Eq => Cond::Ne,
|
Cond::Eq => Cond::Ne,
|
||||||
Cond::Ne => Cond::Eq,
|
Cond::Ne => Cond::Eq,
|
||||||
|
|
||||||
Cond::Hs => Cond::Lo,
|
Cond::Hs => Cond::Lo,
|
||||||
Cond::Lo => Cond::Hs,
|
Cond::Lo => Cond::Hs,
|
||||||
|
|
||||||
Cond::Mi => Cond::Pl,
|
Cond::Mi => Cond::Pl,
|
||||||
Cond::Pl => Cond::Mi,
|
Cond::Pl => Cond::Mi,
|
||||||
|
|
||||||
Cond::Vs => Cond::Vc,
|
Cond::Vs => Cond::Vc,
|
||||||
Cond::Vc => Cond::Vs,
|
Cond::Vc => Cond::Vs,
|
||||||
|
|
||||||
Cond::Hi => Cond::Ls,
|
Cond::Hi => Cond::Ls,
|
||||||
Cond::Ls => Cond::Hi,
|
Cond::Ls => Cond::Hi,
|
||||||
|
|
||||||
Cond::Ge => Cond::Lt,
|
Cond::Ge => Cond::Lt,
|
||||||
Cond::Lt => Cond::Ge,
|
Cond::Lt => Cond::Ge,
|
||||||
|
|
||||||
Cond::Gt => Cond::Le,
|
Cond::Gt => Cond::Le,
|
||||||
Cond::Le => Cond::Gt,
|
Cond::Le => Cond::Gt,
|
||||||
|
|
||||||
Cond::Al => Cond::Nv,
|
Cond::Al => Cond::Nv,
|
||||||
Cond::Nv => Cond::Al,
|
Cond::Nv => Cond::Al,
|
||||||
}
|
}
|
||||||
@@ -243,24 +250,7 @@ impl Cond {
|
|||||||
|
|
||||||
/// Return the machine encoding of this condition.
|
/// Return the machine encoding of this condition.
|
||||||
pub fn bits(self) -> u32 {
|
pub fn bits(self) -> u32 {
|
||||||
match self {
|
self as u32
|
||||||
Cond::Eq => 0,
|
|
||||||
Cond::Ne => 1,
|
|
||||||
Cond::Hs => 2,
|
|
||||||
Cond::Lo => 3,
|
|
||||||
Cond::Mi => 4,
|
|
||||||
Cond::Pl => 5,
|
|
||||||
Cond::Vs => 6,
|
|
||||||
Cond::Vc => 7,
|
|
||||||
Cond::Hi => 8,
|
|
||||||
Cond::Ls => 9,
|
|
||||||
Cond::Ge => 10,
|
|
||||||
Cond::Lt => 11,
|
|
||||||
Cond::Gt => 12,
|
|
||||||
Cond::Le => 13,
|
|
||||||
Cond::Al => 14,
|
|
||||||
Cond::Nv => 15,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +295,7 @@ impl BranchTarget {
|
|||||||
pub fn lower(&mut self, targets: &[CodeOffset], my_offset: CodeOffset) {
|
pub fn lower(&mut self, targets: &[CodeOffset], my_offset: CodeOffset) {
|
||||||
match self {
|
match self {
|
||||||
&mut BranchTarget::Block(bix) => {
|
&mut BranchTarget::Block(bix) => {
|
||||||
let bix = bix as usize;
|
let bix = usize::try_from(bix).unwrap();
|
||||||
assert!(bix < targets.len());
|
assert!(bix < targets.len());
|
||||||
let block_offset_in_func = targets[bix];
|
let block_offset_in_func = targets[bix];
|
||||||
let branch_offset = (block_offset_in_func as isize) - (my_offset as isize);
|
let branch_offset = (block_offset_in_func as isize) - (my_offset as isize);
|
||||||
@@ -343,7 +333,7 @@ impl BranchTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the offset as a 16-bit offset, or `None` if overflow.
|
/// Get the offset as a 19-bit offset, or `None` if overflow.
|
||||||
pub fn as_off19(&self) -> Option<u32> {
|
pub fn as_off19(&self) -> Option<u32> {
|
||||||
let off = self.as_offset_words();
|
let off = self.as_offset_words();
|
||||||
if (off < (1 << 18)) && (off >= -(1 << 18)) {
|
if (off < (1 << 18)) && (off >= -(1 << 18)) {
|
||||||
@@ -357,7 +347,7 @@ impl BranchTarget {
|
|||||||
pub fn map(&mut self, block_index_map: &[BlockIndex]) {
|
pub fn map(&mut self, block_index_map: &[BlockIndex]) {
|
||||||
match self {
|
match self {
|
||||||
&mut BranchTarget::Block(ref mut bix) => {
|
&mut BranchTarget::Block(ref mut bix) => {
|
||||||
let n = block_index_map[*bix as usize];
|
let n = block_index_map[usize::try_from(*bix).unwrap()];
|
||||||
*bix = n;
|
*bix = n;
|
||||||
}
|
}
|
||||||
&mut BranchTarget::ResolvedOffset(_) => {}
|
&mut BranchTarget::ResolvedOffset(_) => {}
|
||||||
@@ -392,7 +382,7 @@ fn shift_for_type(ty: Type) -> usize {
|
|||||||
4 => 2,
|
4 => 2,
|
||||||
8 => 3,
|
8 => 3,
|
||||||
16 => 4,
|
16 => 4,
|
||||||
_ => panic!("unknown type"),
|
_ => panic!("unknown type: {}", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,15 +417,15 @@ impl ShowWithRRU for MemArg {
|
|||||||
}
|
}
|
||||||
&MemArg::RegScaledExtended(r1, r2, ty, op) => {
|
&MemArg::RegScaledExtended(r1, r2, ty, op) => {
|
||||||
let shift = shift_for_type(ty);
|
let shift = shift_for_type(ty);
|
||||||
let is32 = match op {
|
let size = match op {
|
||||||
ExtendOp::SXTW | ExtendOp::UXTW => true,
|
ExtendOp::SXTW | ExtendOp::UXTW => InstSize::Size32,
|
||||||
_ => false,
|
_ => InstSize::Size64,
|
||||||
};
|
};
|
||||||
let op = op.show_rru(mb_rru);
|
let op = op.show_rru(mb_rru);
|
||||||
format!(
|
format!(
|
||||||
"[{}, {}, {} #{}]",
|
"[{}, {}, {} #{}]",
|
||||||
r1.show_rru(mb_rru),
|
r1.show_rru(mb_rru),
|
||||||
show_ireg_sized(r2, mb_rru, is32),
|
show_ireg_sized(r2, mb_rru, size),
|
||||||
op,
|
op,
|
||||||
shift
|
shift
|
||||||
)
|
)
|
||||||
@@ -499,3 +489,40 @@ impl ShowWithRRU for BranchTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Type used to communicate the operand size of a machine instruction, as AArch64 has 32- and
|
||||||
|
/// 64-bit variants of many instructions (and integer registers).
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum InstSize {
|
||||||
|
Size32,
|
||||||
|
Size64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstSize {
|
||||||
|
/// 32-bit case?
|
||||||
|
pub fn is32(self) -> bool {
|
||||||
|
self == InstSize::Size32
|
||||||
|
}
|
||||||
|
/// 64-bit case?
|
||||||
|
pub fn is64(self) -> bool {
|
||||||
|
self == InstSize::Size64
|
||||||
|
}
|
||||||
|
/// Convert from an `is32` boolean flag to an `InstSize`.
|
||||||
|
pub fn from_is32(is32: bool) -> InstSize {
|
||||||
|
if is32 {
|
||||||
|
InstSize::Size32
|
||||||
|
} else {
|
||||||
|
InstSize::Size64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Convert from a needed width to the smallest size that fits.
|
||||||
|
pub fn from_bits<I: Into<usize>>(bits: I) -> InstSize {
|
||||||
|
let bits: usize = bits.into();
|
||||||
|
assert!(bits <= 64);
|
||||||
|
if bits <= 32 {
|
||||||
|
InstSize::Size32
|
||||||
|
} else {
|
||||||
|
InstSize::Size64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,14 @@
|
|||||||
//! ARM64 ISA: binary code emission.
|
//! AArch64 ISA: binary code emission.
|
||||||
|
|
||||||
#![allow(dead_code)]
|
use crate::binemit::{CodeOffset, Reloc};
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use crate::binemit::{CodeOffset, CodeSink, Reloc};
|
|
||||||
use crate::ir::constant::ConstantData;
|
use crate::ir::constant::ConstantData;
|
||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::{Opcode, TrapCode, Type};
|
use crate::ir::TrapCode;
|
||||||
use crate::isa::arm64::inst::*;
|
use crate::isa::aarch64::inst::*;
|
||||||
use crate::machinst::*;
|
|
||||||
use cranelift_entity::EntityRef;
|
|
||||||
|
|
||||||
use std::env;
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
use regalloc::{
|
use regalloc::{Reg, RegClass, Writable};
|
||||||
RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, SpillSlot, VirtualReg, Writable,
|
|
||||||
NUM_REG_CLASSES,
|
|
||||||
};
|
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
@@ -66,16 +58,7 @@ pub fn mem_finalize(insn_off: CodeOffset, mem: &MemArg) -> (Vec<Inst>, MemArg) {
|
|||||||
|
|
||||||
/// Helper: get a ConstantData from a u64.
|
/// Helper: get a ConstantData from a u64.
|
||||||
pub fn u64_constant(bits: u64) -> ConstantData {
|
pub fn u64_constant(bits: u64) -> ConstantData {
|
||||||
let data = [
|
let data = bits.to_le_bytes();
|
||||||
(bits & 0xff) as u8,
|
|
||||||
((bits >> 8) & 0xff) as u8,
|
|
||||||
((bits >> 16) & 0xff) as u8,
|
|
||||||
((bits >> 24) & 0xff) as u8,
|
|
||||||
((bits >> 32) & 0xff) as u8,
|
|
||||||
((bits >> 40) & 0xff) as u8,
|
|
||||||
((bits >> 48) & 0xff) as u8,
|
|
||||||
((bits >> 56) & 0xff) as u8,
|
|
||||||
];
|
|
||||||
ConstantData::from(&data[..])
|
ConstantData::from(&data[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,41 +67,42 @@ pub fn u64_constant(bits: u64) -> ConstantData {
|
|||||||
|
|
||||||
fn machreg_to_gpr(m: Reg) -> u32 {
|
fn machreg_to_gpr(m: Reg) -> u32 {
|
||||||
assert!(m.get_class() == RegClass::I64);
|
assert!(m.get_class() == RegClass::I64);
|
||||||
assert!(m.is_real());
|
u32::try_from(m.to_real_reg().get_hw_encoding()).unwrap()
|
||||||
m.to_real_reg().get_hw_encoding() as u32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn machreg_to_vec(m: Reg) -> u32 {
|
fn machreg_to_vec(m: Reg) -> u32 {
|
||||||
assert!(m.get_class() == RegClass::V128);
|
assert!(m.get_class() == RegClass::V128);
|
||||||
assert!(m.is_real());
|
u32::try_from(m.to_real_reg().get_hw_encoding()).unwrap()
|
||||||
m.to_real_reg().get_hw_encoding() as u32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn machreg_to_gpr_or_vec(m: Reg) -> u32 {
|
fn machreg_to_gpr_or_vec(m: Reg) -> u32 {
|
||||||
m.to_real_reg().get_hw_encoding() as u32
|
u32::try_from(m.to_real_reg().get_hw_encoding()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enc_arith_rrr(bits_31_21: u16, bits_15_10: u8, rd: Writable<Reg>, rn: Reg, rm: Reg) -> u32 {
|
fn enc_arith_rrr(bits_31_21: u32, bits_15_10: u32, rd: Writable<Reg>, rn: Reg, rm: Reg) -> u32 {
|
||||||
((bits_31_21 as u32) << 21)
|
(bits_31_21 << 21)
|
||||||
| ((bits_15_10 as u32) << 10)
|
| (bits_15_10 << 10)
|
||||||
| machreg_to_gpr(rd.to_reg())
|
| machreg_to_gpr(rd.to_reg())
|
||||||
| (machreg_to_gpr(rn) << 5)
|
| (machreg_to_gpr(rn) << 5)
|
||||||
| (machreg_to_gpr(rm) << 16)
|
| (machreg_to_gpr(rm) << 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enc_arith_rr_imm12(bits_31_24: u8, immshift: u8, imm12: u16, rn: Reg, rd: Writable<Reg>) -> u32 {
|
fn enc_arith_rr_imm12(
|
||||||
((bits_31_24 as u32) << 24)
|
bits_31_24: u32,
|
||||||
| ((immshift as u32) << 22)
|
immshift: u32,
|
||||||
| ((imm12 as u32) << 10)
|
imm12: u32,
|
||||||
|
rn: Reg,
|
||||||
|
rd: Writable<Reg>,
|
||||||
|
) -> u32 {
|
||||||
|
(bits_31_24 << 24)
|
||||||
|
| (immshift << 22)
|
||||||
|
| (imm12 << 10)
|
||||||
| (machreg_to_gpr(rn) << 5)
|
| (machreg_to_gpr(rn) << 5)
|
||||||
| machreg_to_gpr(rd.to_reg())
|
| machreg_to_gpr(rd.to_reg())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enc_arith_rr_imml(bits_31_23: u16, imm_bits: u16, rn: Reg, rd: Writable<Reg>) -> u32 {
|
fn enc_arith_rr_imml(bits_31_23: u32, imm_bits: u32, rn: Reg, rd: Writable<Reg>) -> u32 {
|
||||||
((bits_31_23 as u32) << 23)
|
(bits_31_23 << 23) | (imm_bits << 10) | (machreg_to_gpr(rn) << 5) | machreg_to_gpr(rd.to_reg())
|
||||||
| ((imm_bits as u32) << 10)
|
|
||||||
| (machreg_to_gpr(rn) << 5)
|
|
||||||
| machreg_to_gpr(rd.to_reg())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enc_arith_rrrr(top11: u32, rm: Reg, bit15: u32, ra: Reg, rn: Reg, rd: Writable<Reg>) -> u32 {
|
fn enc_arith_rrrr(top11: u32, rm: Reg, bit15: u32, ra: Reg, rn: Reg, rd: Writable<Reg>) -> u32 {
|
||||||
@@ -159,8 +143,8 @@ fn enc_move_wide(op: MoveWideOpcode, rd: Writable<Reg>, imm: MoveWideConst) -> u
|
|||||||
assert!(imm.shift <= 0b11);
|
assert!(imm.shift <= 0b11);
|
||||||
MOVE_WIDE_FIXED
|
MOVE_WIDE_FIXED
|
||||||
| (op as u32) << 29
|
| (op as u32) << 29
|
||||||
| (imm.shift as u32) << 21
|
| u32::from(imm.shift) << 21
|
||||||
| (imm.bits as u32) << 5
|
| u32::from(imm.bits) << 5
|
||||||
| machreg_to_gpr(rd.to_reg())
|
| machreg_to_gpr(rd.to_reg())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +185,7 @@ fn enc_ldst_reg(
|
|||||||
Some(ExtendOp::UXTW) => 0b010,
|
Some(ExtendOp::UXTW) => 0b010,
|
||||||
Some(ExtendOp::SXTW) => 0b110,
|
Some(ExtendOp::SXTW) => 0b110,
|
||||||
Some(ExtendOp::SXTX) => 0b111,
|
Some(ExtendOp::SXTX) => 0b111,
|
||||||
None => 0b011, /* LSL */
|
None => 0b011, // LSL
|
||||||
_ => panic!("bad extend mode for ld/st MemArg"),
|
_ => panic!("bad extend mode for ld/st MemArg"),
|
||||||
};
|
};
|
||||||
(op_31_22 << 22)
|
(op_31_22 << 22)
|
||||||
@@ -244,7 +228,7 @@ fn enc_br(rn: Reg) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn enc_adr(off: i32, rd: Writable<Reg>) -> u32 {
|
fn enc_adr(off: i32, rd: Writable<Reg>) -> u32 {
|
||||||
let off = off as u32;
|
let off = u32::try_from(off).unwrap();
|
||||||
let immlo = off & 3;
|
let immlo = off & 3;
|
||||||
let immhi = (off >> 2) & ((1 << 19) - 1);
|
let immhi = (off >> 2) & ((1 << 19) - 1);
|
||||||
(0b00010000 << 24) | (immlo << 29) | (immhi << 5) | machreg_to_gpr(rd.to_reg())
|
(0b00010000 << 24) | (immlo << 29) | (immhi << 5) | machreg_to_gpr(rd.to_reg())
|
||||||
@@ -258,8 +242,8 @@ fn enc_csel(rd: Writable<Reg>, rn: Reg, rm: Reg, cond: Cond) -> u32 {
|
|||||||
| (cond.bits() << 12)
|
| (cond.bits() << 12)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enc_fcsel(rd: Writable<Reg>, rn: Reg, rm: Reg, cond: Cond, is32: bool) -> u32 {
|
fn enc_fcsel(rd: Writable<Reg>, rn: Reg, rm: Reg, cond: Cond, size: InstSize) -> u32 {
|
||||||
let ty_bit = if is32 { 0 } else { 1 };
|
let ty_bit = if size.is32() { 0 } else { 1 };
|
||||||
0b000_11110_00_1_00000_0000_11_00000_00000
|
0b000_11110_00_1_00000_0000_11_00000_00000
|
||||||
| (machreg_to_vec(rm) << 16)
|
| (machreg_to_vec(rm) << 16)
|
||||||
| (machreg_to_vec(rn) << 5)
|
| (machreg_to_vec(rn) << 5)
|
||||||
@@ -301,8 +285,8 @@ fn enc_fpurrrr(top17: u32, rd: Writable<Reg>, rn: Reg, rm: Reg, ra: Reg) -> u32
|
|||||||
| machreg_to_vec(rd.to_reg())
|
| machreg_to_vec(rd.to_reg())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enc_fcmp(is32: bool, rn: Reg, rm: Reg) -> u32 {
|
fn enc_fcmp(size: InstSize, rn: Reg, rm: Reg) -> u32 {
|
||||||
let bits = if is32 {
|
let bits = if size.is32() {
|
||||||
0b000_11110_00_1_00000_00_1000_00000_00000
|
0b000_11110_00_1_00000_00_1000_00000_00000
|
||||||
} else {
|
} else {
|
||||||
0b000_11110_01_1_00000_00_1000_00000_00000
|
0b000_11110_01_1_00000_00_1000_00000_00000
|
||||||
@@ -359,7 +343,7 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
| ALUOp::SMulH
|
| ALUOp::SMulH
|
||||||
| ALUOp::UMulH => {
|
| ALUOp::UMulH => {
|
||||||
//// RRRR ops.
|
//// RRRR ops.
|
||||||
panic!("Bad ALUOp in RRR form!");
|
panic!("Bad ALUOp {:?} in RRR form!", alu_op);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let bit15_10 = match alu_op {
|
let bit15_10 = match alu_op {
|
||||||
@@ -450,14 +434,14 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
} => {
|
} => {
|
||||||
let amt = immshift.value();
|
let amt = immshift.value();
|
||||||
let (top10, immr, imms) = match alu_op {
|
let (top10, immr, imms) = match alu_op {
|
||||||
ALUOp::RotR32 => (0b0001001110, machreg_to_gpr(rn), amt as u32),
|
ALUOp::RotR32 => (0b0001001110, machreg_to_gpr(rn), u32::from(amt)),
|
||||||
ALUOp::RotR64 => (0b1001001111, machreg_to_gpr(rn), amt as u32),
|
ALUOp::RotR64 => (0b1001001111, machreg_to_gpr(rn), u32::from(amt)),
|
||||||
ALUOp::Lsr32 => (0b0101001100, amt as u32, 0b011111),
|
ALUOp::Lsr32 => (0b0101001100, u32::from(amt), 0b011111),
|
||||||
ALUOp::Lsr64 => (0b1101001101, amt as u32, 0b111111),
|
ALUOp::Lsr64 => (0b1101001101, u32::from(amt), 0b111111),
|
||||||
ALUOp::Asr32 => (0b0001001100, amt as u32, 0b011111),
|
ALUOp::Asr32 => (0b0001001100, u32::from(amt), 0b011111),
|
||||||
ALUOp::Asr64 => (0b1001001101, amt as u32, 0b111111),
|
ALUOp::Asr64 => (0b1001001101, u32::from(amt), 0b111111),
|
||||||
ALUOp::Lsl32 => (0b0101001100, (32 - amt) as u32, (31 - amt) as u32),
|
ALUOp::Lsl32 => (0b0101001100, u32::from(32 - amt), u32::from(31 - amt)),
|
||||||
ALUOp::Lsl64 => (0b1101001101, (64 - amt) as u32, (63 - amt) as u32),
|
ALUOp::Lsl64 => (0b1101001101, u32::from(64 - amt), u32::from(63 - amt)),
|
||||||
_ => unimplemented!("{:?}", alu_op),
|
_ => unimplemented!("{:?}", alu_op),
|
||||||
};
|
};
|
||||||
sink.put4(
|
sink.put4(
|
||||||
@@ -476,7 +460,7 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
rm,
|
rm,
|
||||||
ref shiftop,
|
ref shiftop,
|
||||||
} => {
|
} => {
|
||||||
let top11: u16 = match alu_op {
|
let top11: u32 = match alu_op {
|
||||||
ALUOp::Add32 => 0b000_01011000,
|
ALUOp::Add32 => 0b000_01011000,
|
||||||
ALUOp::Add64 => 0b100_01011000,
|
ALUOp::Add64 => 0b100_01011000,
|
||||||
ALUOp::AddS32 => 0b001_01011000,
|
ALUOp::AddS32 => 0b001_01011000,
|
||||||
@@ -499,8 +483,8 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
ALUOp::AndNot64 => 0b100_01010001,
|
ALUOp::AndNot64 => 0b100_01010001,
|
||||||
_ => unimplemented!("{:?}", alu_op),
|
_ => unimplemented!("{:?}", alu_op),
|
||||||
};
|
};
|
||||||
let top11 = top11 | ((shiftop.op().bits() as u16) << 1);
|
let top11 = top11 | (u32::from(shiftop.op().bits()) << 1);
|
||||||
let bits_15_10 = shiftop.amt().value();
|
let bits_15_10 = u32::from(shiftop.amt().value());
|
||||||
sink.put4(enc_arith_rrr(top11, bits_15_10, rd, rn, rm));
|
sink.put4(enc_arith_rrr(top11, bits_15_10, rd, rn, rm));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,7 +495,7 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
rm,
|
rm,
|
||||||
extendop,
|
extendop,
|
||||||
} => {
|
} => {
|
||||||
let top11 = match alu_op {
|
let top11: u32 = match alu_op {
|
||||||
ALUOp::Add32 => 0b00001011001,
|
ALUOp::Add32 => 0b00001011001,
|
||||||
ALUOp::Add64 => 0b10001011001,
|
ALUOp::Add64 => 0b10001011001,
|
||||||
ALUOp::Sub32 => 0b01001011001,
|
ALUOp::Sub32 => 0b01001011001,
|
||||||
@@ -522,12 +506,12 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
ALUOp::SubS64 => 0b11101011001,
|
ALUOp::SubS64 => 0b11101011001,
|
||||||
_ => unimplemented!("{:?}", alu_op),
|
_ => unimplemented!("{:?}", alu_op),
|
||||||
};
|
};
|
||||||
let bits_15_10 = extendop.bits() << 3;
|
let bits_15_10 = u32::from(extendop.bits()) << 3;
|
||||||
sink.put4(enc_arith_rrr(top11, bits_15_10, rd, rn, rm));
|
sink.put4(enc_arith_rrr(top11, bits_15_10, rd, rn, rm));
|
||||||
}
|
}
|
||||||
|
|
||||||
&Inst::BitRR { op, rd, rn, .. } => {
|
&Inst::BitRR { op, rd, rn, .. } => {
|
||||||
let size = if op.is_32_bit() { 0b0 } else { 0b1 };
|
let size = if op.inst_size().is32() { 0b0 } else { 0b1 };
|
||||||
let (op1, op2) = match op {
|
let (op1, op2) = match op {
|
||||||
BitOp::RBit32 | BitOp::RBit64 => (0b00000, 0b000000),
|
BitOp::RBit32 | BitOp::RBit64 => (0b00000, 0b000000),
|
||||||
BitOp::Clz32 | BitOp::Clz64 => (0b00000, 0b000100),
|
BitOp::Clz32 | BitOp::Clz64 => (0b00000, 0b000100),
|
||||||
@@ -655,6 +639,7 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
}
|
}
|
||||||
&MemArg::Label(ref label) => {
|
&MemArg::Label(ref label) => {
|
||||||
let offset = match label {
|
let offset = match label {
|
||||||
|
// cast i32 to u32 (two's-complement)
|
||||||
&MemLabel::PCRel(off) => off as u32,
|
&MemLabel::PCRel(off) => off as u32,
|
||||||
} / 4;
|
} / 4;
|
||||||
assert!(offset < (1 << 19));
|
assert!(offset < (1 << 19));
|
||||||
@@ -825,10 +810,16 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
&Inst::Mov { rd, rm } => {
|
&Inst::Mov { rd, rm } => {
|
||||||
assert!(rd.to_reg().get_class() == rm.get_class());
|
assert!(rd.to_reg().get_class() == rm.get_class());
|
||||||
assert!(rm.get_class() == RegClass::I64);
|
assert!(rm.get_class() == RegClass::I64);
|
||||||
|
// MOV to SP is interpreted as MOV to XZR instead. And our codegen
|
||||||
|
// should never MOV to XZR.
|
||||||
|
assert!(machreg_to_gpr(rd.to_reg()) != 31);
|
||||||
// Encoded as ORR rd, rm, zero.
|
// Encoded as ORR rd, rm, zero.
|
||||||
sink.put4(enc_arith_rrr(0b10101010_000, 0b000_000, rd, zero_reg(), rm));
|
sink.put4(enc_arith_rrr(0b10101010_000, 0b000_000, rd, zero_reg(), rm));
|
||||||
}
|
}
|
||||||
&Inst::Mov32 { rd, rm } => {
|
&Inst::Mov32 { rd, rm } => {
|
||||||
|
// MOV to SP is interpreted as MOV to XZR instead. And our codegen
|
||||||
|
// should never MOV to XZR.
|
||||||
|
assert!(machreg_to_gpr(rd.to_reg()) != 31);
|
||||||
// Encoded as ORR rd, rm, zero.
|
// Encoded as ORR rd, rm, zero.
|
||||||
sink.put4(enc_arith_rrr(0b00101010_000, 0b000_000, rd, zero_reg(), rm));
|
sink.put4(enc_arith_rrr(0b00101010_000, 0b000_000, rd, zero_reg(), rm));
|
||||||
}
|
}
|
||||||
@@ -888,10 +879,10 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
sink.put4(enc_fpurrrr(top17, rd, rn, rm, ra));
|
sink.put4(enc_fpurrrr(top17, rd, rn, rm, ra));
|
||||||
}
|
}
|
||||||
&Inst::FpuCmp32 { rn, rm } => {
|
&Inst::FpuCmp32 { rn, rm } => {
|
||||||
sink.put4(enc_fcmp(/* is32 = */ true, rn, rm));
|
sink.put4(enc_fcmp(InstSize::Size32, rn, rm));
|
||||||
}
|
}
|
||||||
&Inst::FpuCmp64 { rn, rm } => {
|
&Inst::FpuCmp64 { rn, rm } => {
|
||||||
sink.put4(enc_fcmp(/* is32 = */ false, rn, rm));
|
sink.put4(enc_fcmp(InstSize::Size64, rn, rm));
|
||||||
}
|
}
|
||||||
&Inst::FpuToInt { op, rd, rn } => {
|
&Inst::FpuToInt { op, rd, rn } => {
|
||||||
let top16 = match op {
|
let top16 = match op {
|
||||||
@@ -962,10 +953,10 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
sink.put8(const_data.to_bits());
|
sink.put8(const_data.to_bits());
|
||||||
}
|
}
|
||||||
&Inst::FpuCSel32 { rd, rn, rm, cond } => {
|
&Inst::FpuCSel32 { rd, rn, rm, cond } => {
|
||||||
sink.put4(enc_fcsel(rd, rn, rm, cond, /* is32 = */ true));
|
sink.put4(enc_fcsel(rd, rn, rm, cond, InstSize::Size32));
|
||||||
}
|
}
|
||||||
&Inst::FpuCSel64 { rd, rn, rm, cond } => {
|
&Inst::FpuCSel64 { rd, rn, rm, cond } => {
|
||||||
sink.put4(enc_fcsel(rd, rn, rm, cond, /* is32 = */ false));
|
sink.put4(enc_fcsel(rd, rn, rm, cond, InstSize::Size64));
|
||||||
}
|
}
|
||||||
&Inst::FpuRound { op, rd, rn } => {
|
&Inst::FpuRound { op, rd, rn } => {
|
||||||
let top22 = match op {
|
let top22 = match op {
|
||||||
@@ -1093,10 +1084,10 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
// do early (fake) emission for size computation.
|
// do early (fake) emission for size computation.
|
||||||
sink.put4(enc_jump26(0b000101, dest.as_off26().unwrap()));
|
sink.put4(enc_jump26(0b000101, dest.as_off26().unwrap()));
|
||||||
}
|
}
|
||||||
&Inst::Ret {} => {
|
&Inst::Ret => {
|
||||||
sink.put4(0xd65f03c0);
|
sink.put4(0xd65f03c0);
|
||||||
}
|
}
|
||||||
&Inst::EpiloguePlaceholder {} => {
|
&Inst::EpiloguePlaceholder => {
|
||||||
// Noop; this is just a placeholder for epilogues.
|
// Noop; this is just a placeholder for epilogues.
|
||||||
}
|
}
|
||||||
&Inst::Call {
|
&Inst::Call {
|
||||||
@@ -1168,7 +1159,7 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
&Inst::IndirectBr { rn, .. } => {
|
&Inst::IndirectBr { rn, .. } => {
|
||||||
sink.put4(enc_br(rn));
|
sink.put4(enc_br(rn));
|
||||||
}
|
}
|
||||||
&Inst::Nop => {}
|
&Inst::Nop0 => {}
|
||||||
&Inst::Nop4 => {
|
&Inst::Nop4 => {
|
||||||
sink.put4(0xd503201f);
|
sink.put4(0xd503201f);
|
||||||
}
|
}
|
||||||
@@ -1204,7 +1195,7 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
// the middle; we depend on hardcoded PC-rel addressing below.
|
// the middle; we depend on hardcoded PC-rel addressing below.
|
||||||
//
|
//
|
||||||
// N.B.: if PC-rel addressing on ADR below is changed, also update
|
// N.B.: if PC-rel addressing on ADR below is changed, also update
|
||||||
// `Inst::with_block_offsets()` in arm64/inst/mod.rs.
|
// `Inst::with_block_offsets()` in aarch64/inst/mod.rs.
|
||||||
|
|
||||||
// Save index in a tmp (the live range of ridx only goes to start of this
|
// Save index in a tmp (the live range of ridx only goes to start of this
|
||||||
// sequence; rtmp1 or rtmp2 may overwrite it).
|
// sequence; rtmp1 or rtmp2 may overwrite it).
|
||||||
@@ -1219,7 +1210,7 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
// Load value out of jump table
|
// Load value out of jump table
|
||||||
let inst = Inst::SLoad32 {
|
let inst = Inst::SLoad32 {
|
||||||
rd: rtmp2,
|
rd: rtmp2,
|
||||||
mem: MemArg::reg_reg_scaled_extended(
|
mem: MemArg::reg_plus_reg_scaled_extended(
|
||||||
rtmp1.to_reg(),
|
rtmp1.to_reg(),
|
||||||
rtmp2.to_reg(),
|
rtmp2.to_reg(),
|
||||||
I32,
|
I32,
|
||||||
@@ -1246,7 +1237,9 @@ impl<O: MachSectionOutput> MachInstEmit<O> for Inst {
|
|||||||
// Emit jump table (table of 32-bit offsets).
|
// Emit jump table (table of 32-bit offsets).
|
||||||
for target in targets {
|
for target in targets {
|
||||||
let off = target.as_offset_words() * 4;
|
let off = target.as_offset_words() * 4;
|
||||||
let off = off as i32 as u32;
|
let off = i32::try_from(off).unwrap();
|
||||||
|
// cast i32 to u32 (two's-complement)
|
||||||
|
let off = off as u32;
|
||||||
sink.put4(off);
|
sink.put4(off);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1292,7 +1285,7 @@ mod test {
|
|||||||
use crate::isa::test_utils;
|
use crate::isa::test_utils;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_arm64_binemit() {
|
fn test_aarch64_binemit() {
|
||||||
let mut insns = Vec::<(Inst, &str, &str)>::new();
|
let mut insns = Vec::<(Inst, &str, &str)>::new();
|
||||||
|
|
||||||
// N.B.: the architecture is little-endian, so when transcribing the 32-bit
|
// N.B.: the architecture is little-endian, so when transcribing the 32-bit
|
||||||
@@ -1310,10 +1303,10 @@ mod test {
|
|||||||
//
|
//
|
||||||
// Then:
|
// Then:
|
||||||
//
|
//
|
||||||
// $ echo "mov x1, x2" | arm64inst.sh
|
// $ echo "mov x1, x2" | aarch64inst.sh
|
||||||
insns.push((Inst::Ret {}, "C0035FD6", "ret"));
|
insns.push((Inst::Ret, "C0035FD6", "ret"));
|
||||||
insns.push((Inst::Nop {}, "", "nop-zero-len"));
|
insns.push((Inst::Nop0, "", "nop-zero-len"));
|
||||||
insns.push((Inst::Nop4 {}, "1F2003D5", "nop"));
|
insns.push((Inst::Nop4, "1F2003D5", "nop"));
|
||||||
insns.push((
|
insns.push((
|
||||||
Inst::AluRRR {
|
Inst::AluRRR {
|
||||||
alu_op: ALUOp::Add32,
|
alu_op: ALUOp::Add32,
|
||||||
@@ -4052,7 +4045,7 @@ mod test {
|
|||||||
let rru = create_reg_universe();
|
let rru = create_reg_universe();
|
||||||
for (insn, expected_encoding, expected_printing) in insns {
|
for (insn, expected_encoding, expected_printing) in insns {
|
||||||
println!(
|
println!(
|
||||||
"ARM64: {:?}, {}, {}",
|
"AArch64: {:?}, {}, {}",
|
||||||
insn, expected_encoding, expected_printing
|
insn, expected_encoding, expected_printing
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
//! ARM64 ISA definitions: immediate constants.
|
//! AArch64 ISA definitions: immediate constants.
|
||||||
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
|
// Some variants are never constructed, but we still want them as options in the future.
|
||||||
|
#[allow(dead_code)]
|
||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::Type;
|
use crate::ir::Type;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
@@ -28,12 +27,12 @@ impl SImm7Scaled {
|
|||||||
assert!(scale_ty == I64 || scale_ty == I32);
|
assert!(scale_ty == I64 || scale_ty == I32);
|
||||||
let scale = scale_ty.bytes();
|
let scale = scale_ty.bytes();
|
||||||
assert!(scale.is_power_of_two());
|
assert!(scale.is_power_of_two());
|
||||||
let scale = scale as i64;
|
let scale = i64::from(scale);
|
||||||
let upper_limit = 63 * scale;
|
let upper_limit = 63 * scale;
|
||||||
let lower_limit = -(64 * scale);
|
let lower_limit = -(64 * scale);
|
||||||
if value >= lower_limit && value <= upper_limit && (value & (scale - 1)) == 0 {
|
if value >= lower_limit && value <= upper_limit && (value & (scale - 1)) == 0 {
|
||||||
Some(SImm7Scaled {
|
Some(SImm7Scaled {
|
||||||
value: value as i16,
|
value: i16::try_from(value).unwrap(),
|
||||||
scale_ty,
|
scale_ty,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -48,7 +47,12 @@ impl SImm7Scaled {
|
|||||||
|
|
||||||
/// Bits for encoding.
|
/// Bits for encoding.
|
||||||
pub fn bits(&self) -> u32 {
|
pub fn bits(&self) -> u32 {
|
||||||
((self.value / self.scale_ty.bytes() as i16) as u32) & 0x7f
|
let ty_bytes: i16 = self.scale_ty.bytes() as i16;
|
||||||
|
let scaled: i16 = self.value / ty_bytes;
|
||||||
|
assert!(scaled <= 63 && scaled >= -64);
|
||||||
|
let scaled: i8 = scaled as i8;
|
||||||
|
let encoded: u32 = scaled as u32;
|
||||||
|
encoded & 0x7f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +129,7 @@ impl UImm12Scaled {
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Imm12 {
|
pub struct Imm12 {
|
||||||
/// The immediate bits.
|
/// The immediate bits.
|
||||||
pub bits: usize,
|
pub bits: u16,
|
||||||
/// Whether the immediate bits are shifted left by 12 or not.
|
/// Whether the immediate bits are shifted left by 12 or not.
|
||||||
pub shift12: bool,
|
pub shift12: bool,
|
||||||
}
|
}
|
||||||
@@ -140,12 +144,12 @@ impl Imm12 {
|
|||||||
})
|
})
|
||||||
} else if val < 0xfff {
|
} else if val < 0xfff {
|
||||||
Some(Imm12 {
|
Some(Imm12 {
|
||||||
bits: val as usize,
|
bits: val as u16,
|
||||||
shift12: false,
|
shift12: false,
|
||||||
})
|
})
|
||||||
} else if val < 0xfff_000 && (val & 0xfff == 0) {
|
} else if val < 0xfff_000 && (val & 0xfff == 0) {
|
||||||
Some(Imm12 {
|
Some(Imm12 {
|
||||||
bits: (val as usize) >> 12,
|
bits: (val >> 12) as u16,
|
||||||
shift12: true,
|
shift12: true,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@@ -154,7 +158,7 @@ impl Imm12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Bits for 2-bit "shift" field in e.g. AddI.
|
/// Bits for 2-bit "shift" field in e.g. AddI.
|
||||||
pub fn shift_bits(&self) -> u8 {
|
pub fn shift_bits(&self) -> u32 {
|
||||||
if self.shift12 {
|
if self.shift12 {
|
||||||
0b01
|
0b01
|
||||||
} else {
|
} else {
|
||||||
@@ -163,8 +167,8 @@ impl Imm12 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Bits for 12-bit "imm" field in e.g. AddI.
|
/// Bits for 12-bit "imm" field in e.g. AddI.
|
||||||
pub fn imm_bits(&self) -> u16 {
|
pub fn imm_bits(&self) -> u32 {
|
||||||
self.bits as u16
|
self.bits as u32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,11 +179,11 @@ pub struct ImmLogic {
|
|||||||
/// The actual value.
|
/// The actual value.
|
||||||
value: u64,
|
value: u64,
|
||||||
/// `N` flag.
|
/// `N` flag.
|
||||||
pub N: bool,
|
pub n: bool,
|
||||||
/// `S` field: element size and element bits.
|
/// `S` field: element size and element bits.
|
||||||
pub R: u8,
|
pub r: u8,
|
||||||
/// `R` field: rotate amount.
|
/// `R` field: rotate amount.
|
||||||
pub S: u8,
|
pub s: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImmLogic {
|
impl ImmLogic {
|
||||||
@@ -367,24 +371,19 @@ impl ImmLogic {
|
|||||||
debug_assert!(u8::try_from(s).is_ok());
|
debug_assert!(u8::try_from(s).is_ok());
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: original_value,
|
value: original_value,
|
||||||
N: out_n != 0,
|
n: out_n != 0,
|
||||||
R: r as u8,
|
r: r as u8,
|
||||||
S: s as u8,
|
s: s as u8,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_raw(value: u64, n: bool, r: u8, s: u8) -> ImmLogic {
|
pub fn from_raw(value: u64, n: bool, r: u8, s: u8) -> ImmLogic {
|
||||||
ImmLogic {
|
ImmLogic { n, r, s, value }
|
||||||
N: n,
|
|
||||||
R: r,
|
|
||||||
S: s,
|
|
||||||
value,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns bits ready for encoding: (N:1, R:6, S:6)
|
/// Returns bits ready for encoding: (N:1, R:6, S:6)
|
||||||
pub fn enc_bits(&self) -> u16 {
|
pub fn enc_bits(&self) -> u32 {
|
||||||
((self.N as u16) << 12) | ((self.R as u16) << 6) | (self.S as u16)
|
((self.n as u32) << 12) | ((self.r as u32) << 6) | (self.s as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value that this immediate represents.
|
/// Returns the value that this immediate represents.
|
||||||
@@ -427,7 +426,7 @@ impl ImmShift {
|
|||||||
pub struct MoveWideConst {
|
pub struct MoveWideConst {
|
||||||
/// The value.
|
/// The value.
|
||||||
pub bits: u16,
|
pub bits: u16,
|
||||||
/// shifted 16*shift bits to the left.
|
/// Result is `bits` shifted 16*shift bits to the left.
|
||||||
pub shift: u8,
|
pub shift: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +486,7 @@ impl MoveWideConst {
|
|||||||
impl ShowWithRRU for Imm12 {
|
impl ShowWithRRU for Imm12 {
|
||||||
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
|
||||||
let shift = if self.shift12 { 12 } else { 0 };
|
let shift = if self.shift12 { 12 } else { 0 };
|
||||||
let value = self.bits << shift;
|
let value = u32::from(self.bits) << shift;
|
||||||
format!("#{}", value)
|
format!("#{}", value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,9 +543,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 1,
|
value: 1,
|
||||||
N: true,
|
n: true,
|
||||||
R: 0,
|
r: 0,
|
||||||
S: 0
|
s: 0
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(1, I64)
|
ImmLogic::maybe_from_u64(1, I64)
|
||||||
);
|
);
|
||||||
@@ -554,9 +553,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 2,
|
value: 2,
|
||||||
N: true,
|
n: true,
|
||||||
R: 63,
|
r: 63,
|
||||||
S: 0
|
s: 0
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(2, I64)
|
ImmLogic::maybe_from_u64(2, I64)
|
||||||
);
|
);
|
||||||
@@ -568,9 +567,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 248,
|
value: 248,
|
||||||
N: true,
|
n: true,
|
||||||
R: 61,
|
r: 61,
|
||||||
S: 4
|
s: 4
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(248, I64)
|
ImmLogic::maybe_from_u64(248, I64)
|
||||||
);
|
);
|
||||||
@@ -580,9 +579,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 1920,
|
value: 1920,
|
||||||
N: true,
|
n: true,
|
||||||
R: 57,
|
r: 57,
|
||||||
S: 3
|
s: 3
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(1920, I64)
|
ImmLogic::maybe_from_u64(1920, I64)
|
||||||
);
|
);
|
||||||
@@ -590,9 +589,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 0x7ffe,
|
value: 0x7ffe,
|
||||||
N: true,
|
n: true,
|
||||||
R: 63,
|
r: 63,
|
||||||
S: 13
|
s: 13
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(0x7ffe, I64)
|
ImmLogic::maybe_from_u64(0x7ffe, I64)
|
||||||
);
|
);
|
||||||
@@ -600,9 +599,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 0x30000,
|
value: 0x30000,
|
||||||
N: true,
|
n: true,
|
||||||
R: 48,
|
r: 48,
|
||||||
S: 1
|
s: 1
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(0x30000, I64)
|
ImmLogic::maybe_from_u64(0x30000, I64)
|
||||||
);
|
);
|
||||||
@@ -610,9 +609,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 0x100000,
|
value: 0x100000,
|
||||||
N: true,
|
n: true,
|
||||||
R: 44,
|
r: 44,
|
||||||
S: 0
|
s: 0
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(0x100000, I64)
|
ImmLogic::maybe_from_u64(0x100000, I64)
|
||||||
);
|
);
|
||||||
@@ -620,9 +619,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: u64::max_value() - 1,
|
value: u64::max_value() - 1,
|
||||||
N: true,
|
n: true,
|
||||||
R: 63,
|
r: 63,
|
||||||
S: 62
|
s: 62
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(u64::max_value() - 1, I64)
|
ImmLogic::maybe_from_u64(u64::max_value() - 1, I64)
|
||||||
);
|
);
|
||||||
@@ -630,9 +629,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 0xaaaaaaaaaaaaaaaa,
|
value: 0xaaaaaaaaaaaaaaaa,
|
||||||
N: false,
|
n: false,
|
||||||
R: 1,
|
r: 1,
|
||||||
S: 60
|
s: 60
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(0xaaaaaaaaaaaaaaaa, I64)
|
ImmLogic::maybe_from_u64(0xaaaaaaaaaaaaaaaa, I64)
|
||||||
);
|
);
|
||||||
@@ -640,9 +639,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 0x8181818181818181,
|
value: 0x8181818181818181,
|
||||||
N: false,
|
n: false,
|
||||||
R: 1,
|
r: 1,
|
||||||
S: 49
|
s: 49
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(0x8181818181818181, I64)
|
ImmLogic::maybe_from_u64(0x8181818181818181, I64)
|
||||||
);
|
);
|
||||||
@@ -650,9 +649,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 0xffc3ffc3ffc3ffc3,
|
value: 0xffc3ffc3ffc3ffc3,
|
||||||
N: false,
|
n: false,
|
||||||
R: 10,
|
r: 10,
|
||||||
S: 43
|
s: 43
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(0xffc3ffc3ffc3ffc3, I64)
|
ImmLogic::maybe_from_u64(0xffc3ffc3ffc3ffc3, I64)
|
||||||
);
|
);
|
||||||
@@ -660,9 +659,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 0x100000001,
|
value: 0x100000001,
|
||||||
N: false,
|
n: false,
|
||||||
R: 0,
|
r: 0,
|
||||||
S: 0
|
s: 0
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(0x100000001, I64)
|
ImmLogic::maybe_from_u64(0x100000001, I64)
|
||||||
);
|
);
|
||||||
@@ -670,9 +669,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(ImmLogic {
|
Some(ImmLogic {
|
||||||
value: 0x1111111111111111,
|
value: 0x1111111111111111,
|
||||||
N: false,
|
n: false,
|
||||||
R: 0,
|
r: 0,
|
||||||
S: 56
|
s: 56
|
||||||
}),
|
}),
|
||||||
ImmLogic::maybe_from_u64(0x1111111111111111, I64)
|
ImmLogic::maybe_from_u64(0x1111111111111111, I64)
|
||||||
);
|
);
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,9 @@
|
|||||||
//! ARM64 ISA definitions: registers.
|
//! AArch64 ISA definitions: registers.
|
||||||
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
|
use crate::isa::aarch64::inst::InstSize;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
|
|
||||||
use regalloc::{
|
use regalloc::{RealRegUniverse, Reg, RegClass, RegClassInfo, Writable, NUM_REG_CLASSES};
|
||||||
RealReg, RealRegUniverse, Reg, RegClass, RegClassInfo, SpillSlot, VirtualReg, Writable,
|
|
||||||
NUM_REG_CLASSES,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::string::{String, ToString};
|
use std::string::{String, ToString};
|
||||||
|
|
||||||
@@ -83,7 +79,7 @@ pub fn writable_zero_reg() -> Writable<Reg> {
|
|||||||
/// Get a reference to the stack-pointer register.
|
/// Get a reference to the stack-pointer register.
|
||||||
pub fn stack_reg() -> Reg {
|
pub fn stack_reg() -> Reg {
|
||||||
// XSP (stack) and XZR (zero) are logically different registers which have
|
// XSP (stack) and XZR (zero) are logically different registers which have
|
||||||
// the same hardware encoding, and whose meaning, in real arm64
|
// the same hardware encoding, and whose meaning, in real aarch64
|
||||||
// instructions, is context-dependent. For convenience of
|
// instructions, is context-dependent. For convenience of
|
||||||
// universe-construction and for correct printing, we make them be two
|
// universe-construction and for correct printing, we make them be two
|
||||||
// different real registers.
|
// different real registers.
|
||||||
@@ -134,7 +130,7 @@ pub fn writable_spilltmp_reg() -> Writable<Reg> {
|
|||||||
Writable::from_reg(spilltmp_reg())
|
Writable::from_reg(spilltmp_reg())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create the register universe for ARM64.
|
/// Create the register universe for AArch64.
|
||||||
pub fn create_reg_universe() -> RealRegUniverse {
|
pub fn create_reg_universe() -> RealRegUniverse {
|
||||||
let mut regs = vec![];
|
let mut regs = vec![];
|
||||||
let mut allocable_by_class = [None; NUM_REG_CLASSES];
|
let mut allocable_by_class = [None; NUM_REG_CLASSES];
|
||||||
@@ -217,37 +213,38 @@ pub fn create_reg_universe() -> RealRegUniverse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If |ireg| denotes an I64-classed reg, make a best-effort attempt to show
|
/// If `ireg` denotes an I64-classed reg, make a best-effort attempt to show
|
||||||
/// its name at the 32-bit size.
|
/// its name at the 32-bit size.
|
||||||
pub fn show_ireg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, is32: bool) -> String {
|
pub fn show_ireg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: InstSize) -> String {
|
||||||
let mut s = reg.show_rru(mb_rru);
|
let mut s = reg.show_rru(mb_rru);
|
||||||
if reg.get_class() != RegClass::I64 || !is32 {
|
if reg.get_class() != RegClass::I64 || !size.is32() {
|
||||||
// We can't do any better.
|
// We can't do any better.
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
if reg.is_real() {
|
if reg.is_real() {
|
||||||
// Change (eg) "x42" into "w42" as appropriate
|
// Change (eg) "x42" into "w42" as appropriate
|
||||||
if reg.get_class() == RegClass::I64 && is32 && s.starts_with("x") {
|
if reg.get_class() == RegClass::I64 && size.is32() && s.starts_with("x") {
|
||||||
s = "w".to_string() + &s[1..];
|
s = "w".to_string() + &s[1..];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add a "w" suffix to RegClass::I64 vregs used in a 32-bit role
|
// Add a "w" suffix to RegClass::I64 vregs used in a 32-bit role
|
||||||
if reg.get_class() == RegClass::I64 && is32 {
|
if reg.get_class() == RegClass::I64 && size.is32() {
|
||||||
s = s + &"w";
|
s.push('w');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show a vector register when its use as a 32-bit or 64-bit float is known.
|
/// Show a vector register when its use as a 32-bit or 64-bit float is known.
|
||||||
pub fn show_freg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, is32: bool) -> String {
|
pub fn show_freg_sized(reg: Reg, mb_rru: Option<&RealRegUniverse>, size: InstSize) -> String {
|
||||||
let s = reg.show_rru(mb_rru);
|
let mut s = reg.show_rru(mb_rru);
|
||||||
if reg.get_class() != RegClass::V128 {
|
if reg.get_class() != RegClass::V128 {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
let prefix = if is32 { "s" } else { "d" };
|
let prefix = if size.is32() { "s" } else { "d" };
|
||||||
prefix.to_string() + &s[1..]
|
s.replace_range(0..1, prefix);
|
||||||
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show a vector register used in a scalar context.
|
/// Show a vector register used in a scalar context.
|
||||||
@@ -261,12 +258,12 @@ pub fn show_vreg_scalar(reg: Reg, mb_rru: Option<&RealRegUniverse>) -> String {
|
|||||||
if reg.is_real() {
|
if reg.is_real() {
|
||||||
// Change (eg) "v0" into "d0".
|
// Change (eg) "v0" into "d0".
|
||||||
if reg.get_class() == RegClass::V128 && s.starts_with("v") {
|
if reg.get_class() == RegClass::V128 && s.starts_with("v") {
|
||||||
s = "d".to_string() + &s[1..];
|
s.replace_range(0..1, "d");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add a "d" suffix to RegClass::V128 vregs.
|
// Add a "d" suffix to RegClass::V128 vregs.
|
||||||
if reg.get_class() == RegClass::V128 {
|
if reg.get_class() == RegClass::V128 {
|
||||||
s = s + &"d";
|
s.push('d');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Lowering rules for ARM64.
|
//! Lowering rules for AArch64.
|
||||||
//!
|
//!
|
||||||
//! TODO: opportunities for better code generation:
|
//! TODO: opportunities for better code generation:
|
||||||
//!
|
//!
|
||||||
@@ -6,45 +6,24 @@
|
|||||||
//! and incorporate sign/zero extension on indicies. Recognize pre/post-index
|
//! and incorporate sign/zero extension on indicies. Recognize pre/post-index
|
||||||
//! opportunities.
|
//! opportunities.
|
||||||
//!
|
//!
|
||||||
//! - Logical-immediate args.
|
//! - Floating-point immediates (FIMM instruction).
|
||||||
//!
|
|
||||||
//! - Floating-point immediates.
|
|
||||||
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use crate::ir::condcodes::{FloatCC, IntCC};
|
use crate::ir::condcodes::{FloatCC, IntCC};
|
||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::Inst as IRInst;
|
use crate::ir::Inst as IRInst;
|
||||||
use crate::ir::{Block, InstructionData, Opcode, TrapCode, Type};
|
use crate::ir::{InstructionData, Opcode, TrapCode, Type};
|
||||||
use crate::machinst::lower::*;
|
use crate::machinst::lower::*;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
|
|
||||||
use crate::isa::arm64::abi::*;
|
use crate::isa::aarch64::abi::*;
|
||||||
use crate::isa::arm64::inst::*;
|
use crate::isa::aarch64::inst::*;
|
||||||
use crate::isa::arm64::Arm64Backend;
|
use crate::isa::aarch64::AArch64Backend;
|
||||||
|
|
||||||
use regalloc::{Reg, RegClass, Writable};
|
use regalloc::{Reg, RegClass, Writable};
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
// Helpers: opcode conversions
|
|
||||||
|
|
||||||
fn op_to_aluop(op: Opcode, ty: Type) -> Option<ALUOp> {
|
|
||||||
match (op, ty) {
|
|
||||||
(Opcode::Iadd, I32) => Some(ALUOp::Add32),
|
|
||||||
(Opcode::Iadd, I64) => Some(ALUOp::Add64),
|
|
||||||
(Opcode::Isub, I32) => Some(ALUOp::Sub32),
|
|
||||||
(Opcode::Isub, I64) => Some(ALUOp::Sub64),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_alu_op(op: Opcode, ctrl_typevar: Type) -> bool {
|
|
||||||
op_to_aluop(op, ctrl_typevar).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
// Result enum types.
|
// Result enum types.
|
||||||
//
|
//
|
||||||
@@ -163,7 +142,7 @@ impl InsnInputSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_input<C: LowerCtx<Inst>>(ctx: &mut C, output: InsnOutput, num: usize) -> InsnInput {
|
fn get_input<C: LowerCtx<I = Inst>>(ctx: &mut C, output: InsnOutput, num: usize) -> InsnInput {
|
||||||
assert!(num <= ctx.num_inputs(output.insn));
|
assert!(num <= ctx.num_inputs(output.insn));
|
||||||
InsnInput {
|
InsnInput {
|
||||||
insn: output.insn,
|
insn: output.insn,
|
||||||
@@ -173,7 +152,7 @@ fn get_input<C: LowerCtx<Inst>>(ctx: &mut C, output: InsnOutput, num: usize) ->
|
|||||||
|
|
||||||
/// Convert an instruction input to a producing instruction's output if possible (in same BB), or a
|
/// Convert an instruction input to a producing instruction's output if possible (in same BB), or a
|
||||||
/// register otherwise.
|
/// register otherwise.
|
||||||
fn input_source<C: LowerCtx<Inst>>(ctx: &mut C, input: InsnInput) -> InsnInputSource {
|
fn input_source<C: LowerCtx<I = Inst>>(ctx: &mut C, input: InsnInput) -> InsnInputSource {
|
||||||
if let Some((input_inst, result_num)) = ctx.input_inst(input.insn, input.input) {
|
if let Some((input_inst, result_num)) = ctx.input_inst(input.insn, input.input) {
|
||||||
let out = InsnOutput {
|
let out = InsnOutput {
|
||||||
insn: input_inst,
|
insn: input_inst,
|
||||||
@@ -190,7 +169,7 @@ fn input_source<C: LowerCtx<Inst>>(ctx: &mut C, input: InsnInput) -> InsnInputSo
|
|||||||
// Lowering: convert instruction outputs to result types.
|
// Lowering: convert instruction outputs to result types.
|
||||||
|
|
||||||
/// Lower an instruction output to a 64-bit constant, if possible.
|
/// Lower an instruction output to a 64-bit constant, if possible.
|
||||||
fn output_to_const<C: LowerCtx<Inst>>(ctx: &mut C, out: InsnOutput) -> Option<u64> {
|
fn output_to_const<C: LowerCtx<I = Inst>>(ctx: &mut C, out: InsnOutput) -> Option<u64> {
|
||||||
if out.output > 0 {
|
if out.output > 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -204,7 +183,7 @@ fn output_to_const<C: LowerCtx<Inst>>(ctx: &mut C, out: InsnOutput) -> Option<u6
|
|||||||
let imm: i64 = imm.into();
|
let imm: i64 = imm.into();
|
||||||
Some(imm as u64)
|
Some(imm as u64)
|
||||||
}
|
}
|
||||||
&InstructionData::UnaryIeee32 { opcode: _, imm } => Some(imm.bits() as u64),
|
&InstructionData::UnaryIeee32 { opcode: _, imm } => Some(u64::from(imm.bits())),
|
||||||
&InstructionData::UnaryIeee64 { opcode: _, imm } => Some(imm.bits()),
|
&InstructionData::UnaryIeee64 { opcode: _, imm } => Some(imm.bits()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -212,16 +191,19 @@ fn output_to_const<C: LowerCtx<Inst>>(ctx: &mut C, out: InsnOutput) -> Option<u6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output_to_const_f32<C: LowerCtx<Inst>>(ctx: &mut C, out: InsnOutput) -> Option<f32> {
|
fn output_to_const_f32<C: LowerCtx<I = Inst>>(ctx: &mut C, out: InsnOutput) -> Option<f32> {
|
||||||
output_to_const(ctx, out).map(|value| f32::from_bits(value as u32))
|
output_to_const(ctx, out).map(|value| f32::from_bits(value as u32))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output_to_const_f64<C: LowerCtx<Inst>>(ctx: &mut C, out: InsnOutput) -> Option<f64> {
|
fn output_to_const_f64<C: LowerCtx<I = Inst>>(ctx: &mut C, out: InsnOutput) -> Option<f64> {
|
||||||
output_to_const(ctx, out).map(|value| f64::from_bits(value))
|
output_to_const(ctx, out).map(|value| f64::from_bits(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lower an instruction output to a constant register-shift amount, if possible.
|
/// Lower an instruction output to a constant register-shift amount, if possible.
|
||||||
fn output_to_shiftimm<C: LowerCtx<Inst>>(ctx: &mut C, out: InsnOutput) -> Option<ShiftOpShiftImm> {
|
fn output_to_shiftimm<C: LowerCtx<I = Inst>>(
|
||||||
|
ctx: &mut C,
|
||||||
|
out: InsnOutput,
|
||||||
|
) -> Option<ShiftOpShiftImm> {
|
||||||
output_to_const(ctx, out).and_then(ShiftOpShiftImm::maybe_from_shift)
|
output_to_const(ctx, out).and_then(ShiftOpShiftImm::maybe_from_shift)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +233,7 @@ impl NarrowValueMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Lower an instruction output to a reg.
|
/// Lower an instruction output to a reg.
|
||||||
fn output_to_reg<C: LowerCtx<Inst>>(ctx: &mut C, out: InsnOutput) -> Writable<Reg> {
|
fn output_to_reg<C: LowerCtx<I = Inst>>(ctx: &mut C, out: InsnOutput) -> Writable<Reg> {
|
||||||
ctx.output(out.insn, out.output)
|
ctx.output(out.insn, out.output)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,7 +242,7 @@ fn output_to_reg<C: LowerCtx<Inst>>(ctx: &mut C, out: InsnOutput) -> Writable<Re
|
|||||||
/// The given register will be extended appropriately, according to
|
/// The given register will be extended appropriately, according to
|
||||||
/// `narrow_mode` and the input's type. If extended, the value is
|
/// `narrow_mode` and the input's type. If extended, the value is
|
||||||
/// always extended to 64 bits, for simplicity.
|
/// always extended to 64 bits, for simplicity.
|
||||||
fn input_to_reg<C: LowerCtx<Inst>>(
|
fn input_to_reg<C: LowerCtx<I = Inst>>(
|
||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
input: InsnInput,
|
input: InsnInput,
|
||||||
narrow_mode: NarrowValueMode,
|
narrow_mode: NarrowValueMode,
|
||||||
@@ -292,9 +274,7 @@ fn input_to_reg<C: LowerCtx<Inst>>(
|
|||||||
});
|
});
|
||||||
tmp.to_reg()
|
tmp.to_reg()
|
||||||
}
|
}
|
||||||
(NarrowValueMode::ZeroExtend32, n) | (NarrowValueMode::SignExtend32, n) if n == 32 => {
|
(NarrowValueMode::ZeroExtend32, 32) | (NarrowValueMode::SignExtend32, 32) => in_reg,
|
||||||
in_reg
|
|
||||||
}
|
|
||||||
|
|
||||||
(NarrowValueMode::ZeroExtend64, n) if n < 64 => {
|
(NarrowValueMode::ZeroExtend64, n) if n < 64 => {
|
||||||
let tmp = ctx.tmp(RegClass::I64, I32);
|
let tmp = ctx.tmp(RegClass::I64, I32);
|
||||||
@@ -318,7 +298,7 @@ fn input_to_reg<C: LowerCtx<Inst>>(
|
|||||||
});
|
});
|
||||||
tmp.to_reg()
|
tmp.to_reg()
|
||||||
}
|
}
|
||||||
(_, n) if n == 64 => in_reg,
|
(_, 64) => in_reg,
|
||||||
|
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Unsupported input width: input ty {} bits {} mode {:?}",
|
"Unsupported input width: input ty {} bits {} mode {:?}",
|
||||||
@@ -340,7 +320,7 @@ fn input_to_reg<C: LowerCtx<Inst>>(
|
|||||||
/// divide or a right-shift or a compare-to-zero), `narrow_mode` should be
|
/// divide or a right-shift or a compare-to-zero), `narrow_mode` should be
|
||||||
/// set to `ZeroExtend` or `SignExtend` as appropriate, and the resulting
|
/// set to `ZeroExtend` or `SignExtend` as appropriate, and the resulting
|
||||||
/// register will be provided the extended value.
|
/// register will be provided the extended value.
|
||||||
fn input_to_rs<C: LowerCtx<Inst>>(
|
fn input_to_rs<C: LowerCtx<I = Inst>>(
|
||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
input: InsnInput,
|
input: InsnInput,
|
||||||
narrow_mode: NarrowValueMode,
|
narrow_mode: NarrowValueMode,
|
||||||
@@ -374,7 +354,7 @@ fn input_to_rs<C: LowerCtx<Inst>>(
|
|||||||
/// vreg into which the source instruction will generate its value.
|
/// vreg into which the source instruction will generate its value.
|
||||||
///
|
///
|
||||||
/// See note on `input_to_rs` for a description of `narrow_mode`.
|
/// See note on `input_to_rs` for a description of `narrow_mode`.
|
||||||
fn input_to_rse<C: LowerCtx<Inst>>(
|
fn input_to_rse<C: LowerCtx<I = Inst>>(
|
||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
input: InsnInput,
|
input: InsnInput,
|
||||||
narrow_mode: NarrowValueMode,
|
narrow_mode: NarrowValueMode,
|
||||||
@@ -448,7 +428,7 @@ fn input_to_rse<C: LowerCtx<Inst>>(
|
|||||||
ResultRSE::from_rs(input_to_rs(ctx, input, narrow_mode))
|
ResultRSE::from_rs(input_to_rs(ctx, input, narrow_mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_to_rse_imm12<C: LowerCtx<Inst>>(
|
fn input_to_rse_imm12<C: LowerCtx<I = Inst>>(
|
||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
input: InsnInput,
|
input: InsnInput,
|
||||||
narrow_mode: NarrowValueMode,
|
narrow_mode: NarrowValueMode,
|
||||||
@@ -465,7 +445,7 @@ fn input_to_rse_imm12<C: LowerCtx<Inst>>(
|
|||||||
ResultRSEImm12::from_rse(input_to_rse(ctx, input, narrow_mode))
|
ResultRSEImm12::from_rse(input_to_rse(ctx, input, narrow_mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_to_rs_immlogic<C: LowerCtx<Inst>>(
|
fn input_to_rs_immlogic<C: LowerCtx<I = Inst>>(
|
||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
input: InsnInput,
|
input: InsnInput,
|
||||||
narrow_mode: NarrowValueMode,
|
narrow_mode: NarrowValueMode,
|
||||||
@@ -484,7 +464,10 @@ fn input_to_rs_immlogic<C: LowerCtx<Inst>>(
|
|||||||
ResultRSImmLogic::from_rs(input_to_rs(ctx, input, narrow_mode))
|
ResultRSImmLogic::from_rs(input_to_rs(ctx, input, narrow_mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_to_reg_immshift<C: LowerCtx<Inst>>(ctx: &mut C, input: InsnInput) -> ResultRegImmShift {
|
fn input_to_reg_immshift<C: LowerCtx<I = Inst>>(
|
||||||
|
ctx: &mut C,
|
||||||
|
input: InsnInput,
|
||||||
|
) -> ResultRegImmShift {
|
||||||
if let InsnInputSource::Output(out) = input_source(ctx, input) {
|
if let InsnInputSource::Output(out) = input_source(ctx, input) {
|
||||||
if let Some(imm_value) = output_to_const(ctx, out) {
|
if let Some(imm_value) = output_to_const(ctx, out) {
|
||||||
if let Some(immshift) = ImmShift::maybe_from_u64(imm_value) {
|
if let Some(immshift) = ImmShift::maybe_from_u64(imm_value) {
|
||||||
@@ -577,7 +560,7 @@ fn alu_inst_immshift(op: ALUOp, rd: Writable<Reg>, rn: Reg, rm: ResultRegImmShif
|
|||||||
// than an `InsnInput`, to do more introspection.
|
// than an `InsnInput`, to do more introspection.
|
||||||
|
|
||||||
/// Lower the address of a load or store.
|
/// Lower the address of a load or store.
|
||||||
fn lower_address<C: LowerCtx<Inst>>(
|
fn lower_address<C: LowerCtx<I = Inst>>(
|
||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
elem_ty: Type,
|
elem_ty: Type,
|
||||||
addends: &[InsnInput],
|
addends: &[InsnInput],
|
||||||
@@ -598,7 +581,7 @@ fn lower_address<C: LowerCtx<Inst>>(
|
|||||||
if addends.len() == 2 && offset == 0 {
|
if addends.len() == 2 && offset == 0 {
|
||||||
let ra = input_to_reg(ctx, addends[0], NarrowValueMode::ZeroExtend64);
|
let ra = input_to_reg(ctx, addends[0], NarrowValueMode::ZeroExtend64);
|
||||||
let rb = input_to_reg(ctx, addends[1], NarrowValueMode::ZeroExtend64);
|
let rb = input_to_reg(ctx, addends[1], NarrowValueMode::ZeroExtend64);
|
||||||
return MemArg::reg_reg(ra, rb);
|
return MemArg::reg_plus_reg(ra, rb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, generate add instructions.
|
// Otherwise, generate add instructions.
|
||||||
@@ -621,17 +604,17 @@ fn lower_address<C: LowerCtx<Inst>>(
|
|||||||
MemArg::reg(addr.to_reg())
|
MemArg::reg(addr.to_reg())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_constant_u64<C: LowerCtx<Inst>>(ctx: &mut C, rd: Writable<Reg>, value: u64) {
|
fn lower_constant_u64<C: LowerCtx<I = Inst>>(ctx: &mut C, rd: Writable<Reg>, value: u64) {
|
||||||
for inst in Inst::load_constant(rd, value) {
|
for inst in Inst::load_constant(rd, value) {
|
||||||
ctx.emit(inst);
|
ctx.emit(inst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_constant_f32<C: LowerCtx<Inst>>(ctx: &mut C, rd: Writable<Reg>, value: f32) {
|
fn lower_constant_f32<C: LowerCtx<I = Inst>>(ctx: &mut C, rd: Writable<Reg>, value: f32) {
|
||||||
ctx.emit(Inst::load_fp_constant32(rd, value));
|
ctx.emit(Inst::load_fp_constant32(rd, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_constant_f64<C: LowerCtx<Inst>>(ctx: &mut C, rd: Writable<Reg>, value: f64) {
|
fn lower_constant_f64<C: LowerCtx<I = Inst>>(ctx: &mut C, rd: Writable<Reg>, value: f64) {
|
||||||
ctx.emit(Inst::load_fp_constant64(rd, value));
|
ctx.emit(Inst::load_fp_constant64(rd, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -653,7 +636,7 @@ fn lower_condcode(cc: IntCC) -> Cond {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn lower_fp_condcode(cc: FloatCC) -> Cond {
|
fn lower_fp_condcode(cc: FloatCC) -> Cond {
|
||||||
// Refer to `codegen/shared/src/condcodes.rs` and to the `FCMP` ARM64 docs.
|
// Refer to `codegen/shared/src/condcodes.rs` and to the `FCMP` AArch64 docs.
|
||||||
// The FCMP instruction sets:
|
// The FCMP instruction sets:
|
||||||
// NZCV
|
// NZCV
|
||||||
// - PCSR.NZCV = 0011 on UN (unordered),
|
// - PCSR.NZCV = 0011 on UN (unordered),
|
||||||
@@ -717,7 +700,7 @@ pub fn condcode_is_signed(cc: IntCC) -> bool {
|
|||||||
// Top-level instruction lowering entry point, for one instruction.
|
// Top-level instruction lowering entry point, for one instruction.
|
||||||
|
|
||||||
/// Actually codegen an instruction's results into registers.
|
/// Actually codegen an instruction's results into registers.
|
||||||
fn lower_insn_to_regs<C: LowerCtx<Inst>>(ctx: &mut C, insn: IRInst) {
|
fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(ctx: &mut C, insn: IRInst) {
|
||||||
let op = ctx.data(insn).opcode();
|
let op = ctx.data(insn).opcode();
|
||||||
let inputs: SmallVec<[InsnInput; 4]> = (0..ctx.num_inputs(insn))
|
let inputs: SmallVec<[InsnInput; 4]> = (0..ctx.num_inputs(insn))
|
||||||
.map(|i| InsnInput { insn, input: i })
|
.map(|i| InsnInput { insn, input: i })
|
||||||
@@ -1032,13 +1015,13 @@ fn lower_insn_to_regs<C: LowerCtx<Inst>>(ctx: &mut C, insn: IRInst) {
|
|||||||
|
|
||||||
Opcode::Ishl | Opcode::Ushr | Opcode::Sshr => {
|
Opcode::Ishl | Opcode::Ushr | Opcode::Sshr => {
|
||||||
let ty = ty.unwrap();
|
let ty = ty.unwrap();
|
||||||
let is32 = ty_bits(ty) <= 32;
|
let size = InstSize::from_bits(ty_bits(ty));
|
||||||
let narrow_mode = match (op, is32) {
|
let narrow_mode = match (op, size) {
|
||||||
(Opcode::Ishl, _) => NarrowValueMode::None,
|
(Opcode::Ishl, _) => NarrowValueMode::None,
|
||||||
(Opcode::Ushr, false) => NarrowValueMode::ZeroExtend64,
|
(Opcode::Ushr, InstSize::Size64) => NarrowValueMode::ZeroExtend64,
|
||||||
(Opcode::Ushr, true) => NarrowValueMode::ZeroExtend32,
|
(Opcode::Ushr, InstSize::Size32) => NarrowValueMode::ZeroExtend32,
|
||||||
(Opcode::Sshr, false) => NarrowValueMode::SignExtend64,
|
(Opcode::Sshr, InstSize::Size64) => NarrowValueMode::SignExtend64,
|
||||||
(Opcode::Sshr, true) => NarrowValueMode::SignExtend32,
|
(Opcode::Sshr, InstSize::Size32) => NarrowValueMode::SignExtend32,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let rd = output_to_reg(ctx, outputs[0]);
|
let rd = output_to_reg(ctx, outputs[0]);
|
||||||
@@ -1160,7 +1143,7 @@ fn lower_insn_to_regs<C: LowerCtx<Inst>>(ctx: &mut C, insn: IRInst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Rotl => {
|
Opcode::Rotl => {
|
||||||
// ARM64 does not have a ROL instruction, so we always synthesize
|
// AArch64 does not have a ROL instruction, so we always synthesize
|
||||||
// this as:
|
// this as:
|
||||||
//
|
//
|
||||||
// rotl rd, rn, rm
|
// rotl rd, rn, rm
|
||||||
@@ -1854,26 +1837,17 @@ fn lower_insn_to_regs<C: LowerCtx<Inst>>(ctx: &mut C, insn: IRInst) {
|
|||||||
Opcode::Call => {
|
Opcode::Call => {
|
||||||
let extname = ctx.call_target(insn).unwrap();
|
let extname = ctx.call_target(insn).unwrap();
|
||||||
let extname = extname.clone();
|
let extname = extname.clone();
|
||||||
// HACK: get the function address with an Abs8 reloc in the constant pool.
|
|
||||||
//let tmp = ctx.tmp(RegClass::I64, I64);
|
|
||||||
//ctx.emit(Inst::LoadExtName {
|
|
||||||
//rd: tmp,
|
|
||||||
//name: extname,
|
|
||||||
//srcloc: loc,
|
|
||||||
//offset: 0,
|
|
||||||
//});
|
|
||||||
let sig = ctx.call_sig(insn).unwrap();
|
let sig = ctx.call_sig(insn).unwrap();
|
||||||
assert!(inputs.len() == sig.params.len());
|
assert!(inputs.len() == sig.params.len());
|
||||||
assert!(outputs.len() == sig.returns.len());
|
assert!(outputs.len() == sig.returns.len());
|
||||||
(ARM64ABICall::from_func(sig, &extname, loc), &inputs[..])
|
(AArch64ABICall::from_func(sig, &extname, loc), &inputs[..])
|
||||||
//(ARM64ABICall::from_ptr(sig, tmp.to_reg(), loc), &inputs[..])
|
|
||||||
}
|
}
|
||||||
Opcode::CallIndirect => {
|
Opcode::CallIndirect => {
|
||||||
let ptr = input_to_reg(ctx, inputs[0], NarrowValueMode::ZeroExtend64);
|
let ptr = input_to_reg(ctx, inputs[0], NarrowValueMode::ZeroExtend64);
|
||||||
let sig = ctx.call_sig(insn).unwrap();
|
let sig = ctx.call_sig(insn).unwrap();
|
||||||
assert!(inputs.len() - 1 == sig.params.len());
|
assert!(inputs.len() - 1 == sig.params.len());
|
||||||
assert!(outputs.len() == sig.returns.len());
|
assert!(outputs.len() == sig.returns.len());
|
||||||
(ARM64ABICall::from_ptr(sig, ptr, loc, op), &inputs[1..])
|
(AArch64ABICall::from_ptr(sig, ptr, loc, op), &inputs[1..])
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
@@ -2357,21 +2331,6 @@ fn choose_32_64<T: Copy>(ty: Type, op32: T, op64: T) -> T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn branch_target(data: &InstructionData) -> Option<Block> {
|
|
||||||
match data {
|
|
||||||
&InstructionData::BranchIcmp { destination, .. }
|
|
||||||
| &InstructionData::Branch { destination, .. }
|
|
||||||
| &InstructionData::BranchInt { destination, .. }
|
|
||||||
| &InstructionData::Jump { destination, .. }
|
|
||||||
| &InstructionData::BranchTable { destination, .. }
|
|
||||||
| &InstructionData::BranchFloat { destination, .. } => Some(destination),
|
|
||||||
_ => {
|
|
||||||
assert!(!data.opcode().is_branch());
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ldst_offset(data: &InstructionData) -> Option<i32> {
|
fn ldst_offset(data: &InstructionData) -> Option<i32> {
|
||||||
match data {
|
match data {
|
||||||
&InstructionData::Load { offset, .. }
|
&InstructionData::Load { offset, .. }
|
||||||
@@ -2418,7 +2377,11 @@ fn inst_trapcode(data: &InstructionData) -> Option<TrapCode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Checks for an instance of `op` feeding the given input. Marks as merged (decrementing refcount) if so.
|
/// Checks for an instance of `op` feeding the given input. Marks as merged (decrementing refcount) if so.
|
||||||
fn maybe_input_insn<C: LowerCtx<Inst>>(c: &mut C, input: InsnInput, op: Opcode) -> Option<IRInst> {
|
fn maybe_input_insn<C: LowerCtx<I = Inst>>(
|
||||||
|
c: &mut C,
|
||||||
|
input: InsnInput,
|
||||||
|
op: Opcode,
|
||||||
|
) -> Option<IRInst> {
|
||||||
if let InsnInputSource::Output(out) = input_source(c, input) {
|
if let InsnInputSource::Output(out) = input_source(c, input) {
|
||||||
let data = c.data(out.insn);
|
let data = c.data(out.insn);
|
||||||
if data.opcode() == op {
|
if data.opcode() == op {
|
||||||
@@ -2434,7 +2397,7 @@ fn maybe_input_insn<C: LowerCtx<Inst>>(c: &mut C, input: InsnInput, op: Opcode)
|
|||||||
///
|
///
|
||||||
/// FIXME cfallin 2020-03-30: this is really ugly. Factor out tree-matching stuff and make it
|
/// FIXME cfallin 2020-03-30: this is really ugly. Factor out tree-matching stuff and make it
|
||||||
/// a bit more generic.
|
/// a bit more generic.
|
||||||
fn maybe_input_insn_via_conv<C: LowerCtx<Inst>>(
|
fn maybe_input_insn_via_conv<C: LowerCtx<I = Inst>>(
|
||||||
c: &mut C,
|
c: &mut C,
|
||||||
input: InsnInput,
|
input: InsnInput,
|
||||||
op: Opcode,
|
op: Opcode,
|
||||||
@@ -2461,7 +2424,7 @@ fn maybe_input_insn_via_conv<C: LowerCtx<Inst>>(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_icmp_or_ifcmp_to_flags<C: LowerCtx<Inst>>(ctx: &mut C, insn: IRInst, is_signed: bool) {
|
fn lower_icmp_or_ifcmp_to_flags<C: LowerCtx<I = Inst>>(ctx: &mut C, insn: IRInst, is_signed: bool) {
|
||||||
let ty = ctx.input_ty(insn, 0);
|
let ty = ctx.input_ty(insn, 0);
|
||||||
let bits = ty_bits(ty);
|
let bits = ty_bits(ty);
|
||||||
let narrow_mode = match (bits <= 32, is_signed) {
|
let narrow_mode = match (bits <= 32, is_signed) {
|
||||||
@@ -2488,7 +2451,7 @@ fn lower_icmp_or_ifcmp_to_flags<C: LowerCtx<Inst>>(ctx: &mut C, insn: IRInst, is
|
|||||||
ctx.emit(alu_inst_imm12(alu_op, rd, rn, rm));
|
ctx.emit(alu_inst_imm12(alu_op, rd, rn, rm));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_fcmp_or_ffcmp_to_flags<C: LowerCtx<Inst>>(ctx: &mut C, insn: IRInst) {
|
fn lower_fcmp_or_ffcmp_to_flags<C: LowerCtx<I = Inst>>(ctx: &mut C, insn: IRInst) {
|
||||||
let ty = ctx.input_ty(insn, 0);
|
let ty = ctx.input_ty(insn, 0);
|
||||||
let bits = ty_bits(ty);
|
let bits = ty_bits(ty);
|
||||||
let inputs = [
|
let inputs = [
|
||||||
@@ -2517,14 +2480,14 @@ fn lower_fcmp_or_ffcmp_to_flags<C: LowerCtx<Inst>>(ctx: &mut C, insn: IRInst) {
|
|||||||
//=============================================================================
|
//=============================================================================
|
||||||
// Lowering-backend trait implementation.
|
// Lowering-backend trait implementation.
|
||||||
|
|
||||||
impl LowerBackend for Arm64Backend {
|
impl LowerBackend for AArch64Backend {
|
||||||
type MInst = Inst;
|
type MInst = Inst;
|
||||||
|
|
||||||
fn lower<C: LowerCtx<Inst>>(&self, ctx: &mut C, ir_inst: IRInst) {
|
fn lower<C: LowerCtx<I = Inst>>(&self, ctx: &mut C, ir_inst: IRInst) {
|
||||||
lower_insn_to_regs(ctx, ir_inst);
|
lower_insn_to_regs(ctx, ir_inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_branch_group<C: LowerCtx<Inst>>(
|
fn lower_branch_group<C: LowerCtx<I = Inst>>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
branches: &[IRInst],
|
branches: &[IRInst],
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
use crate::ir::Function;
|
use crate::ir::Function;
|
||||||
use crate::isa::Builder as IsaBuilder;
|
use crate::isa::Builder as IsaBuilder;
|
||||||
use crate::isa::TargetIsa;
|
|
||||||
use crate::machinst::{
|
use crate::machinst::{
|
||||||
compile, MachBackend, MachCompileResult, ShowWithRRU, TargetIsaAdapter, VCode,
|
compile, MachBackend, MachCompileResult, ShowWithRRU, TargetIsaAdapter, VCode,
|
||||||
};
|
};
|
||||||
@@ -10,10 +9,9 @@ use crate::result::CodegenResult;
|
|||||||
use crate::settings;
|
use crate::settings;
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use regalloc::RealRegUniverse;
|
use regalloc::RealRegUniverse;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::{Aarch64Architecture, Architecture, Triple};
|
||||||
|
|
||||||
// New backend:
|
// New backend:
|
||||||
mod abi;
|
mod abi;
|
||||||
@@ -22,29 +20,30 @@ mod lower;
|
|||||||
|
|
||||||
use inst::create_reg_universe;
|
use inst::create_reg_universe;
|
||||||
|
|
||||||
/// An ARM64 backend.
|
/// An AArch64 backend.
|
||||||
pub struct Arm64Backend {
|
pub struct AArch64Backend {
|
||||||
|
triple: Triple,
|
||||||
flags: settings::Flags,
|
flags: settings::Flags,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arm64Backend {
|
impl AArch64Backend {
|
||||||
/// Create a new ARM64 backend with the given (shared) flags.
|
/// Create a new AArch64 backend with the given (shared) flags.
|
||||||
pub fn new_with_flags(flags: settings::Flags) -> Arm64Backend {
|
pub fn new_with_flags(triple: Triple, flags: settings::Flags) -> AArch64Backend {
|
||||||
Arm64Backend { flags }
|
AArch64Backend { triple, flags }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_vcode(&self, mut func: Function, flags: &settings::Flags) -> VCode<inst::Inst> {
|
fn compile_vcode(&self, func: &Function, flags: &settings::Flags) -> VCode<inst::Inst> {
|
||||||
// This performs lowering to VCode, register-allocates the code, computes
|
// This performs lowering to VCode, register-allocates the code, computes
|
||||||
// block layout and finalizes branches. The result is ready for binary emission.
|
// block layout and finalizes branches. The result is ready for binary emission.
|
||||||
let abi = Box::new(abi::ARM64ABIBody::new(&func));
|
let abi = Box::new(abi::AArch64ABIBody::new(func));
|
||||||
compile::compile::<Arm64Backend>(&mut func, self, abi, flags)
|
compile::compile::<AArch64Backend>(func, self, abi, flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MachBackend for Arm64Backend {
|
impl MachBackend for AArch64Backend {
|
||||||
fn compile_function(
|
fn compile_function(
|
||||||
&self,
|
&self,
|
||||||
func: Function,
|
func: &Function,
|
||||||
want_disasm: bool,
|
want_disasm: bool,
|
||||||
) -> CodegenResult<MachCompileResult> {
|
) -> CodegenResult<MachCompileResult> {
|
||||||
let flags = self.flags();
|
let flags = self.flags();
|
||||||
@@ -66,11 +65,11 @@ impl MachBackend for Arm64Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"arm64"
|
"aarch64"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn triple(&self) -> Triple {
|
fn triple(&self) -> Triple {
|
||||||
FromStr::from_str("arm64").unwrap()
|
self.triple.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flags(&self) -> &settings::Flags {
|
fn flags(&self) -> &settings::Flags {
|
||||||
@@ -84,32 +83,28 @@ impl MachBackend for Arm64Backend {
|
|||||||
|
|
||||||
/// Create a new `isa::Builder`.
|
/// Create a new `isa::Builder`.
|
||||||
pub fn isa_builder(triple: Triple) -> IsaBuilder {
|
pub fn isa_builder(triple: Triple) -> IsaBuilder {
|
||||||
|
assert!(triple.architecture == Architecture::Aarch64(Aarch64Architecture::Aarch64));
|
||||||
IsaBuilder {
|
IsaBuilder {
|
||||||
triple,
|
triple,
|
||||||
setup: settings::builder(),
|
setup: settings::builder(),
|
||||||
constructor: isa_constructor,
|
constructor: |triple, shared_flags, _| {
|
||||||
|
let backend = AArch64Backend::new_with_flags(triple, shared_flags);
|
||||||
|
Box::new(TargetIsaAdapter::new(backend))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isa_constructor(
|
|
||||||
_: Triple,
|
|
||||||
shared_flags: settings::Flags,
|
|
||||||
_arch_flag_builder: settings::Builder,
|
|
||||||
) -> Box<dyn TargetIsa> {
|
|
||||||
let backend = Arm64Backend::new_with_flags(shared_flags);
|
|
||||||
Box::new(TargetIsaAdapter::new(backend))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::binemit::{NullRelocSink, NullStackmapSink, NullTrapSink};
|
|
||||||
use crate::cursor::{Cursor, FuncCursor};
|
use crate::cursor::{Cursor, FuncCursor};
|
||||||
use crate::ir::types::*;
|
use crate::ir::types::*;
|
||||||
use crate::ir::{AbiParam, ExternalName, Function, InstBuilder, Signature};
|
use crate::ir::{AbiParam, ExternalName, Function, InstBuilder, Signature};
|
||||||
use crate::isa::CallConv;
|
use crate::isa::CallConv;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
use crate::settings::Configurable;
|
use crate::settings::Configurable;
|
||||||
|
use core::str::FromStr;
|
||||||
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compile_function() {
|
fn test_compile_function() {
|
||||||
@@ -130,8 +125,11 @@ mod test {
|
|||||||
|
|
||||||
let mut shared_flags = settings::builder();
|
let mut shared_flags = settings::builder();
|
||||||
shared_flags.set("opt_level", "none").unwrap();
|
shared_flags.set("opt_level", "none").unwrap();
|
||||||
let backend = Arm64Backend::new_with_flags(settings::Flags::new(shared_flags));
|
let backend = AArch64Backend::new_with_flags(
|
||||||
let sections = backend.compile_function(func, false).unwrap().sections;
|
Triple::from_str("aarch64").unwrap(),
|
||||||
|
settings::Flags::new(shared_flags),
|
||||||
|
);
|
||||||
|
let sections = backend.compile_function(&mut func, false).unwrap().sections;
|
||||||
let code = §ions.sections[0].data;
|
let code = §ions.sections[0].data;
|
||||||
|
|
||||||
// stp x29, x30, [sp, #-16]!
|
// stp x29, x30, [sp, #-16]!
|
||||||
@@ -182,9 +180,12 @@ mod test {
|
|||||||
|
|
||||||
let mut shared_flags = settings::builder();
|
let mut shared_flags = settings::builder();
|
||||||
shared_flags.set("opt_level", "none").unwrap();
|
shared_flags.set("opt_level", "none").unwrap();
|
||||||
let backend = Arm64Backend::new_with_flags(settings::Flags::new(shared_flags));
|
let backend = AArch64Backend::new_with_flags(
|
||||||
|
Triple::from_str("aarch64").unwrap(),
|
||||||
|
settings::Flags::new(shared_flags),
|
||||||
|
);
|
||||||
let result = backend
|
let result = backend
|
||||||
.compile_function(func, /* want_disasm = */ false)
|
.compile_function(&mut func, /* want_disasm = */ false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let code = &result.sections.sections[0].data;
|
let code = &result.sections.sections[0].data;
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ pub mod fde;
|
|||||||
mod arm32;
|
mod arm32;
|
||||||
|
|
||||||
#[cfg(feature = "arm64")]
|
#[cfg(feature = "arm64")]
|
||||||
mod arm64;
|
mod aarch64;
|
||||||
|
|
||||||
mod call_conv;
|
mod call_conv;
|
||||||
mod constraints;
|
mod constraints;
|
||||||
@@ -93,6 +93,9 @@ mod encoding;
|
|||||||
pub mod registers;
|
pub mod registers;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_utils;
|
||||||
|
|
||||||
/// Returns a builder that can create a corresponding `TargetIsa`
|
/// Returns a builder that can create a corresponding `TargetIsa`
|
||||||
/// or `Err(LookupError::SupportDisabled)` if not enabled.
|
/// or `Err(LookupError::SupportDisabled)` if not enabled.
|
||||||
macro_rules! isa_builder {
|
macro_rules! isa_builder {
|
||||||
@@ -117,7 +120,7 @@ pub fn lookup(triple: Triple) -> Result<Builder, LookupError> {
|
|||||||
isa_builder!(x86, "x86", triple)
|
isa_builder!(x86, "x86", triple)
|
||||||
}
|
}
|
||||||
Architecture::Arm { .. } => isa_builder!(arm32, "arm32", triple),
|
Architecture::Arm { .. } => isa_builder!(arm32, "arm32", triple),
|
||||||
Architecture::Aarch64 { .. } => isa_builder!(arm64, "arm64", triple),
|
Architecture::Aarch64 { .. } => isa_builder!(aarch64, "arm64", triple),
|
||||||
_ => Err(LookupError::Unsupported),
|
_ => Err(LookupError::Unsupported),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
// This is unused when no platforms with the new backend are enabled.
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc};
|
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc};
|
||||||
use crate::ir::Value;
|
use crate::ir::Value;
|
||||||
use crate::ir::{ConstantOffset, ExternalName, Function, JumpTable, Opcode, SourceLoc, TrapCode};
|
use crate::ir::{ConstantOffset, ExternalName, Function, JumpTable, Opcode, SourceLoc, TrapCode};
|
||||||
use crate::isa::TargetIsa;
|
use crate::isa::TargetIsa;
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use std::string::{String, ToString};
|
use std::string::String;
|
||||||
|
|
||||||
pub struct TestCodeSink {
|
pub struct TestCodeSink {
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
@@ -16,11 +19,13 @@ impl TestCodeSink {
|
|||||||
TestCodeSink { bytes: vec![] }
|
TestCodeSink { bytes: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is pretty lame, but whatever ..
|
/// Return the code emitted to this sink as a hex string.
|
||||||
pub fn stringify(&self) -> String {
|
pub fn stringify(&self) -> String {
|
||||||
let mut s = "".to_string();
|
// This is pretty lame, but whatever ..
|
||||||
|
use std::fmt::Write;
|
||||||
|
let mut s = String::with_capacity(self.bytes.len() * 2);
|
||||||
for b in &self.bytes {
|
for b in &self.bytes {
|
||||||
s = s + &format!("{:02X}", b).to_string();
|
write!(&mut s, "{:02X}", b).unwrap();
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ mod context;
|
|||||||
mod dce;
|
mod dce;
|
||||||
mod divconst_magic_numbers;
|
mod divconst_magic_numbers;
|
||||||
mod fx;
|
mod fx;
|
||||||
|
mod inst_predicates;
|
||||||
mod iterators;
|
mod iterators;
|
||||||
mod legalizer;
|
mod legalizer;
|
||||||
mod licm;
|
mod licm;
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
//! ABI definitions.
|
//! ABI definitions.
|
||||||
|
|
||||||
use crate::ir;
|
|
||||||
use crate::ir::StackSlot;
|
use crate::ir::StackSlot;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
|
|
||||||
use regalloc::{Reg, Set, SpillSlot, VirtualReg, Writable};
|
use regalloc::{Reg, Set, SpillSlot, Writable};
|
||||||
|
|
||||||
/// Trait implemented by an object that tracks ABI-related state (e.g., stack
|
/// Trait implemented by an object that tracks ABI-related state (e.g., stack
|
||||||
/// layout) and can generate code while emitting the *body* of a function.
|
/// layout) and can generate code while emitting the *body* of a function.
|
||||||
pub trait ABIBody<I: VCodeInst> {
|
pub trait ABIBody {
|
||||||
|
/// The instruction type for the ISA associated with this ABI.
|
||||||
|
type I: VCodeInst;
|
||||||
|
|
||||||
/// Get the liveins of the function.
|
/// Get the liveins of the function.
|
||||||
fn liveins(&self) -> Set<RealReg>;
|
fn liveins(&self) -> Set<RealReg>;
|
||||||
|
|
||||||
@@ -27,17 +29,19 @@ pub trait ABIBody<I: VCodeInst> {
|
|||||||
|
|
||||||
/// Generate an instruction which copies an argument to a destination
|
/// Generate an instruction which copies an argument to a destination
|
||||||
/// register.
|
/// register.
|
||||||
fn gen_copy_arg_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> I;
|
fn gen_copy_arg_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> Self::I;
|
||||||
|
|
||||||
/// Generate an instruction which copies a source register to a return
|
/// Generate an instruction which copies a source register to a return
|
||||||
/// value slot.
|
/// value slot.
|
||||||
fn gen_copy_reg_to_retval(&self, idx: usize, from_reg: Reg) -> I;
|
fn gen_copy_reg_to_retval(&self, idx: usize, from_reg: Reg) -> Self::I;
|
||||||
|
|
||||||
/// Generate a return instruction.
|
/// Generate a return instruction.
|
||||||
fn gen_ret(&self) -> I;
|
fn gen_ret(&self) -> Self::I;
|
||||||
|
|
||||||
/// Generate an epilogue placeholder.
|
/// Generate an epilogue placeholder. The returned instruction should return `true` from
|
||||||
fn gen_epilogue_placeholder(&self) -> I;
|
/// `is_epilogue_placeholder()`; this is used to indicate to the lowering driver when
|
||||||
|
/// the epilogue should be inserted.
|
||||||
|
fn gen_epilogue_placeholder(&self) -> Self::I;
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
// Every function above this line may only be called pre-regalloc.
|
// Every function above this line may only be called pre-regalloc.
|
||||||
@@ -56,32 +60,32 @@ pub trait ABIBody<I: VCodeInst> {
|
|||||||
fn load_stackslot(
|
fn load_stackslot(
|
||||||
&self,
|
&self,
|
||||||
slot: StackSlot,
|
slot: StackSlot,
|
||||||
offset: usize,
|
offset: u32,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
into_reg: Writable<Reg>,
|
into_reg: Writable<Reg>,
|
||||||
) -> I;
|
) -> Self::I;
|
||||||
|
|
||||||
/// Store to a stackslot.
|
/// Store to a stackslot.
|
||||||
fn store_stackslot(&self, slot: StackSlot, offset: usize, ty: Type, from_reg: Reg) -> I;
|
fn store_stackslot(&self, slot: StackSlot, offset: u32, ty: Type, from_reg: Reg) -> Self::I;
|
||||||
|
|
||||||
/// Load from a spillslot.
|
/// Load from a spillslot.
|
||||||
fn load_spillslot(&self, slot: SpillSlot, ty: Type, into_reg: Writable<Reg>) -> I;
|
fn load_spillslot(&self, slot: SpillSlot, ty: Type, into_reg: Writable<Reg>) -> Self::I;
|
||||||
|
|
||||||
/// Store to a spillslot.
|
/// Store to a spillslot.
|
||||||
fn store_spillslot(&self, slot: SpillSlot, ty: Type, from_reg: Reg) -> I;
|
fn store_spillslot(&self, slot: SpillSlot, ty: Type, from_reg: Reg) -> Self::I;
|
||||||
|
|
||||||
/// Generate a prologue, post-regalloc. This should include any stack
|
/// Generate a prologue, post-regalloc. This should include any stack
|
||||||
/// frame or other setup necessary to use the other methods (`load_arg`,
|
/// frame or other setup necessary to use the other methods (`load_arg`,
|
||||||
/// `store_retval`, and spillslot accesses.) |self| is mutable so that we
|
/// `store_retval`, and spillslot accesses.) `self` is mutable so that we
|
||||||
/// can store information in it which will be useful when creating the
|
/// can store information in it which will be useful when creating the
|
||||||
/// epilogue.
|
/// epilogue.
|
||||||
fn gen_prologue(&mut self, flags: &settings::Flags) -> Vec<I>;
|
fn gen_prologue(&mut self, flags: &settings::Flags) -> Vec<Self::I>;
|
||||||
|
|
||||||
/// Generate an epilogue, post-regalloc. Note that this must generate the
|
/// Generate an epilogue, post-regalloc. Note that this must generate the
|
||||||
/// actual return instruction (rather than emitting this in the lowering
|
/// actual return instruction (rather than emitting this in the lowering
|
||||||
/// logic), because the epilogue code comes before the return and the two are
|
/// logic), because the epilogue code comes before the return and the two are
|
||||||
/// likely closely related.
|
/// likely closely related.
|
||||||
fn gen_epilogue(&self, flags: &settings::Flags) -> Vec<I>;
|
fn gen_epilogue(&self, flags: &settings::Flags) -> Vec<Self::I>;
|
||||||
|
|
||||||
/// Returns the full frame size for the given function, after prologue emission has run. This
|
/// Returns the full frame size for the given function, after prologue emission has run. This
|
||||||
/// comprises the spill space, incoming argument space, alignment padding, etc.
|
/// comprises the spill space, incoming argument space, alignment padding, etc.
|
||||||
@@ -91,10 +95,10 @@ pub trait ABIBody<I: VCodeInst> {
|
|||||||
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32;
|
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32;
|
||||||
|
|
||||||
/// Generate a spill.
|
/// Generate a spill.
|
||||||
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> I;
|
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> Self::I;
|
||||||
|
|
||||||
/// Generate a reload (fill).
|
/// Generate a reload (fill).
|
||||||
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> I;
|
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> Self::I;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait implemented by an object that tracks ABI-related state and can
|
/// Trait implemented by an object that tracks ABI-related state and can
|
||||||
@@ -111,22 +115,25 @@ pub trait ABIBody<I: VCodeInst> {
|
|||||||
/// and retval copies, and attach the register use/def info to the call.
|
/// and retval copies, and attach the register use/def info to the call.
|
||||||
///
|
///
|
||||||
/// This trait is thus provided for convenience to the backends.
|
/// This trait is thus provided for convenience to the backends.
|
||||||
pub trait ABICall<I: VCodeInst> {
|
pub trait ABICall {
|
||||||
|
/// The instruction type for the ISA associated with this ABI.
|
||||||
|
type I: VCodeInst;
|
||||||
|
|
||||||
/// Get the number of arguments expected.
|
/// Get the number of arguments expected.
|
||||||
fn num_args(&self) -> usize;
|
fn num_args(&self) -> usize;
|
||||||
|
|
||||||
/// Save the clobbered registers.
|
/// Save the clobbered registers.
|
||||||
/// Copy an argument value from a source register, prior to the call.
|
/// Copy an argument value from a source register, prior to the call.
|
||||||
fn gen_copy_reg_to_arg(&self, idx: usize, from_reg: Reg) -> I;
|
fn gen_copy_reg_to_arg(&self, idx: usize, from_reg: Reg) -> Self::I;
|
||||||
|
|
||||||
/// Copy a return value into a destination register, after the call returns.
|
/// Copy a return value into a destination register, after the call returns.
|
||||||
fn gen_copy_retval_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> I;
|
fn gen_copy_retval_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> Self::I;
|
||||||
|
|
||||||
/// Pre-adjust the stack, prior to argument copies and call.
|
/// Pre-adjust the stack, prior to argument copies and call.
|
||||||
fn gen_stack_pre_adjust(&self) -> Vec<I>;
|
fn gen_stack_pre_adjust(&self) -> Vec<Self::I>;
|
||||||
|
|
||||||
/// Post-adjust the satck, after call return and return-value copies.
|
/// Post-adjust the satck, after call return and return-value copies.
|
||||||
fn gen_stack_post_adjust(&self) -> Vec<I>;
|
fn gen_stack_post_adjust(&self) -> Vec<Self::I>;
|
||||||
|
|
||||||
/// Generate the call itself.
|
/// Generate the call itself.
|
||||||
///
|
///
|
||||||
@@ -138,5 +145,5 @@ pub trait ABICall<I: VCodeInst> {
|
|||||||
/// registers are also logically defs, but should never be read; their
|
/// registers are also logically defs, but should never be read; their
|
||||||
/// values are "defined" (to the regalloc) but "undefined" in every other
|
/// values are "defined" (to the regalloc) but "undefined" in every other
|
||||||
/// sense.)
|
/// sense.)
|
||||||
fn gen_call(&self) -> Vec<I>;
|
fn gen_call(&self) -> Vec<Self::I>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ use crate::binemit;
|
|||||||
use crate::ir;
|
use crate::ir;
|
||||||
use crate::isa::{EncInfo, Encoding, Encodings, Legalize, RegClass, RegInfo, TargetIsa};
|
use crate::isa::{EncInfo, Encoding, Encodings, Legalize, RegClass, RegInfo, TargetIsa};
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::regalloc::{RegDiversions, RegisterSet};
|
use crate::regalloc::RegisterSet;
|
||||||
use crate::settings::Flags;
|
use crate::settings::Flags;
|
||||||
|
|
||||||
|
#[cfg(feature = "testing_hooks")]
|
||||||
|
use crate::regalloc::RegDiversions;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
@@ -30,7 +33,11 @@ impl TargetIsaAdapter {
|
|||||||
|
|
||||||
impl fmt::Display for TargetIsaAdapter {
|
impl fmt::Display for TargetIsaAdapter {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "MachBackend")
|
f.debug_struct("MachBackend")
|
||||||
|
.field("name", &self.backend.name())
|
||||||
|
.field("triple", &self.backend.triple())
|
||||||
|
.field("flags", &format!("{}", self.backend.flags()))
|
||||||
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! Computation of basic block order in emitted code.
|
//! Computation of basic block order in emitted code.
|
||||||
|
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
|
use regalloc::{BlockIx, Function};
|
||||||
|
|
||||||
/// Simple reverse postorder-based block order emission.
|
/// Simple reverse postorder-based block order emission.
|
||||||
///
|
///
|
||||||
@@ -29,9 +30,8 @@ impl BlockRPO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (start, end) = &vcode.block_ranges[block as usize];
|
for i in vcode.block_insns(BlockIx::new(block)) {
|
||||||
for i in *start..*end {
|
if vcode.get_insn(i).is_epilogue_placeholder() {
|
||||||
if vcode.insts[i as usize].is_epilogue_placeholder() {
|
|
||||||
debug_assert!(self.deferred_last.is_none());
|
debug_assert!(self.deferred_last.is_none());
|
||||||
self.deferred_last = Some(block);
|
self.deferred_last = Some(block);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -7,14 +7,13 @@ use crate::timing;
|
|||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use regalloc::{allocate_registers, RegAllocAlgorithm};
|
use regalloc::{allocate_registers, RegAllocAlgorithm};
|
||||||
use std::env;
|
|
||||||
|
|
||||||
/// Compile the given function down to VCode with allocated registers, ready
|
/// Compile the given function down to VCode with allocated registers, ready
|
||||||
/// for binary emission.
|
/// for binary emission.
|
||||||
pub fn compile<B: LowerBackend>(
|
pub fn compile<B: LowerBackend>(
|
||||||
f: &mut Function,
|
f: &Function,
|
||||||
b: &B,
|
b: &B,
|
||||||
abi: Box<dyn ABIBody<B::MInst>>,
|
abi: Box<dyn ABIBody<I = B::MInst>>,
|
||||||
flags: &settings::Flags,
|
flags: &settings::Flags,
|
||||||
) -> VCode<B::MInst>
|
) -> VCode<B::MInst>
|
||||||
where
|
where
|
||||||
@@ -28,18 +27,8 @@ where
|
|||||||
debug!("vcode from lowering: \n{}", vcode.show_rru(Some(universe)));
|
debug!("vcode from lowering: \n{}", vcode.show_rru(Some(universe)));
|
||||||
|
|
||||||
// Perform register allocation.
|
// Perform register allocation.
|
||||||
let algorithm = match env::var("REGALLOC") {
|
// TODO: select register allocation algorithm from flags.
|
||||||
Ok(str) => match str.as_str() {
|
let algorithm = RegAllocAlgorithm::Backtracking;
|
||||||
"lsrac" => RegAllocAlgorithm::LinearScanChecked,
|
|
||||||
"lsra" => RegAllocAlgorithm::LinearScan,
|
|
||||||
// to wit: btc doesn't mean "bitcoin" here
|
|
||||||
"btc" => RegAllocAlgorithm::BacktrackingChecked,
|
|
||||||
_ => RegAllocAlgorithm::Backtracking,
|
|
||||||
},
|
|
||||||
// By default use backtracking, which is the fastest.
|
|
||||||
Err(_) => RegAllocAlgorithm::Backtracking,
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
let _tt = timing::regalloc();
|
let _tt = timing::regalloc();
|
||||||
allocate_registers(
|
allocate_registers(
|
||||||
@@ -70,7 +59,5 @@ where
|
|||||||
vcode.show_rru(Some(universe))
|
vcode.show_rru(Some(universe))
|
||||||
);
|
);
|
||||||
|
|
||||||
//println!("{}\n", vcode.show_rru(Some(&B::MInst::reg_universe())));
|
|
||||||
|
|
||||||
vcode
|
vcode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,39 +2,37 @@
|
|||||||
//! to machine instructions with virtual registers. This is *almost* the final
|
//! to machine instructions with virtual registers. This is *almost* the final
|
||||||
//! machine code, except for register allocation.
|
//! machine code, except for register allocation.
|
||||||
|
|
||||||
use crate::binemit::CodeSink;
|
|
||||||
use crate::dce::has_side_effect;
|
|
||||||
use crate::entity::SecondaryMap;
|
use crate::entity::SecondaryMap;
|
||||||
|
use crate::inst_predicates::has_side_effect;
|
||||||
|
use crate::ir::instructions::BranchInfo;
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
Block, ExternalName, Function, GlobalValueData, Inst, InstructionData, MemFlags, Opcode,
|
Block, ExternalName, Function, GlobalValueData, Inst, InstructionData, MemFlags, Opcode,
|
||||||
Signature, SourceLoc, Type, Value, ValueDef,
|
Signature, SourceLoc, Type, Value, ValueDef,
|
||||||
};
|
};
|
||||||
use crate::isa::registers::RegUnit;
|
use crate::machinst::{ABIBody, BlockIndex, VCode, VCodeBuilder, VCodeInst};
|
||||||
use crate::machinst::{
|
|
||||||
ABIBody, BlockIndex, MachInst, MachInstEmit, VCode, VCodeBuilder, VCodeInst,
|
|
||||||
};
|
|
||||||
use crate::num_uses::NumUses;
|
use crate::num_uses::NumUses;
|
||||||
|
|
||||||
use regalloc::Function as RegallocFunction;
|
use regalloc::{Reg, RegClass, Set, VirtualReg, Writable};
|
||||||
use regalloc::{RealReg, Reg, RegClass, Set, VirtualReg, Writable};
|
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
/// A context that machine-specific lowering code can use to emit lowered instructions. This is the
|
/// A context that machine-specific lowering code can use to emit lowered instructions. This is the
|
||||||
/// view of the machine-independent per-function lowering context that is seen by the machine
|
/// view of the machine-independent per-function lowering context that is seen by the machine
|
||||||
/// backend.
|
/// backend.
|
||||||
pub trait LowerCtx<I> {
|
pub trait LowerCtx {
|
||||||
|
/// The instruction type for which this lowering framework is instantiated.
|
||||||
|
type I;
|
||||||
|
|
||||||
/// Get the instdata for a given IR instruction.
|
/// Get the instdata for a given IR instruction.
|
||||||
fn data(&self, ir_inst: Inst) -> &InstructionData;
|
fn data(&self, ir_inst: Inst) -> &InstructionData;
|
||||||
/// Get the controlling type for a polymorphic IR instruction.
|
/// Get the controlling type for a polymorphic IR instruction.
|
||||||
fn ty(&self, ir_inst: Inst) -> Type;
|
fn ty(&self, ir_inst: Inst) -> Type;
|
||||||
/// Emit a machine instruction.
|
/// Emit a machine instruction.
|
||||||
fn emit(&mut self, mach_inst: I);
|
fn emit(&mut self, mach_inst: Self::I);
|
||||||
/// Indicate that an IR instruction has been merged, and so one of its
|
/// Indicate that an IR instruction has been merged, and so one of its
|
||||||
/// uses is gone (replaced by uses of the instruction's inputs). This
|
/// uses is gone (replaced by uses of the instruction's inputs). This
|
||||||
/// helps the lowering algorithm to perform on-the-fly DCE, skipping over
|
/// helps the lowering algorithm to perform on-the-fly DCE, skipping over
|
||||||
@@ -87,11 +85,11 @@ pub trait LowerBackend {
|
|||||||
/// Lower a single instruction. Instructions are lowered in reverse order.
|
/// Lower a single instruction. Instructions are lowered in reverse order.
|
||||||
/// This function need not handle branches; those are always passed to
|
/// This function need not handle branches; those are always passed to
|
||||||
/// `lower_branch_group` below.
|
/// `lower_branch_group` below.
|
||||||
fn lower<C: LowerCtx<Self::MInst>>(&self, ctx: &mut C, inst: Inst);
|
fn lower<C: LowerCtx<I = Self::MInst>>(&self, ctx: &mut C, inst: Inst);
|
||||||
|
|
||||||
/// Lower a block-terminating group of branches (which together can be seen as one
|
/// Lower a block-terminating group of branches (which together can be seen as one
|
||||||
/// N-way branch), given a vcode BlockIndex for each target.
|
/// N-way branch), given a vcode BlockIndex for each target.
|
||||||
fn lower_branch_group<C: LowerCtx<Self::MInst>>(
|
fn lower_branch_group<C: LowerCtx<I = Self::MInst>>(
|
||||||
&self,
|
&self,
|
||||||
ctx: &mut C,
|
ctx: &mut C,
|
||||||
insts: &[Inst],
|
insts: &[Inst],
|
||||||
@@ -103,22 +101,22 @@ pub trait LowerBackend {
|
|||||||
/// Machine-independent lowering driver / machine-instruction container. Maintains a correspondence
|
/// Machine-independent lowering driver / machine-instruction container. Maintains a correspondence
|
||||||
/// from original Inst to MachInsts.
|
/// from original Inst to MachInsts.
|
||||||
pub struct Lower<'a, I: VCodeInst> {
|
pub struct Lower<'a, I: VCodeInst> {
|
||||||
// The function to lower.
|
/// The function to lower.
|
||||||
f: &'a Function,
|
f: &'a Function,
|
||||||
|
|
||||||
// Lowered machine instructions.
|
/// Lowered machine instructions.
|
||||||
vcode: VCodeBuilder<I>,
|
vcode: VCodeBuilder<I>,
|
||||||
|
|
||||||
// Number of active uses (minus `dec_use()` calls by backend) of each instruction.
|
/// Number of active uses (minus `dec_use()` calls by backend) of each instruction.
|
||||||
num_uses: SecondaryMap<Inst, u32>,
|
num_uses: SecondaryMap<Inst, u32>,
|
||||||
|
|
||||||
// Mapping from `Value` (SSA value in IR) to virtual register.
|
/// Mapping from `Value` (SSA value in IR) to virtual register.
|
||||||
value_regs: SecondaryMap<Value, Reg>,
|
value_regs: SecondaryMap<Value, Reg>,
|
||||||
|
|
||||||
// Return-value vregs.
|
/// Return-value vregs.
|
||||||
retval_regs: Vec<Reg>,
|
retval_regs: Vec<Reg>,
|
||||||
|
|
||||||
// Next virtual register number to allocate.
|
/// Next virtual register number to allocate.
|
||||||
next_vreg: u32,
|
next_vreg: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +142,7 @@ enum GenerateReturn {
|
|||||||
|
|
||||||
impl<'a, I: VCodeInst> Lower<'a, I> {
|
impl<'a, I: VCodeInst> Lower<'a, I> {
|
||||||
/// Prepare a new lowering context for the given IR function.
|
/// Prepare a new lowering context for the given IR function.
|
||||||
pub fn new(f: &'a Function, abi: Box<dyn ABIBody<I>>) -> Lower<'a, I> {
|
pub fn new(f: &'a Function, abi: Box<dyn ABIBody<I = I>>) -> Lower<'a, I> {
|
||||||
let mut vcode = VCodeBuilder::new(abi);
|
let mut vcode = VCodeBuilder::new(abi);
|
||||||
|
|
||||||
let num_uses = NumUses::compute(f).take_uses();
|
let num_uses = NumUses::compute(f).take_uses();
|
||||||
@@ -244,7 +242,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
|
|||||||
let mut succs: SmallVec<[Block; 16]> = SmallVec::new();
|
let mut succs: SmallVec<[Block; 16]> = SmallVec::new();
|
||||||
for inst in self.f.layout.block_insts(b) {
|
for inst in self.f.layout.block_insts(b) {
|
||||||
if self.f.dfg[inst].opcode().is_branch() {
|
if self.f.dfg[inst].opcode().is_branch() {
|
||||||
succs.extend(branch_targets(self.f, b, inst).into_iter());
|
visit_branch_targets(self.f, b, inst, |succ| {
|
||||||
|
succs.push(succ);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for succ in succs.into_iter() {
|
for succ in succs.into_iter() {
|
||||||
@@ -264,17 +264,14 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
|
|||||||
/// Lower the function.
|
/// Lower the function.
|
||||||
pub fn lower<B: LowerBackend<MInst = I>>(mut self, backend: &B) -> VCode<I> {
|
pub fn lower<B: LowerBackend<MInst = I>>(mut self, backend: &B) -> VCode<I> {
|
||||||
// Find all reachable blocks.
|
// Find all reachable blocks.
|
||||||
let mut bbs = self.find_reachable_bbs();
|
let bbs = self.find_reachable_bbs();
|
||||||
// Work backward (reverse block order, reverse through each block), skipping insns with zero
|
|
||||||
// uses.
|
|
||||||
bbs.reverse();
|
|
||||||
|
|
||||||
// This records a Block-to-BlockIndex map so that branch targets can be resolved.
|
// This records a Block-to-BlockIndex map so that branch targets can be resolved.
|
||||||
let mut next_bindex = self.vcode.init_bb_map(&bbs[..]);
|
let mut next_bindex = self.vcode.init_bb_map(&bbs[..]);
|
||||||
|
|
||||||
// Allocate a separate BlockIndex for each control-flow instruction so that we can create
|
// Allocate a separate BlockIndex for each control-flow instruction so that we can create
|
||||||
// the edge blocks later. Each entry for a control-flow inst is the edge block; the list
|
// the edge blocks later. Each entry for a control-flow inst is the edge block; the list
|
||||||
// has (cf-inst, edge block, orig block) tuples.
|
// has (control flow inst, edge block, orig block) tuples.
|
||||||
let mut edge_blocks_by_inst: SecondaryMap<Inst, Vec<BlockIndex>> =
|
let mut edge_blocks_by_inst: SecondaryMap<Inst, Vec<BlockIndex>> =
|
||||||
SecondaryMap::with_default(vec![]);
|
SecondaryMap::with_default(vec![]);
|
||||||
let mut edge_blocks: Vec<(Inst, BlockIndex, Block)> = vec![];
|
let mut edge_blocks: Vec<(Inst, BlockIndex, Block)> = vec![];
|
||||||
@@ -282,7 +279,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
|
|||||||
debug!("about to lower function: {:?}", self.f);
|
debug!("about to lower function: {:?}", self.f);
|
||||||
debug!("bb map: {:?}", self.vcode.blocks_by_bb());
|
debug!("bb map: {:?}", self.vcode.blocks_by_bb());
|
||||||
|
|
||||||
for bb in bbs.iter() {
|
// Work backward (reverse block order, reverse through each block), skipping insns with zero
|
||||||
|
// uses.
|
||||||
|
for bb in bbs.iter().rev() {
|
||||||
for inst in self.f.layout.block_insts(*bb) {
|
for inst in self.f.layout.block_insts(*bb) {
|
||||||
let op = self.f.dfg[inst].opcode();
|
let op = self.f.dfg[inst].opcode();
|
||||||
if op.is_branch() {
|
if op.is_branch() {
|
||||||
@@ -293,9 +292,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
|
|||||||
edge_blocks_by_inst[inst].push(edge_block);
|
edge_blocks_by_inst[inst].push(edge_block);
|
||||||
edge_blocks.push((inst, edge_block, next_bb));
|
edge_blocks.push((inst, edge_block, next_bb));
|
||||||
};
|
};
|
||||||
for succ in branch_targets(self.f, *bb, inst).into_iter() {
|
visit_branch_targets(self.f, *bb, inst, |succ| {
|
||||||
add_succ(succ);
|
add_succ(succ);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,7 +302,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
|
|||||||
for bb in bbs.iter() {
|
for bb in bbs.iter() {
|
||||||
debug!("lowering bb: {}", bb);
|
debug!("lowering bb: {}", bb);
|
||||||
|
|
||||||
// If this is a return block, produce the return value setup.
|
// If this is a return block, produce the return value setup. N.B.: this comes
|
||||||
|
// *before* the below because it must occur *after* any other instructions, and
|
||||||
|
// instructions are lowered in reverse order.
|
||||||
let last_insn = self.f.layout.block_insts(*bb).last().unwrap();
|
let last_insn = self.f.layout.block_insts(*bb).last().unwrap();
|
||||||
let last_insn_opcode = self.f.dfg[last_insn].opcode();
|
let last_insn_opcode = self.f.dfg[last_insn].opcode();
|
||||||
if last_insn_opcode.is_return() {
|
if last_insn_opcode.is_return() {
|
||||||
@@ -513,7 +514,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I: VCodeInst> LowerCtx<I> for Lower<'a, I> {
|
impl<'a, I: VCodeInst> LowerCtx for Lower<'a, I> {
|
||||||
|
type I = I;
|
||||||
|
|
||||||
/// Get the instdata for a given IR instruction.
|
/// Get the instdata for a given IR instruction.
|
||||||
fn data(&self, ir_inst: Inst) -> &InstructionData {
|
fn data(&self, ir_inst: Inst) -> &InstructionData {
|
||||||
&self.f.dfg[ir_inst]
|
&self.f.dfg[ir_inst]
|
||||||
@@ -695,29 +698,23 @@ impl<'a, I: VCodeInst> LowerCtx<I> for Lower<'a, I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn branch_targets(f: &Function, block: Block, inst: Inst) -> SmallVec<[Block; 16]> {
|
fn visit_branch_targets<F: FnMut(Block)>(f: &Function, block: Block, inst: Inst, mut visit: F) {
|
||||||
let mut ret = SmallVec::new();
|
|
||||||
if f.dfg[inst].opcode() == Opcode::Fallthrough {
|
if f.dfg[inst].opcode() == Opcode::Fallthrough {
|
||||||
ret.push(f.layout.next_block(block).unwrap());
|
visit(f.layout.next_block(block).unwrap());
|
||||||
} else {
|
} else {
|
||||||
match &f.dfg[inst] {
|
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
|
||||||
&InstructionData::Jump { destination, .. }
|
BranchInfo::NotABranch => {}
|
||||||
| &InstructionData::Branch { destination, .. }
|
BranchInfo::SingleDest(dest, _) => {
|
||||||
| &InstructionData::BranchInt { destination, .. }
|
visit(dest);
|
||||||
| &InstructionData::BranchIcmp { destination, .. }
|
|
||||||
| &InstructionData::BranchFloat { destination, .. } => {
|
|
||||||
ret.push(destination);
|
|
||||||
}
|
}
|
||||||
&InstructionData::BranchTable {
|
BranchInfo::Table(table, maybe_dest) => {
|
||||||
destination, table, ..
|
if let Some(dest) = maybe_dest {
|
||||||
} => {
|
visit(dest);
|
||||||
ret.push(destination);
|
}
|
||||||
for dest in f.jump_tables[table].as_slice() {
|
for &dest in f.jump_tables[table].as_slice() {
|
||||||
ret.push(*dest);
|
visit(dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,105 +17,97 @@
|
|||||||
//! (N.B.: though we show the VCode separately at each stage, the passes
|
//! (N.B.: though we show the VCode separately at each stage, the passes
|
||||||
//! mutate the VCode in place; these are not separate copies of the code.)
|
//! mutate the VCode in place; these are not separate copies of the code.)
|
||||||
//!
|
//!
|
||||||
//! | ir::Function (SSA IR, machine-independent opcodes)
|
//! ```plain
|
||||||
//! | |
|
//!
|
||||||
//! | | [lower]
|
//! ir::Function (SSA IR, machine-independent opcodes)
|
||||||
//! | |
|
//! |
|
||||||
//! | VCode<arch_backend::Inst> (machine instructions:
|
//! | [lower]
|
||||||
//! | | - mostly virtual registers.
|
//! |
|
||||||
//! | | - cond branches in two-target form.
|
//! VCode<arch_backend::Inst> (machine instructions:
|
||||||
//! | | - branch targets are block indices.
|
//! | - mostly virtual registers.
|
||||||
//! | | - in-memory constants held by insns,
|
//! | - cond branches in two-target form.
|
||||||
//! | | with unknown offsets.
|
//! | - branch targets are block indices.
|
||||||
//! | | - critical edges (actually all edges)
|
//! | - in-memory constants held by insns,
|
||||||
//! | | are split.)
|
//! | with unknown offsets.
|
||||||
//! | | [regalloc]
|
//! | - critical edges (actually all edges)
|
||||||
//! | |
|
//! | are split.)
|
||||||
//! | VCode<arch_backend::Inst> (machine instructions:
|
//! | [regalloc]
|
||||||
//! | | - all real registers.
|
//! |
|
||||||
//! | | - new instruction sequence returned
|
//! VCode<arch_backend::Inst> (machine instructions:
|
||||||
//! | | out-of-band in RegAllocResult.
|
//! | - all real registers.
|
||||||
//! | | - instruction sequence has spills,
|
//! | - new instruction sequence returned
|
||||||
//! | | reloads, and moves inserted.
|
//! | out-of-band in RegAllocResult.
|
||||||
//! | | - other invariants same as above.)
|
//! | - instruction sequence has spills,
|
||||||
//! | |
|
//! | reloads, and moves inserted.
|
||||||
//! | | [preamble/postamble]
|
//! | - other invariants same as above.)
|
||||||
//! | |
|
//! |
|
||||||
//! | VCode<arch_backend::Inst> (machine instructions:
|
//! | [preamble/postamble]
|
||||||
//! | | - stack-frame size known.
|
//! |
|
||||||
//! | | - out-of-band instruction sequence
|
//! VCode<arch_backend::Inst> (machine instructions:
|
||||||
//! | | has preamble prepended to entry
|
//! | - stack-frame size known.
|
||||||
//! | | block, and postamble injected before
|
//! | - out-of-band instruction sequence
|
||||||
//! | | every return instruction.
|
//! | has preamble prepended to entry
|
||||||
//! | | - all symbolic stack references to
|
//! | block, and postamble injected before
|
||||||
//! | | stackslots and spillslots are resolved
|
//! | every return instruction.
|
||||||
//! | | to concrete FP-offset mem addresses.)
|
//! | - all symbolic stack references to
|
||||||
//! | | [block/insn ordering]
|
//! | stackslots and spillslots are resolved
|
||||||
//! | |
|
//! | to concrete FP-offset mem addresses.)
|
||||||
//! | VCode<arch_backend::Inst> (machine instructions:
|
//! | [block/insn ordering]
|
||||||
//! | | - vcode.final_block_order is filled in.
|
//! |
|
||||||
//! | | - new insn sequence from regalloc is
|
//! VCode<arch_backend::Inst> (machine instructions:
|
||||||
//! | | placed back into vcode and block
|
//! | - vcode.final_block_order is filled in.
|
||||||
//! | | boundaries are updated.)
|
//! | - new insn sequence from regalloc is
|
||||||
//! | | [redundant branch/block
|
//! | placed back into vcode and block
|
||||||
//! | | removal]
|
//! | boundaries are updated.)
|
||||||
//! | |
|
//! | [redundant branch/block
|
||||||
//! | VCode<arch_backend::Inst> (machine instructions:
|
//! | removal]
|
||||||
//! | | - all blocks that were just an
|
//! |
|
||||||
//! | | unconditional branch are removed.)
|
//! VCode<arch_backend::Inst> (machine instructions:
|
||||||
//! | |
|
//! | - all blocks that were just an
|
||||||
//! | | [branch finalization
|
//! | unconditional branch are removed.)
|
||||||
//! | | (fallthroughs)]
|
//! |
|
||||||
//! | |
|
//! | [branch finalization
|
||||||
//! | VCode<arch_backend::Inst> (machine instructions:
|
//! | (fallthroughs)]
|
||||||
//! | | - all branches are in lowered one-
|
//! |
|
||||||
//! | | target form, but targets are still
|
//! VCode<arch_backend::Inst> (machine instructions:
|
||||||
//! | | block indices.)
|
//! | - all branches are in lowered one-
|
||||||
//! | |
|
//! | target form, but targets are still
|
||||||
//! | | [branch finalization
|
//! | block indices.)
|
||||||
//! | | (offsets)]
|
//! |
|
||||||
//! | |
|
//! | [branch finalization
|
||||||
//! | VCode<arch_backend::Inst> (machine instructions:
|
//! | (offsets)]
|
||||||
//! | | - all branch offsets from start of
|
//! |
|
||||||
//! | | function are known, and all branches
|
//! VCode<arch_backend::Inst> (machine instructions:
|
||||||
//! | | have resolved-offset targets.)
|
//! | - all branch offsets from start of
|
||||||
//! | |
|
//! | function are known, and all branches
|
||||||
//! | | [MemArg finalization]
|
//! | have resolved-offset targets.)
|
||||||
//! | |
|
//! |
|
||||||
//! | VCode<arch_backend::Inst> (machine instructions:
|
//! | [MemArg finalization]
|
||||||
//! | | - all MemArg references to the constant
|
//! |
|
||||||
//! | | pool are replaced with offsets.
|
//! VCode<arch_backend::Inst> (machine instructions:
|
||||||
//! | | - all constant-pool data is collected
|
//! | - all MemArg references to the constant
|
||||||
//! | | in the VCode.)
|
//! | pool are replaced with offsets.
|
||||||
//! | |
|
//! | - all constant-pool data is collected
|
||||||
//! | | [binary emission]
|
//! | in the VCode.)
|
||||||
//! | |
|
//! |
|
||||||
//! | Vec<u8> (machine code!)
|
//! | [binary emission]
|
||||||
//! |
|
//! |
|
||||||
|
//! Vec<u8> (machine code!)
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
|
||||||
#![allow(unused_imports)]
|
use crate::binemit::{CodeInfo, CodeOffset};
|
||||||
|
|
||||||
use crate::binemit::{
|
|
||||||
CodeInfo, CodeOffset, CodeSink, MemoryCodeSink, RelocSink, StackmapSink, TrapSink,
|
|
||||||
};
|
|
||||||
use crate::entity::EntityRef;
|
|
||||||
use crate::entity::SecondaryMap;
|
use crate::entity::SecondaryMap;
|
||||||
use crate::ir::condcodes::IntCC;
|
use crate::ir::condcodes::IntCC;
|
||||||
use crate::ir::ValueLocations;
|
use crate::ir::{Function, Type};
|
||||||
use crate::ir::{DataFlowGraph, Function, Inst, Opcode, Type, Value};
|
|
||||||
use crate::isa::RegUnit;
|
|
||||||
use crate::result::CodegenResult;
|
use crate::result::CodegenResult;
|
||||||
use crate::settings::Flags;
|
use crate::settings::Flags;
|
||||||
use crate::HashMap;
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::iter::Sum;
|
|
||||||
use regalloc::Map as RegallocMap;
|
use regalloc::Map as RegallocMap;
|
||||||
use regalloc::RegUsageCollector;
|
use regalloc::RegUsageCollector;
|
||||||
use regalloc::{RealReg, RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
|
use regalloc::{RealReg, RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
|
||||||
use smallvec::SmallVec;
|
|
||||||
use std::hash::Hash;
|
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
@@ -129,8 +121,8 @@ pub mod blockorder;
|
|||||||
pub use blockorder::*;
|
pub use blockorder::*;
|
||||||
pub mod abi;
|
pub mod abi;
|
||||||
pub use abi::*;
|
pub use abi::*;
|
||||||
pub mod pp;
|
pub mod pretty_print;
|
||||||
pub use pp::*;
|
pub use pretty_print::*;
|
||||||
pub mod sections;
|
pub mod sections;
|
||||||
pub use sections::*;
|
pub use sections::*;
|
||||||
pub mod adapter;
|
pub mod adapter;
|
||||||
@@ -255,10 +247,10 @@ impl MachCompileResult {
|
|||||||
/// Top-level machine backend trait, which wraps all monomorphized code and
|
/// Top-level machine backend trait, which wraps all monomorphized code and
|
||||||
/// allows a virtual call from the machine-independent `Function::compile()`.
|
/// allows a virtual call from the machine-independent `Function::compile()`.
|
||||||
pub trait MachBackend {
|
pub trait MachBackend {
|
||||||
/// Compile the given function. Consumes the function.
|
/// Compile the given function.
|
||||||
fn compile_function(
|
fn compile_function(
|
||||||
&self,
|
&self,
|
||||||
func: Function,
|
func: &Function,
|
||||||
want_disasm: bool,
|
want_disasm: bool,
|
||||||
) -> CodegenResult<MachCompileResult>;
|
) -> CodegenResult<MachCompileResult>;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! simultaneously, so we buffer the result in memory and hand off to the
|
//! simultaneously, so we buffer the result in memory and hand off to the
|
||||||
//! caller at the end of compilation.
|
//! caller at the end of compilation.
|
||||||
|
|
||||||
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc, RelocSink, StackmapSink, TrapSink};
|
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc};
|
||||||
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode};
|
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode};
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -104,28 +104,31 @@ pub trait MachSectionOutput {
|
|||||||
|
|
||||||
/// Add 2 bytes to the section.
|
/// Add 2 bytes to the section.
|
||||||
fn put2(&mut self, value: u16) {
|
fn put2(&mut self, value: u16) {
|
||||||
self.put1((value & 0xff) as u8);
|
let [b0, b1] = value.to_le_bytes();
|
||||||
self.put1(((value >> 8) & 0xff) as u8);
|
self.put1(b0);
|
||||||
|
self.put1(b1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add 4 bytes to the section.
|
/// Add 4 bytes to the section.
|
||||||
fn put4(&mut self, value: u32) {
|
fn put4(&mut self, value: u32) {
|
||||||
self.put1((value & 0xff) as u8);
|
let [b0, b1, b2, b3] = value.to_le_bytes();
|
||||||
self.put1(((value >> 8) & 0xff) as u8);
|
self.put1(b0);
|
||||||
self.put1(((value >> 16) & 0xff) as u8);
|
self.put1(b1);
|
||||||
self.put1(((value >> 24) & 0xff) as u8);
|
self.put1(b2);
|
||||||
|
self.put1(b3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add 8 bytes to the section.
|
/// Add 8 bytes to the section.
|
||||||
fn put8(&mut self, value: u64) {
|
fn put8(&mut self, value: u64) {
|
||||||
self.put1((value & 0xff) as u8);
|
let [b0, b1, b2, b3, b4, b5, b6, b7] = value.to_le_bytes();
|
||||||
self.put1(((value >> 8) & 0xff) as u8);
|
self.put1(b0);
|
||||||
self.put1(((value >> 16) & 0xff) as u8);
|
self.put1(b1);
|
||||||
self.put1(((value >> 24) & 0xff) as u8);
|
self.put1(b2);
|
||||||
self.put1(((value >> 32) & 0xff) as u8);
|
self.put1(b3);
|
||||||
self.put1(((value >> 40) & 0xff) as u8);
|
self.put1(b4);
|
||||||
self.put1(((value >> 48) & 0xff) as u8);
|
self.put1(b5);
|
||||||
self.put1(((value >> 56) & 0xff) as u8);
|
self.put1(b6);
|
||||||
|
self.put1(b7);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a slice of bytes to the section.
|
/// Add a slice of bytes to the section.
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
//! See the main module comment in `mod.rs` for more details on the VCode-based
|
//! See the main module comment in `mod.rs` for more details on the VCode-based
|
||||||
//! backend pipeline.
|
//! backend pipeline.
|
||||||
|
|
||||||
use crate::binemit::Reloc;
|
|
||||||
use crate::ir;
|
use crate::ir;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
@@ -32,7 +31,6 @@ use log::debug;
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::Index;
|
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
/// Index referring to an instruction in VCode.
|
/// Index referring to an instruction in VCode.
|
||||||
@@ -59,13 +57,13 @@ pub struct VCode<I: VCodeInst> {
|
|||||||
vreg_types: Vec<Type>,
|
vreg_types: Vec<Type>,
|
||||||
|
|
||||||
/// Lowered machine instructions in order corresponding to the original IR.
|
/// Lowered machine instructions in order corresponding to the original IR.
|
||||||
pub insts: Vec<I>,
|
insts: Vec<I>,
|
||||||
|
|
||||||
/// Entry block.
|
/// Entry block.
|
||||||
entry: BlockIndex,
|
entry: BlockIndex,
|
||||||
|
|
||||||
/// Block instruction indices.
|
/// Block instruction indices.
|
||||||
pub block_ranges: Vec<(InsnIndex, InsnIndex)>,
|
block_ranges: Vec<(InsnIndex, InsnIndex)>,
|
||||||
|
|
||||||
/// Block successors: index range in the successor-list below.
|
/// Block successors: index range in the successor-list below.
|
||||||
block_succ_range: Vec<(usize, usize)>,
|
block_succ_range: Vec<(usize, usize)>,
|
||||||
@@ -94,7 +92,7 @@ pub struct VCode<I: VCodeInst> {
|
|||||||
code_size: CodeOffset,
|
code_size: CodeOffset,
|
||||||
|
|
||||||
/// ABI object.
|
/// ABI object.
|
||||||
abi: Box<dyn ABIBody<I>>,
|
abi: Box<dyn ABIBody<I = I>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A builder for a VCode function body. This builder is designed for the
|
/// A builder for a VCode function body. This builder is designed for the
|
||||||
@@ -128,7 +126,7 @@ pub struct VCodeBuilder<I: VCodeInst> {
|
|||||||
|
|
||||||
impl<I: VCodeInst> VCodeBuilder<I> {
|
impl<I: VCodeInst> VCodeBuilder<I> {
|
||||||
/// Create a new VCodeBuilder.
|
/// Create a new VCodeBuilder.
|
||||||
pub fn new(abi: Box<dyn ABIBody<I>>) -> VCodeBuilder<I> {
|
pub fn new(abi: Box<dyn ABIBody<I = I>>) -> VCodeBuilder<I> {
|
||||||
let vcode = VCode::new(abi);
|
let vcode = VCode::new(abi);
|
||||||
VCodeBuilder {
|
VCodeBuilder {
|
||||||
vcode,
|
vcode,
|
||||||
@@ -139,7 +137,7 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Access the ABI object.
|
/// Access the ABI object.
|
||||||
pub fn abi(&mut self) -> &mut dyn ABIBody<I> {
|
pub fn abi(&mut self) -> &mut dyn ABIBody<I = I> {
|
||||||
&mut *self.vcode.abi
|
&mut *self.vcode.abi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +280,7 @@ fn is_trivial_jump_block<I: VCodeInst>(vcode: &VCode<I>, block: BlockIndex) -> O
|
|||||||
|
|
||||||
impl<I: VCodeInst> VCode<I> {
|
impl<I: VCodeInst> VCode<I> {
|
||||||
/// New empty VCode.
|
/// New empty VCode.
|
||||||
fn new(abi: Box<dyn ABIBody<I>>) -> VCode<I> {
|
fn new(abi: Box<dyn ABIBody<I = I>>) -> VCode<I> {
|
||||||
VCode {
|
VCode {
|
||||||
liveins: abi.liveins(),
|
liveins: abi.liveins(),
|
||||||
liveouts: abi.liveouts(),
|
liveouts: abi.liveouts(),
|
||||||
@@ -472,10 +470,10 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
// Compute block offsets.
|
// Compute block offsets.
|
||||||
let mut code_section = MachSectionSize::new(0);
|
let mut code_section = MachSectionSize::new(0);
|
||||||
let mut block_offsets = vec![0; self.num_blocks()];
|
let mut block_offsets = vec![0; self.num_blocks()];
|
||||||
for block in &self.final_block_order {
|
for &block in &self.final_block_order {
|
||||||
code_section.offset = I::align_basic_block(code_section.offset);
|
code_section.offset = I::align_basic_block(code_section.offset);
|
||||||
block_offsets[*block as usize] = code_section.offset;
|
block_offsets[block as usize] = code_section.offset;
|
||||||
let (start, end) = self.block_ranges[*block as usize];
|
let (start, end) = self.block_ranges[block as usize];
|
||||||
for iix in start..end {
|
for iix in start..end {
|
||||||
self.insts[iix as usize].emit(&mut code_section);
|
self.insts[iix as usize].emit(&mut code_section);
|
||||||
}
|
}
|
||||||
@@ -490,9 +488,9 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
// it (so forward references are now possible), and (ii) mutates the
|
// it (so forward references are now possible), and (ii) mutates the
|
||||||
// instructions.
|
// instructions.
|
||||||
let mut code_section = MachSectionSize::new(0);
|
let mut code_section = MachSectionSize::new(0);
|
||||||
for block in &self.final_block_order {
|
for &block in &self.final_block_order {
|
||||||
code_section.offset = I::align_basic_block(code_section.offset);
|
code_section.offset = I::align_basic_block(code_section.offset);
|
||||||
let (start, end) = self.block_ranges[*block as usize];
|
let (start, end) = self.block_ranges[block as usize];
|
||||||
for iix in start..end {
|
for iix in start..end {
|
||||||
self.insts[iix as usize]
|
self.insts[iix as usize]
|
||||||
.with_block_offsets(code_section.offset, &self.final_block_offsets[..]);
|
.with_block_offsets(code_section.offset, &self.final_block_offsets[..]);
|
||||||
@@ -510,7 +508,7 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
let code_idx = sections.add_section(0, self.code_size);
|
let code_idx = sections.add_section(0, self.code_size);
|
||||||
let code_section = sections.get_section(code_idx);
|
let code_section = sections.get_section(code_idx);
|
||||||
|
|
||||||
for block in &self.final_block_order {
|
for &block in &self.final_block_order {
|
||||||
let new_offset = I::align_basic_block(code_section.cur_offset_from_start());
|
let new_offset = I::align_basic_block(code_section.cur_offset_from_start());
|
||||||
while new_offset > code_section.cur_offset_from_start() {
|
while new_offset > code_section.cur_offset_from_start() {
|
||||||
// Pad with NOPs up to the aligned block offset.
|
// Pad with NOPs up to the aligned block offset.
|
||||||
@@ -519,7 +517,7 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
}
|
}
|
||||||
assert_eq!(code_section.cur_offset_from_start(), new_offset);
|
assert_eq!(code_section.cur_offset_from_start(), new_offset);
|
||||||
|
|
||||||
let (start, end) = self.block_ranges[*block as usize];
|
let (start, end) = self.block_ranges[block as usize];
|
||||||
for iix in start..end {
|
for iix in start..end {
|
||||||
self.insts[iix as usize].emit(code_section);
|
self.insts[iix as usize].emit(code_section);
|
||||||
}
|
}
|
||||||
@@ -639,9 +637,6 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// N.B.: Debug impl assumes that VCode has already been through all compilation
|
|
||||||
// passes, and so has a final block order and offsets.
|
|
||||||
|
|
||||||
impl<I: VCodeInst> fmt::Debug for VCode<I> {
|
impl<I: VCodeInst> fmt::Debug for VCode<I> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
writeln!(f, "VCode_Debug {{")?;
|
writeln!(f, "VCode_Debug {{")?;
|
||||||
@@ -665,22 +660,21 @@ impl<I: VCodeInst> fmt::Debug for VCode<I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pretty-printing with `RealRegUniverse` context.
|
/// Pretty-printing with `RealRegUniverse` context.
|
||||||
impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> {
|
impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> {
|
||||||
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
|
||||||
use crate::alloc::string::ToString;
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
// Calculate an order in which to display the blocks. This is the same
|
// Calculate an order in which to display the blocks. This is the same
|
||||||
// as final_block_order, but also includes blocks which are in the
|
// as final_block_order, but also includes blocks which are in the
|
||||||
// representation but not in final_block_order.
|
// representation but not in final_block_order.
|
||||||
let mut display_order = Vec::<usize>::new();
|
let mut display_order = Vec::<usize>::new();
|
||||||
// First display blocks in |final_block_order|
|
// First display blocks in `final_block_order`
|
||||||
for bix in &self.final_block_order {
|
for bix in &self.final_block_order {
|
||||||
assert!((*bix as usize) < self.num_blocks());
|
assert!((*bix as usize) < self.num_blocks());
|
||||||
display_order.push(*bix as usize);
|
display_order.push(*bix as usize);
|
||||||
}
|
}
|
||||||
// Now also take care of those not listed in |final_block_order|.
|
// Now also take care of those not listed in `final_block_order`.
|
||||||
// This is quadratic, but it's also debug-only code.
|
// This is quadratic, but it's also debug-only code.
|
||||||
for bix in 0..self.num_blocks() {
|
for bix in 0..self.num_blocks() {
|
||||||
if display_order.contains(&bix) {
|
if display_order.contains(&bix) {
|
||||||
@@ -690,48 +684,46 @@ impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s = s + &format!("VCode_ShowWithRRU {{{{");
|
write!(&mut s, "VCode_ShowWithRRU {{{{\n").unwrap();
|
||||||
s = s + &"\n".to_string();
|
write!(&mut s, " Entry block: {}\n", self.entry).unwrap();
|
||||||
s = s + &format!(" Entry block: {}", self.entry);
|
write!(
|
||||||
s = s + &"\n".to_string();
|
&mut s,
|
||||||
s = s + &format!(" Final block order: {:?}", self.final_block_order);
|
" Final block order: {:?}\n",
|
||||||
s = s + &"\n".to_string();
|
self.final_block_order
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
for i in 0..self.num_blocks() {
|
for i in 0..self.num_blocks() {
|
||||||
let block = display_order[i];
|
let block = display_order[i];
|
||||||
|
|
||||||
let omitted =
|
let omitted = if !self.final_block_order.is_empty() && i >= self.final_block_order.len()
|
||||||
(if !self.final_block_order.is_empty() && i >= self.final_block_order.len() {
|
{
|
||||||
"** OMITTED **"
|
"** OMITTED **"
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
})
|
};
|
||||||
.to_string();
|
|
||||||
|
|
||||||
s = s + &format!("Block {}: {}", block, omitted);
|
write!(&mut s, "Block {}: {}\n", block, omitted).unwrap();
|
||||||
s = s + &"\n".to_string();
|
|
||||||
if let Some(bb) = self.bindex_to_bb(block as BlockIndex) {
|
if let Some(bb) = self.bindex_to_bb(block as BlockIndex) {
|
||||||
s = s + &format!(" (original IR block: {})\n", bb);
|
write!(&mut s, " (original IR block: {})\n", bb).unwrap();
|
||||||
}
|
}
|
||||||
for succ in self.succs(block as BlockIndex) {
|
for succ in self.succs(block as BlockIndex) {
|
||||||
s = s + &format!(" (successor: Block {})", succ);
|
write!(&mut s, " (successor: Block {})\n", succ).unwrap();
|
||||||
s = s + &"\n".to_string();
|
|
||||||
}
|
}
|
||||||
let (start, end) = self.block_ranges[block];
|
let (start, end) = self.block_ranges[block];
|
||||||
s = s + &format!(" (instruction range: {} .. {})", start, end);
|
write!(&mut s, " (instruction range: {} .. {})\n", start, end).unwrap();
|
||||||
s = s + &"\n".to_string();
|
|
||||||
for inst in start..end {
|
for inst in start..end {
|
||||||
s = s + &format!(
|
write!(
|
||||||
" Inst {}: {}",
|
&mut s,
|
||||||
|
" Inst {}: {}\n",
|
||||||
inst,
|
inst,
|
||||||
self.insts[inst as usize].show_rru(mb_rru)
|
self.insts[inst as usize].show_rru(mb_rru)
|
||||||
);
|
)
|
||||||
s = s + &"\n".to_string();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s = s + &format!("}}}}");
|
write!(&mut s, "}}}}\n").unwrap();
|
||||||
s = s + &"\n".to_string();
|
|
||||||
|
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
//! A pass that computes the number of uses of any given instruction.
|
//! A pass that computes the number of uses of any given instruction.
|
||||||
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
|
|
||||||
use crate::cursor::{Cursor, FuncCursor};
|
|
||||||
use crate::dce::has_side_effect;
|
|
||||||
use crate::entity::SecondaryMap;
|
use crate::entity::SecondaryMap;
|
||||||
use crate::ir::dfg::ValueDef;
|
use crate::ir::dfg::ValueDef;
|
||||||
use crate::ir::instructions::InstructionData;
|
|
||||||
use crate::ir::Value;
|
use crate::ir::Value;
|
||||||
use crate::ir::{DataFlowGraph, Function, Inst, Opcode};
|
use crate::ir::{DataFlowGraph, Function, Inst};
|
||||||
|
|
||||||
/// Auxiliary data structure that counts the number of uses of any given
|
/// Auxiliary data structure that counts the number of uses of any given
|
||||||
/// instruction in a Function. This is used during instruction selection
|
/// instruction in a Function. This is used during instruction selection
|
||||||
@@ -51,16 +45,6 @@ impl NumUses {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How many times is an instruction used?
|
|
||||||
pub fn use_count(&self, i: Inst) -> usize {
|
|
||||||
self.uses[i] as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is an instruction used at all?
|
|
||||||
pub fn is_used(&self, i: Inst) -> bool {
|
|
||||||
self.use_count(i) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Take the complete uses map, consuming this analysis result.
|
/// Take the complete uses map, consuming this analysis result.
|
||||||
pub fn take_uses(self) -> SecondaryMap<Inst, u32> {
|
pub fn take_uses(self) -> SecondaryMap<Inst, u32> {
|
||||||
self.uses
|
self.uses
|
||||||
|
|||||||
@@ -364,19 +364,17 @@ pub fn do_postopt(func: &mut Function, isa: &dyn TargetIsa) {
|
|||||||
while let Some(_block) = pos.next_block() {
|
while let Some(_block) = pos.next_block() {
|
||||||
let mut last_flags_clobber = None;
|
let mut last_flags_clobber = None;
|
||||||
while let Some(inst) = pos.next_inst() {
|
while let Some(inst) = pos.next_inst() {
|
||||||
if isa.uses_cpu_flags() {
|
if !is_mach_backend && isa.uses_cpu_flags() {
|
||||||
// Optimize instructions to make use of flags.
|
// Optimize instructions to make use of flags.
|
||||||
optimize_cpu_flags(&mut pos, inst, last_flags_clobber, isa);
|
optimize_cpu_flags(&mut pos, inst, last_flags_clobber, isa);
|
||||||
|
|
||||||
if !is_mach_backend {
|
// Track the most recent seen instruction that clobbers the flags.
|
||||||
// Track the most recent seen instruction that clobbers the flags.
|
if let Some(constraints) = isa
|
||||||
if let Some(constraints) = isa
|
.encoding_info()
|
||||||
.encoding_info()
|
.operand_constraints(pos.func.encodings[inst])
|
||||||
.operand_constraints(pos.func.encodings[inst])
|
{
|
||||||
{
|
if constraints.clobbers_flags {
|
||||||
if constraints.clobbers_flags {
|
last_flags_clobber = Some(inst)
|
||||||
last_flags_clobber = Some(inst)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,17 +28,18 @@ pub fn verify_flags(
|
|||||||
errors: &mut VerifierErrors,
|
errors: &mut VerifierErrors,
|
||||||
) -> VerifierStepResult<()> {
|
) -> VerifierStepResult<()> {
|
||||||
let _tt = timing::verify_flags();
|
let _tt = timing::verify_flags();
|
||||||
if isa.is_none() || isa.unwrap().get_mach_backend().is_none() {
|
let encinfo = if isa.is_none() || isa.unwrap().get_mach_backend().is_some() {
|
||||||
let mut verifier = FlagsVerifier {
|
None
|
||||||
func,
|
|
||||||
cfg,
|
|
||||||
encinfo: isa.map(|isa| isa.encoding_info()),
|
|
||||||
livein: SecondaryMap::new(),
|
|
||||||
};
|
|
||||||
verifier.check(errors)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Some(isa.unwrap().encoding_info())
|
||||||
}
|
};
|
||||||
|
let mut verifier = FlagsVerifier {
|
||||||
|
func,
|
||||||
|
cfg,
|
||||||
|
encinfo,
|
||||||
|
livein: SecondaryMap::new(),
|
||||||
|
};
|
||||||
|
verifier.check(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FlagsVerifier<'a> {
|
struct FlagsVerifier<'a> {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i64, i64) -> i64 {
|
function %f(i64, i64) -> i64 {
|
||||||
block0(v0: i64, v1: i64):
|
block0(v0: i64, v1: i64):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i32, i32) -> i32 {
|
function %f(i32, i32) -> i32 {
|
||||||
block0(v0: i32, v1: i32):
|
block0(v0: i32, v1: i32):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %a(i32) -> i32 {
|
function %a(i32) -> i32 {
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i64, i64) -> i64 {
|
function %f(i64, i64) -> i64 {
|
||||||
sig0 = (i64) -> i64
|
sig0 = (i64) -> i64
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i64) -> i64 {
|
function %f(i64) -> i64 {
|
||||||
fn0 = %g(i64) -> i64
|
fn0 = %g(i64) -> i64
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i64, i64) -> b1 {
|
function %f(i64, i64) -> b1 {
|
||||||
block0(v0: i64, v1: i64):
|
block0(v0: i64, v1: i64):
|
||||||
@@ -33,7 +34,7 @@ block2:
|
|||||||
; nextln: mov fp, sp
|
; nextln: mov fp, sp
|
||||||
; nextln: subs xzr, x0, x1
|
; nextln: subs xzr, x0, x1
|
||||||
; nextln: b.eq 20
|
; nextln: b.eq 20
|
||||||
; check: Block 0:
|
; check: Block 2:
|
||||||
; check: movz x0, #2
|
; check: movz x0, #2
|
||||||
; nextln: mov sp, fp
|
; nextln: mov sp, fp
|
||||||
; nextln: ldp fp, lr, [sp], #16
|
; nextln: ldp fp, lr, [sp], #16
|
||||||
@@ -58,7 +59,7 @@ block1:
|
|||||||
; check: stp fp, lr, [sp, #-16]!
|
; check: stp fp, lr, [sp, #-16]!
|
||||||
; nextln: mov fp, sp
|
; nextln: mov fp, sp
|
||||||
; nextln: subs xzr, x0, x1
|
; nextln: subs xzr, x0, x1
|
||||||
; check: Block 0:
|
; check: Block 1:
|
||||||
; check: movz x0, #1
|
; check: movz x0, #1
|
||||||
; nextln: mov sp, fp
|
; nextln: mov sp, fp
|
||||||
; nextln: ldp fp, lr, [sp], #16
|
; nextln: ldp fp, lr, [sp], #16
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i8, i64, i64) -> i64 {
|
function %f(i8, i64, i64) -> i64 {
|
||||||
block0(v0: i8, v1: i64, v2: i64):
|
block0(v0: i8, v1: i64, v2: i64):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f() -> i64 {
|
function %f() -> i64 {
|
||||||
block0:
|
block0:
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i8) -> i64 {
|
function %f(i8) -> i64 {
|
||||||
block0(v0: i8):
|
block0(v0: i8):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i64) -> i64 {
|
function %f(i64) -> i64 {
|
||||||
jt0 = jump_table [block1, block2, block3]
|
jt0 = jump_table [block1, block2, block3]
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %add8(i8, i8) -> i8 {
|
function %add8(i8, i8) -> i8 {
|
||||||
block0(v0: i8, v1: i8):
|
block0(v0: i8, v1: i8):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %uaddsat64(i64, i64) -> i64 {
|
function %uaddsat64(i64, i64) -> i64 {
|
||||||
block0(v0: i64, v1: i64):
|
block0(v0: i64, v1: i64):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f(i64) -> i64 {
|
function %f(i64) -> i64 {
|
||||||
block0(v0: i64):
|
block0(v0: i64):
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; ROR, variable
|
;; ROR, variable
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f() -> i64 {
|
function %f() -> i64 {
|
||||||
gv0 = symbol %my_global
|
gv0 = symbol %my_global
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f() {
|
function %f() {
|
||||||
block0:
|
block0:
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
test vcode arch=arm64
|
test vcode
|
||||||
|
target aarch64
|
||||||
|
|
||||||
function %f_u_8_64(i8) -> i64 {
|
function %f_u_8_64(i8) -> i64 {
|
||||||
block0(v0: i8):
|
block0(v0: i8):
|
||||||
@@ -4,11 +4,9 @@ use cranelift_codegen::isa::lookup;
|
|||||||
use cranelift_codegen::settings;
|
use cranelift_codegen::settings;
|
||||||
use cranelift_codegen::Context as CodegenContext;
|
use cranelift_codegen::Context as CodegenContext;
|
||||||
use cranelift_reader::{TestCommand, TestOption};
|
use cranelift_reader::{TestCommand, TestOption};
|
||||||
use target_lexicon::Triple;
|
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::str::FromStr;
|
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
struct TestVCode {
|
struct TestVCode {
|
||||||
@@ -41,15 +39,13 @@ impl SubTest for TestVCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn needs_isa(&self) -> bool {
|
fn needs_isa(&self) -> bool {
|
||||||
false
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
fn run(&self, func: Cow<Function>, context: &Context) -> SubtestResult<()> {
|
||||||
|
let triple = context.isa.unwrap().triple().clone();
|
||||||
let func = func.into_owned();
|
let func = func.into_owned();
|
||||||
|
|
||||||
let triple =
|
|
||||||
Triple::from_str(&self.arch).map_err(|_| format!("Unknown arch: '{}'", self.arch))?;
|
|
||||||
|
|
||||||
let mut isa = lookup(triple)
|
let mut isa = lookup(triple)
|
||||||
.map_err(|_| format!("Could not look up backend for arch '{}'", self.arch))?
|
.map_err(|_| format!("Could not look up backend for arch '{}'", self.arch))?
|
||||||
.finish(settings::Flags::new(settings::builder()));
|
.finish(settings::Flags::new(settings::builder()));
|
||||||
|
|||||||
@@ -142,12 +142,17 @@ cfg_if::cfg_if! {
|
|||||||
pub fn ___chkstk();
|
pub fn ___chkstk();
|
||||||
}
|
}
|
||||||
const PROBESTACK: unsafe extern "C" fn() = ___chkstk;
|
const PROBESTACK: unsafe extern "C" fn() = ___chkstk;
|
||||||
|
} else if #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))] {
|
||||||
|
// As per
|
||||||
|
// https://github.com/rust-lang/compiler-builtins/blob/cae3e6ea23739166504f9f9fb50ec070097979d4/src/probestack.rs#L39,
|
||||||
|
// LLVM only has stack-probe support on x86-64 and x86. Thus, on any other CPU
|
||||||
|
// architecture, we simply use an empty stack-probe function.
|
||||||
|
extern "C" fn empty_probestack() {}
|
||||||
|
const PROBESTACK: unsafe extern "C" fn() = empty_probestack;
|
||||||
} else {
|
} else {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn __rust_probestack();
|
pub fn __rust_probestack();
|
||||||
}
|
}
|
||||||
static PROBESTACK: unsafe extern "C" fn() = empty_probestack;
|
static PROBESTACK: unsafe extern "C" fn() = __rust_probestack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" fn empty_probestack() {}
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ cfg_if::cfg_if! {
|
|||||||
static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
||||||
static mut PREV_SIGILL: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
static mut PREV_SIGILL: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
||||||
static mut PREV_SIGFPE: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
static mut PREV_SIGFPE: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
||||||
static mut PREV_SIGTRAP: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
|
|
||||||
|
|
||||||
unsafe fn platform_init() {
|
unsafe fn platform_init() {
|
||||||
let register = |slot: &mut MaybeUninit<libc::sigaction>, signal: i32| {
|
let register = |slot: &mut MaybeUninit<libc::sigaction>, signal: i32| {
|
||||||
@@ -71,9 +70,6 @@ cfg_if::cfg_if! {
|
|||||||
register(&mut PREV_SIGFPE, libc::SIGFPE);
|
register(&mut PREV_SIGFPE, libc::SIGFPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// on ARM64, we use `brk` to report traps, which generates SIGTRAP.
|
|
||||||
register(&mut PREV_SIGTRAP, libc::SIGTRAP);
|
|
||||||
|
|
||||||
// On ARM, handle Unaligned Accesses.
|
// On ARM, handle Unaligned Accesses.
|
||||||
// On Darwin, guard page accesses are raised as SIGBUS.
|
// On Darwin, guard page accesses are raised as SIGBUS.
|
||||||
if cfg!(target_arch = "arm") || cfg!(target_os = "macos") {
|
if cfg!(target_arch = "arm") || cfg!(target_os = "macos") {
|
||||||
@@ -91,7 +87,6 @@ cfg_if::cfg_if! {
|
|||||||
libc::SIGBUS => &PREV_SIGBUS,
|
libc::SIGBUS => &PREV_SIGBUS,
|
||||||
libc::SIGFPE => &PREV_SIGFPE,
|
libc::SIGFPE => &PREV_SIGFPE,
|
||||||
libc::SIGILL => &PREV_SIGILL,
|
libc::SIGILL => &PREV_SIGILL,
|
||||||
libc::SIGTRAP => &PREV_SIGTRAP,
|
|
||||||
_ => panic!("unknown signal: {}", signum),
|
_ => panic!("unknown signal: {}", signum),
|
||||||
};
|
};
|
||||||
let handled = tls::with(|info| {
|
let handled = tls::with(|info| {
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ mod tests {
|
|||||||
.downcast::<Trap>()?;
|
.downcast::<Trap>()?;
|
||||||
assert!(
|
assert!(
|
||||||
trap.message()
|
trap.message()
|
||||||
.starts_with("wasm trap: out of bounds"),
|
.starts_with("wasm trap: out of bounds memory access"),
|
||||||
"bad trap message: {:?}",
|
"bad trap message: {:?}",
|
||||||
trap.message()
|
trap.message()
|
||||||
);
|
);
|
||||||
@@ -149,7 +149,7 @@ mod tests {
|
|||||||
.downcast::<Trap>()?;
|
.downcast::<Trap>()?;
|
||||||
assert!(trap
|
assert!(trap
|
||||||
.message()
|
.message()
|
||||||
.starts_with("wasm trap: out of bounds"));
|
.starts_with("wasm trap: out of bounds memory access"));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user