[meta] Remove name lookups in formats;
This does a lot at once, since there was no clear way to split the three commits: - Instruction need to be passed an explicit InstructionFormat, - InstructionFormat deduplication is checked once all entities have been defined;
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
use crate::cdsl::operands::{Operand, OperandKind};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use crate::cdsl::operands::OperandKind;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use std::slice;
|
||||
|
||||
/// An immediate field in an instruction format.
|
||||
///
|
||||
@@ -30,7 +27,7 @@ pub struct FormatField {
|
||||
///
|
||||
/// All instruction formats must be predefined in the meta shared/formats.rs module.
|
||||
#[derive(Debug)]
|
||||
pub struct InstructionFormat {
|
||||
pub(crate) struct InstructionFormat {
|
||||
/// Instruction format name in CamelCase. This is used as a Rust variant name in both the
|
||||
/// `InstructionData` and `InstructionFormat` enums.
|
||||
pub name: &'static str,
|
||||
@@ -47,6 +44,14 @@ pub struct InstructionFormat {
|
||||
pub typevar_operand: Option<usize>,
|
||||
}
|
||||
|
||||
/// A tuple serving as a key to deduplicate InstructionFormat.
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub(crate) struct FormatStructure {
|
||||
pub num_value_operands: usize,
|
||||
pub has_value_list: bool,
|
||||
pub imm_field_names: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl fmt::Display for InstructionFormat {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
let imm_args = self
|
||||
@@ -75,9 +80,22 @@ impl InstructionFormat {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a tuple that uniquely identifies the structure.
|
||||
pub fn structure(&self) -> FormatStructure {
|
||||
FormatStructure {
|
||||
num_value_operands: self.num_value_operands,
|
||||
has_value_list: self.has_value_list,
|
||||
imm_field_names: self
|
||||
.imm_fields
|
||||
.iter()
|
||||
.map(|field| field.kind.name)
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InstructionFormatBuilder {
|
||||
pub(crate) struct InstructionFormatBuilder {
|
||||
name: &'static str,
|
||||
num_value_operands: usize,
|
||||
has_value_list: bool,
|
||||
@@ -131,7 +149,7 @@ impl InstructionFormatBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> InstructionFormat {
|
||||
pub fn build(self) -> Rc<InstructionFormat> {
|
||||
let typevar_operand = if self.typevar_operand.is_some() {
|
||||
self.typevar_operand
|
||||
} else if self.has_value_list || self.num_value_operands > 0 {
|
||||
@@ -141,98 +159,12 @@ impl InstructionFormatBuilder {
|
||||
None
|
||||
};
|
||||
|
||||
InstructionFormat {
|
||||
Rc::new(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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FormatRegistry {
|
||||
/// Map (immediate kinds names, number of values, has varargs) to an instruction format.
|
||||
sig_to_index: HashMap<(Vec<String>, usize, bool), usize>,
|
||||
formats: Vec<Rc<InstructionFormat>>,
|
||||
name_set: HashSet<&'static str>,
|
||||
}
|
||||
|
||||
impl FormatRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
sig_to_index: HashMap::new(),
|
||||
formats: Vec::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<Operand>) -> &Rc<InstructionFormat> {
|
||||
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;
|
||||
}
|
||||
if !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);
|
||||
let index = *self
|
||||
.sig_to_index
|
||||
.get(&sig)
|
||||
.expect("unknown InstructionFormat; please define it in shared/formats.rs first");
|
||||
&self.formats[index]
|
||||
}
|
||||
|
||||
pub fn by_name(&self, name: &str) -> &Rc<InstructionFormat> {
|
||||
&self
|
||||
.formats
|
||||
.iter()
|
||||
.find(|format| format.name == name)
|
||||
.unwrap_or_else(|| panic!("format with name '{}' doesn't exist", name))
|
||||
}
|
||||
|
||||
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.build();
|
||||
|
||||
// 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.formats.len();
|
||||
self.formats.push(Rc::new(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.formats[index].name,
|
||||
self.formats[already_inserted].name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> slice::Iter<Rc<InstructionFormat>> {
|
||||
self.formats.iter()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use cranelift_codegen_shared::condcodes::IntCC;
|
||||
use cranelift_entity::{entity_impl, PrimaryMap};
|
||||
|
||||
use std::collections::HashMap;
|
||||
@@ -6,13 +7,14 @@ use std::fmt::{Display, Error, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::cdsl::camel_case;
|
||||
use crate::cdsl::formats::{FormatField, FormatRegistry, InstructionFormat};
|
||||
use crate::cdsl::formats::{FormatField, InstructionFormat};
|
||||
use crate::cdsl::operands::Operand;
|
||||
use crate::cdsl::type_inference::Constraint;
|
||||
use crate::cdsl::types::{LaneType, ReferenceType, ValueType, VectorType};
|
||||
use crate::cdsl::typevar::TypeVar;
|
||||
|
||||
use crate::shared::formats::Formats;
|
||||
use crate::shared::types::{Bool, Float, Int, Reference};
|
||||
use cranelift_codegen_shared::condcodes::IntCC;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub(crate) struct OpcodeNumber(u32);
|
||||
@@ -20,19 +22,14 @@ entity_impl!(OpcodeNumber);
|
||||
|
||||
pub(crate) type AllInstructions = PrimaryMap<OpcodeNumber, Instruction>;
|
||||
|
||||
pub(crate) struct InstructionGroupBuilder<'format_reg, 'all_inst> {
|
||||
format_registry: &'format_reg FormatRegistry,
|
||||
pub(crate) struct InstructionGroupBuilder<'all_inst> {
|
||||
all_instructions: &'all_inst mut AllInstructions,
|
||||
own_instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
impl<'format_reg, 'all_inst> InstructionGroupBuilder<'format_reg, 'all_inst> {
|
||||
pub fn new(
|
||||
all_instructions: &'all_inst mut AllInstructions,
|
||||
format_registry: &'format_reg FormatRegistry,
|
||||
) -> Self {
|
||||
impl<'all_inst> InstructionGroupBuilder<'all_inst> {
|
||||
pub fn new(all_instructions: &'all_inst mut AllInstructions) -> Self {
|
||||
Self {
|
||||
format_registry,
|
||||
all_instructions,
|
||||
own_instructions: Vec::new(),
|
||||
}
|
||||
@@ -40,7 +37,7 @@ impl<'format_reg, 'all_inst> InstructionGroupBuilder<'format_reg, 'all_inst> {
|
||||
|
||||
pub fn push(&mut self, builder: InstructionBuilder) {
|
||||
let opcode_number = OpcodeNumber(self.all_instructions.next_key().as_u32());
|
||||
let inst = builder.build(self.format_registry, opcode_number);
|
||||
let inst = builder.build(opcode_number);
|
||||
// Note this clone is cheap, since Instruction is a Rc<> wrapper for InstructionContent.
|
||||
self.own_instructions.push(inst.clone());
|
||||
self.all_instructions.push(inst);
|
||||
@@ -201,6 +198,7 @@ impl fmt::Display for InstructionContent {
|
||||
pub(crate) struct InstructionBuilder {
|
||||
name: String,
|
||||
doc: String,
|
||||
format: Rc<InstructionFormat>,
|
||||
operands_in: Option<Vec<Operand>>,
|
||||
operands_out: Option<Vec<Operand>>,
|
||||
constraints: Option<Vec<Constraint>>,
|
||||
@@ -219,10 +217,11 @@ pub(crate) struct InstructionBuilder {
|
||||
}
|
||||
|
||||
impl InstructionBuilder {
|
||||
pub fn new<S: Into<String>>(name: S, doc: S) -> Self {
|
||||
pub fn new<S: Into<String>>(name: S, doc: S, format: &Rc<InstructionFormat>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
doc: doc.into(),
|
||||
format: format.clone(),
|
||||
operands_in: None,
|
||||
operands_out: None,
|
||||
constraints: None,
|
||||
@@ -297,7 +296,7 @@ impl InstructionBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
fn build(self, format_registry: &FormatRegistry, opcode_number: OpcodeNumber) -> Instruction {
|
||||
fn build(self, opcode_number: OpcodeNumber) -> Instruction {
|
||||
let operands_in = self.operands_in.unwrap_or_else(Vec::new);
|
||||
let operands_out = self.operands_out.unwrap_or_else(Vec::new);
|
||||
|
||||
@@ -319,9 +318,10 @@ impl InstructionBuilder {
|
||||
.filter_map(|(i, op)| if op.is_value() { Some(i) } else { None })
|
||||
.collect();
|
||||
|
||||
let format = format_registry.lookup(&operands_in).clone();
|
||||
verify_format(&self.name, &operands_in, &self.format);
|
||||
|
||||
let polymorphic_info =
|
||||
verify_polymorphic(&operands_in, &operands_out, &format, &value_opnums);
|
||||
verify_polymorphic(&operands_in, &operands_out, &self.format, &value_opnums);
|
||||
|
||||
// Infer from output operands whether an instruciton clobbers CPU flags or not.
|
||||
let writes_cpu_flags = operands_out.iter().any(|op| op.is_cpu_flags());
|
||||
@@ -336,7 +336,7 @@ impl InstructionBuilder {
|
||||
operands_in,
|
||||
operands_out,
|
||||
constraints: self.constraints.unwrap_or_else(Vec::new),
|
||||
format,
|
||||
format: self.format,
|
||||
polymorphic_info,
|
||||
value_opnums,
|
||||
value_results,
|
||||
@@ -533,6 +533,57 @@ impl Bindable for BoundInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the input operands actually match the given format.
|
||||
fn verify_format(inst_name: &str, operands_in: &Vec<Operand>, format: &InstructionFormat) {
|
||||
// A format is defined by:
|
||||
// - its number of input value operands,
|
||||
// - its number and names of input immediate operands,
|
||||
// - whether it has a value list or not.
|
||||
let mut num_values = 0;
|
||||
let mut imm_index = 0;
|
||||
|
||||
for operand in operands_in.iter() {
|
||||
if operand.is_varargs() {
|
||||
assert!(
|
||||
format.has_value_list,
|
||||
"instruction {} has varargs, but its format {} doesn't have a value list; you may \
|
||||
need to use a different format.",
|
||||
inst_name, format.name
|
||||
);
|
||||
}
|
||||
if operand.is_value() {
|
||||
num_values += 1;
|
||||
}
|
||||
if let Some(imm_name) = operand.kind.imm_name() {
|
||||
if let Some(format_field) = format.imm_fields.get(imm_index) {
|
||||
assert_eq!(
|
||||
format_field.kind.name, imm_name,
|
||||
"{}th operand of {} should be {} (according to format), not {} (according to \
|
||||
inst definition). You may need to use a different format.",
|
||||
imm_index, inst_name, format_field.kind.name, imm_name
|
||||
);
|
||||
imm_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
num_values, format.num_value_operands,
|
||||
"inst {} doesnt' have as many value input operand as its format {} declares; you may need \
|
||||
to use a different format.",
|
||||
inst_name, format.name
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
imm_index,
|
||||
format.imm_fields.len(),
|
||||
"inst {} doesn't have as many immediate input \
|
||||
operands as its format {} declares; you may need to use a different format.",
|
||||
inst_name,
|
||||
format.name
|
||||
);
|
||||
}
|
||||
|
||||
/// Check if this instruction is polymorphic, and verify its use of type variables.
|
||||
fn verify_polymorphic(
|
||||
operands_in: &Vec<Operand>,
|
||||
@@ -1089,8 +1140,8 @@ impl InstructionPredicate {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn new_is_colocated_data(format_registry: &FormatRegistry) -> InstructionPredicateNode {
|
||||
let format = format_registry.by_name("UnaryGlobalValue");
|
||||
pub fn new_is_colocated_data(formats: &Formats) -> InstructionPredicateNode {
|
||||
let format = &formats.unary_global_value;
|
||||
InstructionPredicateNode::FormatPredicate(FormatPredicateNode::new(
|
||||
&*format,
|
||||
"global_value",
|
||||
@@ -1271,7 +1322,6 @@ mod test {
|
||||
outputs: Vec<OperandKindFields>,
|
||||
) -> Instruction {
|
||||
// setup a format from the input operands
|
||||
let mut formats = FormatRegistry::new();
|
||||
let mut format = InstructionFormatBuilder::new("fake");
|
||||
for (i, f) in inputs.iter().enumerate() {
|
||||
match f {
|
||||
@@ -1282,13 +1332,13 @@ mod test {
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
formats.insert(format);
|
||||
let format = format.build();
|
||||
|
||||
// create the fake instruction
|
||||
InstructionBuilder::new("fake", "A fake instruction for testing.")
|
||||
InstructionBuilder::new("fake", "A fake instruction for testing.", &format)
|
||||
.operands_in(field_to_operands(inputs).iter().collect())
|
||||
.operands_out(field_to_operands(outputs).iter().collect())
|
||||
.build(&formats, OpcodeNumber(42))
|
||||
.build(OpcodeNumber(42))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -138,11 +138,11 @@ pub struct OperandKind {
|
||||
}
|
||||
|
||||
impl OperandKind {
|
||||
pub fn imm_key(&self) -> Option<String> {
|
||||
pub fn imm_name(&self) -> Option<&str> {
|
||||
match self.fields {
|
||||
OperandKindFields::ImmEnum(_)
|
||||
| OperandKindFields::ImmValue
|
||||
| OperandKindFields::EntityRef => Some(self.name.to_string()),
|
||||
| OperandKindFields::EntityRef => Some(&self.name),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,14 +465,15 @@ impl TransformGroups {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_double_custom_legalization() {
|
||||
use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder};
|
||||
use crate::cdsl::formats::InstructionFormatBuilder;
|
||||
use crate::cdsl::instructions::{AllInstructions, InstructionBuilder, InstructionGroupBuilder};
|
||||
|
||||
let nullary = InstructionFormatBuilder::new("nullary").build();
|
||||
|
||||
let mut dummy_all = AllInstructions::new();
|
||||
let mut format = FormatRegistry::new();
|
||||
format.insert(InstructionFormatBuilder::new("nullary"));
|
||||
let mut inst_group = InstructionGroupBuilder::new(&mut dummy_all, &format);
|
||||
inst_group.push(InstructionBuilder::new("dummy", "doc"));
|
||||
let mut inst_group = InstructionGroupBuilder::new(&mut dummy_all);
|
||||
inst_group.push(InstructionBuilder::new("dummy", "doc", &nullary));
|
||||
|
||||
let inst_group = inst_group.build();
|
||||
let dummy_inst = inst_group.by_name("dummy");
|
||||
|
||||
|
||||
@@ -4,13 +4,11 @@ use cranelift_codegen_shared::constant_hash;
|
||||
use cranelift_entity::EntityRef;
|
||||
|
||||
use crate::cdsl::camel_case;
|
||||
use crate::cdsl::formats::{FormatRegistry, InstructionFormat};
|
||||
use crate::cdsl::formats::InstructionFormat;
|
||||
use crate::cdsl::instructions::{AllInstructions, Instruction};
|
||||
use crate::cdsl::operands::Operand;
|
||||
use crate::cdsl::typevar::{TypeSet, TypeVar};
|
||||
|
||||
use crate::shared::Definitions as SharedDefinitions;
|
||||
|
||||
use crate::error;
|
||||
use crate::srcgen::{Formatter, Match};
|
||||
use crate::unique_table::{UniqueSeqTable, UniqueTable};
|
||||
@@ -19,7 +17,7 @@ use crate::unique_table::{UniqueSeqTable, UniqueTable};
|
||||
const TYPESET_LIMIT: usize = 0xff;
|
||||
|
||||
/// Generate an instruction format enumeration.
|
||||
fn gen_formats(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fn gen_formats(formats: &Vec<&InstructionFormat>, fmt: &mut Formatter) {
|
||||
fmt.doc_comment(
|
||||
r#"
|
||||
An instruction format
|
||||
@@ -32,7 +30,7 @@ fn gen_formats(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug)]");
|
||||
fmt.line("pub enum InstructionFormat {");
|
||||
fmt.indent(|fmt| {
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
fmt.doc_comment(format.to_string());
|
||||
fmtln!(fmt, "{},", format.name);
|
||||
}
|
||||
@@ -47,7 +45,7 @@ fn gen_formats(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fmt.line("fn from(inst: &'a InstructionData) -> Self {");
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new("*inst");
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
m.arm(
|
||||
format!("InstructionData::{}", format.name),
|
||||
vec![".."],
|
||||
@@ -67,12 +65,12 @@ fn gen_formats(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
/// Every variant must contain an `opcode` field. The size of `InstructionData` should be kept at
|
||||
/// 16 bytes on 64-bit architectures. If more space is needed to represent an instruction, use a
|
||||
/// `ValueList` to store the additional information out of line.
|
||||
fn gen_instruction_data(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fn gen_instruction_data(formats: &Vec<&InstructionFormat>, fmt: &mut Formatter) {
|
||||
fmt.line("#[derive(Clone, Debug)]");
|
||||
fmt.line("#[allow(missing_docs)]");
|
||||
fmt.line("pub enum InstructionData {");
|
||||
fmt.indent(|fmt| {
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
fmtln!(fmt, "{} {{", format.name);
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("opcode: Opcode,");
|
||||
@@ -95,7 +93,7 @@ fn gen_instruction_data(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fmt.line("}");
|
||||
}
|
||||
|
||||
fn gen_arguments_method(registry: &FormatRegistry, fmt: &mut Formatter, is_mut: bool) {
|
||||
fn gen_arguments_method(formats: &Vec<&InstructionFormat>, fmt: &mut Formatter, is_mut: bool) {
|
||||
let (method, mut_, rslice, as_slice) = if is_mut {
|
||||
(
|
||||
"arguments_mut",
|
||||
@@ -117,7 +115,7 @@ fn gen_arguments_method(registry: &FormatRegistry, fmt: &mut Formatter, is_mut:
|
||||
);
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new("*self");
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
let name = format!("InstructionData::{}", format.name);
|
||||
|
||||
// Formats with a value list put all of their arguments in the list. We don't split
|
||||
@@ -165,14 +163,14 @@ fn gen_arguments_method(registry: &FormatRegistry, fmt: &mut Formatter, is_mut:
|
||||
/// - `pub fn put_value_list(&mut self, args: ir::ValueList>`
|
||||
/// - `pub fn eq(&self, &other: Self, &pool) -> bool`
|
||||
/// - `pub fn hash<H: Hasher>(&self, state: &mut H, &pool)`
|
||||
fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fn gen_instruction_data_impl(formats: &Vec<&InstructionFormat>, fmt: &mut Formatter) {
|
||||
fmt.line("impl InstructionData {");
|
||||
fmt.indent(|fmt| {
|
||||
fmt.doc_comment("Get the opcode of this instruction.");
|
||||
fmt.line("pub fn opcode(&self) -> Opcode {");
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new("*self");
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
m.arm(format!("InstructionData::{}", format.name), vec!["opcode", ".."],
|
||||
"opcode".to_string());
|
||||
}
|
||||
@@ -185,7 +183,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fmt.line("pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> Option<Value> {");
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new("*self");
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
let name = format!("InstructionData::{}", format.name);
|
||||
if format.typevar_operand.is_none() {
|
||||
m.arm(name, vec![".."], "None".to_string());
|
||||
@@ -208,12 +206,12 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fmt.empty_line();
|
||||
|
||||
fmt.doc_comment("Get the value arguments to this instruction.");
|
||||
gen_arguments_method(registry, fmt, false);
|
||||
gen_arguments_method(formats, fmt, false);
|
||||
fmt.empty_line();
|
||||
|
||||
fmt.doc_comment(r#"Get mutable references to the value arguments to this
|
||||
instruction."#);
|
||||
gen_arguments_method(registry, fmt, true);
|
||||
gen_arguments_method(formats, fmt, true);
|
||||
fmt.empty_line();
|
||||
|
||||
fmt.doc_comment(r#"
|
||||
@@ -227,7 +225,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fmt.indent(|fmt| {
|
||||
let mut m = Match::new("*self");
|
||||
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
if format.has_value_list {
|
||||
m.arm(format!("InstructionData::{}", format.name),
|
||||
vec!["ref mut args", ".."],
|
||||
@@ -254,7 +252,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("let args = match *self {");
|
||||
fmt.indent(|fmt| {
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
if format.has_value_list {
|
||||
fmtln!(fmt, "InstructionData::{} {{ ref mut args, .. }} => args,", format.name);
|
||||
}
|
||||
@@ -284,7 +282,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
|
||||
fmt.line("match (self, other) {");
|
||||
fmt.indent(|fmt| {
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
let name = format!("&InstructionData::{}", format.name);
|
||||
let mut members = vec!["opcode"];
|
||||
|
||||
@@ -336,7 +334,7 @@ fn gen_instruction_data_impl(registry: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fmt.indent(|fmt| {
|
||||
fmt.line("match *self {");
|
||||
fmt.indent(|fmt| {
|
||||
for format in registry.iter() {
|
||||
for format in formats {
|
||||
let name = format!("InstructionData::{}", format.name);
|
||||
let mut members = vec!["opcode"];
|
||||
|
||||
@@ -1037,7 +1035,11 @@ fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Fo
|
||||
}
|
||||
|
||||
/// Generate a Builder trait with methods for all instructions.
|
||||
fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &mut Formatter) {
|
||||
fn gen_builder(
|
||||
instructions: &AllInstructions,
|
||||
formats: &Vec<&InstructionFormat>,
|
||||
fmt: &mut Formatter,
|
||||
) {
|
||||
fmt.doc_comment(
|
||||
r#"
|
||||
Convenience methods for building instructions.
|
||||
@@ -1060,7 +1062,7 @@ fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &m
|
||||
for inst in instructions.values() {
|
||||
gen_inst_builder(inst, &*inst.format, fmt);
|
||||
}
|
||||
for format in formats.iter() {
|
||||
for format in formats {
|
||||
gen_format_constructor(format, fmt);
|
||||
}
|
||||
});
|
||||
@@ -1068,20 +1070,18 @@ fn gen_builder(instructions: &AllInstructions, formats: &FormatRegistry, fmt: &m
|
||||
}
|
||||
|
||||
pub(crate) fn generate(
|
||||
shared_defs: &SharedDefinitions,
|
||||
formats: Vec<&InstructionFormat>,
|
||||
all_inst: &AllInstructions,
|
||||
opcode_filename: &str,
|
||||
inst_builder_filename: &str,
|
||||
out_dir: &str,
|
||||
) -> Result<(), error::Error> {
|
||||
let format_registry = &shared_defs.format_registry;
|
||||
let all_inst = &shared_defs.all_instructions;
|
||||
|
||||
// Opcodes.
|
||||
let mut fmt = Formatter::new();
|
||||
gen_formats(format_registry, &mut fmt);
|
||||
gen_instruction_data(format_registry, &mut fmt);
|
||||
gen_formats(&formats, &mut fmt);
|
||||
gen_instruction_data(&formats, &mut fmt);
|
||||
fmt.empty_line();
|
||||
gen_instruction_data_impl(format_registry, &mut fmt);
|
||||
gen_instruction_data_impl(&formats, &mut fmt);
|
||||
fmt.empty_line();
|
||||
gen_opcodes(all_inst, &mut fmt);
|
||||
gen_type_constraints(all_inst, &mut fmt);
|
||||
@@ -1089,7 +1089,7 @@ pub(crate) fn generate(
|
||||
|
||||
// Instruction builder.
|
||||
let mut fmt = Formatter::new();
|
||||
gen_builder(all_inst, format_registry, &mut fmt);
|
||||
gen_builder(all_inst, &formats, &mut fmt);
|
||||
fmt.update_file(inst_builder_filename, out_dir)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -53,11 +53,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
|
||||
let settings = define_settings(&shared_defs.settings);
|
||||
let regs = define_regs();
|
||||
|
||||
let inst_group = InstructionGroupBuilder::new(
|
||||
&mut shared_defs.all_instructions,
|
||||
&shared_defs.format_registry,
|
||||
)
|
||||
.build();
|
||||
let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build();
|
||||
|
||||
// CPU modes for 32-bit ARM and Thumb2.
|
||||
let mut a32 = CpuMode::new("A32");
|
||||
|
||||
@@ -49,11 +49,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
|
||||
let settings = define_settings(&shared_defs.settings);
|
||||
let regs = define_registers();
|
||||
|
||||
let inst_group = InstructionGroupBuilder::new(
|
||||
&mut shared_defs.all_instructions,
|
||||
&shared_defs.format_registry,
|
||||
)
|
||||
.build();
|
||||
let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build();
|
||||
|
||||
let mut a64 = CpuMode::new("A64");
|
||||
|
||||
|
||||
@@ -89,11 +89,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
|
||||
let settings = define_settings(&shared_defs.settings);
|
||||
let regs = define_registers();
|
||||
|
||||
let inst_group = InstructionGroupBuilder::new(
|
||||
&mut shared_defs.all_instructions,
|
||||
&shared_defs.format_registry,
|
||||
)
|
||||
.build();
|
||||
let inst_group = InstructionGroupBuilder::new(&mut shared_defs.all_instructions).build();
|
||||
|
||||
// CPU modes for 32-bit and 64-bit operation.
|
||||
let mut rv_32 = CpuMode::new("RV32");
|
||||
|
||||
@@ -46,23 +46,7 @@ impl RecipeGroup {
|
||||
}
|
||||
|
||||
pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeGroup {
|
||||
// Format shorthands.
|
||||
let formats = &shared_defs.format_registry;
|
||||
|
||||
let f_binary = formats.by_name("Binary");
|
||||
let f_binary_imm = formats.by_name("BinaryImm");
|
||||
let f_branch = formats.by_name("Branch");
|
||||
let f_branch_icmp = formats.by_name("BranchIcmp");
|
||||
let f_call = formats.by_name("Call");
|
||||
let f_call_indirect = formats.by_name("CallIndirect");
|
||||
let f_copy_to_ssa = formats.by_name("CopyToSsa");
|
||||
let f_int_compare = formats.by_name("IntCompare");
|
||||
let f_int_compare_imm = formats.by_name("IntCompareImm");
|
||||
let f_jump = formats.by_name("Jump");
|
||||
let f_multiary = formats.by_name("MultiAry");
|
||||
let f_regmove = formats.by_name("RegMove");
|
||||
let f_unary = formats.by_name("Unary");
|
||||
let f_unary_imm = formats.by_name("UnaryImm");
|
||||
let formats = &shared_defs.formats;
|
||||
|
||||
// Register classes shorthands.
|
||||
let gpr = regs.class_by_name("GPR");
|
||||
@@ -73,7 +57,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
// R-type 32-bit instructions: These are mostly binary arithmetic instructions.
|
||||
// The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8)
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("R", f_binary, 4)
|
||||
EncodingRecipeBuilder::new("R", &formats.binary, 4)
|
||||
.operands_in(vec![gpr, gpr])
|
||||
.operands_out(vec![gpr])
|
||||
.emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"),
|
||||
@@ -81,7 +65,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// R-type with an immediate shift amount instead of rs2.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Rshamt", f_binary_imm, 4)
|
||||
EncodingRecipeBuilder::new("Rshamt", &formats.binary_imm, 4)
|
||||
.operands_in(vec![gpr])
|
||||
.operands_out(vec![gpr])
|
||||
.emit("put_rshamt(bits, in_reg0, imm.into(), out_reg0, sink);"),
|
||||
@@ -89,18 +73,18 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// R-type encoding of an integer comparison.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Ricmp", f_int_compare, 4)
|
||||
EncodingRecipeBuilder::new("Ricmp", &formats.int_compare, 4)
|
||||
.operands_in(vec![gpr, gpr])
|
||||
.operands_out(vec![gpr])
|
||||
.emit("put_r(bits, in_reg0, in_reg1, out_reg0, sink);"),
|
||||
);
|
||||
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Ii", f_binary_imm, 4)
|
||||
EncodingRecipeBuilder::new("Ii", &formats.binary_imm, 4)
|
||||
.operands_in(vec![gpr])
|
||||
.operands_out(vec![gpr])
|
||||
.inst_predicate(InstructionPredicate::new_is_signed_int(
|
||||
&*f_binary_imm,
|
||||
&*formats.binary_imm,
|
||||
"imm",
|
||||
12,
|
||||
0,
|
||||
@@ -110,10 +94,10 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// I-type instruction with a hardcoded %x0 rs1.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Iz", f_unary_imm, 4)
|
||||
EncodingRecipeBuilder::new("Iz", &formats.unary_imm, 4)
|
||||
.operands_out(vec![gpr])
|
||||
.inst_predicate(InstructionPredicate::new_is_signed_int(
|
||||
&*f_unary_imm,
|
||||
&*&formats.unary_imm,
|
||||
"imm",
|
||||
12,
|
||||
0,
|
||||
@@ -123,11 +107,11 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// I-type encoding of an integer comparison.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Iicmp", f_int_compare_imm, 4)
|
||||
EncodingRecipeBuilder::new("Iicmp", &formats.int_compare_imm, 4)
|
||||
.operands_in(vec![gpr])
|
||||
.operands_out(vec![gpr])
|
||||
.inst_predicate(InstructionPredicate::new_is_signed_int(
|
||||
&*f_int_compare_imm,
|
||||
&*&formats.int_compare_imm,
|
||||
"imm",
|
||||
12,
|
||||
0,
|
||||
@@ -137,7 +121,8 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// I-type encoding for `jalr` as a return instruction. We won't use the immediate offset. The
|
||||
// variable return values are not encoded.
|
||||
recipes.push(EncodingRecipeBuilder::new("Iret", f_multiary, 4).emit(
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Iret", &formats.multiary, 4).emit(
|
||||
r#"
|
||||
// Return instructions are always a jalr to %x1.
|
||||
// The return address is provided as a special-purpose link argument.
|
||||
@@ -149,11 +134,12 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
sink,
|
||||
);
|
||||
"#,
|
||||
));
|
||||
),
|
||||
);
|
||||
|
||||
// I-type encoding for `jalr` as a call_indirect.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Icall", f_call_indirect, 4)
|
||||
EncodingRecipeBuilder::new("Icall", &formats.call_indirect, 4)
|
||||
.operands_in(vec![gpr])
|
||||
.emit(
|
||||
r#"
|
||||
@@ -171,7 +157,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// Copy of a GPR is implemented as addi x, 0.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Icopy", f_unary, 4)
|
||||
EncodingRecipeBuilder::new("Icopy", &formats.unary, 4)
|
||||
.operands_in(vec![gpr])
|
||||
.operands_out(vec![gpr])
|
||||
.emit("put_i(bits, in_reg0, 0, out_reg0, sink);"),
|
||||
@@ -179,14 +165,14 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// Same for a GPR regmove.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("Irmov", f_regmove, 4)
|
||||
EncodingRecipeBuilder::new("Irmov", &formats.reg_move, 4)
|
||||
.operands_in(vec![gpr])
|
||||
.emit("put_i(bits, src, 0, dst, sink);"),
|
||||
);
|
||||
|
||||
// Same for copy-to-SSA -- GPR regmove.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("copytossa", f_copy_to_ssa, 4)
|
||||
EncodingRecipeBuilder::new("copytossa", &formats.copy_to_ssa, 4)
|
||||
// No operands_in to mention, because a source register is specified directly.
|
||||
.operands_out(vec![gpr])
|
||||
.emit("put_i(bits, src, 0, out_reg0, sink);"),
|
||||
@@ -194,10 +180,10 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// U-type instructions have a 20-bit immediate that targets bits 12-31.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("U", f_unary_imm, 4)
|
||||
EncodingRecipeBuilder::new("U", &formats.unary_imm, 4)
|
||||
.operands_out(vec![gpr])
|
||||
.inst_predicate(InstructionPredicate::new_is_signed_int(
|
||||
&*f_unary_imm,
|
||||
&*&formats.unary_imm,
|
||||
"imm",
|
||||
32,
|
||||
12,
|
||||
@@ -207,7 +193,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// UJ-type unconditional branch instructions.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("UJ", f_jump, 4)
|
||||
EncodingRecipeBuilder::new("UJ", &formats.jump, 4)
|
||||
.branch_range((0, 21))
|
||||
.emit(
|
||||
r#"
|
||||
@@ -218,7 +204,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
),
|
||||
);
|
||||
|
||||
recipes.push(EncodingRecipeBuilder::new("UJcall", f_call, 4).emit(
|
||||
recipes.push(EncodingRecipeBuilder::new("UJcall", &formats.call, 4).emit(
|
||||
r#"
|
||||
sink.reloc_external(Reloc::RiscvCall,
|
||||
&func.dfg.ext_funcs[func_ref].name,
|
||||
@@ -230,7 +216,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// SB-type branch instructions.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("SB", f_branch_icmp, 4)
|
||||
EncodingRecipeBuilder::new("SB", &formats.branch_icmp, 4)
|
||||
.operands_in(vec![gpr, gpr])
|
||||
.branch_range((0, 13))
|
||||
.emit(
|
||||
@@ -244,7 +230,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// SB-type branch instruction with rs2 fixed to zero.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("SBzero", f_branch, 4)
|
||||
EncodingRecipeBuilder::new("SBzero", &formats.branch, 4)
|
||||
.operands_in(vec![gpr])
|
||||
.branch_range((0, 13))
|
||||
.emit(
|
||||
@@ -258,7 +244,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// Spill of a GPR.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("GPsp", f_unary, 4)
|
||||
EncodingRecipeBuilder::new("GPsp", &formats.unary, 4)
|
||||
.operands_in(vec![gpr])
|
||||
.operands_out(vec![Stack::new(gpr)])
|
||||
.emit("unimplemented!();"),
|
||||
@@ -266,7 +252,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// Fill of a GPR.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("GPfi", f_unary, 4)
|
||||
EncodingRecipeBuilder::new("GPfi", &formats.unary, 4)
|
||||
.operands_in(vec![Stack::new(gpr)])
|
||||
.operands_out(vec![gpr])
|
||||
.emit("unimplemented!();"),
|
||||
@@ -274,7 +260,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// Stack-slot to same stack-slot copy, which is guaranteed to turn into a no-op.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("stacknull", f_unary, 0)
|
||||
EncodingRecipeBuilder::new("stacknull", &formats.unary, 0)
|
||||
.operands_in(vec![Stack::new(gpr)])
|
||||
.operands_out(vec![Stack::new(gpr)])
|
||||
.emit(""),
|
||||
@@ -282,7 +268,7 @@ pub(crate) fn define(shared_defs: &SharedDefinitions, regs: &IsaRegs) -> RecipeG
|
||||
|
||||
// No-op fills, created by late-stage redundant-fill removal.
|
||||
recipes.push(
|
||||
EncodingRecipeBuilder::new("fillnull", f_unary, 0)
|
||||
EncodingRecipeBuilder::new("fillnull", &formats.unary, 0)
|
||||
.operands_in(vec![Stack::new(gpr)])
|
||||
.operands_out(vec![gpr])
|
||||
.clobbers_flags(false)
|
||||
|
||||
@@ -374,7 +374,7 @@ pub(crate) fn define(
|
||||
r: &RecipeGroup,
|
||||
) -> PerCpuModeEncodings {
|
||||
let shared = &shared_defs.instructions;
|
||||
let formats = &shared_defs.format_registry;
|
||||
let formats = &shared_defs.formats;
|
||||
|
||||
// Shorthands for instructions.
|
||||
let adjust_sp_down = shared.by_name("adjust_sp_down");
|
||||
@@ -774,8 +774,8 @@ pub(crate) fn define(
|
||||
e.enc64(iconst.bind(I32), rec_pu_id.opcodes(&MOV_IMM));
|
||||
|
||||
// The 32-bit immediate movl also zero-extends to 64 bits.
|
||||
let f_unary_imm = formats.by_name("UnaryImm");
|
||||
let is_unsigned_int32 = InstructionPredicate::new_is_unsigned_int(&*f_unary_imm, "imm", 32, 0);
|
||||
let is_unsigned_int32 =
|
||||
InstructionPredicate::new_is_unsigned_int(&*formats.unary_imm, "imm", 32, 0);
|
||||
|
||||
e.enc64_func(
|
||||
iconst.bind(I64),
|
||||
@@ -801,7 +801,7 @@ pub(crate) fn define(
|
||||
}
|
||||
e.enc64(bconst.bind(B64), rec_pu_id_bool.opcodes(&MOV_IMM).rex());
|
||||
|
||||
let is_zero_int = InstructionPredicate::new_is_zero_int(f_unary_imm, "imm");
|
||||
let is_zero_int = InstructionPredicate::new_is_zero_int(&formats.unary_imm, "imm");
|
||||
e.enc_both_instp(
|
||||
iconst.bind(I8),
|
||||
rec_u_id_z.opcodes(&XORB),
|
||||
@@ -880,8 +880,8 @@ pub(crate) fn define(
|
||||
e.enc64_isap(ctz.bind(I32), rec_urm.opcodes(&TZCNT), use_bmi1);
|
||||
|
||||
// Loads and stores.
|
||||
let f_load_complex = formats.by_name("LoadComplex");
|
||||
let is_load_complex_length_two = InstructionPredicate::new_length_equals(&*f_load_complex, 2);
|
||||
let is_load_complex_length_two =
|
||||
InstructionPredicate::new_length_equals(&*formats.load_complex, 2);
|
||||
|
||||
for recipe in &[rec_ldWithIndex, rec_ldWithIndexDisp8, rec_ldWithIndexDisp32] {
|
||||
e.enc_i32_i64_instp(
|
||||
@@ -925,9 +925,8 @@ pub(crate) fn define(
|
||||
);
|
||||
}
|
||||
|
||||
let f_store_complex = formats.by_name("StoreComplex");
|
||||
let is_store_complex_length_three =
|
||||
InstructionPredicate::new_length_equals(&*f_store_complex, 3);
|
||||
InstructionPredicate::new_length_equals(&*formats.store_complex, 3);
|
||||
|
||||
for recipe in &[rec_stWithIndex, rec_stWithIndexDisp8, rec_stWithIndexDisp32] {
|
||||
e.enc_i32_i64_instp(
|
||||
@@ -1233,8 +1232,8 @@ pub(crate) fn define(
|
||||
);
|
||||
|
||||
// 64-bit, colocated, both PIC and non-PIC. Use the lea instruction's pc-relative field.
|
||||
let f_func_addr = formats.by_name("FuncAddr");
|
||||
let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*f_func_addr, "func_ref");
|
||||
let is_colocated_func =
|
||||
InstructionPredicate::new_is_colocated_func(&*formats.func_addr, "func_ref");
|
||||
e.enc64_instp(
|
||||
func_addr.bind(I64),
|
||||
rec_pcrel_fnaddr8.opcodes(&LEA).rex().w(),
|
||||
@@ -1293,8 +1292,7 @@ pub(crate) fn define(
|
||||
e.enc32(call, rec_call_id.opcodes(&CALL_RELATIVE));
|
||||
|
||||
// 64-bit, colocated, both PIC and non-PIC. Use the call instruction's pc-relative field.
|
||||
let f_call = formats.by_name("Call");
|
||||
let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*f_call, "func_ref");
|
||||
let is_colocated_func = InstructionPredicate::new_is_colocated_func(&*formats.call, "func_ref");
|
||||
e.enc64_instp(call, rec_call_id.opcodes(&CALL_RELATIVE), is_colocated_func);
|
||||
|
||||
// 64-bit, non-colocated, PIC. There is no 64-bit non-colocated non-PIC version, since non-PIC
|
||||
@@ -1564,18 +1562,16 @@ pub(crate) fn define(
|
||||
|
||||
// Floating-point constants equal to 0.0 can be encoded using either `xorps` or `xorpd`, for
|
||||
// 32-bit and 64-bit floats respectively.
|
||||
let f_unary_ieee32 = formats.by_name("UnaryIeee32");
|
||||
let is_zero_32_bit_float =
|
||||
InstructionPredicate::new_is_zero_32bit_float(&*f_unary_ieee32, "imm");
|
||||
InstructionPredicate::new_is_zero_32bit_float(&*formats.unary_ieee32, "imm");
|
||||
e.enc32_instp(
|
||||
f32const,
|
||||
rec_f32imm_z.opcodes(&XORPS),
|
||||
is_zero_32_bit_float.clone(),
|
||||
);
|
||||
|
||||
let f_unary_ieee64 = formats.by_name("UnaryIeee64");
|
||||
let is_zero_64_bit_float =
|
||||
InstructionPredicate::new_is_zero_64bit_float(&*f_unary_ieee64, "imm");
|
||||
InstructionPredicate::new_is_zero_64bit_float(&*formats.unary_ieee64, "imm");
|
||||
e.enc32_instp(
|
||||
f64const,
|
||||
rec_f64imm_z.opcodes(&XORPD),
|
||||
@@ -1847,18 +1843,17 @@ pub(crate) fn define(
|
||||
// this must be encoded prior to the MOVUPS implementation (below) so the compiler sees this
|
||||
// encoding first
|
||||
for ty in ValueType::all_lane_types().filter(allowed_simd_type) {
|
||||
let f_unary_const = formats.by_name("UnaryConst");
|
||||
let instruction = vconst.bind(vector(ty, sse_vector_size));
|
||||
|
||||
let is_zero_128bit =
|
||||
InstructionPredicate::new_is_all_zeroes(&*f_unary_const, "constant_handle");
|
||||
InstructionPredicate::new_is_all_zeroes(&*formats.unary_const, "constant_handle");
|
||||
let template = rec_vconst_optimized.nonrex().opcodes(&PXOR);
|
||||
e.enc_32_64_func(instruction.clone(), template, |builder| {
|
||||
builder.inst_predicate(is_zero_128bit)
|
||||
});
|
||||
|
||||
let is_ones_128bit =
|
||||
InstructionPredicate::new_is_all_ones(&*f_unary_const, "constant_handle");
|
||||
InstructionPredicate::new_is_all_ones(&*formats.unary_const, "constant_handle");
|
||||
let template = rec_vconst_optimized.nonrex().opcodes(&PCMPEQB);
|
||||
e.enc_32_64_func(instruction, template, |builder| {
|
||||
builder.inst_predicate(is_ones_128bit)
|
||||
@@ -2038,9 +2033,11 @@ pub(crate) fn define(
|
||||
};
|
||||
|
||||
let instruction = icmp.bind(vector(ty, sse_vector_size));
|
||||
let f_int_compare = formats.by_name("IntCompare");
|
||||
let has_eq_condition_code =
|
||||
InstructionPredicate::new_has_condition_code(&*f_int_compare, IntCC::Equal, "cond");
|
||||
let has_eq_condition_code = InstructionPredicate::new_has_condition_code(
|
||||
&*formats.int_compare,
|
||||
IntCC::Equal,
|
||||
"cond",
|
||||
);
|
||||
let template = rec_icscc_fpr.nonrex().opcodes(opcodes);
|
||||
e.enc_32_64_func(instruction, template, |builder| {
|
||||
let builder = builder.inst_predicate(has_eq_condition_code);
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::cdsl::formats::FormatRegistry;
|
||||
use crate::cdsl::instructions::{
|
||||
AllInstructions, InstructionBuilder as Inst, InstructionGroup, InstructionGroupBuilder,
|
||||
};
|
||||
use crate::cdsl::operands::{create_operand as operand, create_operand_doc as operand_doc};
|
||||
use crate::cdsl::types::ValueType;
|
||||
use crate::cdsl::typevar::{Interval, TypeSetBuilder, TypeVar};
|
||||
|
||||
use crate::shared::formats::Formats;
|
||||
use crate::shared::immediates::Immediates;
|
||||
use crate::shared::types;
|
||||
|
||||
pub(crate) fn define(
|
||||
mut all_instructions: &mut AllInstructions,
|
||||
format_registry: &FormatRegistry,
|
||||
formats: &Formats,
|
||||
immediates: &Immediates,
|
||||
) -> InstructionGroup {
|
||||
let mut ig = InstructionGroupBuilder::new(&mut all_instructions, format_registry);
|
||||
let mut ig = InstructionGroupBuilder::new(&mut all_instructions);
|
||||
|
||||
let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into();
|
||||
|
||||
@@ -43,6 +44,7 @@ pub(crate) fn define(
|
||||
|
||||
Return both quotient and remainder.
|
||||
"#,
|
||||
&formats.ternary,
|
||||
)
|
||||
.operands_in(vec![nlo, nhi, d])
|
||||
.operands_out(vec![q, r])
|
||||
@@ -62,6 +64,7 @@ pub(crate) fn define(
|
||||
|
||||
Return both quotient and remainder.
|
||||
"#,
|
||||
&formats.ternary,
|
||||
)
|
||||
.operands_in(vec![nlo, nhi, d])
|
||||
.operands_out(vec![q, r])
|
||||
@@ -82,6 +85,7 @@ pub(crate) fn define(
|
||||
Polymorphic over all scalar integer types, but does not support vector
|
||||
types.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![argL, argR])
|
||||
.operands_out(vec![resLo, resHi]),
|
||||
@@ -96,6 +100,7 @@ pub(crate) fn define(
|
||||
Polymorphic over all scalar integer types, but does not support vector
|
||||
types.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![argL, argR])
|
||||
.operands_out(vec![resLo, resHi]),
|
||||
@@ -132,6 +137,7 @@ pub(crate) fn define(
|
||||
|
||||
This instruction does not trap.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![a]),
|
||||
@@ -154,6 +160,7 @@ pub(crate) fn define(
|
||||
When the two operands don't compare as LT, `y` is returned unchanged,
|
||||
even if it is a signalling NaN.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
@@ -172,6 +179,7 @@ pub(crate) fn define(
|
||||
When the two operands don't compare as GT, `y` is returned unchanged,
|
||||
even if it is a signalling NaN.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
@@ -190,6 +198,7 @@ pub(crate) fn define(
|
||||
This is polymorphic in i32 and i64. However, it is only implemented for i64
|
||||
in 64-bit mode, and only for i32 in 32-bit mode.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.other_side_effects(true)
|
||||
@@ -208,6 +217,7 @@ pub(crate) fn define(
|
||||
This is polymorphic in i32 and i64. However, it is only implemented for i64
|
||||
in 64-bit mode, and only for i32 in 32-bit mode.
|
||||
"#,
|
||||
&formats.nullary,
|
||||
)
|
||||
.operands_out(vec![x])
|
||||
.other_side_effects(true)
|
||||
@@ -229,6 +239,7 @@ pub(crate) fn define(
|
||||
This is polymorphic in i32 and i64. It is implemented for both i64 and
|
||||
i32 in 64-bit mode, and only for i32 in 32-bit mode.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![y, rflags]),
|
||||
@@ -241,6 +252,7 @@ pub(crate) fn define(
|
||||
Bit Scan Forwards -- returns the bit-index of the least significant 1
|
||||
in the word. Is otherwise identical to 'bsr', just above.
|
||||
"#,
|
||||
&formats.unary,
|
||||
)
|
||||
.operands_in(vec![x])
|
||||
.operands_out(vec![y, rflags]),
|
||||
@@ -269,6 +281,7 @@ pub(crate) fn define(
|
||||
Packed Shuffle Doublewords -- copies data from either memory or lanes in an extended
|
||||
register and re-orders the data according to the passed immediate byte.
|
||||
"#,
|
||||
&formats.extract_lane,
|
||||
)
|
||||
.operands_in(vec![a, i]) // TODO allow copying from memory here (need more permissive type than TxN)
|
||||
.operands_out(vec![a]),
|
||||
@@ -281,6 +294,7 @@ pub(crate) fn define(
|
||||
Packed Shuffle Bytes -- re-orders data in an extended register using a shuffle
|
||||
mask from either memory or another extended register
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![a, b]) // TODO allow re-ordering from memory here (need more permissive type than TxN)
|
||||
.operands_out(vec![a]),
|
||||
@@ -298,6 +312,7 @@ pub(crate) fn define(
|
||||
The lane index, ``Idx``, is an immediate value, not an SSA value. It
|
||||
must indicate a valid lane index for the type of ``x``.
|
||||
"#,
|
||||
&formats.extract_lane,
|
||||
)
|
||||
.operands_in(vec![x, Idx])
|
||||
.operands_out(vec![a]),
|
||||
@@ -325,6 +340,7 @@ pub(crate) fn define(
|
||||
The lane index, ``Idx``, is an immediate value, not an SSA value. It
|
||||
must indicate a valid lane index for the type of ``x``.
|
||||
"#,
|
||||
&formats.insert_lane,
|
||||
)
|
||||
.operands_in(vec![x, Idx, y])
|
||||
.operands_out(vec![a]),
|
||||
@@ -351,6 +367,7 @@ pub(crate) fn define(
|
||||
extracted from and which it is inserted to. This is similar to x86_pinsr but inserts
|
||||
floats, which are already stored in an XMM register.
|
||||
"#,
|
||||
&formats.insert_lane,
|
||||
)
|
||||
.operands_in(vec![x, Idx, y])
|
||||
.operands_out(vec![a]),
|
||||
@@ -366,6 +383,7 @@ pub(crate) fn define(
|
||||
r#"
|
||||
Move the low 64 bits of the float vector ``y`` to the low 64 bits of float vector ``x``
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
@@ -377,6 +395,7 @@ pub(crate) fn define(
|
||||
r#"
|
||||
Move the low 64 bits of the float vector ``y`` to the high 64 bits of float vector ``x``
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
@@ -401,9 +420,11 @@ pub(crate) fn define(
|
||||
.includes_scalars(false)
|
||||
.build(),
|
||||
);
|
||||
|
||||
let x = &operand_doc("x", IxN, "Vector value to shift");
|
||||
let y = &operand_doc("y", I64x2, "Number of bits to shift");
|
||||
let a = &operand("a", IxN);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"x86_psll",
|
||||
@@ -412,10 +433,12 @@ pub(crate) fn define(
|
||||
``ishl`` but alters the shift operand to live in an XMM register as expected by the PSLL*
|
||||
family of instructions.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"x86_psrl",
|
||||
@@ -424,10 +447,12 @@ pub(crate) fn define(
|
||||
``ushr`` but alters the shift operand to live in an XMM register as expected by the PSRL*
|
||||
family of instructions.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
);
|
||||
|
||||
ig.push(
|
||||
Inst::new(
|
||||
"x86_psra",
|
||||
@@ -436,6 +461,7 @@ pub(crate) fn define(
|
||||
instruction ``sshr`` but alters the shift operand to live in an XMM register as expected by
|
||||
the PSRA* family of instructions.
|
||||
"#,
|
||||
&formats.binary,
|
||||
)
|
||||
.operands_in(vec![x, y])
|
||||
.operands_out(vec![a]),
|
||||
|
||||
@@ -20,7 +20,7 @@ pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
|
||||
|
||||
let inst_group = instructions::define(
|
||||
&mut shared_defs.all_instructions,
|
||||
&shared_defs.format_registry,
|
||||
&shared_defs.formats,
|
||||
&shared_defs.imm,
|
||||
);
|
||||
legalize::define(shared_defs, &inst_group);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,8 @@ pub fn isa_from_arch(arch: &str) -> Result<isa::Isa, String> {
|
||||
|
||||
/// Generates all the Rust source files used in Cranelift from the meta-language.
|
||||
pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error> {
|
||||
// Common definitions.
|
||||
// Create all the definitions:
|
||||
// - common definitions.
|
||||
let mut shared_defs = shared::define();
|
||||
|
||||
gen_settings::generate(
|
||||
@@ -34,10 +35,20 @@ pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error>
|
||||
)?;
|
||||
gen_types::generate("types.rs", &out_dir)?;
|
||||
|
||||
// Per ISA definitions.
|
||||
// - per ISA definitions.
|
||||
let isas = isa::define(isas, &mut shared_defs);
|
||||
|
||||
gen_inst::generate(&shared_defs, "opcodes.rs", "inst_builder.rs", &out_dir)?;
|
||||
// At this point, all definitions are done.
|
||||
let all_formats = shared_defs.verify_instruction_formats();
|
||||
|
||||
// Generate all the code.
|
||||
gen_inst::generate(
|
||||
all_formats,
|
||||
&shared_defs.all_instructions,
|
||||
"opcodes.rs",
|
||||
"inst_builder.rs",
|
||||
&out_dir,
|
||||
)?;
|
||||
|
||||
gen_legalizer::generate(&isas, &shared_defs.transform_groups, "legalize", &out_dir)?;
|
||||
|
||||
|
||||
@@ -1,226 +1,303 @@
|
||||
use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder as Builder};
|
||||
use crate::cdsl::formats::{InstructionFormat, InstructionFormatBuilder as Builder};
|
||||
use crate::shared::{entities::EntityRefs, immediates::Immediates};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(crate) fn define(imm: &Immediates, entities: &EntityRefs) -> FormatRegistry {
|
||||
let mut registry = FormatRegistry::new();
|
||||
pub(crate) struct Formats {
|
||||
pub(crate) binary: Rc<InstructionFormat>,
|
||||
pub(crate) binary_imm: Rc<InstructionFormat>,
|
||||
pub(crate) branch: Rc<InstructionFormat>,
|
||||
pub(crate) branch_float: Rc<InstructionFormat>,
|
||||
pub(crate) branch_icmp: Rc<InstructionFormat>,
|
||||
pub(crate) branch_int: Rc<InstructionFormat>,
|
||||
pub(crate) branch_table: Rc<InstructionFormat>,
|
||||
pub(crate) branch_table_base: Rc<InstructionFormat>,
|
||||
pub(crate) branch_table_entry: Rc<InstructionFormat>,
|
||||
pub(crate) call: Rc<InstructionFormat>,
|
||||
pub(crate) call_indirect: Rc<InstructionFormat>,
|
||||
pub(crate) cond_trap: Rc<InstructionFormat>,
|
||||
pub(crate) copy_special: Rc<InstructionFormat>,
|
||||
pub(crate) copy_to_ssa: Rc<InstructionFormat>,
|
||||
pub(crate) extract_lane: Rc<InstructionFormat>,
|
||||
pub(crate) float_compare: Rc<InstructionFormat>,
|
||||
pub(crate) float_cond: Rc<InstructionFormat>,
|
||||
pub(crate) float_cond_trap: Rc<InstructionFormat>,
|
||||
pub(crate) func_addr: Rc<InstructionFormat>,
|
||||
pub(crate) heap_addr: Rc<InstructionFormat>,
|
||||
pub(crate) indirect_jump: Rc<InstructionFormat>,
|
||||
pub(crate) insert_lane: Rc<InstructionFormat>,
|
||||
pub(crate) int_compare: Rc<InstructionFormat>,
|
||||
pub(crate) int_compare_imm: Rc<InstructionFormat>,
|
||||
pub(crate) int_cond: Rc<InstructionFormat>,
|
||||
pub(crate) int_cond_trap: Rc<InstructionFormat>,
|
||||
pub(crate) int_select: Rc<InstructionFormat>,
|
||||
pub(crate) jump: Rc<InstructionFormat>,
|
||||
pub(crate) load: Rc<InstructionFormat>,
|
||||
pub(crate) load_complex: Rc<InstructionFormat>,
|
||||
pub(crate) multiary: Rc<InstructionFormat>,
|
||||
pub(crate) nullary: Rc<InstructionFormat>,
|
||||
pub(crate) reg_fill: Rc<InstructionFormat>,
|
||||
pub(crate) reg_move: Rc<InstructionFormat>,
|
||||
pub(crate) reg_spill: Rc<InstructionFormat>,
|
||||
pub(crate) shuffle: Rc<InstructionFormat>,
|
||||
pub(crate) stack_load: Rc<InstructionFormat>,
|
||||
pub(crate) stack_store: Rc<InstructionFormat>,
|
||||
pub(crate) store: Rc<InstructionFormat>,
|
||||
pub(crate) store_complex: Rc<InstructionFormat>,
|
||||
pub(crate) table_addr: Rc<InstructionFormat>,
|
||||
pub(crate) ternary: Rc<InstructionFormat>,
|
||||
pub(crate) trap: Rc<InstructionFormat>,
|
||||
pub(crate) unary: Rc<InstructionFormat>,
|
||||
pub(crate) unary_bool: Rc<InstructionFormat>,
|
||||
pub(crate) unary_const: Rc<InstructionFormat>,
|
||||
pub(crate) unary_global_value: Rc<InstructionFormat>,
|
||||
pub(crate) unary_ieee32: Rc<InstructionFormat>,
|
||||
pub(crate) unary_ieee64: Rc<InstructionFormat>,
|
||||
pub(crate) unary_imm: Rc<InstructionFormat>,
|
||||
}
|
||||
|
||||
registry.insert(Builder::new("Unary").value());
|
||||
registry.insert(Builder::new("UnaryImm").imm(&imm.imm64));
|
||||
registry.insert(Builder::new("UnaryIeee32").imm(&imm.ieee32));
|
||||
registry.insert(Builder::new("UnaryIeee64").imm(&imm.ieee64));
|
||||
registry.insert(Builder::new("UnaryBool").imm(&imm.boolean));
|
||||
registry.insert(Builder::new("UnaryConst").imm(&imm.pool_constant));
|
||||
registry.insert(Builder::new("UnaryGlobalValue").imm(&entities.global_value));
|
||||
impl Formats {
|
||||
pub fn new(imm: &Immediates, entities: &EntityRefs) -> Self {
|
||||
Self {
|
||||
unary: Builder::new("Unary").value().build(),
|
||||
|
||||
registry.insert(Builder::new("Binary").value().value());
|
||||
registry.insert(Builder::new("BinaryImm").value().imm(&imm.imm64));
|
||||
unary_imm: Builder::new("UnaryImm").imm(&imm.imm64).build(),
|
||||
|
||||
unary_ieee32: Builder::new("UnaryIeee32").imm(&imm.ieee32).build(),
|
||||
|
||||
unary_ieee64: Builder::new("UnaryIeee64").imm(&imm.ieee64).build(),
|
||||
|
||||
unary_bool: Builder::new("UnaryBool").imm(&imm.boolean).build(),
|
||||
|
||||
unary_const: Builder::new("UnaryConst").imm(&imm.pool_constant).build(),
|
||||
|
||||
unary_global_value: Builder::new("UnaryGlobalValue")
|
||||
.imm(&entities.global_value)
|
||||
.build(),
|
||||
|
||||
binary: Builder::new("Binary").value().value().build(),
|
||||
|
||||
binary_imm: Builder::new("BinaryImm").value().imm(&imm.imm64).build(),
|
||||
|
||||
// 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")
|
||||
ternary: Builder::new("Ternary")
|
||||
.value()
|
||||
.value()
|
||||
.value()
|
||||
.typevar_operand(1),
|
||||
);
|
||||
.typevar_operand(1)
|
||||
.build(),
|
||||
|
||||
// Catch-all for instructions with many outputs and inputs and no immediate
|
||||
// operands.
|
||||
registry.insert(Builder::new("MultiAry").varargs());
|
||||
multiary: Builder::new("MultiAry").varargs().build(),
|
||||
|
||||
registry.insert(Builder::new("NullAry"));
|
||||
nullary: Builder::new("NullAry").build(),
|
||||
|
||||
registry.insert(
|
||||
Builder::new("InsertLane")
|
||||
insert_lane: Builder::new("InsertLane")
|
||||
.value()
|
||||
.imm_with_name("lane", &imm.uimm8)
|
||||
.value(),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("ExtractLane")
|
||||
.value()
|
||||
.imm_with_name("lane", &imm.uimm8),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("Shuffle")
|
||||
.value()
|
||||
.value()
|
||||
.imm_with_name("mask", &imm.uimm128),
|
||||
);
|
||||
.build(),
|
||||
|
||||
registry.insert(Builder::new("IntCompare").imm(&imm.intcc).value().value());
|
||||
registry.insert(
|
||||
Builder::new("IntCompareImm")
|
||||
extract_lane: Builder::new("ExtractLane")
|
||||
.value()
|
||||
.imm_with_name("lane", &imm.uimm8)
|
||||
.build(),
|
||||
|
||||
shuffle: Builder::new("Shuffle")
|
||||
.value()
|
||||
.value()
|
||||
.imm_with_name("mask", &imm.uimm128)
|
||||
.build(),
|
||||
|
||||
int_compare: Builder::new("IntCompare")
|
||||
.imm(&imm.intcc)
|
||||
.value()
|
||||
.imm(&imm.imm64),
|
||||
);
|
||||
registry.insert(Builder::new("IntCond").imm(&imm.intcc).value());
|
||||
.value()
|
||||
.build(),
|
||||
|
||||
registry.insert(
|
||||
Builder::new("FloatCompare")
|
||||
int_compare_imm: Builder::new("IntCompareImm")
|
||||
.imm(&imm.intcc)
|
||||
.value()
|
||||
.imm(&imm.imm64)
|
||||
.build(),
|
||||
|
||||
int_cond: Builder::new("IntCond").imm(&imm.intcc).value().build(),
|
||||
|
||||
float_compare: Builder::new("FloatCompare")
|
||||
.imm(&imm.floatcc)
|
||||
.value()
|
||||
.value(),
|
||||
);
|
||||
registry.insert(Builder::new("FloatCond").imm(&imm.floatcc).value());
|
||||
.value()
|
||||
.build(),
|
||||
|
||||
registry.insert(
|
||||
Builder::new("IntSelect")
|
||||
float_cond: Builder::new("FloatCond").imm(&imm.floatcc).value().build(),
|
||||
|
||||
int_select: Builder::new("IntSelect")
|
||||
.imm(&imm.intcc)
|
||||
.value()
|
||||
.value()
|
||||
.value(),
|
||||
);
|
||||
.value()
|
||||
.build(),
|
||||
|
||||
registry.insert(Builder::new("Jump").imm(&entities.ebb).varargs());
|
||||
registry.insert(Builder::new("Branch").value().imm(&entities.ebb).varargs());
|
||||
registry.insert(
|
||||
Builder::new("BranchInt")
|
||||
jump: Builder::new("Jump").imm(&entities.ebb).varargs().build(),
|
||||
|
||||
branch: Builder::new("Branch")
|
||||
.value()
|
||||
.imm(&entities.ebb)
|
||||
.varargs()
|
||||
.build(),
|
||||
|
||||
branch_int: Builder::new("BranchInt")
|
||||
.imm(&imm.intcc)
|
||||
.value()
|
||||
.imm(&entities.ebb)
|
||||
.varargs(),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("BranchFloat")
|
||||
.varargs()
|
||||
.build(),
|
||||
|
||||
branch_float: Builder::new("BranchFloat")
|
||||
.imm(&imm.floatcc)
|
||||
.value()
|
||||
.imm(&entities.ebb)
|
||||
.varargs(),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("BranchIcmp")
|
||||
.varargs()
|
||||
.build(),
|
||||
|
||||
branch_icmp: Builder::new("BranchIcmp")
|
||||
.imm(&imm.intcc)
|
||||
.value()
|
||||
.value()
|
||||
.imm(&entities.ebb)
|
||||
.varargs(),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("BranchTable")
|
||||
.varargs()
|
||||
.build(),
|
||||
|
||||
branch_table: Builder::new("BranchTable")
|
||||
.value()
|
||||
.imm(&entities.ebb)
|
||||
.imm(&entities.jump_table),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("BranchTableEntry")
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
branch_table_entry: Builder::new("BranchTableEntry")
|
||||
.value()
|
||||
.value()
|
||||
.imm(&imm.uimm8)
|
||||
.imm(&entities.jump_table),
|
||||
);
|
||||
registry.insert(Builder::new("BranchTableBase").imm(&entities.jump_table));
|
||||
registry.insert(
|
||||
Builder::new("IndirectJump")
|
||||
.value()
|
||||
.imm(&entities.jump_table),
|
||||
);
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
registry.insert(Builder::new("Call").imm(&entities.func_ref).varargs());
|
||||
registry.insert(
|
||||
Builder::new("CallIndirect")
|
||||
branch_table_base: Builder::new("BranchTableBase")
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
indirect_jump: Builder::new("IndirectJump")
|
||||
.value()
|
||||
.imm(&entities.jump_table)
|
||||
.build(),
|
||||
|
||||
call: Builder::new("Call")
|
||||
.imm(&entities.func_ref)
|
||||
.varargs()
|
||||
.build(),
|
||||
|
||||
call_indirect: Builder::new("CallIndirect")
|
||||
.imm(&entities.sig_ref)
|
||||
.value()
|
||||
.varargs(),
|
||||
);
|
||||
registry.insert(Builder::new("FuncAddr").imm(&entities.func_ref));
|
||||
.varargs()
|
||||
.build(),
|
||||
|
||||
registry.insert(
|
||||
Builder::new("Load")
|
||||
func_addr: Builder::new("FuncAddr").imm(&entities.func_ref).build(),
|
||||
|
||||
load: Builder::new("Load")
|
||||
.imm(&imm.memflags)
|
||||
.value()
|
||||
.imm(&imm.offset32),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("LoadComplex")
|
||||
.imm(&imm.offset32)
|
||||
.build(),
|
||||
|
||||
load_complex: Builder::new("LoadComplex")
|
||||
.imm(&imm.memflags)
|
||||
.varargs()
|
||||
.imm(&imm.offset32),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("Store")
|
||||
.imm(&imm.offset32)
|
||||
.build(),
|
||||
|
||||
store: Builder::new("Store")
|
||||
.imm(&imm.memflags)
|
||||
.value()
|
||||
.value()
|
||||
.imm(&imm.offset32),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("StoreComplex")
|
||||
.imm(&imm.offset32)
|
||||
.build(),
|
||||
|
||||
store_complex: Builder::new("StoreComplex")
|
||||
.imm(&imm.memflags)
|
||||
.value()
|
||||
.varargs()
|
||||
.imm(&imm.offset32),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("StackLoad")
|
||||
.imm(&imm.offset32)
|
||||
.build(),
|
||||
|
||||
stack_load: Builder::new("StackLoad")
|
||||
.imm(&entities.stack_slot)
|
||||
.imm(&imm.offset32),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("StackStore")
|
||||
.imm(&imm.offset32)
|
||||
.build(),
|
||||
|
||||
stack_store: Builder::new("StackStore")
|
||||
.value()
|
||||
.imm(&entities.stack_slot)
|
||||
.imm(&imm.offset32),
|
||||
);
|
||||
.imm(&imm.offset32)
|
||||
.build(),
|
||||
|
||||
// Accessing a WebAssembly heap.
|
||||
registry.insert(
|
||||
Builder::new("HeapAddr")
|
||||
heap_addr: Builder::new("HeapAddr")
|
||||
.imm(&entities.heap)
|
||||
.value()
|
||||
.imm(&imm.uimm32),
|
||||
);
|
||||
.imm(&imm.uimm32)
|
||||
.build(),
|
||||
|
||||
// Accessing a WebAssembly table.
|
||||
registry.insert(
|
||||
Builder::new("TableAddr")
|
||||
table_addr: Builder::new("TableAddr")
|
||||
.imm(&entities.table)
|
||||
.value()
|
||||
.imm(&imm.offset32),
|
||||
);
|
||||
.imm(&imm.offset32)
|
||||
.build(),
|
||||
|
||||
registry.insert(
|
||||
Builder::new("RegMove")
|
||||
reg_move: Builder::new("RegMove")
|
||||
.value()
|
||||
.imm_with_name("src", &imm.regunit)
|
||||
.imm_with_name("dst", &imm.regunit),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("CopySpecial")
|
||||
.imm_with_name("dst", &imm.regunit)
|
||||
.build(),
|
||||
|
||||
copy_special: Builder::new("CopySpecial")
|
||||
.imm_with_name("src", &imm.regunit)
|
||||
.imm_with_name("dst", &imm.regunit),
|
||||
);
|
||||
registry.insert(Builder::new("CopyToSsa").imm_with_name("src", &imm.regunit));
|
||||
registry.insert(
|
||||
Builder::new("RegSpill")
|
||||
.imm_with_name("dst", &imm.regunit)
|
||||
.build(),
|
||||
|
||||
copy_to_ssa: Builder::new("CopyToSsa")
|
||||
.imm_with_name("src", &imm.regunit)
|
||||
.build(),
|
||||
|
||||
reg_spill: Builder::new("RegSpill")
|
||||
.value()
|
||||
.imm_with_name("src", &imm.regunit)
|
||||
.imm_with_name("dst", &entities.stack_slot),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("RegFill")
|
||||
.imm_with_name("dst", &entities.stack_slot)
|
||||
.build(),
|
||||
|
||||
reg_fill: Builder::new("RegFill")
|
||||
.value()
|
||||
.imm_with_name("src", &entities.stack_slot)
|
||||
.imm_with_name("dst", &imm.regunit),
|
||||
);
|
||||
.imm_with_name("dst", &imm.regunit)
|
||||
.build(),
|
||||
|
||||
registry.insert(Builder::new("Trap").imm(&imm.trapcode));
|
||||
registry.insert(Builder::new("CondTrap").value().imm(&imm.trapcode));
|
||||
registry.insert(
|
||||
Builder::new("IntCondTrap")
|
||||
trap: Builder::new("Trap").imm(&imm.trapcode).build(),
|
||||
|
||||
cond_trap: Builder::new("CondTrap").value().imm(&imm.trapcode).build(),
|
||||
|
||||
int_cond_trap: Builder::new("IntCondTrap")
|
||||
.imm(&imm.intcc)
|
||||
.value()
|
||||
.imm(&imm.trapcode),
|
||||
);
|
||||
registry.insert(
|
||||
Builder::new("FloatCondTrap")
|
||||
.imm(&imm.trapcode)
|
||||
.build(),
|
||||
|
||||
float_cond_trap: Builder::new("FloatCondTrap")
|
||||
.imm(&imm.floatcc)
|
||||
.value()
|
||||
.imm(&imm.trapcode),
|
||||
);
|
||||
|
||||
registry
|
||||
.imm(&imm.trapcode)
|
||||
.build(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,20 +8,25 @@ pub mod legalize;
|
||||
pub mod settings;
|
||||
pub mod types;
|
||||
|
||||
use crate::cdsl::formats::FormatRegistry;
|
||||
use crate::cdsl::formats::{FormatStructure, InstructionFormat};
|
||||
use crate::cdsl::instructions::{AllInstructions, InstructionGroup};
|
||||
use crate::cdsl::settings::SettingGroup;
|
||||
use crate::cdsl::xform::TransformGroups;
|
||||
|
||||
use crate::shared::entities::EntityRefs;
|
||||
use crate::shared::formats::Formats;
|
||||
use crate::shared::immediates::Immediates;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::iter::FromIterator;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(crate) struct Definitions {
|
||||
pub settings: SettingGroup,
|
||||
pub all_instructions: AllInstructions,
|
||||
pub instructions: InstructionGroup,
|
||||
pub imm: Immediates,
|
||||
pub format_registry: FormatRegistry,
|
||||
pub formats: Formats,
|
||||
pub transform_groups: TransformGroups,
|
||||
}
|
||||
|
||||
@@ -30,13 +35,9 @@ pub(crate) fn define() -> Definitions {
|
||||
|
||||
let immediates = Immediates::new();
|
||||
let entities = EntityRefs::new();
|
||||
let format_registry = formats::define(&immediates, &entities);
|
||||
let instructions = instructions::define(
|
||||
&mut all_instructions,
|
||||
&format_registry,
|
||||
&immediates,
|
||||
&entities,
|
||||
);
|
||||
let formats = Formats::new(&immediates, &entities);
|
||||
let instructions =
|
||||
instructions::define(&mut all_instructions, &formats, &immediates, &entities);
|
||||
let transform_groups = legalize::define(&instructions, &immediates);
|
||||
|
||||
Definitions {
|
||||
@@ -44,7 +45,53 @@ pub(crate) fn define() -> Definitions {
|
||||
all_instructions,
|
||||
instructions,
|
||||
imm: immediates,
|
||||
format_registry,
|
||||
formats,
|
||||
transform_groups,
|
||||
}
|
||||
}
|
||||
|
||||
impl Definitions {
|
||||
/// Verifies certain properties of formats.
|
||||
///
|
||||
/// - Formats must be uniquely named: if two formats have the same name, they must refer to the
|
||||
/// same data. Otherwise, two format variants in the codegen crate would have the same name.
|
||||
/// - Formats must be structurally different from each other. Otherwise, this would lead to
|
||||
/// code duplicate in the codegen crate.
|
||||
///
|
||||
/// Returns a list of all the instruction formats effectively used.
|
||||
pub fn verify_instruction_formats(&self) -> Vec<&InstructionFormat> {
|
||||
let mut format_names: HashMap<&'static str, &Rc<InstructionFormat>> = HashMap::new();
|
||||
|
||||
// A structure is: number of input value operands / whether there's varargs or not / names
|
||||
// of immediate fields.
|
||||
let mut format_structures: HashMap<FormatStructure, &InstructionFormat> = HashMap::new();
|
||||
|
||||
for inst in self.all_instructions.values() {
|
||||
// Check name.
|
||||
if let Some(existing_format) = format_names.get(&inst.format.name) {
|
||||
assert!(
|
||||
Rc::ptr_eq(&existing_format, &inst.format),
|
||||
"formats must uniquely named; there's a\
|
||||
conflict on the name '{}', please make sure it is used only once.",
|
||||
existing_format.name
|
||||
);
|
||||
} else {
|
||||
format_names.insert(inst.format.name, &inst.format);
|
||||
}
|
||||
|
||||
// Check structure.
|
||||
let key = inst.format.structure();
|
||||
if let Some(existing_format) = format_structures.get(&key) {
|
||||
assert_eq!(
|
||||
existing_format.name, inst.format.name,
|
||||
"duplicate instruction formats {} and {}; please remove one.",
|
||||
existing_format.name, inst.format.name
|
||||
);
|
||||
} else {
|
||||
format_structures.insert(key, &inst.format);
|
||||
}
|
||||
}
|
||||
|
||||
Vec::from_iter(format_structures.into_iter().map(|(_, v)| v))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user