From d59bef1902a9be700d9d16f258c419483503b2b1 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 11 Mar 2019 19:25:11 +0100 Subject: [PATCH] [meta] Port Formats and Operands to the Rust crate; --- cranelift/codegen/meta/src/cdsl/formats.rs | 246 +++++++++++++++ cranelift/codegen/meta/src/cdsl/mod.rs | 2 + cranelift/codegen/meta/src/cdsl/operands.rs | 285 ++++++++++++++++++ cranelift/codegen/meta/src/shared/entities.rs | 65 ++++ cranelift/codegen/meta/src/shared/formats.rs | 184 +++++++++++ .../codegen/meta/src/shared/immediates.rs | 145 +++++++++ cranelift/codegen/meta/src/shared/mod.rs | 51 ++++ 7 files changed, 978 insertions(+) create mode 100644 cranelift/codegen/meta/src/cdsl/formats.rs create mode 100644 cranelift/codegen/meta/src/cdsl/operands.rs create mode 100644 cranelift/codegen/meta/src/shared/entities.rs create mode 100644 cranelift/codegen/meta/src/shared/formats.rs create mode 100644 cranelift/codegen/meta/src/shared/immediates.rs diff --git a/cranelift/codegen/meta/src/cdsl/formats.rs b/cranelift/codegen/meta/src/cdsl/formats.rs new file mode 100644 index 0000000000..837440d2c0 --- /dev/null +++ b/cranelift/codegen/meta/src/cdsl/formats.rs @@ -0,0 +1,246 @@ +use crate::cdsl::operands::{Operand, OperandKind}; + +use std::collections::{HashMap, HashSet}; +use std::fmt; +use std::slice; + +use cranelift_entity::{entity_impl, PrimaryMap}; + +/// An immediate field in an instruction format. +/// +/// This corresponds to a single member of a variant of the `InstructionData` +/// data type. +/// +/// :param iform: Parent `InstructionFormat`. +/// :param kind: Immediate Operand kind. +/// :param member: Member name in `InstructionData` variant. +#[derive(Debug)] +pub struct FormatField { + /// Immediate operand number in parent. + immnum: usize, + + /// Immediate operand kind. + pub kind: OperandKind, + + /// Member name in InstructionDate variant. + pub member: &'static str, +} + +/// Every instruction opcode has a corresponding instruction format which +/// determines the number of operands and their kinds. Instruction formats are +/// identified structurally, i.e., the format of an instruction is derived from +/// the kinds of operands used in its declaration. +/// +/// The instruction format stores two separate lists of operands: Immediates +/// and values. Immediate operands (including entity references) are +/// represented as explicit members in the `InstructionData` variants. The +/// value operands are stored differently, depending on how many there are. +/// Beyond a certain point, instruction formats switch to an external value +/// list for storing value arguments. Value lists can hold an arbitrary number +/// of values. +/// +/// All instruction formats must be predefined in the meta shared/formats module. +/// +/// :param kinds: List of `OperandKind` objects describing the operands. +/// :param name: Instruction format name in CamelCase. This is used as a Rust +/// variant name in both the `InstructionData` and `InstructionFormat` +/// enums. +/// :param typevar_operand: Index of the value input operand that is used to +/// infer the controlling type variable. By default, this is `0`, the first +/// `value` operand. The index is relative to the values only, ignoring +/// immediate operands. +#[derive(Debug)] +pub struct InstructionFormat { + pub name: &'static str, + pub num_value_operands: usize, + pub has_value_list: bool, + pub imm_fields: Vec, + pub typevar_operand: Option, +} + +impl fmt::Display for InstructionFormat { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let args = self + .imm_fields + .iter() + .map(|field| format!("{}: {}", field.member, field.kind.name)) + .collect::>() + .join(", "); + fmt.write_fmt(format_args!( + "{}(imms=({}), vals={})", + self.name, args, self.num_value_operands + ))?; + Ok(()) + } +} + +pub struct InstructionFormatBuilder { + name: &'static str, + num_value_operands: usize, + has_value_list: bool, + imm_fields: Vec, + typevar_operand: Option, +} + +pub struct ImmParameter { + kind: OperandKind, + member: &'static str, +} +impl Into for (&'static str, &OperandKind) { + fn into(self) -> ImmParameter { + ImmParameter { + kind: self.1.clone(), + member: self.0, + } + } +} +impl Into for &OperandKind { + fn into(self) -> ImmParameter { + ImmParameter { + kind: self.clone(), + member: self.default_member.unwrap(), + } + } +} + +impl InstructionFormatBuilder { + pub fn new(name: &'static str) -> Self { + Self { + name, + num_value_operands: 0, + has_value_list: false, + imm_fields: Vec::new(), + typevar_operand: None, + } + } + + pub fn value(mut self) -> Self { + self.num_value_operands += 1; + self + } + + pub fn varargs(mut self) -> Self { + self.has_value_list = true; + self + } + + pub fn imm(mut self, param: impl Into) -> Self { + let imm_param = param.into(); + let field = FormatField { + immnum: self.imm_fields.len(), + kind: imm_param.kind, + member: imm_param.member, + }; + self.imm_fields.push(field); + self + } + + pub fn typevar_operand(mut self, operand_index: usize) -> Self { + assert!(self.typevar_operand.is_none()); + assert!(self.has_value_list || operand_index < self.num_value_operands); + self.typevar_operand = Some(operand_index); + self + } + + pub fn finish(self) -> InstructionFormat { + let typevar_operand = if self.typevar_operand.is_some() { + self.typevar_operand + } else if self.has_value_list || self.num_value_operands > 0 { + // Default to the first value operand, if there's one. + Some(0) + } else { + None + }; + + InstructionFormat { + name: self.name, + num_value_operands: self.num_value_operands, + has_value_list: self.has_value_list, + imm_fields: self.imm_fields, + typevar_operand, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct InstructionFormatIndex(u32); +entity_impl!(InstructionFormatIndex); + +pub struct FormatRegistry { + /// Map (immediate kinds names, number of values, has varargs) to an instruction format index + /// in the actual map. + sig_to_index: HashMap<(Vec, usize, bool), InstructionFormatIndex>, + map: PrimaryMap, + name_set: HashSet<&'static str>, +} + +impl FormatRegistry { + pub fn new() -> Self { + Self { + sig_to_index: HashMap::new(), + map: PrimaryMap::new(), + name_set: HashSet::new(), + } + } + + /// Find an existing instruction format that matches the given lists of instruction inputs and + /// outputs. + pub fn lookup(&self, operands_in: &Vec) -> InstructionFormatIndex { + let mut imm_keys = Vec::new(); + let mut num_values = 0; + let mut has_varargs = false; + + for operand in operands_in.iter() { + if operand.is_value() { + num_values += 1; + } + has_varargs = has_varargs || operand.is_varargs(); + if let Some(imm_key) = operand.kind.imm_key() { + imm_keys.push(imm_key); + } + } + + let sig = (imm_keys, num_values, has_varargs); + *self + .sig_to_index + .get(&sig) + .expect("unknown InstructionFormat; please define it in shared/formats.rs first") + } + + pub fn get(&self, index: InstructionFormatIndex) -> &InstructionFormat { + self.map.get(index).unwrap() + } + + pub fn insert(&mut self, inst_format: InstructionFormatBuilder) { + let name = &inst_format.name; + if !self.name_set.insert(name) { + panic!( + "Trying to add an InstructionFormat named {}, but it already exists!", + name + ); + } + + let format = inst_format.finish(); + + // Compute key. + let imm_keys = format + .imm_fields + .iter() + .map(|field| field.kind.imm_key().unwrap()) + .collect(); + let key = (imm_keys, format.num_value_operands, format.has_value_list); + + let index = self.map.push(format); + if let Some(already_inserted) = self.sig_to_index.insert(key, index) { + panic!( + "duplicate InstructionFormat: trying to insert '{}' while '{}' already has the same structure.", + self.map.get(index).unwrap().name, + self.map.get(already_inserted).unwrap().name + ); + } + } + + pub fn iter(&self) -> slice::Iter { + self.map.values() + } +} diff --git a/cranelift/codegen/meta/src/cdsl/mod.rs b/cranelift/codegen/meta/src/cdsl/mod.rs index dd3a7ec859..fcfe977d14 100644 --- a/cranelift/codegen/meta/src/cdsl/mod.rs +++ b/cranelift/codegen/meta/src/cdsl/mod.rs @@ -3,7 +3,9 @@ //! This module defines the classes that are used to define Cranelift //! instructions and other entities. +pub mod formats; pub mod isa; +pub mod operands; pub mod regs; pub mod settings; pub mod types; diff --git a/cranelift/codegen/meta/src/cdsl/operands.rs b/cranelift/codegen/meta/src/cdsl/operands.rs new file mode 100644 index 0000000000..f460225bfe --- /dev/null +++ b/cranelift/codegen/meta/src/cdsl/operands.rs @@ -0,0 +1,285 @@ +use std::collections::HashMap; + +use crate::cdsl::camel_case; +use crate::cdsl::typevar::TypeVar; + +/// An instruction operand can be an *immediate*, an *SSA value*, or an *entity reference*. The +/// type of the operand is one of: +/// +/// 1. A `ValueType` instance indicates an SSA value operand with a concrete type. +/// +/// 2. A `TypeVar` instance indicates an SSA value operand, and the instruction is polymorphic over +/// the possible concrete types that the type variable can assume. +/// +/// 3. An `ImmediateKind` instance indicates an immediate operand whose value is encoded in the +/// instruction itself rather than being passed as an SSA value. +/// +/// 4. An `EntityRefKind` instance indicates an operand that references another entity in the +/// function, typically something declared in the function preamble. +#[derive(Clone, Debug)] +pub struct Operand { + pub name: &'static str, + pub doc: Option, + pub kind: OperandKind, +} + +impl Operand { + pub fn is_value(&self) -> bool { + match self.kind.fields { + OperandKindFields::TypeVar(_) => true, + _ => false, + } + } + + pub fn type_var(&self) -> Option<&TypeVar> { + match &self.kind.fields { + OperandKindFields::TypeVar(typevar) => Some(typevar), + _ => None, + } + } + + pub fn is_varargs(&self) -> bool { + match self.kind.fields { + OperandKindFields::VariableArgs => true, + _ => false, + } + } + + /// Returns true if the operand has an immediate kind or is an EntityRef. + // TODO inherited name from the python, rename to is_immediate_or_entityref later. + pub fn is_immediate(&self) -> bool { + match self.kind.fields { + OperandKindFields::ImmEnum(_) + | OperandKindFields::ImmValue + | OperandKindFields::EntityRef => true, + _ => false, + } + } + + /// Returns true if the operand has an immediate kind. + pub fn is_pure_immediate(&self) -> bool { + match self.kind.fields { + OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => true, + _ => false, + } + } + + pub fn is_cpu_flags(&self) -> bool { + match &self.kind.fields { + OperandKindFields::TypeVar(type_var) + if type_var.name == "iflags" || type_var.name == "fflags" => + { + true + } + _ => false, + } + } +} + +pub struct OperandBuilder { + name: &'static str, + doc: Option, + kind: OperandKind, +} + +impl OperandBuilder { + pub fn new(name: &'static str, kind: OperandKind) -> Self { + Self { + name, + doc: None, + kind, + } + } + pub fn doc(mut self, doc: impl Into) -> Self { + assert!(self.doc.is_none()); + self.doc = Some(doc.into()); + self + } + pub fn finish(self) -> Operand { + let doc = match self.doc { + Some(doc) => Some(doc), + None => match &self.kind.fields { + OperandKindFields::TypeVar(tvar) => Some(tvar.doc.clone()), + _ => self.kind.doc.clone(), + }, + }; + + Operand { + name: self.name, + doc, + kind: self.kind, + } + } +} + +type EnumValues = HashMap<&'static str, &'static str>; + +#[derive(Clone, Debug)] +pub enum OperandKindFields { + EntityRef, + VariableArgs, + ImmValue, + ImmEnum(EnumValues), + TypeVar(TypeVar), +} + +#[derive(Clone, Debug)] +pub struct OperandKind { + pub name: &'static str, + + doc: Option, + + pub default_member: Option<&'static str>, + + /// The camel-cased name of an operand kind is also the Rust type used to represent it. + pub rust_type: String, + + fields: OperandKindFields, +} + +impl OperandKind { + pub fn imm_key(&self) -> Option { + match self.fields { + OperandKindFields::ImmEnum(_) + | OperandKindFields::ImmValue + | OperandKindFields::EntityRef => Some(self.name.to_string()), + _ => None, + } + } + + pub fn type_var(&self) -> TypeVar { + match &self.fields { + OperandKindFields::TypeVar(tvar) => tvar.clone(), + _ => panic!("not a typevar"), + } + } +} + +pub struct OperandKindBuilder { + name: &'static str, + + doc: Option, + + default_member: Option<&'static str>, + + /// The camel-cased name of an operand kind is also the Rust type used to represent it. + rust_type: Option, + + fields: OperandKindFields, +} + +impl OperandKindBuilder { + pub fn new(name: &'static str, fields: OperandKindFields) -> Self { + Self { + name, + doc: None, + default_member: None, + rust_type: None, + fields, + } + } + + pub fn new_imm(name: &'static str) -> Self { + Self { + name, + doc: None, + default_member: None, + rust_type: None, + fields: OperandKindFields::ImmValue, + } + } + + pub fn new_enum(name: &'static str, values: EnumValues) -> Self { + Self { + name, + doc: None, + default_member: None, + rust_type: None, + fields: OperandKindFields::ImmEnum(values), + } + } + + pub fn doc(mut self, doc: &'static str) -> Self { + assert!(self.doc.is_none()); + self.doc = Some(doc.to_string()); + self + } + pub fn default_member(mut self, default_member: &'static str) -> Self { + assert!(self.default_member.is_none()); + self.default_member = Some(default_member); + self + } + pub fn rust_type(mut self, rust_type: &'static str) -> Self { + assert!(self.rust_type.is_none()); + self.rust_type = Some(rust_type.to_string()); + self + } + + pub fn finish(self) -> OperandKind { + let default_member = match self.default_member { + Some(default_member) => Some(default_member), + None => match &self.fields { + OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => Some("imm"), + OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => Some(self.name), + OperandKindFields::VariableArgs => None, + }, + }; + + let rust_type = match self.rust_type { + Some(rust_type) => rust_type.to_string(), + None => match &self.fields { + OperandKindFields::ImmEnum(_) | OperandKindFields::ImmValue => { + format!("ir::immediates::{}", camel_case(self.name)) + } + OperandKindFields::VariableArgs => "&[Value]".to_string(), + OperandKindFields::TypeVar(_) | OperandKindFields::EntityRef => { + format!("ir::{}", camel_case(self.name)) + } + }, + }; + + let doc = match self.doc { + Some(doc) => Some(doc), + None => match &self.fields { + OperandKindFields::TypeVar(type_var) => Some(type_var.doc.clone()), + OperandKindFields::ImmEnum(_) + | OperandKindFields::ImmValue + | OperandKindFields::EntityRef + | OperandKindFields::VariableArgs => None, + }, + }; + + OperandKind { + name: self.name, + doc, + default_member, + rust_type, + fields: self.fields, + } + } +} + +impl Into for &TypeVar { + fn into(self) -> OperandKind { + OperandKindBuilder::new("value", OperandKindFields::TypeVar(self.into())).finish() + } +} +impl Into for &OperandKind { + fn into(self) -> OperandKind { + self.clone() + } +} + +/// Helper to create an operand in definitions files. +pub fn create_operand(name: &'static str, kind: impl Into) -> Operand { + OperandBuilder::new(name, kind.into()).finish() +} + +/// Helper to create an operand with a documentation in definitions files. +pub fn create_operand_doc( + name: &'static str, + kind: impl Into, + doc: &'static str, +) -> Operand { + OperandBuilder::new(name, kind.into()).doc(doc).finish() +} diff --git a/cranelift/codegen/meta/src/shared/entities.rs b/cranelift/codegen/meta/src/shared/entities.rs new file mode 100644 index 0000000000..e3ff8ac705 --- /dev/null +++ b/cranelift/codegen/meta/src/shared/entities.rs @@ -0,0 +1,65 @@ +use crate::cdsl::operands::{OperandKind, OperandKindBuilder as Builder, OperandKindFields}; + +/// Small helper to initialize an OperandBuilder with the right kind, for a given name and doc. +fn create(name: &'static str, doc: &'static str) -> Builder { + Builder::new(name, OperandKindFields::EntityRef).doc(doc) +} + +pub fn define() -> Vec { + let mut kinds = Vec::new(); + + // A reference to an extended basic block in the same function. + // This is primarliy used in control flow instructions. + let ebb = create("ebb", "An extended basic block in the same function.") + .default_member("destination") + .finish(); + kinds.push(ebb); + + // A reference to a stack slot declared in the function preamble. + let stack_slot = create("stack_slot", "A stack slot").finish(); + kinds.push(stack_slot); + + // A reference to a global value. + let global_value = create("global_value", "A global value.").finish(); + kinds.push(global_value); + + // A reference to a function signature declared in the function preamble. + // This is used to provide the call signature in a call_indirect instruction. + let sig_ref = create("sig_ref", "A function signature.").finish(); + kinds.push(sig_ref); + + // A reference to an external function declared in the function preamble. + // This is used to provide the callee and signature in a call instruction. + let func_ref = create("func_ref", "An external function.").finish(); + kinds.push(func_ref); + + // A reference to a jump table declared in the function preamble. + let jump_table = create("jump_table", "A jump table.") + .default_member("table") + .finish(); + kinds.push(jump_table); + + // A reference to a heap declared in the function preamble. + let heap = create("heap", "A heap.").finish(); + kinds.push(heap); + + // A reference to a table declared in the function preamble. + let table = create("table", "A table.").finish(); + kinds.push(table); + + // A variable-sized list of value operands. Use for Ebb and function call arguments. + let varargs = Builder::new("variable_args", OperandKindFields::VariableArgs) + .doc( + r#" + A variable size list of `value` operands. + + Use this to represent arguments passed to a function call, arguments + passed to an extended basic block, or a variable number of results + returned from an instruction. + "#, + ) + .finish(); + kinds.push(varargs); + + return kinds; +} diff --git a/cranelift/codegen/meta/src/shared/formats.rs b/cranelift/codegen/meta/src/shared/formats.rs new file mode 100644 index 0000000000..0af3c264ae --- /dev/null +++ b/cranelift/codegen/meta/src/shared/formats.rs @@ -0,0 +1,184 @@ +use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder as Builder}; +use crate::shared::OperandKinds; + +pub fn define(immediates: &OperandKinds, entities: &OperandKinds) -> FormatRegistry { + // Shorthands for immediates. + let uimm8 = immediates.by_name("uimm8"); + let uimm32 = immediates.by_name("uimm32"); + let imm64 = immediates.by_name("imm64"); + let ieee32 = immediates.by_name("ieee32"); + let ieee64 = immediates.by_name("ieee64"); + let boolean = immediates.by_name("boolean"); + let intcc = immediates.by_name("intcc"); + let floatcc = immediates.by_name("floatcc"); + let memflags = immediates.by_name("memflags"); + let offset32 = immediates.by_name("offset32"); + let trapcode = immediates.by_name("trapcode"); + let regunit = immediates.by_name("regunit"); + + // Shorthands for entities. + let global_value = entities.by_name("global_value"); + let ebb = entities.by_name("ebb"); + let jump_table = entities.by_name("jump_table"); + let func_ref = entities.by_name("func_ref"); + let sig_ref = entities.by_name("sig_ref"); + let stack_slot = entities.by_name("stack_slot"); + let heap = entities.by_name("heap"); + let table = entities.by_name("table"); + + let mut registry = FormatRegistry::new(); + + registry.insert(Builder::new("Unary").value()); + registry.insert(Builder::new("UnaryImm").imm(imm64)); + registry.insert(Builder::new("UnaryIeee32").imm(ieee32)); + registry.insert(Builder::new("UnaryIeee64").imm(ieee64)); + registry.insert(Builder::new("UnaryBool").imm(boolean)); + registry.insert(Builder::new("UnaryGlobalValue").imm(global_value)); + + registry.insert(Builder::new("Binary").value().value()); + registry.insert(Builder::new("BinaryImm").value().imm(imm64)); + + // The select instructions are controlled by the second VALUE operand. + // The first VALUE operand is the controlling flag which has a derived type. + // The fma instruction has the same constraint on all inputs. + registry.insert( + Builder::new("Ternary") + .value() + .value() + .value() + .typevar_operand(1), + ); + + // Catch-all for instructions with many outputs and inputs and no immediate + // operands. + registry.insert(Builder::new("MultiAry").varargs()); + + registry.insert(Builder::new("NullAry")); + + registry.insert( + Builder::new("InsertLane") + .value() + .imm(("lane", uimm8)) + .value(), + ); + registry.insert(Builder::new("ExtractLane").value().imm(("lane", uimm8))); + + registry.insert(Builder::new("IntCompare").imm(intcc).value().value()); + registry.insert(Builder::new("IntCompareImm").imm(intcc).value().imm(imm64)); + registry.insert(Builder::new("IntCond").imm(intcc).value()); + + registry.insert(Builder::new("FloatCompare").imm(floatcc).value().value()); + registry.insert(Builder::new("FloatCond").imm(floatcc).value());; + + registry.insert(Builder::new("IntSelect").imm(intcc).value().value().value()); + + registry.insert(Builder::new("Jump").imm(ebb).varargs()); + registry.insert(Builder::new("Branch").value().imm(ebb).varargs()); + registry.insert( + Builder::new("BranchInt") + .imm(intcc) + .value() + .imm(ebb) + .varargs(), + ); + registry.insert( + Builder::new("BranchFloat") + .imm(floatcc) + .value() + .imm(ebb) + .varargs(), + ); + registry.insert( + Builder::new("BranchIcmp") + .imm(intcc) + .value() + .value() + .imm(ebb) + .varargs(), + ); + registry.insert(Builder::new("BranchTable").value().imm(ebb).imm(jump_table)); + registry.insert( + Builder::new("BranchTableEntry") + .value() + .value() + .imm(uimm8) + .imm(jump_table), + ); + registry.insert(Builder::new("BranchTableBase").imm(jump_table)); + registry.insert(Builder::new("IndirectJump").value().imm(jump_table)); + + registry.insert(Builder::new("Call").imm(func_ref).varargs()); + registry.insert(Builder::new("CallIndirect").imm(sig_ref).value().varargs()); + registry.insert(Builder::new("FuncAddr").imm(func_ref)); + + registry.insert(Builder::new("Load").imm(memflags).value().imm(offset32)); + registry.insert( + Builder::new("LoadComplex") + .imm(memflags) + .varargs() + .imm(offset32), + ); + registry.insert( + Builder::new("Store") + .imm(memflags) + .value() + .value() + .imm(offset32), + ); + registry.insert( + Builder::new("StoreComplex") + .imm(memflags) + .value() + .varargs() + .imm(offset32), + ); + registry.insert(Builder::new("StackLoad").imm(stack_slot).imm(offset32)); + registry.insert( + Builder::new("StackStore") + .value() + .imm(stack_slot) + .imm(offset32), + ); + + // Accessing a WebAssembly heap. + registry.insert(Builder::new("HeapAddr").imm(heap).value().imm(uimm32)); + + // Accessing a WebAssembly table. + registry.insert(Builder::new("TableAddr").imm(table).value().imm(offset32)); + + registry.insert( + Builder::new("RegMove") + .value() + .imm(("src", regunit)) + .imm(("dst", regunit)), + ); + registry.insert( + Builder::new("CopySpecial") + .imm(("src", regunit)) + .imm(("dst", regunit)), + ); + registry.insert( + Builder::new("RegSpill") + .value() + .imm(("src", regunit)) + .imm(("dst", stack_slot)), + ); + registry.insert( + Builder::new("RegFill") + .value() + .imm(("src", stack_slot)) + .imm(("dst", regunit)), + ); + + registry.insert(Builder::new("Trap").imm(trapcode)); + registry.insert(Builder::new("CondTrap").value().imm(trapcode)); + registry.insert(Builder::new("IntCondTrap").imm(intcc).value().imm(trapcode)); + registry.insert( + Builder::new("FloatCondTrap") + .imm(floatcc) + .value() + .imm(trapcode), + ); + + registry +} diff --git a/cranelift/codegen/meta/src/shared/immediates.rs b/cranelift/codegen/meta/src/shared/immediates.rs new file mode 100644 index 0000000000..077fe0a511 --- /dev/null +++ b/cranelift/codegen/meta/src/shared/immediates.rs @@ -0,0 +1,145 @@ +use crate::cdsl::operands::{OperandKind, OperandKindBuilder as Builder}; + +use std::collections::HashMap; + +pub fn define() -> Vec { + let mut kinds = Vec::new(); + + // A 64-bit immediate integer operand. + // + // This type of immediate integer can interact with SSA values with any + // IntType type. + let imm64 = Builder::new_imm("imm64") + .doc("A 64-bit immediate integer.") + .finish(); + kinds.push(imm64); + + // An unsigned 8-bit immediate integer operand. + // + // This small operand is used to indicate lane indexes in SIMD vectors and + // immediate bit counts on shift instructions. + let uimm8 = Builder::new_imm("uimm8") + .doc("An 8-bit immediate unsigned integer.") + .finish(); + kinds.push(uimm8); + + // An unsigned 32-bit immediate integer operand. + let uimm32 = Builder::new_imm("uimm32") + .doc("A 32-bit immediate unsigned integer.") + .finish(); + kinds.push(uimm32); + + // A 32-bit immediate signed offset. + // + // This is used to represent an immediate address offset in load/store + // instructions. + let offset32 = Builder::new_imm("offset32") + .doc("A 32-bit immediate signed offset.") + .default_member("offset") + .finish(); + kinds.push(offset32); + + // A 32-bit immediate floating point operand. + // + // IEEE 754-2008 binary32 interchange format. + let ieee32 = Builder::new_imm("ieee32") + .doc("A 32-bit immediate floating point number.") + .finish(); + kinds.push(ieee32); + + // A 64-bit immediate floating point operand. + // + // IEEE 754-2008 binary64 interchange format. + let ieee64 = Builder::new_imm("ieee64") + .doc("A 64-bit immediate floating point number.") + .finish(); + kinds.push(ieee64); + + // An immediate boolean operand. + // + // This type of immediate boolean can interact with SSA values with any + // BoolType type. + let boolean = Builder::new_imm("boolean") + .doc("An immediate boolean.") + .rust_type("bool") + .finish(); + kinds.push(boolean); + + // A condition code for comparing integer values. + // This enumerated operand kind is used for the `icmp` instruction and corresponds to the + // condcodes::IntCC` Rust type. + let mut intcc_values = HashMap::new(); + intcc_values.insert("eq", "Equal"); + intcc_values.insert("ne", "NotEqual"); + intcc_values.insert("sge", "UnsignedGreaterThanOrEqual"); + intcc_values.insert("sgt", "UnsignedGreaterThan"); + intcc_values.insert("sle", "UnsignedLessThanOrEqual"); + intcc_values.insert("slt", "UnsignedLessThan"); + intcc_values.insert("uge", "UnsignedGreaterThanOrEqual"); + intcc_values.insert("ugt", "UnsignedGreaterThan"); + intcc_values.insert("ule", "UnsignedLessThanOrEqual"); + intcc_values.insert("ult", "UnsignedLessThan"); + let intcc = Builder::new_enum("intcc", intcc_values) + .doc("An integer comparison condition code.") + .default_member("cond") + .rust_type("ir::condcodes::IntCC") + .finish(); + kinds.push(intcc); + + // A condition code for comparing floating point values. This enumerated operand kind is used + // for the `fcmp` instruction and corresponds to the `condcodes::FloatCC` Rust type. + let mut floatcc_values = HashMap::new(); + floatcc_values.insert("ord", "Ordered"); + floatcc_values.insert("uno", "Unordered"); + floatcc_values.insert("eq", "Equal"); + floatcc_values.insert("ne", "NotEqual"); + floatcc_values.insert("one", "OrderedNotEqual"); + floatcc_values.insert("ueq", "UnorderedOrEqual"); + floatcc_values.insert("lt", "LessThan"); + floatcc_values.insert("le", "LessThanOrEqual"); + floatcc_values.insert("gt", "GreaterThan"); + floatcc_values.insert("ge", "GreaterThanOrEqual"); + floatcc_values.insert("ult", "UnorderedOrLessThan"); + floatcc_values.insert("ule", "UnorderedOrLessThanOrEqual"); + floatcc_values.insert("ugt", "UnorderedOrGreaterThan"); + floatcc_values.insert("uge", "UnorderedOrGreaterThanOrEqual"); + let floatcc = Builder::new_enum("floatcc", floatcc_values) + .doc("A floating point comparison condition code") + .default_member("cond") + .rust_type("ir::condcodes::FloatCC") + .finish(); + kinds.push(floatcc); + + // Flags for memory operations like :clif:inst:`load` and :clif:inst:`store`. + let memflags = Builder::new_imm("memflags") + .doc("Memory operation flags") + .default_member("flags") + .rust_type("ir::MemFlags") + .finish(); + kinds.push(memflags); + + // A register unit in the current target ISA. + let regunit = Builder::new_imm("regunit") + .doc("A register unit in the target ISA") + .rust_type("isa::RegUnit") + .finish(); + kinds.push(regunit); + + // A trap code indicating the reason for trapping. + // + // The Rust enum type also has a `User(u16)` variant for user-provided trap + // codes. + let mut trapcode_values = HashMap::new(); + trapcode_values.insert("stk_ovf", "StackOverflow"); + trapcode_values.insert("heap_oob", "HeapOutOfBounds"); + trapcode_values.insert("int_ovf", "IntegerOverflow"); + trapcode_values.insert("int_divz", "IntegerDivisionByZero"); + let trapcode = Builder::new_enum("trapcode", trapcode_values) + .doc("A trap reason code.") + .default_member("code") + .rust_type("ir::TrapCode") + .finish(); + kinds.push(trapcode); + + return kinds; +} diff --git a/cranelift/codegen/meta/src/shared/mod.rs b/cranelift/codegen/meta/src/shared/mod.rs index bdd9a14f2c..c8dd17ff4b 100644 --- a/cranelift/codegen/meta/src/shared/mod.rs +++ b/cranelift/codegen/meta/src/shared/mod.rs @@ -1,4 +1,55 @@ //! Shared definitions for the Cranelift intermediate language. +pub mod entities; +pub mod formats; +pub mod immediates; pub mod settings; pub mod types; + +use crate::cdsl::formats::FormatRegistry; +use crate::cdsl::operands::OperandKind; +use crate::cdsl::settings::SettingGroup; + +pub struct Definitions { + pub settings: SettingGroup, + pub operand_kinds: OperandKinds, + pub format_registry: FormatRegistry, +} + +pub struct OperandKinds(Vec); + +impl OperandKinds { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn by_name(&self, name: &'static str) -> &OperandKind { + self.0 + .iter() + .find(|op| op.name == name) + .expect(&format!("unknown Operand name: {}", name)) + } + + pub fn push(&mut self, operand_kind: OperandKind) { + assert!( + self.0 + .iter() + .find(|existing| existing.name == operand_kind.name) + .is_none(), + "trying to insert operand kind '{}' for the second time", + operand_kind.name + ); + self.0.push(operand_kind); + } +} + +pub fn define() -> Definitions { + let immediates = OperandKinds(immediates::define()); + let entities = OperandKinds(entities::define()); + let format_registry = formats::define(&immediates, &entities); + Definitions { + settings: settings::define(), + operand_kinds: immediates, + format_registry, + } +}