aarch64: Initial work to transition backend to ISLE (#3541)
* 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
This commit is contained in:
@@ -198,6 +198,8 @@ fn get_isle_compilations(crate_dir: &std::path::Path) -> Result<IsleCompilations
|
||||
make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("prelude.isle"));
|
||||
let src_isa_x64 =
|
||||
make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("isa").join("x64"));
|
||||
let src_isa_aarch64 =
|
||||
make_isle_source_path_relative(&cur_dir, crate_dir.join("src").join("isa").join("aarch64"));
|
||||
|
||||
// This is a set of ISLE compilation units.
|
||||
//
|
||||
@@ -217,12 +219,25 @@ fn get_isle_compilations(crate_dir: &std::path::Path) -> Result<IsleCompilations
|
||||
.join("isle")
|
||||
.join("generated_code.rs"),
|
||||
inputs: vec![
|
||||
clif_isle,
|
||||
prelude_isle,
|
||||
clif_isle.clone(),
|
||||
prelude_isle.clone(),
|
||||
src_isa_x64.join("inst.isle"),
|
||||
src_isa_x64.join("lower.isle"),
|
||||
],
|
||||
},
|
||||
// The aarch64 instruction selector.
|
||||
IsleCompilation {
|
||||
output: src_isa_aarch64
|
||||
.join("lower")
|
||||
.join("isle")
|
||||
.join("generated_code.rs"),
|
||||
inputs: vec![
|
||||
clif_isle.clone(),
|
||||
prelude_isle.clone(),
|
||||
src_isa_aarch64.join("inst.isle"),
|
||||
src_isa_aarch64.join("lower.isle"),
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
@@ -254,7 +269,8 @@ fn maybe_rebuild_isle(
|
||||
println!("cargo:rerun-if-changed={}", file.display());
|
||||
}
|
||||
|
||||
let manifest = std::fs::read_to_string(compilation.manifest_filename())?;
|
||||
let manifest =
|
||||
std::fs::read_to_string(compilation.manifest_filename()).unwrap_or(String::new());
|
||||
// Canonicalize Windows line-endings into Unix line-endings in
|
||||
// the manifest text itself.
|
||||
let manifest = manifest.replace("\r\n", "\n");
|
||||
|
||||
1359
cranelift/codegen/src/isa/aarch64/inst.isle
Normal file
1359
cranelift/codegen/src/isa/aarch64/inst.isle
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
use crate::ir::types::*;
|
||||
use crate::ir::TrapCode;
|
||||
use crate::isa::aarch64::inst::*;
|
||||
use crate::isa::test_utils;
|
||||
use crate::isa::CallConv;
|
||||
|
||||
@@ -327,7 +327,7 @@ impl Imm12 {
|
||||
}
|
||||
|
||||
/// An immediate for logical instructions.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct ImmLogic {
|
||||
/// The actual value.
|
||||
value: u64,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
23
cranelift/codegen/src/isa/aarch64/lower.isle
Normal file
23
cranelift/codegen/src/isa/aarch64/lower.isle
Normal file
@@ -0,0 +1,23 @@
|
||||
;; aarch64 instruction selection and CLIF-to-MachInst lowering.
|
||||
|
||||
;; The main lowering constructor term: takes a clif `Inst` and returns the
|
||||
;; register(s) within which the lowered instruction's result values live.
|
||||
(decl lower (Inst) ValueRegs)
|
||||
|
||||
;;;; Rules for `iconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type ty (iconst (u64_from_imm64 n))))
|
||||
(value_reg (imm ty n)))
|
||||
|
||||
;;;; Rules for `bconst` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type ty (bconst $false)))
|
||||
(value_reg (imm ty 0)))
|
||||
|
||||
(rule (lower (has_type ty (bconst $true)))
|
||||
(value_reg (imm ty 1)))
|
||||
|
||||
;;;; Rules for `null` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(rule (lower (has_type ty (null)))
|
||||
(value_reg (imm ty 0)))
|
||||
@@ -25,6 +25,8 @@ use regalloc::{Reg, Writable};
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp;
|
||||
|
||||
pub mod isle;
|
||||
|
||||
//============================================================================
|
||||
// Result enum types.
|
||||
//
|
||||
|
||||
187
cranelift/codegen/src/isa/aarch64/lower/isle.rs
Normal file
187
cranelift/codegen/src/isa/aarch64/lower/isle.rs
Normal file
@@ -0,0 +1,187 @@
|
||||
//! ISLE integration glue code for aarch64 lowering.
|
||||
|
||||
// Pull in the ISLE generated code.
|
||||
pub mod generated_code;
|
||||
|
||||
// Types that the generated ISLE code uses via `use super::*`.
|
||||
use super::{
|
||||
zero_reg, AMode, ASIMDFPModImm, ASIMDMovModImm, AtomicRmwOp, BranchTarget, CallIndInfo,
|
||||
CallInfo, Cond, CondBrKind, ExtendOp, FPUOpRI, Imm12, ImmLogic, ImmShift, Inst as MInst,
|
||||
JTSequenceInfo, MachLabel, MoveWideConst, Opcode, OperandSize, PairAMode, Reg, ScalarSize,
|
||||
ShiftOpAndAmt, UImm5, VectorSize, NZCV,
|
||||
};
|
||||
use crate::isa::aarch64::settings as aarch64_settings;
|
||||
use crate::machinst::isle::*;
|
||||
use crate::{
|
||||
binemit::CodeOffset,
|
||||
ir::{
|
||||
immediates::*, types::*, ExternalName, Inst, InstructionData, MemFlags, TrapCode, Value,
|
||||
ValueLabel, ValueList,
|
||||
},
|
||||
isa::aarch64::inst::aarch64_map_regs,
|
||||
isa::unwind::UnwindInst,
|
||||
machinst::{get_output_reg, InsnOutput, LowerCtx, RegRenamer},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::boxed::Box;
|
||||
use std::vec::Vec;
|
||||
|
||||
type BoxCallInfo = Box<CallInfo>;
|
||||
type BoxCallIndInfo = Box<CallIndInfo>;
|
||||
type VecMachLabel = Vec<MachLabel>;
|
||||
type BoxJTSequenceInfo = Box<JTSequenceInfo>;
|
||||
type BoxExternalName = Box<ExternalName>;
|
||||
|
||||
/// The main entry point for lowering with ISLE.
|
||||
pub(crate) fn lower<C>(
|
||||
lower_ctx: &mut C,
|
||||
isa_flags: &aarch64_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() {
|
||||
aarch64_map_regs(&mut inst, &renamer);
|
||||
lower_ctx.emit(inst);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct IsleContext<'a, C> {
|
||||
lower_ctx: &'a mut C,
|
||||
#[allow(dead_code)] // dead for now, but probably not for long
|
||||
isa_flags: &'a aarch64_settings::Flags,
|
||||
emitted_insts: SmallVec<[MInst; 6]>,
|
||||
}
|
||||
|
||||
impl<'a, C> IsleContext<'a, C> {
|
||||
pub fn new(lower_ctx: &'a mut C, isa_flags: &'a aarch64_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!();
|
||||
|
||||
fn move_wide_const_from_u64(&mut self, n: u64) -> Option<MoveWideConst> {
|
||||
MoveWideConst::maybe_from_u64(n)
|
||||
}
|
||||
|
||||
fn move_wide_const_from_negated_u64(&mut self, n: u64) -> Option<MoveWideConst> {
|
||||
MoveWideConst::maybe_from_u64(!n)
|
||||
}
|
||||
|
||||
fn imm_logic_from_u64(&mut self, n: u64) -> Option<ImmLogic> {
|
||||
ImmLogic::maybe_from_u64(n, I64)
|
||||
}
|
||||
|
||||
fn integral_ty(&mut self, ty: Type) -> Option<Type> {
|
||||
match ty {
|
||||
I8 | I16 | I32 | I64 | R64 => Some(ty),
|
||||
ty if ty.is_bool() => Some(ty),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the fallback case for loading a 64-bit integral constant into a
|
||||
/// register.
|
||||
///
|
||||
/// The logic here is nontrivial enough that it's not really worth porting
|
||||
/// this over to ISLE.
|
||||
fn load_constant64_full(&mut self, value: u64) -> Reg {
|
||||
// If the top 32 bits are zero, use 32-bit `mov` operations.
|
||||
let (num_half_words, size, negated) = if value >> 32 == 0 {
|
||||
(2, OperandSize::Size32, (!value << 32) >> 32)
|
||||
} else {
|
||||
(4, OperandSize::Size64, !value)
|
||||
};
|
||||
// If the number of 0xffff half words is greater than the number of 0x0000 half words
|
||||
// it is more efficient to use `movn` for the first instruction.
|
||||
let first_is_inverted = count_zero_half_words(negated, num_half_words)
|
||||
> count_zero_half_words(value, num_half_words);
|
||||
// Either 0xffff or 0x0000 half words can be skipped, depending on the first
|
||||
// instruction used.
|
||||
let ignored_halfword = if first_is_inverted { 0xffff } else { 0 };
|
||||
let mut first_mov_emitted = false;
|
||||
|
||||
let rd = self.temp_writable_reg(I64);
|
||||
|
||||
for i in 0..num_half_words {
|
||||
let imm16 = (value >> (16 * i)) & 0xffff;
|
||||
if imm16 != ignored_halfword {
|
||||
if !first_mov_emitted {
|
||||
first_mov_emitted = true;
|
||||
if first_is_inverted {
|
||||
let imm =
|
||||
MoveWideConst::maybe_with_shift(((!imm16) & 0xffff) as u16, i * 16)
|
||||
.unwrap();
|
||||
self.emitted_insts.push(MInst::MovN { rd, imm, size });
|
||||
} else {
|
||||
let imm = MoveWideConst::maybe_with_shift(imm16 as u16, i * 16).unwrap();
|
||||
self.emitted_insts.push(MInst::MovZ { rd, imm, size });
|
||||
}
|
||||
} else {
|
||||
let imm = MoveWideConst::maybe_with_shift(imm16 as u16, i * 16).unwrap();
|
||||
self.emitted_insts.push(MInst::MovK { rd, imm, size });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert!(first_mov_emitted);
|
||||
|
||||
return self.writable_reg_to_reg(rd);
|
||||
|
||||
fn count_zero_half_words(mut value: u64, num_half_words: u8) -> usize {
|
||||
let mut count = 0;
|
||||
for _ in 0..num_half_words {
|
||||
if value & 0xffff == 0 {
|
||||
count += 1;
|
||||
}
|
||||
value >>= 16;
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_reg(&mut self) -> Reg {
|
||||
zero_reg()
|
||||
}
|
||||
|
||||
fn emit(&mut self, inst: &MInst) -> Unit {
|
||||
self.emitted_insts.push(inst.clone());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
src/clif.isle 9c0563583e5500de00ec5e226edc0547ac3ea789c8d76f1da0401c80ec619320fdc9a6f17fd76bbcac74a5894f85385c1f51c900c2b83bc9906d03d0f29bf5cb
|
||||
src/prelude.isle a069d14321601afc63959af23086709d67d189dafcdc7d1fc8534b32d89d49008acb8368b7b5a7bc51a353736a378197ac352ccce2bb3be89d93afb6979e480a
|
||||
src/isa/aarch64/inst.isle 841748c9c5900821b7086a09a41c6dcdb2172eb47a45293b6ef10f2e1f1389620bf6a2c75152af807d8bc8929029a357af5191f5d87bac2c9ec54bf63a9a2a8f
|
||||
src/isa/aarch64/lower.isle b3cd0834484e543f39d477d47ee66042276e99955c21fb8c9340a5f27ac317936acb2907a30f758bf596066e36db801a179fda6dbcecaee758a0187a5a5f1412
|
||||
1108
cranelift/codegen/src/isa/aarch64/lower/isle/generated_code.rs
generated
Normal file
1108
cranelift/codegen/src/isa/aarch64/lower/isle/generated_code.rs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -38,22 +38,21 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
None
|
||||
};
|
||||
|
||||
if let Ok(()) = super::lower::isle::lower(ctx, isa_flags, &outputs, insn) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let implemented_in_isle = || {
|
||||
unreachable!(
|
||||
"implemented in ISLE: inst = `{}`, type = `{:?}`",
|
||||
ctx.dfg().display_inst(insn),
|
||||
ty
|
||||
);
|
||||
};
|
||||
|
||||
match op {
|
||||
Opcode::Iconst | Opcode::Bconst | Opcode::Null => {
|
||||
let value = ctx.get_constant(insn).unwrap();
|
||||
match ty.unwrap() {
|
||||
I8 | I16 | I32 | I64 | R64 => {}
|
||||
ty if ty.is_bool() => {}
|
||||
ty => {
|
||||
return Err(CodegenError::Unsupported(format!(
|
||||
"{}: Unsupported type: {:?}",
|
||||
op, ty
|
||||
)));
|
||||
}
|
||||
}
|
||||
let rd = get_output_reg(ctx, outputs[0]).only_reg().unwrap();
|
||||
lower_constant_u64(ctx, rd, value);
|
||||
}
|
||||
Opcode::Iconst | Opcode::Bconst | Opcode::Null => implemented_in_isle(),
|
||||
|
||||
Opcode::F32const => {
|
||||
let value = f32::from_bits(ctx.get_constant(insn).unwrap() as u32);
|
||||
let rd = get_output_reg(ctx, outputs[0]).only_reg().unwrap();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Instruction operand sub-components (aka "parts"): definitions and printing.
|
||||
|
||||
use super::regs::{self, show_ireg_sized};
|
||||
use super::{EmitState, RegMapper};
|
||||
use super::EmitState;
|
||||
use crate::ir::condcodes::{FloatCC, IntCC};
|
||||
use crate::ir::{MemFlags, Type};
|
||||
use crate::isa::x64::inst::Inst;
|
||||
|
||||
@@ -2565,64 +2565,17 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
||||
//=============================================================================
|
||||
// Instructions and subcomponents: map_regs
|
||||
|
||||
// Define our own register-mapping trait so we can do arbitrary register
|
||||
// renaming that are more free form than what `regalloc` constrains us to with
|
||||
// its `RegUsageMapper` trait definition.
|
||||
pub trait RegMapper {
|
||||
fn get_use(&self, reg: Reg) -> Option<Reg>;
|
||||
fn get_def(&self, reg: Reg) -> Option<Reg>;
|
||||
fn get_mod(&self, reg: Reg) -> Option<Reg>;
|
||||
}
|
||||
|
||||
impl<T> RegMapper for T
|
||||
where
|
||||
T: regalloc::RegUsageMapper,
|
||||
{
|
||||
fn get_use(&self, reg: Reg) -> Option<Reg> {
|
||||
let v = reg.as_virtual_reg()?;
|
||||
self.get_use(v).map(|r| r.to_reg())
|
||||
}
|
||||
|
||||
fn get_def(&self, reg: Reg) -> Option<Reg> {
|
||||
let v = reg.as_virtual_reg()?;
|
||||
self.get_def(v).map(|r| r.to_reg())
|
||||
}
|
||||
|
||||
fn get_mod(&self, reg: Reg) -> Option<Reg> {
|
||||
let v = reg.as_virtual_reg()?;
|
||||
self.get_mod(v).map(|r| r.to_reg())
|
||||
}
|
||||
}
|
||||
|
||||
fn map_use<RM: RegMapper>(m: &RM, r: &mut Reg) {
|
||||
if let Some(new) = m.get_use(*r) {
|
||||
*r = new;
|
||||
}
|
||||
}
|
||||
|
||||
fn map_def<RM: RegMapper>(m: &RM, r: &mut Writable<Reg>) {
|
||||
if let Some(new) = m.get_def(r.to_reg()) {
|
||||
*r = Writable::from_reg(new);
|
||||
}
|
||||
}
|
||||
|
||||
fn map_mod<RM: RegMapper>(m: &RM, r: &mut Writable<Reg>) {
|
||||
if let Some(new) = m.get_mod(r.to_reg()) {
|
||||
*r = Writable::from_reg(new);
|
||||
}
|
||||
}
|
||||
|
||||
impl Amode {
|
||||
fn map_uses<RM: RegMapper>(&mut self, map: &RM) {
|
||||
match self {
|
||||
Amode::ImmReg { ref mut base, .. } => map_use(map, base),
|
||||
Amode::ImmReg { ref mut base, .. } => map.map_use(base),
|
||||
Amode::ImmRegRegShift {
|
||||
ref mut base,
|
||||
ref mut index,
|
||||
..
|
||||
} => {
|
||||
map_use(map, base);
|
||||
map_use(map, index);
|
||||
map.map_use(base);
|
||||
map.map_use(index);
|
||||
}
|
||||
Amode::RipRelative { .. } => {
|
||||
// RIP isn't involved in regalloc.
|
||||
@@ -2645,7 +2598,7 @@ impl Amode {
|
||||
impl RegMemImm {
|
||||
fn map_uses<RM: RegMapper>(&mut self, map: &RM) {
|
||||
match self {
|
||||
RegMemImm::Reg { ref mut reg } => map_use(map, reg),
|
||||
RegMemImm::Reg { ref mut reg } => map.map_use(reg),
|
||||
RegMemImm::Mem { ref mut addr } => addr.map_uses(map),
|
||||
RegMemImm::Imm { .. } => {}
|
||||
}
|
||||
@@ -2655,7 +2608,7 @@ impl RegMemImm {
|
||||
match self {
|
||||
Self::Reg { reg } => {
|
||||
let mut writable_src = Writable::from_reg(*reg);
|
||||
map_def(mapper, &mut writable_src);
|
||||
mapper.map_def(&mut writable_src);
|
||||
*self = Self::reg(writable_src.to_reg());
|
||||
}
|
||||
_ => panic!("unexpected RegMemImm kind in map_src_reg_as_def"),
|
||||
@@ -2666,7 +2619,7 @@ impl RegMemImm {
|
||||
impl RegMem {
|
||||
fn map_uses<RM: RegMapper>(&mut self, map: &RM) {
|
||||
match self {
|
||||
RegMem::Reg { ref mut reg } => map_use(map, reg),
|
||||
RegMem::Reg { ref mut reg } => map.map_use(reg),
|
||||
RegMem::Mem { ref mut addr, .. } => addr.map_uses(map),
|
||||
}
|
||||
}
|
||||
@@ -2675,7 +2628,7 @@ impl RegMem {
|
||||
match self {
|
||||
Self::Reg { reg } => {
|
||||
let mut writable_src = Writable::from_reg(*reg);
|
||||
map_def(mapper, &mut writable_src);
|
||||
mapper.map_def(&mut writable_src);
|
||||
*self = Self::reg(writable_src.to_reg());
|
||||
}
|
||||
_ => panic!("unexpected RegMem kind in map_src_reg_as_def"),
|
||||
@@ -2698,25 +2651,25 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
debug_assert_eq!(*src1, dst.to_reg());
|
||||
if produces_const {
|
||||
src2.map_as_def(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
*src1 = dst.to_reg();
|
||||
} else {
|
||||
src2.map_uses(mapper);
|
||||
map_mod(mapper, dst);
|
||||
mapper.map_mod(dst);
|
||||
*src1 = dst.to_reg();
|
||||
}
|
||||
}
|
||||
Inst::Not { src, dst, .. } | Inst::Neg { src, dst, .. } => {
|
||||
debug_assert_eq!(*src, dst.to_reg());
|
||||
map_mod(mapper, dst);
|
||||
mapper.map_mod(dst);
|
||||
*src = dst.to_reg();
|
||||
}
|
||||
Inst::Div { divisor, .. } => divisor.map_uses(mapper),
|
||||
Inst::MulHi { src2, .. } => src2.map_uses(mapper),
|
||||
Inst::CheckedDivOrRemSeq { divisor, tmp, .. } => {
|
||||
map_mod(mapper, divisor);
|
||||
mapper.map_mod(divisor);
|
||||
if let Some(tmp) = tmp {
|
||||
map_def(mapper, tmp)
|
||||
mapper.map_def(tmp)
|
||||
}
|
||||
}
|
||||
Inst::SignExtendData { .. } => {}
|
||||
@@ -2736,7 +2689,7 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src.map_uses(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
Inst::XmmRmRImm {
|
||||
ref op,
|
||||
@@ -2748,7 +2701,7 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
debug_assert_eq!(*src1, dst.to_reg());
|
||||
if produces_const {
|
||||
src2.map_as_def(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
*src1 = dst.to_reg();
|
||||
} else if *op == SseOpcode::Pextrb
|
||||
|| *op == SseOpcode::Pextrw
|
||||
@@ -2760,11 +2713,11 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
|| *op == SseOpcode::Roundpd
|
||||
{
|
||||
src2.map_uses(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
*src1 = dst.to_reg();
|
||||
} else {
|
||||
src2.map_uses(mapper);
|
||||
map_mod(mapper, dst);
|
||||
mapper.map_mod(dst);
|
||||
*src1 = dst.to_reg();
|
||||
}
|
||||
}
|
||||
@@ -2777,11 +2730,11 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
debug_assert_eq!(*src1, dst.to_reg());
|
||||
if produces_const {
|
||||
src2.map_as_def(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
*src1 = dst.to_reg();
|
||||
} else {
|
||||
src2.map_uses(mapper);
|
||||
map_mod(mapper, dst);
|
||||
mapper.map_mod(dst);
|
||||
*src1 = dst.to_reg();
|
||||
}
|
||||
}
|
||||
@@ -2793,10 +2746,10 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src1.map_uses(mapper);
|
||||
map_use(mapper, src2);
|
||||
mapper.map_use(src2);
|
||||
match *op {
|
||||
Avx512Opcode::Vpermi2b => map_mod(mapper, dst),
|
||||
_ => map_def(mapper, dst),
|
||||
Avx512Opcode::Vpermi2b => mapper.map_mod(dst),
|
||||
_ => mapper.map_def(dst),
|
||||
}
|
||||
}
|
||||
Inst::XmmRmiReg {
|
||||
@@ -2807,29 +2760,29 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
} => {
|
||||
debug_assert_eq!(*src1, dst.to_reg());
|
||||
src2.map_uses(mapper);
|
||||
map_mod(mapper, dst);
|
||||
mapper.map_mod(dst);
|
||||
*src1 = dst.to_reg();
|
||||
}
|
||||
Inst::XmmUninitializedValue { ref mut dst, .. } => {
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
Inst::XmmLoadConst { ref mut dst, .. } => {
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
Inst::XmmMinMaxSeq {
|
||||
ref mut lhs,
|
||||
ref mut rhs_dst,
|
||||
..
|
||||
} => {
|
||||
map_use(mapper, lhs);
|
||||
map_mod(mapper, rhs_dst);
|
||||
mapper.map_use(lhs);
|
||||
mapper.map_mod(rhs_dst);
|
||||
}
|
||||
Inst::XmmMovRM {
|
||||
ref mut src,
|
||||
ref mut dst,
|
||||
..
|
||||
} => {
|
||||
map_use(mapper, src);
|
||||
mapper.map_use(src);
|
||||
dst.map_uses(mapper);
|
||||
}
|
||||
Inst::XmmCmpRmR {
|
||||
@@ -2838,9 +2791,9 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src.map_uses(mapper);
|
||||
map_use(mapper, dst);
|
||||
mapper.map_use(dst);
|
||||
}
|
||||
Inst::Imm { ref mut dst, .. } => map_def(mapper, dst),
|
||||
Inst::Imm { ref mut dst, .. } => mapper.map_def(dst),
|
||||
Inst::MovRR {
|
||||
ref mut src,
|
||||
ref mut dst,
|
||||
@@ -2851,8 +2804,8 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
ref mut dst,
|
||||
..
|
||||
} => {
|
||||
map_use(mapper, src);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_use(src);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
Inst::GprToXmm {
|
||||
ref mut src,
|
||||
@@ -2860,7 +2813,7 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src.map_uses(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
Inst::CvtUint64ToFloatSeq {
|
||||
ref mut src,
|
||||
@@ -2869,10 +2822,10 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
ref mut tmp_gpr2,
|
||||
..
|
||||
} => {
|
||||
map_mod(mapper, src);
|
||||
map_def(mapper, dst);
|
||||
map_def(mapper, tmp_gpr1);
|
||||
map_def(mapper, tmp_gpr2);
|
||||
mapper.map_mod(src);
|
||||
mapper.map_def(dst);
|
||||
mapper.map_def(tmp_gpr1);
|
||||
mapper.map_def(tmp_gpr2);
|
||||
}
|
||||
Inst::CvtFloatToSintSeq {
|
||||
ref mut src,
|
||||
@@ -2888,10 +2841,10 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
ref mut tmp_xmm,
|
||||
..
|
||||
} => {
|
||||
map_mod(mapper, src);
|
||||
map_def(mapper, dst);
|
||||
map_def(mapper, tmp_gpr);
|
||||
map_def(mapper, tmp_xmm);
|
||||
mapper.map_mod(src);
|
||||
mapper.map_def(dst);
|
||||
mapper.map_def(tmp_gpr);
|
||||
mapper.map_def(tmp_xmm);
|
||||
}
|
||||
Inst::MovzxRmR {
|
||||
ref mut src,
|
||||
@@ -2899,11 +2852,11 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src.map_uses(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
Inst::Mov64MR { src, dst, .. } | Inst::LoadEffectiveAddress { addr: src, dst } => {
|
||||
src.map_uses(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
Inst::MovsxRmR {
|
||||
ref mut src,
|
||||
@@ -2911,14 +2864,14 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src.map_uses(mapper);
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
Inst::MovRM {
|
||||
ref mut src,
|
||||
ref mut dst,
|
||||
..
|
||||
} => {
|
||||
map_use(mapper, src);
|
||||
mapper.map_use(src);
|
||||
dst.map_uses(mapper);
|
||||
}
|
||||
Inst::ShiftR {
|
||||
@@ -2927,7 +2880,7 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
debug_assert_eq!(*src, dst.to_reg());
|
||||
map_mod(mapper, dst);
|
||||
mapper.map_mod(dst);
|
||||
*src = dst.to_reg();
|
||||
}
|
||||
Inst::CmpRmiR {
|
||||
@@ -2936,9 +2889,9 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src.map_uses(mapper);
|
||||
map_use(mapper, dst);
|
||||
mapper.map_use(dst);
|
||||
}
|
||||
Inst::Setcc { ref mut dst, .. } => map_def(mapper, dst),
|
||||
Inst::Setcc { ref mut dst, .. } => mapper.map_def(dst),
|
||||
Inst::Cmove {
|
||||
consequent: ref mut src,
|
||||
ref mut dst,
|
||||
@@ -2946,7 +2899,7 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src.map_uses(mapper);
|
||||
map_mod(mapper, dst);
|
||||
mapper.map_mod(dst);
|
||||
*alternative = dst.to_reg();
|
||||
}
|
||||
Inst::XmmCmove {
|
||||
@@ -2955,11 +2908,11 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
src.map_uses(mapper);
|
||||
map_mod(mapper, dst);
|
||||
mapper.map_mod(dst);
|
||||
}
|
||||
Inst::Push64 { ref mut src } => src.map_uses(mapper),
|
||||
Inst::Pop64 { ref mut dst } => {
|
||||
map_def(mapper, dst);
|
||||
mapper.map_def(dst);
|
||||
}
|
||||
|
||||
Inst::CallKnown {
|
||||
@@ -2968,10 +2921,10 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
for r in uses.iter_mut() {
|
||||
map_use(mapper, r);
|
||||
mapper.map_use(r);
|
||||
}
|
||||
for r in defs.iter_mut() {
|
||||
map_def(mapper, r);
|
||||
mapper.map_def(r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2982,10 +2935,10 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
..
|
||||
} => {
|
||||
for r in uses.iter_mut() {
|
||||
map_use(mapper, r);
|
||||
mapper.map_use(r);
|
||||
}
|
||||
for r in defs.iter_mut() {
|
||||
map_def(mapper, r);
|
||||
mapper.map_def(r);
|
||||
}
|
||||
dest.map_uses(mapper);
|
||||
}
|
||||
@@ -2996,25 +2949,25 @@ pub(crate) fn x64_map_regs<RM: RegMapper>(inst: &mut Inst, mapper: &RM) {
|
||||
ref mut tmp2,
|
||||
..
|
||||
} => {
|
||||
map_use(mapper, idx);
|
||||
map_def(mapper, tmp1);
|
||||
map_def(mapper, tmp2);
|
||||
mapper.map_use(idx);
|
||||
mapper.map_def(tmp1);
|
||||
mapper.map_def(tmp2);
|
||||
}
|
||||
|
||||
Inst::JmpUnknown { ref mut target } => target.map_uses(mapper),
|
||||
|
||||
Inst::LoadExtName { ref mut dst, .. } => map_def(mapper, dst),
|
||||
Inst::LoadExtName { ref mut dst, .. } => mapper.map_def(dst),
|
||||
|
||||
Inst::LockCmpxchg {
|
||||
ref mut replacement,
|
||||
ref mut mem,
|
||||
..
|
||||
} => {
|
||||
map_use(mapper, replacement);
|
||||
mapper.map_use(replacement);
|
||||
mem.map_uses(mapper);
|
||||
}
|
||||
|
||||
Inst::ValueLabelMarker { ref mut reg, .. } => map_use(mapper, reg),
|
||||
Inst::ValueLabelMarker { ref mut reg, .. } => mapper.map_use(reg),
|
||||
|
||||
Inst::Ret
|
||||
| Inst::EpiloguePlaceholder
|
||||
|
||||
@@ -6,70 +6,29 @@ 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,
|
||||
Writable,
|
||||
};
|
||||
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, RegMapper,
|
||||
x64_map_regs,
|
||||
},
|
||||
machinst::{get_output_reg, InsnInput, InsnOutput, LowerCtx},
|
||||
machinst::{get_output_reg, InsnInput, InsnOutput, LowerCtx, RegRenamer},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
type Unit = ();
|
||||
type ValueSlice<'a> = &'a [Value];
|
||||
type ValueArray2 = [Value; 2];
|
||||
type ValueArray3 = [Value; 3];
|
||||
type WritableReg = Writable<Reg>;
|
||||
type ValueRegs = crate::machinst::ValueRegs<Reg>;
|
||||
|
||||
pub struct SinkableLoad {
|
||||
inst: Inst,
|
||||
addr_input: InsnInput,
|
||||
offset: i32,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RegRenamer {
|
||||
// Map of `(old, new)` register names. Use a `SmallVec` because we typically
|
||||
// only have one or two renamings.
|
||||
renames: SmallVec<[(Reg, Reg); 2]>,
|
||||
}
|
||||
|
||||
impl RegRenamer {
|
||||
fn add_rename(&mut self, old: Reg, new: Reg) {
|
||||
self.renames.push((old, new));
|
||||
}
|
||||
|
||||
fn get_rename(&self, reg: Reg) -> Option<Reg> {
|
||||
self.renames
|
||||
.iter()
|
||||
.find(|(old, _)| reg == *old)
|
||||
.map(|(_, new)| *new)
|
||||
}
|
||||
}
|
||||
|
||||
impl RegMapper for RegRenamer {
|
||||
fn get_use(&self, reg: Reg) -> Option<Reg> {
|
||||
self.get_rename(reg)
|
||||
}
|
||||
|
||||
fn get_def(&self, reg: Reg) -> Option<Reg> {
|
||||
self.get_rename(reg)
|
||||
}
|
||||
|
||||
fn get_mod(&self, reg: Reg) -> Option<Reg> {
|
||||
self.get_rename(reg)
|
||||
}
|
||||
}
|
||||
|
||||
/// The main entry point for lowering with ISLE.
|
||||
pub(crate) fn lower<C>(
|
||||
lower_ctx: &mut C,
|
||||
@@ -131,159 +90,7 @@ impl<'a, C> generated_code::Context for IsleContext<'a, C>
|
||||
where
|
||||
C: LowerCtx<I = MInst>,
|
||||
{
|
||||
#[inline]
|
||||
fn unpack_value_array_2(&mut self, arr: &ValueArray2) -> (Value, Value) {
|
||||
let [a, b] = *arr;
|
||||
(a, b)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pack_value_array_2(&mut self, a: Value, b: Value) -> ValueArray2 {
|
||||
[a, b]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unpack_value_array_3(&mut self, arr: &ValueArray3) -> (Value, Value, Value) {
|
||||
let [a, b, c] = *arr;
|
||||
(a, b, c)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pack_value_array_3(&mut self, a: Value, b: Value, c: Value) -> ValueArray3 {
|
||||
[a, b, c]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_reg(&mut self, reg: Reg) -> ValueRegs {
|
||||
ValueRegs::one(reg)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_regs(&mut self, r1: Reg, r2: Reg) -> ValueRegs {
|
||||
ValueRegs::two(r1, r2)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn temp_writable_reg(&mut self, ty: Type) -> WritableReg {
|
||||
let value_regs = self.lower_ctx.alloc_tmp(ty);
|
||||
value_regs.only_reg().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn invalid_reg(&mut self) -> Reg {
|
||||
Reg::invalid()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn put_in_reg(&mut self, val: Value) -> Reg {
|
||||
self.lower_ctx.put_value_in_regs(val).only_reg().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn put_in_regs(&mut self, val: Value) -> ValueRegs {
|
||||
self.lower_ctx.put_value_in_regs(val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_regs_get(&mut self, regs: ValueRegs, i: usize) -> Reg {
|
||||
regs.regs()[i]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn u8_as_u64(&mut self, x: u8) -> u64 {
|
||||
x.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn u16_as_u64(&mut self, x: u16) -> u64 {
|
||||
x.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn u32_as_u64(&mut self, x: u32) -> u64 {
|
||||
x.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ty_bits(&mut self, ty: Type) -> u16 {
|
||||
ty.bits()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fits_in_64(&mut self, ty: Type) -> Option<Type> {
|
||||
if ty.bits() <= 64 {
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_list_slice(&mut self, list: ValueList) -> ValueSlice {
|
||||
list.as_slice(&self.lower_ctx.dfg().value_lists)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unwrap_head_value_list_1(&mut self, list: ValueList) -> (Value, ValueSlice) {
|
||||
match self.value_list_slice(list) {
|
||||
[head, tail @ ..] => (*head, tail),
|
||||
_ => out_of_line_panic("`unwrap_head_value_list_1` on empty `ValueList`"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unwrap_head_value_list_2(&mut self, list: ValueList) -> (Value, Value, ValueSlice) {
|
||||
match self.value_list_slice(list) {
|
||||
[head1, head2, tail @ ..] => (*head1, *head2, tail),
|
||||
_ => out_of_line_panic(
|
||||
"`unwrap_head_value_list_2` on list without at least two elements",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn writable_reg_to_reg(&mut self, r: WritableReg) -> Reg {
|
||||
r.to_reg()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn u64_from_imm64(&mut self, imm: Imm64) -> u64 {
|
||||
imm.bits() as u64
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inst_results(&mut self, inst: Inst) -> ValueSlice {
|
||||
self.lower_ctx.dfg().inst_results(inst)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn first_result(&mut self, inst: Inst) -> Option<Value> {
|
||||
self.lower_ctx.dfg().inst_results(inst).first().copied()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inst_data(&mut self, inst: Inst) -> InstructionData {
|
||||
self.lower_ctx.dfg()[inst].clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&mut self, val: Value) -> Type {
|
||||
self.lower_ctx.dfg().value_type(val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn multi_lane(&mut self, ty: Type) -> Option<(u8, u16)> {
|
||||
if ty.lane_count() > 1 {
|
||||
Some((ty.lane_bits(), ty.lane_count()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn def_inst(&mut self, val: Value) -> Option<Inst> {
|
||||
self.lower_ctx.dfg().value_def(val).inst()
|
||||
}
|
||||
isle_prelude_methods!();
|
||||
|
||||
#[inline]
|
||||
fn operand_size_of_type(&mut self, ty: Type) -> OperandSize {
|
||||
@@ -400,14 +207,6 @@ where
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn u64_from_ieee32(&mut self, val: Ieee32) -> u64 {
|
||||
val.bits().into()
|
||||
}
|
||||
|
||||
fn u64_from_ieee64(&mut self, val: Ieee64) -> u64 {
|
||||
val.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -420,10 +219,3 @@ fn to_simm32(constant: i64) -> Option<RegMemImm> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
fn out_of_line_panic(msg: &str) -> ! {
|
||||
panic!("{}", msg);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
src/clif.isle 9c0563583e5500de00ec5e226edc0547ac3ea789c8d76f1da0401c80ec619320fdc9a6f17fd76bbcac74a5894f85385c1f51c900c2b83bc9906d03d0f29bf5cb
|
||||
src/prelude.isle 21143c73c97885afd9c34acedb8dbc653dca3b5a3e2e73754a5761762f7fe6ce2eefa520227e64c2b18f5626ca0ffa0a6aff54971099e619a464a23adf365bd2
|
||||
src/prelude.isle a069d14321601afc63959af23086709d67d189dafcdc7d1fc8534b32d89d49008acb8368b7b5a7bc51a353736a378197ac352ccce2bb3be89d93afb6979e480a
|
||||
src/isa/x64/inst.isle fdfbfc6dfad1fc5ed252e0a14ccc69baba51d0538e05cfb9916f6213e5a6fcfc9d22605a29bd684d6a66f6d5e1c8ec36a963660d52c2e8b3fb6e0758f7adb7b5
|
||||
src/isa/x64/lower.isle 8555abdae385431c96aaabc392b7b3a8b1bbe733be08b007ef776850860cb77e85a140db02f427586c155c0b0129f9ffd531abd2e4a772c72667535cc015e609
|
||||
|
||||
@@ -73,7 +73,7 @@ pub enum ConsumesFlags {
|
||||
}
|
||||
|
||||
/// Internal type ExtendKind: defined at src/isa/x64/inst.isle line 433.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ExtendKind {
|
||||
Sign,
|
||||
Zero,
|
||||
@@ -82,7 +82,7 @@ pub enum ExtendKind {
|
||||
// Generated as internal constructor for term temp_reg.
|
||||
pub fn constructor_temp_reg<C: Context>(ctx: &mut C, arg0: Type) -> Option<Reg> {
|
||||
let pattern0_0 = arg0;
|
||||
// Rule at src/prelude.isle line 57.
|
||||
// Rule at src/prelude.isle line 60.
|
||||
let expr0_0 = C::temp_writable_reg(ctx, pattern0_0);
|
||||
let expr1_0 = C::writable_reg_to_reg(ctx, expr0_0);
|
||||
return Some(expr1_0);
|
||||
@@ -91,7 +91,7 @@ pub fn constructor_temp_reg<C: Context>(ctx: &mut C, arg0: Type) -> Option<Reg>
|
||||
// Generated as internal constructor for term lo_reg.
|
||||
pub fn constructor_lo_reg<C: Context>(ctx: &mut C, arg0: Value) -> Option<Reg> {
|
||||
let pattern0_0 = arg0;
|
||||
// Rule at src/prelude.isle line 92.
|
||||
// Rule at src/prelude.isle line 95.
|
||||
let expr0_0 = C::put_in_regs(ctx, pattern0_0);
|
||||
let expr1_0: usize = 0;
|
||||
let expr2_0 = C::value_regs_get(ctx, expr0_0, expr1_0);
|
||||
|
||||
@@ -68,6 +68,9 @@ pub use cranelift_entity as entity;
|
||||
#[cfg(feature = "unwind")]
|
||||
pub use gimli;
|
||||
|
||||
#[macro_use]
|
||||
mod machinst;
|
||||
|
||||
pub mod binemit;
|
||||
pub mod cfg_printer;
|
||||
pub mod cursor;
|
||||
@@ -99,7 +102,6 @@ mod iterators;
|
||||
mod legalizer;
|
||||
mod licm;
|
||||
mod log;
|
||||
mod machinst;
|
||||
mod nan_canonicalization;
|
||||
mod remove_constant_phis;
|
||||
mod result;
|
||||
|
||||
185
cranelift/codegen/src/machinst/isle.rs
Normal file
185
cranelift/codegen/src/machinst/isle.rs
Normal file
@@ -0,0 +1,185 @@
|
||||
use crate::ir::Value;
|
||||
use regalloc::{Reg, Writable};
|
||||
|
||||
pub type Unit = ();
|
||||
pub type ValueSlice<'a> = &'a [Value];
|
||||
pub type ValueArray2 = [Value; 2];
|
||||
pub type ValueArray3 = [Value; 3];
|
||||
pub type WritableReg = Writable<Reg>;
|
||||
pub type ValueRegs = crate::machinst::ValueRegs<Reg>;
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! isle_prelude_methods {
|
||||
() => {
|
||||
#[inline]
|
||||
fn unpack_value_array_2(&mut self, arr: &ValueArray2) -> (Value, Value) {
|
||||
let [a, b] = *arr;
|
||||
(a, b)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pack_value_array_2(&mut self, a: Value, b: Value) -> ValueArray2 {
|
||||
[a, b]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unpack_value_array_3(&mut self, arr: &ValueArray3) -> (Value, Value, Value) {
|
||||
let [a, b, c] = *arr;
|
||||
(a, b, c)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pack_value_array_3(&mut self, a: Value, b: Value, c: Value) -> ValueArray3 {
|
||||
[a, b, c]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_reg(&mut self, reg: Reg) -> ValueRegs {
|
||||
ValueRegs::one(reg)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_regs(&mut self, r1: Reg, r2: Reg) -> ValueRegs {
|
||||
ValueRegs::two(r1, r2)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn temp_writable_reg(&mut self, ty: Type) -> WritableReg {
|
||||
let value_regs = self.lower_ctx.alloc_tmp(ty);
|
||||
value_regs.only_reg().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn invalid_reg(&mut self) -> Reg {
|
||||
Reg::invalid()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn put_in_reg(&mut self, val: Value) -> Reg {
|
||||
self.lower_ctx.put_value_in_regs(val).only_reg().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn put_in_regs(&mut self, val: Value) -> ValueRegs {
|
||||
self.lower_ctx.put_value_in_regs(val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_regs_get(&mut self, regs: ValueRegs, i: usize) -> Reg {
|
||||
regs.regs()[i]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn u8_as_u64(&mut self, x: u8) -> u64 {
|
||||
x.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn u16_as_u64(&mut self, x: u16) -> u64 {
|
||||
x.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn u32_as_u64(&mut self, x: u32) -> u64 {
|
||||
x.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ty_bits(&mut self, ty: Type) -> u16 {
|
||||
ty.bits()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fits_in_64(&mut self, ty: Type) -> Option<Type> {
|
||||
if ty.bits() <= 64 {
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_list_slice(&mut self, list: ValueList) -> ValueSlice {
|
||||
list.as_slice(&self.lower_ctx.dfg().value_lists)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unwrap_head_value_list_1(&mut self, list: ValueList) -> (Value, ValueSlice) {
|
||||
match self.value_list_slice(list) {
|
||||
[head, tail @ ..] => (*head, tail),
|
||||
_ => crate::machinst::isle::out_of_line_panic(
|
||||
"`unwrap_head_value_list_1` on empty `ValueList`",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unwrap_head_value_list_2(&mut self, list: ValueList) -> (Value, Value, ValueSlice) {
|
||||
match self.value_list_slice(list) {
|
||||
[head1, head2, tail @ ..] => (*head1, *head2, tail),
|
||||
_ => crate::machinst::isle::out_of_line_panic(
|
||||
"`unwrap_head_value_list_2` on list without at least two elements",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn writable_reg_to_reg(&mut self, r: WritableReg) -> Reg {
|
||||
r.to_reg()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn u64_from_imm64(&mut self, imm: Imm64) -> u64 {
|
||||
imm.bits() as u64
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inst_results(&mut self, inst: Inst) -> ValueSlice {
|
||||
self.lower_ctx.dfg().inst_results(inst)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn first_result(&mut self, inst: Inst) -> Option<Value> {
|
||||
self.lower_ctx.dfg().inst_results(inst).first().copied()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inst_data(&mut self, inst: Inst) -> InstructionData {
|
||||
self.lower_ctx.dfg()[inst].clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&mut self, val: Value) -> Type {
|
||||
self.lower_ctx.dfg().value_type(val)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn multi_lane(&mut self, ty: Type) -> Option<(u8, u16)> {
|
||||
if ty.lane_count() > 1 {
|
||||
Some((ty.lane_bits(), ty.lane_count()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn def_inst(&mut self, val: Value) -> Option<Inst> {
|
||||
self.lower_ctx.dfg().value_def(val).inst()
|
||||
}
|
||||
|
||||
fn u64_from_ieee32(&mut self, val: Ieee32) -> u64 {
|
||||
val.bits().into()
|
||||
}
|
||||
|
||||
fn u64_from_ieee64(&mut self, val: Ieee64) -> u64 {
|
||||
val.bits()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
pub fn out_of_line_panic(msg: &str) -> ! {
|
||||
panic!("{}", msg);
|
||||
}
|
||||
@@ -81,6 +81,9 @@ use target_lexicon::Triple;
|
||||
#[cfg(feature = "unwind")]
|
||||
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||
|
||||
#[macro_use]
|
||||
pub mod isle;
|
||||
|
||||
pub mod lower;
|
||||
pub use lower::*;
|
||||
pub mod vcode;
|
||||
@@ -104,6 +107,8 @@ pub use inst_common::*;
|
||||
pub mod valueregs;
|
||||
pub use valueregs::*;
|
||||
pub mod debug;
|
||||
pub use regmapping::*;
|
||||
pub mod regmapping;
|
||||
|
||||
/// A machine instruction.
|
||||
pub trait MachInst: Clone + Debug {
|
||||
|
||||
83
cranelift/codegen/src/machinst/regmapping.rs
Normal file
83
cranelift/codegen/src/machinst/regmapping.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use regalloc::{Reg, RegUsageMapper, Writable};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
// Define our own register-mapping trait so we can do arbitrary register
|
||||
// renaming that are more free form than what `regalloc` constrains us to with
|
||||
// its `RegUsageMapper` trait definition.
|
||||
pub trait RegMapper {
|
||||
fn get_use(&self, reg: Reg) -> Option<Reg>;
|
||||
fn get_def(&self, reg: Reg) -> Option<Reg>;
|
||||
fn get_mod(&self, reg: Reg) -> Option<Reg>;
|
||||
|
||||
fn map_use(&self, r: &mut Reg) {
|
||||
if let Some(new) = self.get_use(*r) {
|
||||
*r = new;
|
||||
}
|
||||
}
|
||||
|
||||
fn map_def(&self, r: &mut Writable<Reg>) {
|
||||
if let Some(new) = self.get_def(r.to_reg()) {
|
||||
*r = Writable::from_reg(new);
|
||||
}
|
||||
}
|
||||
|
||||
fn map_mod(&self, r: &mut Writable<Reg>) {
|
||||
if let Some(new) = self.get_mod(r.to_reg()) {
|
||||
*r = Writable::from_reg(new);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RegMapper for T
|
||||
where
|
||||
T: RegUsageMapper,
|
||||
{
|
||||
fn get_use(&self, reg: Reg) -> Option<Reg> {
|
||||
let v = reg.as_virtual_reg()?;
|
||||
self.get_use(v).map(|r| r.to_reg())
|
||||
}
|
||||
|
||||
fn get_def(&self, reg: Reg) -> Option<Reg> {
|
||||
let v = reg.as_virtual_reg()?;
|
||||
self.get_def(v).map(|r| r.to_reg())
|
||||
}
|
||||
|
||||
fn get_mod(&self, reg: Reg) -> Option<Reg> {
|
||||
let v = reg.as_virtual_reg()?;
|
||||
self.get_mod(v).map(|r| r.to_reg())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RegRenamer {
|
||||
// Map of `(old, new)` register names. Use a `SmallVec` because we typically
|
||||
// only have one or two renamings.
|
||||
renames: SmallVec<[(Reg, Reg); 2]>,
|
||||
}
|
||||
|
||||
impl RegRenamer {
|
||||
pub fn add_rename(&mut self, old: Reg, new: Reg) {
|
||||
self.renames.push((old, new));
|
||||
}
|
||||
|
||||
fn get_rename(&self, reg: Reg) -> Option<Reg> {
|
||||
self.renames
|
||||
.iter()
|
||||
.find(|(old, _)| reg == *old)
|
||||
.map(|(_, new)| *new)
|
||||
}
|
||||
}
|
||||
|
||||
impl RegMapper for RegRenamer {
|
||||
fn get_use(&self, reg: Reg) -> Option<Reg> {
|
||||
self.get_rename(reg)
|
||||
}
|
||||
|
||||
fn get_def(&self, reg: Reg) -> Option<Reg> {
|
||||
self.get_rename(reg)
|
||||
}
|
||||
|
||||
fn get_mod(&self, reg: Reg) -> Option<Reg> {
|
||||
self.get_rename(reg)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
;; This is a prelude of standard definitions for ISLE, the instruction-selector
|
||||
;; DSL, as we use it bound to our interfaces.
|
||||
;;
|
||||
;; Note that all `extern` functions here are typically defined in the
|
||||
;; `isle_prelude_methods` macro defined in `src/isa/isle.rs`
|
||||
|
||||
;;;; Primitive and External Types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user