use std::rc::Rc;
use cranelift_entity::{entity_impl, PrimaryMap};
use crate::cdsl::formats::InstructionFormat;
use crate::cdsl::instructions::InstructionPredicate;
use crate::cdsl::regs::RegClassIndex;
use crate::cdsl::settings::SettingPredicateNumber;
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub(crate) struct Register {
pub regclass: RegClassIndex,
pub unit: u8,
}
impl Register {
pub fn new(regclass: RegClassIndex, unit: u8) -> Self {
Self { regclass, unit }
}
}
#[derive(Copy, Clone, Hash, PartialEq)]
pub(crate) struct Stack {
pub regclass: RegClassIndex,
}
impl Stack {
pub fn new(regclass: RegClassIndex) -> Self {
Self { regclass }
}
pub fn stack_base_mask(self) -> &'static str {
"StackBaseMask(1)"
}
}
#[derive(Clone, Hash, PartialEq)]
pub(crate) struct BranchRange {
pub inst_size: u64,
pub range: u64,
}
#[derive(Copy, Clone, Hash, PartialEq)]
pub(crate) enum OperandConstraint {
RegClass(RegClassIndex),
FixedReg(Register),
TiedInput(usize),
Stack(Stack),
}
impl Into<OperandConstraint> for RegClassIndex {
fn into(self) -> OperandConstraint {
OperandConstraint::RegClass(self)
}
}
impl Into<OperandConstraint> for Register {
fn into(self) -> OperandConstraint {
OperandConstraint::FixedReg(self)
}
}
impl Into<OperandConstraint> for usize {
fn into(self) -> OperandConstraint {
OperandConstraint::TiedInput(self)
}
}
impl Into<OperandConstraint> for Stack {
fn into(self) -> OperandConstraint {
OperandConstraint::Stack(self)
}
}
#[derive(Clone)]
pub(crate) struct EncodingRecipe {
pub name: String,
pub format: Rc<InstructionFormat>,
pub base_size: u64,
pub operands_in: Vec<OperandConstraint>,
pub operands_out: Vec<OperandConstraint>,
pub compute_size: &'static str,
pub branch_range: Option<BranchRange>,
pub clobbers_flags: bool,
pub inst_predicate: Option<InstructionPredicate>,
pub isa_predicate: Option<SettingPredicateNumber>,
pub emit: Option<String>,
}
impl PartialEq for EncodingRecipe {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.format, &other.format)
&& self.base_size == other.base_size
&& self.operands_in == other.operands_in
&& self.operands_out == other.operands_out
&& self.compute_size == other.compute_size
&& self.branch_range == other.branch_range
&& self.clobbers_flags == other.clobbers_flags
&& self.inst_predicate == other.inst_predicate
&& self.isa_predicate == other.isa_predicate
&& self.emit == other.emit
}
}
impl Eq for EncodingRecipe {}
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) struct EncodingRecipeNumber(u32);
entity_impl!(EncodingRecipeNumber);
pub(crate) type Recipes = PrimaryMap<EncodingRecipeNumber, EncodingRecipe>;
#[derive(Clone)]
pub(crate) struct EncodingRecipeBuilder {
pub name: String,
format: Rc<InstructionFormat>,
pub base_size: u64,
pub operands_in: Option<Vec<OperandConstraint>>,
pub operands_out: Option<Vec<OperandConstraint>>,
pub compute_size: Option<&'static str>,
pub branch_range: Option<BranchRange>,
pub emit: Option<String>,
clobbers_flags: Option<bool>,
inst_predicate: Option<InstructionPredicate>,
isa_predicate: Option<SettingPredicateNumber>,
}
impl EncodingRecipeBuilder {
pub fn new(name: impl Into<String>, format: &Rc<InstructionFormat>, base_size: u64) -> Self {
Self {
name: name.into(),
format: format.clone(),
base_size,
operands_in: None,
operands_out: None,
compute_size: None,
branch_range: None,
emit: None,
clobbers_flags: None,
inst_predicate: None,
isa_predicate: None,
}
}
pub fn operands_in(mut self, constraints: Vec<impl Into<OperandConstraint>>) -> Self {
assert!(self.operands_in.is_none());
self.operands_in = Some(
constraints
.into_iter()
.map(|constr| constr.into())
.collect(),
);
self
}
pub fn operands_out(mut self, constraints: Vec<impl Into<OperandConstraint>>) -> Self {
assert!(self.operands_out.is_none());
self.operands_out = Some(
constraints
.into_iter()
.map(|constr| constr.into())
.collect(),
);
self
}
pub fn clobbers_flags(mut self, flag: bool) -> Self {
assert!(self.clobbers_flags.is_none());
self.clobbers_flags = Some(flag);
self
}
pub fn emit(mut self, code: impl Into<String>) -> Self {
assert!(self.emit.is_none());
self.emit = Some(code.into());
self
}
pub fn branch_range(mut self, range: (u64, u64)) -> Self {
assert!(self.branch_range.is_none());
self.branch_range = Some(BranchRange {
inst_size: range.0,
range: range.1,
});
self
}
pub fn isa_predicate(mut self, pred: SettingPredicateNumber) -> Self {
assert!(self.isa_predicate.is_none());
self.isa_predicate = Some(pred);
self
}
pub fn inst_predicate(mut self, inst_predicate: impl Into<InstructionPredicate>) -> Self {
assert!(self.inst_predicate.is_none());
self.inst_predicate = Some(inst_predicate.into());
self
}
pub fn compute_size(mut self, compute_size: &'static str) -> Self {
assert!(self.compute_size.is_none());
self.compute_size = Some(compute_size);
self
}
pub fn build(self) -> EncodingRecipe {
let operands_in = self.operands_in.unwrap_or_default();
let operands_out = self.operands_out.unwrap_or_default();
if !self.format.has_value_list {
assert!(
operands_in.len() == self.format.num_value_operands,
format!(
"missing operand constraints for recipe {} (format {})",
self.name, self.format.name
)
);
}
for constraint in operands_in.iter().chain(operands_out.iter()) {
if let OperandConstraint::TiedInput(n) = *constraint {
assert!(n < operands_in.len());
}
}
let compute_size = match self.compute_size {
Some(compute_size) => compute_size,
None => "base_size",
};
let clobbers_flags = self.clobbers_flags.unwrap_or(true);
EncodingRecipe {
name: self.name,
format: self.format,
base_size: self.base_size,
operands_in,
operands_out,
compute_size,
branch_range: self.branch_range,
clobbers_flags,
inst_predicate: self.inst_predicate,
isa_predicate: self.isa_predicate,
emit: self.emit,
}
}
}