use crate::cdsl::instructions::{InstSpec, Instruction, InstructionPredicate}; use crate::cdsl::operands::{OperandKind, OperandKindFields}; use crate::cdsl::types::ValueType; use crate::cdsl::typevar::{TypeSetBuilder, TypeVar}; use cranelift_entity::{entity_impl, PrimaryMap, SparseMap, SparseMapValue}; use std::fmt; use std::iter::IntoIterator; pub(crate) enum Expr { Var(VarIndex), Literal(Literal), } impl Expr { pub fn maybe_literal(&self) -> Option<&Literal> { match &self { Expr::Literal(lit) => Some(lit), _ => None, } } pub fn maybe_var(&self) -> Option { if let Expr::Var(var) = &self { Some(*var) } else { None } } pub fn unwrap_var(&self) -> VarIndex { self.maybe_var() .expect("tried to unwrap a non-Var content in Expr::unwrap_var") } pub fn to_rust_code(&self, var_pool: &VarPool) -> String { match self { Expr::Var(var_index) => var_pool.get(*var_index).to_rust_code(), Expr::Literal(literal) => literal.to_rust_code(), } } } /// An AST definition associates a set of variables with the values produced by an expression. pub(crate) struct Def { pub apply: Apply, pub defined_vars: Vec, } impl Def { pub fn to_comment_string(&self, var_pool: &VarPool) -> String { let results = self .defined_vars .iter() .map(|&x| var_pool.get(x).name.as_str()) .collect::>(); let results = if results.len() == 1 { results[0].to_string() } else { format!("({})", results.join(", ")) }; format!("{} := {}", results, self.apply.to_comment_string(var_pool)) } } pub(crate) struct DefPool { pool: PrimaryMap, } impl DefPool { pub fn new() -> Self { Self { pool: PrimaryMap::new(), } } pub fn get(&self, index: DefIndex) -> &Def { self.pool.get(index).unwrap() } pub fn next_index(&self) -> DefIndex { self.pool.next_key() } pub fn create_inst(&mut self, apply: Apply, defined_vars: Vec) -> DefIndex { self.pool.push(Def { apply, defined_vars, }) } } #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) struct DefIndex(u32); entity_impl!(DefIndex); /// A definition which would lead to generate a block creation. #[derive(Clone)] pub(crate) struct Block { /// Instruction index after which the block entry is set. pub location: DefIndex, /// Variable holding the new created block. pub name: VarIndex, } pub(crate) struct BlockPool { pool: SparseMap, } impl SparseMapValue for Block { fn key(&self) -> DefIndex { self.location } } impl BlockPool { pub fn new() -> Self { Self { pool: SparseMap::new(), } } pub fn get(&self, index: DefIndex) -> Option<&Block> { self.pool.get(index) } pub fn create_block(&mut self, name: VarIndex, location: DefIndex) { if self.pool.contains_key(location) { panic!("Attempt to insert 2 blocks after the same instruction") } self.pool.insert(Block { location, name }); } pub fn is_empty(&self) -> bool { self.pool.is_empty() } } // Implement IntoIterator such that we can iterate over blocks which are in the block pool. impl<'a> IntoIterator for &'a BlockPool { type Item = <&'a SparseMap as IntoIterator>::Item; type IntoIter = <&'a SparseMap as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.pool.into_iter() } } #[derive(Clone, Debug)] pub(crate) enum Literal { /// A value of an enumerated immediate operand. /// /// Some immediate operand kinds like `intcc` and `floatcc` have an enumerated range of values /// corresponding to a Rust enum type. An `Enumerator` object is an AST leaf node representing one /// of the values. Enumerator { rust_type: &'static str, value: &'static str, }, /// A bitwise value of an immediate operand, used for bitwise exact floating point constants. Bits { rust_type: &'static str, value: u64 }, /// A value of an integer immediate operand. Int(i64), /// A empty list of variable set of arguments. EmptyVarArgs, } impl Literal { pub fn enumerator_for(kind: &OperandKind, value: &'static str) -> Self { let value = match &kind.fields { OperandKindFields::ImmEnum(values) => values.get(value).unwrap_or_else(|| { panic!( "nonexistent value '{}' in enumeration '{}'", value, kind.rust_type ) }), _ => panic!("enumerator is for enum values"), }; Literal::Enumerator { rust_type: kind.rust_type, value, } } pub fn bits(kind: &OperandKind, bits: u64) -> Self { match kind.fields { OperandKindFields::ImmValue => {} _ => panic!("bits_of is for immediate scalar types"), } Literal::Bits { rust_type: kind.rust_type, value: bits, } } pub fn constant(kind: &OperandKind, value: i64) -> Self { match kind.fields { OperandKindFields::ImmValue => {} _ => panic!("constant is for immediate scalar types"), } Literal::Int(value) } pub fn empty_vararg() -> Self { Literal::EmptyVarArgs } pub fn to_rust_code(&self) -> String { match self { Literal::Enumerator { rust_type, value } => format!("{}::{}", rust_type, value), Literal::Bits { rust_type, value } => format!("{}::with_bits({:#x})", rust_type, value), Literal::Int(val) => val.to_string(), Literal::EmptyVarArgs => "&[]".into(), } } } #[derive(Clone, Copy, Debug)] pub(crate) enum PatternPosition { Source, Destination, } /// A free variable. /// /// When variables are used in `XForms` with source and destination patterns, they are classified /// as follows: /// /// Input values: Uses in the source pattern with no preceding def. These may appear as inputs in /// the destination pattern too, but no new inputs can be introduced. /// /// Output values: Variables that are defined in both the source and destination pattern. These /// values may have uses outside the source pattern, and the destination pattern must compute the /// same value. /// /// Intermediate values: Values that are defined in the source pattern, but not in the destination /// pattern. These may have uses outside the source pattern, so the defining instruction can't be /// deleted immediately. /// /// Temporary values are defined only in the destination pattern. pub(crate) struct Var { pub name: String, /// The `Def` defining this variable in a source pattern. pub src_def: Option, /// The `Def` defining this variable in a destination pattern. pub dst_def: Option, /// TypeVar representing the type of this variable. type_var: Option, /// Is this the original type variable, or has it be redefined with set_typevar? is_original_type_var: bool, } impl Var { fn new(name: String) -> Self { Self { name, src_def: None, dst_def: None, type_var: None, is_original_type_var: false, } } /// Is this an input value to the src pattern? pub fn is_input(&self) -> bool { self.src_def.is_none() && self.dst_def.is_none() } /// Is this an output value, defined in both src and dst patterns? pub fn is_output(&self) -> bool { self.src_def.is_some() && self.dst_def.is_some() } /// Is this an intermediate value, defined only in the src pattern? pub fn is_intermediate(&self) -> bool { self.src_def.is_some() && self.dst_def.is_none() } /// Is this a temp value, defined only in the dst pattern? pub fn is_temp(&self) -> bool { self.src_def.is_none() && self.dst_def.is_some() } /// Get the def of this variable according to the position. pub fn get_def(&self, position: PatternPosition) -> Option { match position { PatternPosition::Source => self.src_def, PatternPosition::Destination => self.dst_def, } } pub fn set_def(&mut self, position: PatternPosition, def: DefIndex) { assert!( self.get_def(position).is_none(), "redefinition of variable {}", self.name ); match position { PatternPosition::Source => { self.src_def = Some(def); } PatternPosition::Destination => { self.dst_def = Some(def); } } } /// Get the type variable representing the type of this variable. pub fn get_or_create_typevar(&mut self) -> TypeVar { match &self.type_var { Some(tv) => tv.clone(), None => { // Create a new type var in which we allow all types. let tv = TypeVar::new( format!("typeof_{}", self.name), format!("Type of the pattern variable {:?}", self), TypeSetBuilder::all(), ); self.type_var = Some(tv.clone()); self.is_original_type_var = true; tv } } } pub fn get_typevar(&self) -> Option { self.type_var.clone() } pub fn set_typevar(&mut self, tv: TypeVar) { self.is_original_type_var = if let Some(previous_tv) = &self.type_var { *previous_tv == tv } else { false }; self.type_var = Some(tv); } /// Check if this variable has a free type variable. If not, the type of this variable is /// computed from the type of another variable. pub fn has_free_typevar(&self) -> bool { match &self.type_var { Some(tv) => tv.base.is_none() && self.is_original_type_var, None => false, } } pub fn to_rust_code(&self) -> String { self.name.clone() } fn rust_type(&self) -> String { self.type_var.as_ref().unwrap().to_rust_code() } } impl fmt::Debug for Var { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.write_fmt(format_args!( "Var({}{}{})", self.name, if self.src_def.is_some() { ", src" } else { "" }, if self.dst_def.is_some() { ", dst" } else { "" } )) } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) struct VarIndex(u32); entity_impl!(VarIndex); pub(crate) struct VarPool { pool: PrimaryMap, } impl VarPool { pub fn new() -> Self { Self { pool: PrimaryMap::new(), } } pub fn get(&self, index: VarIndex) -> &Var { self.pool.get(index).unwrap() } pub fn get_mut(&mut self, index: VarIndex) -> &mut Var { self.pool.get_mut(index).unwrap() } pub fn create(&mut self, name: impl Into) -> VarIndex { self.pool.push(Var::new(name.into())) } } /// Contains constants created in the AST that must be inserted into the true [ConstantPool] when /// the legalizer code is generated. The constant data is named in the order it is inserted; /// inserting data using [insert] will avoid duplicates. /// /// [ConstantPool]: ../../../cranelift_codegen/ir/constant/struct.ConstantPool.html /// [insert]: ConstPool::insert pub(crate) struct ConstPool { pool: Vec>, } impl ConstPool { /// Create an empty constant pool. pub fn new() -> Self { Self { pool: vec![] } } /// Create a name for a constant from its position in the pool. fn create_name(position: usize) -> String { format!("const{}", position) } /// Insert constant data into the pool, returning the name of the variable used to reference it. /// This method will search for data that matches the new data and return the existing constant /// name to avoid duplicates. pub fn insert(&mut self, data: Vec) -> String { let possible_position = self.pool.iter().position(|d| d == &data); let position = if let Some(found_position) = possible_position { found_position } else { let new_position = self.pool.len(); self.pool.push(data); new_position }; ConstPool::create_name(position) } /// Iterate over the name/value pairs in the pool. pub fn iter(&self) -> impl Iterator)> { self.pool .iter() .enumerate() .map(|(i, v)| (ConstPool::create_name(i), v)) } } /// Apply an instruction to arguments. /// /// An `Apply` AST expression is created by using function call syntax on instructions. This /// applies to both bound and unbound polymorphic instructions. pub(crate) struct Apply { pub inst: Instruction, pub args: Vec, pub value_types: Vec, } impl Apply { pub fn new(target: InstSpec, args: Vec) -> Self { let (inst, value_types) = match target { InstSpec::Inst(inst) => (inst, Vec::new()), InstSpec::Bound(bound_inst) => (bound_inst.inst, bound_inst.value_types), }; // Apply should only operate on concrete value types, not "any". let value_types = value_types .into_iter() .map(|vt| vt.expect("shouldn't be Any")) .collect(); // Basic check on number of arguments. assert!( inst.operands_in.len() == args.len(), "incorrect number of arguments in instruction {}", inst.name ); // Check that the kinds of Literals arguments match the expected operand. for &imm_index in &inst.imm_opnums { let arg = &args[imm_index]; if let Some(literal) = arg.maybe_literal() { let op = &inst.operands_in[imm_index]; match &op.kind.fields { OperandKindFields::ImmEnum(values) => { if let Literal::Enumerator { value, .. } = literal { assert!( values.iter().any(|(_key, v)| v == value), "Nonexistent enum value '{}' passed to field of kind '{}' -- \ did you use the right enum?", value, op.kind.rust_type ); } else { panic!( "Passed non-enum field value {:?} to field of kind {}", literal, op.kind.rust_type ); } } OperandKindFields::ImmValue => match &literal { Literal::Enumerator { value, .. } => panic!( "Expected immediate value in immediate field of kind '{}', \ obtained enum value '{}'", op.kind.rust_type, value ), Literal::Bits { .. } | Literal::Int(_) | Literal::EmptyVarArgs => {} }, _ => { panic!( "Literal passed to non-literal field of kind {}", op.kind.rust_type ); } } } } Self { inst, args, value_types, } } fn to_comment_string(&self, var_pool: &VarPool) -> String { let args = self .args .iter() .map(|arg| arg.to_rust_code(var_pool)) .collect::>() .join(", "); let mut inst_and_bound_types = vec![self.inst.name.to_string()]; inst_and_bound_types.extend(self.value_types.iter().map(|vt| vt.to_string())); let inst_name = inst_and_bound_types.join("."); format!("{}({})", inst_name, args) } pub fn inst_predicate(&self, var_pool: &VarPool) -> InstructionPredicate { let mut pred = InstructionPredicate::new(); for (format_field, &op_num) in self .inst .format .imm_fields .iter() .zip(self.inst.imm_opnums.iter()) { let arg = &self.args[op_num]; if arg.maybe_var().is_some() { // Ignore free variables for now. continue; } pred = pred.and(InstructionPredicate::new_is_field_equal_ast( &*self.inst.format, format_field, arg.to_rust_code(var_pool), )); } // Add checks for any bound secondary type variables. We can't check the controlling type // variable this way since it may not appear as the type of an operand. if self.value_types.len() > 1 { let poly = self .inst .polymorphic_info .as_ref() .expect("must have polymorphic info if it has bounded types"); for (bound_type, type_var) in self.value_types[1..].iter().zip(poly.other_typevars.iter()) { pred = pred.and(InstructionPredicate::new_typevar_check( &self.inst, type_var, bound_type, )); } } pred } /// Same as `inst_predicate()`, but also check the controlling type variable. pub fn inst_predicate_with_ctrl_typevar(&self, var_pool: &VarPool) -> InstructionPredicate { let mut pred = self.inst_predicate(var_pool); if !self.value_types.is_empty() { let bound_type = &self.value_types[0]; let poly = self.inst.polymorphic_info.as_ref().unwrap(); let type_check = if poly.use_typevar_operand { InstructionPredicate::new_typevar_check(&self.inst, &poly.ctrl_typevar, bound_type) } else { InstructionPredicate::new_ctrl_typevar_check(&bound_type) }; pred = pred.and(type_check); } pred } pub fn rust_builder(&self, defined_vars: &[VarIndex], var_pool: &VarPool) -> String { let mut args = self .args .iter() .map(|expr| expr.to_rust_code(var_pool)) .collect::>() .join(", "); // Do we need to pass an explicit type argument? if let Some(poly) = &self.inst.polymorphic_info { if !poly.use_typevar_operand { args = format!("{}, {}", var_pool.get(defined_vars[0]).rust_type(), args); } } format!("{}({})", self.inst.snake_name(), args) } } // Simple helpers for legalize actions construction. pub(crate) enum DummyExpr { Var(DummyVar), Literal(Literal), Constant(DummyConstant), Apply(InstSpec, Vec), Block(DummyVar), } #[derive(Clone)] pub(crate) struct DummyVar { pub name: String, } impl Into for DummyVar { fn into(self) -> DummyExpr { DummyExpr::Var(self) } } impl Into for Literal { fn into(self) -> DummyExpr { DummyExpr::Literal(self) } } #[derive(Clone)] pub(crate) struct DummyConstant(pub(crate) Vec); pub(crate) fn constant(data: Vec) -> DummyConstant { DummyConstant(data) } impl Into for DummyConstant { fn into(self) -> DummyExpr { DummyExpr::Constant(self) } } pub(crate) fn var(name: &str) -> DummyVar { DummyVar { name: name.to_owned(), } } pub(crate) struct DummyDef { pub expr: DummyExpr, pub defined_vars: Vec, } pub(crate) struct ExprBuilder { expr: DummyExpr, } impl ExprBuilder { pub fn apply(inst: InstSpec, args: Vec) -> Self { let expr = DummyExpr::Apply(inst, args); Self { expr } } pub fn assign_to(self, defined_vars: Vec) -> DummyDef { DummyDef { expr: self.expr, defined_vars, } } pub fn block(name: DummyVar) -> Self { let expr = DummyExpr::Block(name); Self { expr } } } macro_rules! def_rhs { // inst(a, b, c) ($inst:ident($($src:expr),*)) => { ExprBuilder::apply($inst.into(), vec![$($src.clone().into()),*]) }; // inst.type(a, b, c) ($inst:ident.$type:ident($($src:expr),*)) => { ExprBuilder::apply($inst.bind($type).into(), vec![$($src.clone().into()),*]) }; } // Helper macro to define legalization recipes. macro_rules! def { // x = ... ($dest:ident = $($tt:tt)*) => { def_rhs!($($tt)*).assign_to(vec![$dest.clone()]) }; // (x, y, ...) = ... (($($dest:ident),*) = $($tt:tt)*) => { def_rhs!($($tt)*).assign_to(vec![$($dest.clone()),*]) }; // An instruction with no results. ($($tt:tt)*) => { def_rhs!($($tt)*).assign_to(Vec::new()) } } // Helper macro to define legalization recipes. macro_rules! block { // a basic block definition, splitting the current block in 2. ($block: ident) => { ExprBuilder::block($block).assign_to(Vec::new()) }; } #[cfg(test)] mod tests { use crate::cdsl::ast::ConstPool; #[test] fn const_pool_returns_var_names() { let mut c = ConstPool::new(); assert_eq!(c.insert([0, 1, 2].to_vec()), "const0"); assert_eq!(c.insert([1, 2, 3].to_vec()), "const1"); } #[test] fn const_pool_avoids_duplicates() { let data = [0, 1, 2].to_vec(); let mut c = ConstPool::new(); assert_eq!(c.pool.len(), 0); assert_eq!(c.insert(data.clone()), "const0"); assert_eq!(c.pool.len(), 1); assert_eq!(c.insert(data), "const0"); assert_eq!(c.pool.len(), 1); } #[test] fn const_pool_iterates() { let mut c = ConstPool::new(); c.insert([0, 1, 2].to_vec()); c.insert([3, 4, 5].to_vec()); let mut iter = c.iter(); assert_eq!(iter.next(), Some(("const0".to_owned(), &vec![0, 1, 2]))); assert_eq!(iter.next(), Some(("const1".to_owned(), &vec![3, 4, 5]))); assert_eq!(iter.next(), None); } }