From 3c31eac48c706c6be24ca5c58ba6ac988c894232 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 11 Mar 2019 19:36:45 +0100 Subject: [PATCH] [meta] Port Instruction/InstructionGroup to the Rust meta crate; --- cranelift/codegen/meta/src/cdsl/inst.rs | 458 ++++++++++++++++++ cranelift/codegen/meta/src/cdsl/isa.rs | 14 +- cranelift/codegen/meta/src/cdsl/mod.rs | 2 + .../codegen/meta/src/cdsl/type_inference.rs | 5 + cranelift/codegen/meta/src/isa/arm32/mod.rs | 11 +- cranelift/codegen/meta/src/isa/arm64/mod.rs | 11 +- cranelift/codegen/meta/src/isa/mod.rs | 12 +- cranelift/codegen/meta/src/isa/riscv/mod.rs | 11 +- .../codegen/meta/src/isa/x86/instructions.rs | 244 ++++++++++ cranelift/codegen/meta/src/isa/x86/mod.rs | 13 +- cranelift/codegen/meta/src/lib.rs | 6 +- 11 files changed, 763 insertions(+), 24 deletions(-) create mode 100644 cranelift/codegen/meta/src/cdsl/inst.rs create mode 100644 cranelift/codegen/meta/src/cdsl/type_inference.rs create mode 100644 cranelift/codegen/meta/src/isa/x86/instructions.rs diff --git a/cranelift/codegen/meta/src/cdsl/inst.rs b/cranelift/codegen/meta/src/cdsl/inst.rs new file mode 100644 index 0000000000..4199d6424d --- /dev/null +++ b/cranelift/codegen/meta/src/cdsl/inst.rs @@ -0,0 +1,458 @@ +use crate::cdsl::camel_case; +use crate::cdsl::formats::{FormatRegistry, InstructionFormat, InstructionFormatIndex}; +use crate::cdsl::operands::Operand; +use crate::cdsl::type_inference::Constraint; +use crate::cdsl::typevar::TypeVar; + +use std::fmt; +use std::slice; + +/// Every instruction must belong to exactly one instruction group. A given +/// target architecture can support instructions from multiple groups, and it +/// does not necessarily support all instructions in a group. +pub struct InstructionGroup { + _name: &'static str, + _doc: &'static str, + instructions: Vec, +} + +impl InstructionGroup { + pub fn new(name: &'static str, doc: &'static str) -> Self { + Self { + _name: name, + _doc: doc, + instructions: Vec::new(), + } + } + + pub fn push(&mut self, inst: Instruction) { + self.instructions.push(inst); + } + + pub fn iter(&self) -> slice::Iter { + self.instructions.iter() + } +} + +pub struct PolymorphicInfo { + pub use_typevar_operand: bool, + pub ctrl_typevar: TypeVar, + pub other_typevars: Vec, +} + +pub struct Instruction { + /// Instruction mnemonic, also becomes opcode name. + pub name: &'static str, + pub camel_name: String, + + /// Documentation string. + doc: &'static str, + + /// Input operands. This can be a mix of SSA value operands and other operand kinds. + pub operands_in: Vec, + /// Output operands. The output operands must be SSA values or `variable_args`. + pub operands_out: Vec, + /// Instruction-specific TypeConstraints. + _constraints: Vec, + + /// Instruction format, automatically derived from the input operands. + pub format: InstructionFormatIndex, + + /// One of the input or output operands is a free type variable. None if the instruction is not + /// polymorphic, set otherwise. + pub polymorphic_info: Option, + + pub value_opnums: Vec, + pub value_results: Vec, + pub imm_opnums: Vec, + + /// True for instructions that terminate the EBB. + pub is_terminator: bool, + /// True for all branch or jump instructions. + pub is_branch: bool, + /// True for all indirect branch or jump instructions.', + pub is_indirect_branch: bool, + /// Is this a call instruction? + pub is_call: bool, + /// Is this a return instruction? + pub is_return: bool, + /// Is this a ghost instruction? + pub is_ghost: bool, + /// Can this instruction read from memory? + pub can_load: bool, + /// Can this instruction write to memory? + pub can_store: bool, + /// Can this instruction cause a trap? + pub can_trap: bool, + /// Does this instruction have other side effects besides can_* flags? + pub other_side_effects: bool, + /// Does this instruction write to CPU flags? + pub writes_cpu_flags: bool, +} + +impl Instruction { + pub fn snake_name(&self) -> &'static str { + if self.name == "return" { + "return_" + } else { + self.name + } + } + + pub fn doc_comment_first_line(&self) -> &'static str { + for line in self.doc.split("\n") { + let stripped = line.trim(); + if stripped.len() > 0 { + return stripped; + } + } + "" + } +} + +impl fmt::Display for Instruction { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + if self.operands_out.len() > 0 { + let operands_out = self + .operands_out + .iter() + .map(|op| op.name) + .collect::>() + .join(", "); + fmt.write_str(&operands_out)?; + fmt.write_str(" = ")?; + } + + fmt.write_str(self.name)?; + + if self.operands_in.len() > 0 { + let operands_in = self + .operands_in + .iter() + .map(|op| op.name) + .collect::>() + .join(", "); + fmt.write_str(" ")?; + fmt.write_str(&operands_in)?; + } + + Ok(()) + } +} + +pub struct InstructionBuilder { + name: &'static str, + doc: &'static str, + operands_in: Option>, + operands_out: Option>, + constraints: Option>, + + // See Instruction comments for the meaning of these fields. + is_terminator: bool, + is_branch: bool, + is_indirect_branch: bool, + is_call: bool, + is_return: bool, + is_ghost: bool, + can_load: bool, + can_store: bool, + can_trap: bool, + other_side_effects: bool, +} + +impl InstructionBuilder { + pub fn new(name: &'static str, doc: &'static str) -> Self { + Self { + name, + doc, + operands_in: None, + operands_out: None, + constraints: None, + + is_terminator: false, + is_branch: false, + is_indirect_branch: false, + is_call: false, + is_return: false, + is_ghost: false, + can_load: false, + can_store: false, + can_trap: false, + other_side_effects: false, + } + } + + pub fn operands_in(mut self, operands: Vec<&Operand>) -> Self { + assert!(self.operands_in.is_none()); + self.operands_in = Some(operands.iter().map(|x| (*x).clone()).collect()); + self + } + pub fn operands_out(mut self, operands: Vec<&Operand>) -> Self { + assert!(self.operands_out.is_none()); + self.operands_out = Some(operands.iter().map(|x| (*x).clone()).collect()); + self + } + pub fn constraints(mut self, constraints: Vec) -> Self { + assert!(self.constraints.is_none()); + self.constraints = Some(constraints); + self + } + + pub fn is_terminator(mut self, val: bool) -> Self { + self.is_terminator = val; + self + } + pub fn is_branch(mut self, val: bool) -> Self { + self.is_branch = val; + self + } + pub fn is_indirect_branch(mut self, val: bool) -> Self { + self.is_indirect_branch = val; + self + } + pub fn is_call(mut self, val: bool) -> Self { + self.is_call = val; + self + } + pub fn is_return(mut self, val: bool) -> Self { + self.is_return = val; + self + } + pub fn is_ghost(mut self, val: bool) -> Self { + self.is_ghost = val; + self + } + pub fn can_load(mut self, val: bool) -> Self { + self.can_load = val; + self + } + pub fn can_store(mut self, val: bool) -> Self { + self.can_store = val; + self + } + pub fn can_trap(mut self, val: bool) -> Self { + self.can_trap = val; + self + } + pub fn other_side_effects(mut self, val: bool) -> Self { + self.other_side_effects = val; + self + } + + pub fn finish(self, format_registry: &FormatRegistry) -> Instruction { + let operands_in = self.operands_in.unwrap_or_else(Vec::new); + let operands_out = self.operands_out.unwrap_or_else(Vec::new); + + let format_index = format_registry.lookup(&operands_in); + + let mut value_opnums = Vec::new(); + let mut imm_opnums = Vec::new(); + for (i, op) in operands_in.iter().enumerate() { + if op.is_value() { + value_opnums.push(i); + } else if op.is_immediate() { + imm_opnums.push(i); + } else { + assert!(op.is_varargs()); + } + } + + let mut value_results = Vec::new(); + for (i, op) in operands_out.iter().enumerate() { + if op.is_value() { + value_results.push(i); + } + } + + let format = format_registry.get(format_index); + let polymorphic_info = + verify_polymorphic(&operands_in, &operands_out, &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()); + + Instruction { + name: self.name, + camel_name: camel_case(self.name), + doc: self.doc, + operands_in, + operands_out, + _constraints: self.constraints.unwrap_or_else(Vec::new), + format: format_index, + polymorphic_info, + value_opnums, + value_results, + imm_opnums, + is_terminator: self.is_terminator, + is_branch: self.is_branch, + is_indirect_branch: self.is_indirect_branch, + is_call: self.is_call, + is_return: self.is_return, + is_ghost: self.is_ghost, + can_load: self.can_load, + can_store: self.can_store, + can_trap: self.can_trap, + other_side_effects: self.other_side_effects, + writes_cpu_flags, + } + } +} + +/// Check if this instruction is polymorphic, and verify its use of type variables. +fn verify_polymorphic( + operands_in: &Vec, + operands_out: &Vec, + format: &InstructionFormat, + value_opnums: &Vec, +) -> Option { + // The instruction is polymorphic if it has one free input or output operand. + let is_polymorphic = operands_in + .iter() + .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some()) + || operands_out + .iter() + .any(|op| op.is_value() && op.type_var().unwrap().free_typevar().is_some()); + + if !is_polymorphic { + return None; + } + + // Verify the use of type variables. + let mut use_typevar_operand = false; + let mut ctrl_typevar = None; + let mut other_typevars = None; + let mut maybe_error_message = None; + + let tv_op = format.typevar_operand; + if let Some(tv_op) = tv_op { + if tv_op < value_opnums.len() { + let op_num = value_opnums[tv_op]; + let tv = operands_in[op_num].type_var().unwrap(); + let free_typevar = tv.free_typevar(); + if (free_typevar.is_some() && tv == &free_typevar.unwrap()) + || !tv.singleton_type().is_none() + { + match verify_ctrl_typevar(tv, &value_opnums, &operands_in, &operands_out) { + Ok(typevars) => { + other_typevars = Some(typevars); + ctrl_typevar = Some(tv.clone()); + use_typevar_operand = true; + } + Err(error_message) => { + maybe_error_message = Some(error_message); + } + } + } + } + }; + + if !use_typevar_operand { + if operands_out.len() == 0 { + match maybe_error_message { + Some(msg) => panic!(msg), + None => panic!("typevar_operand must be a free type variable"), + } + } + + let tv = operands_out[0].type_var().unwrap(); + let free_typevar = tv.free_typevar(); + if free_typevar.is_some() && tv != &free_typevar.unwrap() { + panic!("first result must be a free type variable"); + } + + other_typevars = + Some(verify_ctrl_typevar(tv, &value_opnums, &operands_in, &operands_out).unwrap()); + ctrl_typevar = Some(tv.clone()); + } + + // rustc is not capable to determine this statically, so enforce it with options. + assert!(ctrl_typevar.is_some()); + assert!(other_typevars.is_some()); + + Some(PolymorphicInfo { + use_typevar_operand, + ctrl_typevar: ctrl_typevar.unwrap(), + other_typevars: other_typevars.unwrap(), + }) +} + +/// Verify that the use of TypeVars is consistent with `ctrl_typevar` as the controlling type +/// variable. +/// +/// All polymorhic inputs must either be derived from `ctrl_typevar` or be independent free type +/// variables only used once. +/// +/// All polymorphic results must be derived from `ctrl_typevar`. +/// +/// Return a vector of other type variables used, or panics. +fn verify_ctrl_typevar( + ctrl_typevar: &TypeVar, + value_opnums: &Vec, + operands_in: &Vec, + operands_out: &Vec, +) -> Result, String> { + let mut other_typevars = Vec::new(); + + // Check value inputs. + for &op_num in value_opnums { + let typ = operands_in[op_num].type_var(); + + let tv = if let Some(typ) = typ { + typ.free_typevar() + } else { + None + }; + + // Non-polymorphic or derived from ctrl_typevar is OK. + let tv = match tv { + Some(tv) => { + if &tv == ctrl_typevar { + continue; + } + tv + } + None => continue, + }; + + // No other derived typevars allowed. + if typ.is_some() && typ.unwrap() != &tv { + return Err(format!( + "{:?}: type variable {} must be derived from {:?}", + operands_in[op_num], + typ.unwrap().name, + ctrl_typevar + )); + } + + // Other free type variables can only be used once each. + for other_tv in &other_typevars { + if &tv == other_tv { + return Err(format!( + "type variable {} can't be used more than once", + tv.name + )); + } + } + + other_typevars.push(tv); + } + + // Check outputs. + for result in operands_out { + if !result.is_value() { + continue; + } + + let typ = result.type_var().unwrap(); + let tv = typ.free_typevar(); + + // Non-polymorphic or derived form ctrl_typevar is OK. + if tv.is_none() || &tv.unwrap() == ctrl_typevar { + continue; + } + + return Err("type variable in output not derived from ctrl_typevar".into()); + } + + Ok(other_typevars) +} diff --git a/cranelift/codegen/meta/src/cdsl/isa.rs b/cranelift/codegen/meta/src/cdsl/isa.rs index 86b40a12c3..1091f6db80 100644 --- a/cranelift/codegen/meta/src/cdsl/isa.rs +++ b/cranelift/codegen/meta/src/cdsl/isa.rs @@ -1,16 +1,24 @@ -use super::regs::IsaRegs; -use super::settings::SettingGroup; +use crate::cdsl::inst::InstructionGroup; +use crate::cdsl::regs::IsaRegs; +use crate::cdsl::settings::SettingGroup; pub struct TargetIsa { pub name: &'static str, + pub instructions: InstructionGroup, pub settings: SettingGroup, pub regs: IsaRegs, } impl TargetIsa { - pub fn new(name: &'static str, settings: SettingGroup, regs: IsaRegs) -> Self { + pub fn new( + name: &'static str, + instructions: InstructionGroup, + settings: SettingGroup, + regs: IsaRegs, + ) -> Self { Self { name, + instructions, settings, regs, } diff --git a/cranelift/codegen/meta/src/cdsl/mod.rs b/cranelift/codegen/meta/src/cdsl/mod.rs index fcfe977d14..540370624a 100644 --- a/cranelift/codegen/meta/src/cdsl/mod.rs +++ b/cranelift/codegen/meta/src/cdsl/mod.rs @@ -4,10 +4,12 @@ //! instructions and other entities. pub mod formats; +pub mod inst; pub mod isa; pub mod operands; pub mod regs; pub mod settings; +pub mod type_inference; pub mod types; pub mod typevar; diff --git a/cranelift/codegen/meta/src/cdsl/type_inference.rs b/cranelift/codegen/meta/src/cdsl/type_inference.rs new file mode 100644 index 0000000000..de104f4b47 --- /dev/null +++ b/cranelift/codegen/meta/src/cdsl/type_inference.rs @@ -0,0 +1,5 @@ +use crate::cdsl::typevar::TypeVar; + +pub enum Constraint { + WiderOrEq(TypeVar, TypeVar), +} diff --git a/cranelift/codegen/meta/src/isa/arm32/mod.rs b/cranelift/codegen/meta/src/isa/arm32/mod.rs index e3decd4da0..8c32cdcdcd 100644 --- a/cranelift/codegen/meta/src/isa/arm32/mod.rs +++ b/cranelift/codegen/meta/src/isa/arm32/mod.rs @@ -1,6 +1,8 @@ +use crate::cdsl::inst::InstructionGroup; use crate::cdsl::isa::TargetIsa; use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder}; +use crate::shared::Definitions as SharedDefinitions; fn define_settings(_shared: &SettingGroup) -> SettingGroup { let setting = SettingGroupBuilder::new("arm32"); @@ -44,8 +46,11 @@ fn define_regs() -> IsaRegs { regs.finish() } -pub fn define(shared_settings: &SettingGroup) -> TargetIsa { - let settings = define_settings(shared_settings); +pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { + let settings = define_settings(&shared_defs.settings); let regs = define_regs(); - TargetIsa::new("arm32", settings, regs) + + let inst_group = InstructionGroup::new("arm32", "arm32 specific instruction set"); + + TargetIsa::new("arm32", inst_group, settings, regs) } diff --git a/cranelift/codegen/meta/src/isa/arm64/mod.rs b/cranelift/codegen/meta/src/isa/arm64/mod.rs index baca5226ab..46baac90e6 100644 --- a/cranelift/codegen/meta/src/isa/arm64/mod.rs +++ b/cranelift/codegen/meta/src/isa/arm64/mod.rs @@ -1,6 +1,8 @@ +use crate::cdsl::inst::InstructionGroup; use crate::cdsl::isa::TargetIsa; use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder}; +use crate::shared::Definitions as SharedDefinitions; fn define_settings(_shared: &SettingGroup) -> SettingGroup { let setting = SettingGroupBuilder::new("arm64"); @@ -40,8 +42,11 @@ fn define_registers() -> IsaRegs { regs.finish() } -pub fn define(shared_settings: &SettingGroup) -> TargetIsa { - let settings = define_settings(shared_settings); +pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { + let settings = define_settings(&shared_defs.settings); let regs = define_registers(); - TargetIsa::new("arm64", settings, regs) + + let inst_group = InstructionGroup::new("arm64", "arm64 specific instruction set"); + + TargetIsa::new("arm64", inst_group, settings, regs) } diff --git a/cranelift/codegen/meta/src/isa/mod.rs b/cranelift/codegen/meta/src/isa/mod.rs index 9662435867..0c2bb5eed0 100644 --- a/cranelift/codegen/meta/src/isa/mod.rs +++ b/cranelift/codegen/meta/src/isa/mod.rs @@ -1,5 +1,5 @@ use crate::cdsl::isa::TargetIsa; -use crate::cdsl::settings::SettingGroup; +use crate::shared::Definitions as SharedDefinitions; use std::fmt; mod arm32; @@ -55,13 +55,13 @@ impl fmt::Display for Isa { } } -pub fn define(isas: &Vec, shared_settings: &SettingGroup) -> Vec { +pub fn define(isas: &Vec, shared_defs: &mut SharedDefinitions) -> Vec { isas.iter() .map(|isa| match isa { - Isa::Riscv => riscv::define(shared_settings), - Isa::X86 => x86::define(shared_settings), - Isa::Arm32 => arm32::define(shared_settings), - Isa::Arm64 => arm64::define(shared_settings), + Isa::Riscv => riscv::define(shared_defs), + Isa::X86 => x86::define(shared_defs), + Isa::Arm32 => arm32::define(shared_defs), + Isa::Arm64 => arm64::define(shared_defs), }) .collect() } diff --git a/cranelift/codegen/meta/src/isa/riscv/mod.rs b/cranelift/codegen/meta/src/isa/riscv/mod.rs index 040d088386..9d5a78a5a0 100644 --- a/cranelift/codegen/meta/src/isa/riscv/mod.rs +++ b/cranelift/codegen/meta/src/isa/riscv/mod.rs @@ -1,6 +1,8 @@ +use crate::cdsl::inst::InstructionGroup; use crate::cdsl::isa::TargetIsa; use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; +use crate::shared::Definitions as SharedDefinitions; fn define_settings(shared: &SettingGroup) -> SettingGroup { let mut setting = SettingGroupBuilder::new("riscv"); @@ -76,8 +78,11 @@ fn define_registers() -> IsaRegs { regs.finish() } -pub fn define(shared_settings: &SettingGroup) -> TargetIsa { - let settings = define_settings(shared_settings); +pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { + let settings = define_settings(&shared_defs.settings); let regs = define_registers(); - TargetIsa::new("riscv", settings, regs) + + let inst_group = InstructionGroup::new("riscv", "riscv specific instruction set"); + + TargetIsa::new("riscv", inst_group, settings, regs) } diff --git a/cranelift/codegen/meta/src/isa/x86/instructions.rs b/cranelift/codegen/meta/src/isa/x86/instructions.rs new file mode 100644 index 0000000000..68c3b56b85 --- /dev/null +++ b/cranelift/codegen/meta/src/isa/x86/instructions.rs @@ -0,0 +1,244 @@ +#![allow(non_snake_case)] + +use crate::cdsl::formats::FormatRegistry; +use crate::cdsl::inst::{InstructionBuilder as Inst, InstructionGroup}; +use crate::cdsl::operands::{create_operand as operand, create_operand_doc as operand_doc}; +use crate::cdsl::types::ValueType; +use crate::cdsl::typevar::{Interval, TypeVar, TypeVarBuilder}; +use crate::shared::types; + +pub fn define(format_registry: &FormatRegistry) -> InstructionGroup { + let mut ig = InstructionGroup::new("x86", "x86 specific instruction set"); + + let iflags: &TypeVar = &ValueType::Special(types::Flag::IFlags.into()).into(); + + let iWord = &TypeVarBuilder::new("iWord", "A scalar integer machine word") + .ints(32..64) + .finish(); + let nlo = &operand_doc("nlo", iWord, "Low part of numerator"); + let nhi = &operand_doc("nhi", iWord, "High part of numerator"); + let d = &operand_doc("d", iWord, "Denominator"); + let q = &operand_doc("q", iWord, "Quotient"); + let r = &operand_doc("r", iWord, "Remainder"); + + ig.push( + Inst::new( + "x86_udivmodx", + r#" + Extended unsigned division. + + Concatenate the bits in `nhi` and `nlo` to form the numerator. + Interpret the bits as an unsigned number and divide by the unsigned + denominator `d`. Trap when `d` is zero or if the quotient is larger + than the range of the output. + + Return both quotient and remainder. + "#, + ) + .operands_in(vec![nlo, nhi, d]) + .operands_out(vec![q, r]) + .can_trap(true) + .finish(format_registry), + ); + + ig.push( + Inst::new( + "x86_sdivmodx", + r#" + Extended signed division. + + Concatenate the bits in `nhi` and `nlo` to form the numerator. + Interpret the bits as a signed number and divide by the signed + denominator `d`. Trap when `d` is zero or if the quotient is outside + the range of the output. + + Return both quotient and remainder. + "#, + ) + .operands_in(vec![nlo, nhi, d]) + .operands_out(vec![q, r]) + .can_trap(true) + .finish(format_registry), + ); + + let argL = &operand("argL", iWord); + let argR = &operand("argR", iWord); + let resLo = &operand("resLo", iWord); + let resHi = &operand("resHi", iWord); + + ig.push( + Inst::new( + "x86_umulx", + r#" + Unsigned integer multiplication, producing a double-length result. + + Polymorphic over all scalar integer types, but does not support vector + types. + "#, + ) + .operands_in(vec![argL, argR]) + .operands_out(vec![resLo, resHi]) + .finish(format_registry), + ); + + ig.push( + Inst::new( + "x86_smulx", + r#" + Signed integer multiplication, producing a double-length result. + + Polymorphic over all scalar integer types, but does not support vector + types. + "#, + ) + .operands_in(vec![argL, argR]) + .operands_out(vec![resLo, resHi]) + .finish(format_registry), + ); + + let Float = &TypeVarBuilder::new("Float", "A scalar or vector floating point number") + .floats(Interval::All) + .simd_lanes(Interval::All) + .finish(); + let IntTo = &TypeVarBuilder::new("IntTo", "An integer type with the same number of lanes") + .ints(32..64) + .simd_lanes(Interval::All) + .finish(); + let x = &operand("x", Float); + let a = &operand("a", IntTo); + + ig.push( + Inst::new( + "x86_cvtt2si", + r#" + Convert with truncation floating point to signed integer. + + The source floating point operand is converted to a signed integer by + rounding towards zero. If the result can't be represented in the output + type, returns the smallest signed value the output type can represent. + + This instruction does not trap. + "#, + ) + .operands_in(vec![x]) + .operands_out(vec![a]) + .finish(format_registry), + ); + + let x = &operand("x", Float); + let a = &operand("a", Float); + let y = &operand("y", Float); + + ig.push( + Inst::new( + "x86_fmin", + r#" + Floating point minimum with x86 semantics. + + This is equivalent to the C ternary operator `x < y ? x : y` which + differs from :inst:`fmin` when either operand is NaN or when comparing + +0.0 to -0.0. + + When the two operands don't compare as LT, `y` is returned unchanged, + even if it is a signalling NaN. + "#, + ) + .operands_in(vec![x, y]) + .operands_out(vec![a]) + .finish(format_registry), + ); + + ig.push( + Inst::new( + "x86_fmax", + r#" + Floating point maximum with x86 semantics. + + This is equivalent to the C ternary operator `x > y ? x : y` which + differs from :inst:`fmax` when either operand is NaN or when comparing + +0.0 to -0.0. + + When the two operands don't compare as GT, `y` is returned unchanged, + even if it is a signalling NaN. + "#, + ) + .operands_in(vec![x, y]) + .operands_out(vec![a]) + .finish(format_registry), + ); + + let x = &operand("x", iWord); + + ig.push( + Inst::new( + "x86_push", + r#" + Pushes a value onto the stack. + + Decrements the stack pointer and stores the specified value on to the top. + + 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. + "#, + ) + .operands_in(vec![x]) + .other_side_effects(true) + .can_store(true) + .finish(format_registry), + ); + + ig.push( + Inst::new( + "x86_pop", + r#" + Pops a value from the stack. + + Loads a value from the top of the stack and then increments the stack + pointer. + + 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. + "#, + ) + .operands_out(vec![x]) + .other_side_effects(true) + .can_load(true) + .finish(format_registry), + ); + + let y = &operand("y", iWord); + let rflags = &operand("rflags", iflags); + + ig.push( + Inst::new( + "x86_bsr", + r#" + Bit Scan Reverse -- returns the bit-index of the most significant 1 + in the word. Result is undefined if the argument is zero. However, it + sets the Z flag depending on the argument, so it is at least easy to + detect and handle that case. + + 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. + "#, + ) + .operands_in(vec![x]) + .operands_out(vec![y, rflags]) + .finish(format_registry), + ); + + ig.push( + Inst::new( + "x86_bsf", + r#" + Bit Scan Forwards -- returns the bit-index of the least significant 1 + in the word. Is otherwise identical to 'bsr', just above. + "#, + ) + .operands_in(vec![x]) + .operands_out(vec![y, rflags]) + .finish(format_registry), + ); + + ig +} diff --git a/cranelift/codegen/meta/src/isa/x86/mod.rs b/cranelift/codegen/meta/src/isa/x86/mod.rs index da64f3cc50..6c5fe4d097 100644 --- a/cranelift/codegen/meta/src/isa/x86/mod.rs +++ b/cranelift/codegen/meta/src/isa/x86/mod.rs @@ -1,7 +1,11 @@ +mod instructions; + use crate::cdsl::isa::TargetIsa; use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; +use crate::shared::Definitions as SharedDefinitions; + fn define_settings(_shared: &SettingGroup) -> SettingGroup { let mut settings = SettingGroupBuilder::new("x86"); @@ -109,8 +113,11 @@ fn define_registers() -> IsaRegs { regs.finish() } -pub fn define(shared_settings: &SettingGroup) -> TargetIsa { - let settings = define_settings(shared_settings); +pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa { + let settings = define_settings(&shared_defs.settings); let regs = define_registers(); - TargetIsa::new("x86", settings, regs) + + let inst_group = instructions::define(&shared_defs.format_registry); + + TargetIsa::new("x86", inst_group, settings, regs) } diff --git a/cranelift/codegen/meta/src/lib.rs b/cranelift/codegen/meta/src/lib.rs index 1599829dd9..9b50fe1232 100644 --- a/cranelift/codegen/meta/src/lib.rs +++ b/cranelift/codegen/meta/src/lib.rs @@ -20,10 +20,10 @@ pub fn isa_from_arch(arch: &str) -> Result { /// Generates all the Rust source files used in Cranelift from the meta-language. pub fn generate(isas: &Vec, out_dir: &str) -> Result<(), error::Error> { // Common definitions. - let settings = shared::settings::define(); + let mut shared_defs = shared::define(); gen_settings::generate( - &settings, + &shared_defs.settings, gen_settings::ParentGroup::None, "new_settings.rs", &out_dir, @@ -31,7 +31,7 @@ pub fn generate(isas: &Vec, out_dir: &str) -> Result<(), error::Error> gen_types::generate("types.rs", &out_dir)?; // Per ISA definitions. - let isas = isa::define(isas, &settings); + let isas = isa::define(isas, &mut shared_defs); for isa in isas { gen_registers::generate(&isa, &format!("registers-{}.rs", isa.name), &out_dir)?;