* aarch64: Initial work to transition backend to ISLE This commit is what is hoped to be the initial commit towards migrating the aarch64 backend to ISLE. There's seemingly a lot of changes here but it's intended to largely be code motion. The current thinking is to closely follow the x64 backend for how all this is handled and organized. Major changes in this PR are: * The `Inst` enum is now defined in ISLE. This avoids having to define it in two places (once in Rust and once in ISLE). I've preserved all the comments in the ISLE and otherwise this isn't actually a functional change from the Rust perspective, it's still the same enum according to Rust. * Lots of little enums and things were moved to ISLE as well. As with `Inst` their definitions didn't change, only where they're defined. This will give future ISLE PRs access to all these operations. * Initial code for lowering `iconst`, `null`, and `bconst` are implemented. Ironically none of this is actually used right now because constant lowering is handled in `put_input_in_regs` which specially handles constants. Nonetheless I wanted to get at least something simple working which shows off how to special case various things that are specific to AArch64. In a future PR I plan to hook up const-lowering in ISLE to this path so even though `iconst`-the-clif-instruction is never lowered this should use the const lowering defined in ISLE rather than elsewhere in the backend (eventually leading to the deletion of the non-ISLE lowering). * The `IsleContext` skeleton is created and set up for future additions. * Some code for ISLE that's shared across all backends now lives in `isle_prelude_methods!()` and is deduplicated between the AArch64 backend and the x64 backend. * Register mapping is tweaked to do the same thing for AArch64 that it does for x64. Namely mapping virtual registers is supported instead of just virtual to machine registers. My main goal with this PR was to get AArch64 into a place where new instructions can be added with relative ease. Additionally I'm hoping to figure out as part of this change how much to share for ISLE between AArch64 and x64 (and other backends). * Don't use priorities with rules * Update .gitattributes with concise syntax * Deduplicate some type definitions * Rebuild ISLE * Move isa::isle to machinst::isle
222 lines
6.2 KiB
Rust
222 lines
6.2 KiB
Rust
//! ISLE integration glue code for x64 lowering.
|
|
|
|
// Pull in the ISLE generated code.
|
|
mod generated_code;
|
|
|
|
// Types that the generated ISLE code uses via `use super::*`.
|
|
use super::{
|
|
is_mergeable_load, lower_to_amode, AluRmiROpcode, Inst as MInst, OperandSize, Reg, RegMemImm,
|
|
};
|
|
use crate::isa::x64::inst::args::SyntheticAmode;
|
|
use crate::isa::x64::settings as x64_settings;
|
|
use crate::machinst::isle::*;
|
|
use crate::{
|
|
ir::{immediates::*, types::*, Inst, InstructionData, Opcode, Value, ValueList},
|
|
isa::x64::inst::{
|
|
args::{
|
|
Avx512Opcode, CmpOpcode, ExtMode, FcmpImm, Imm8Reg, RegMem, ShiftKind, SseOpcode, CC,
|
|
},
|
|
x64_map_regs,
|
|
},
|
|
machinst::{get_output_reg, InsnInput, InsnOutput, LowerCtx, RegRenamer},
|
|
};
|
|
use smallvec::SmallVec;
|
|
use std::convert::TryFrom;
|
|
|
|
pub struct SinkableLoad {
|
|
inst: Inst,
|
|
addr_input: InsnInput,
|
|
offset: i32,
|
|
}
|
|
|
|
/// The main entry point for lowering with ISLE.
|
|
pub(crate) fn lower<C>(
|
|
lower_ctx: &mut C,
|
|
isa_flags: &x64_settings::Flags,
|
|
outputs: &[InsnOutput],
|
|
inst: Inst,
|
|
) -> Result<(), ()>
|
|
where
|
|
C: LowerCtx<I = MInst>,
|
|
{
|
|
// TODO: reuse the ISLE context across lowerings so we can reuse its
|
|
// internal heap allocations.
|
|
let mut isle_ctx = IsleContext::new(lower_ctx, isa_flags);
|
|
|
|
let temp_regs = generated_code::constructor_lower(&mut isle_ctx, inst).ok_or(())?;
|
|
let mut temp_regs = temp_regs.regs().iter();
|
|
|
|
// The ISLE generated code emits its own registers to define the
|
|
// instruction's lowered values in. We rename those registers to the
|
|
// registers they were assigned when their value was used as an operand in
|
|
// earlier lowerings.
|
|
let mut renamer = RegRenamer::default();
|
|
for output in outputs {
|
|
let dsts = get_output_reg(isle_ctx.lower_ctx, *output);
|
|
for (temp, dst) in temp_regs.by_ref().zip(dsts.regs()) {
|
|
renamer.add_rename(*temp, dst.to_reg());
|
|
}
|
|
}
|
|
|
|
for mut inst in isle_ctx.into_emitted_insts() {
|
|
x64_map_regs(&mut inst, &renamer);
|
|
lower_ctx.emit(inst);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub struct IsleContext<'a, C> {
|
|
lower_ctx: &'a mut C,
|
|
isa_flags: &'a x64_settings::Flags,
|
|
emitted_insts: SmallVec<[MInst; 6]>,
|
|
}
|
|
|
|
impl<'a, C> IsleContext<'a, C> {
|
|
pub fn new(lower_ctx: &'a mut C, isa_flags: &'a x64_settings::Flags) -> Self {
|
|
IsleContext {
|
|
lower_ctx,
|
|
isa_flags,
|
|
emitted_insts: SmallVec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn into_emitted_insts(self) -> SmallVec<[MInst; 6]> {
|
|
self.emitted_insts
|
|
}
|
|
}
|
|
|
|
impl<'a, C> generated_code::Context for IsleContext<'a, C>
|
|
where
|
|
C: LowerCtx<I = MInst>,
|
|
{
|
|
isle_prelude_methods!();
|
|
|
|
#[inline]
|
|
fn operand_size_of_type(&mut self, ty: Type) -> OperandSize {
|
|
if ty.bits() == 64 {
|
|
OperandSize::Size64
|
|
} else {
|
|
OperandSize::Size32
|
|
}
|
|
}
|
|
|
|
fn put_in_reg_mem(&mut self, val: Value) -> RegMem {
|
|
let inputs = self.lower_ctx.get_value_as_source_or_const(val);
|
|
|
|
if let Some(c) = inputs.constant {
|
|
// Generate constants fresh at each use to minimize long-range
|
|
// register pressure.
|
|
let ty = self.value_type(val);
|
|
return RegMem::reg(generated_code::constructor_imm(self, ty, c).unwrap());
|
|
}
|
|
|
|
if let Some((src_insn, 0)) = inputs.inst {
|
|
if let Some((addr_input, offset)) = is_mergeable_load(self.lower_ctx, src_insn) {
|
|
self.lower_ctx.sink_inst(src_insn);
|
|
let amode = lower_to_amode(self.lower_ctx, addr_input, offset);
|
|
return RegMem::mem(amode);
|
|
}
|
|
}
|
|
|
|
RegMem::reg(self.put_in_reg(val))
|
|
}
|
|
|
|
#[inline]
|
|
fn encode_fcmp_imm(&mut self, imm: &FcmpImm) -> u8 {
|
|
imm.encode()
|
|
}
|
|
|
|
#[inline]
|
|
fn avx512vl_enabled(&mut self, _: Type) -> Option<()> {
|
|
if self.isa_flags.use_avx512vl_simd() {
|
|
Some(())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn avx512dq_enabled(&mut self, _: Type) -> Option<()> {
|
|
if self.isa_flags.use_avx512dq_simd() {
|
|
Some(())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn imm8_from_value(&mut self, val: Value) -> Option<Imm8Reg> {
|
|
let inst = self.lower_ctx.dfg().value_def(val).inst()?;
|
|
let constant = self.lower_ctx.get_constant(inst)?;
|
|
let imm = u8::try_from(constant).ok()?;
|
|
Some(Imm8Reg::Imm8 { imm })
|
|
}
|
|
|
|
#[inline]
|
|
fn simm32_from_value(&mut self, val: Value) -> Option<RegMemImm> {
|
|
let inst = self.lower_ctx.dfg().value_def(val).inst()?;
|
|
let constant: u64 = self.lower_ctx.get_constant(inst)?;
|
|
let constant = constant as i64;
|
|
to_simm32(constant)
|
|
}
|
|
|
|
#[inline]
|
|
fn simm32_from_imm64(&mut self, imm: Imm64) -> Option<RegMemImm> {
|
|
to_simm32(imm.bits())
|
|
}
|
|
|
|
fn sinkable_load(&mut self, val: Value) -> Option<SinkableLoad> {
|
|
let input = self.lower_ctx.get_value_as_source_or_const(val);
|
|
if let Some((inst, 0)) = input.inst {
|
|
if let Some((addr_input, offset)) = is_mergeable_load(self.lower_ctx, inst) {
|
|
return Some(SinkableLoad {
|
|
inst,
|
|
addr_input,
|
|
offset,
|
|
});
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn sink_load(&mut self, load: &SinkableLoad) -> RegMemImm {
|
|
self.lower_ctx.sink_inst(load.inst);
|
|
let addr = lower_to_amode(self.lower_ctx, load.addr_input, load.offset);
|
|
RegMemImm::Mem {
|
|
addr: SyntheticAmode::Real(addr),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn ext_mode(&mut self, from_bits: u16, to_bits: u16) -> ExtMode {
|
|
ExtMode::new(from_bits, to_bits).unwrap()
|
|
}
|
|
|
|
fn emit(&mut self, inst: &MInst) -> Unit {
|
|
for inst in inst.clone().mov_mitosis() {
|
|
self.emitted_insts.push(inst);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn nonzero_u64_fits_in_u32(&mut self, x: u64) -> Option<u64> {
|
|
if x != 0 && x < u64::from(u32::MAX) {
|
|
Some(x)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn to_simm32(constant: i64) -> Option<RegMemImm> {
|
|
if constant == ((constant << 32) >> 32) {
|
|
Some(RegMemImm::Imm {
|
|
simm32: constant as u32,
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
}
|