[meta] Port Instruction/InstructionGroup to the Rust meta crate;

This commit is contained in:
Benjamin Bouvier
2019-03-11 19:36:45 +01:00
parent 208c4e6da6
commit 3c31eac48c
11 changed files with 763 additions and 24 deletions

View File

@@ -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<Instruction>,
}
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<Instruction> {
self.instructions.iter()
}
}
pub struct PolymorphicInfo {
pub use_typevar_operand: bool,
pub ctrl_typevar: TypeVar,
pub other_typevars: Vec<TypeVar>,
}
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<Operand>,
/// Output operands. The output operands must be SSA values or `variable_args`.
pub operands_out: Vec<Operand>,
/// Instruction-specific TypeConstraints.
_constraints: Vec<Constraint>,
/// 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<PolymorphicInfo>,
pub value_opnums: Vec<usize>,
pub value_results: Vec<usize>,
pub imm_opnums: Vec<usize>,
/// 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::<Vec<_>>()
.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::<Vec<_>>()
.join(", ");
fmt.write_str(" ")?;
fmt.write_str(&operands_in)?;
}
Ok(())
}
}
pub struct InstructionBuilder {
name: &'static str,
doc: &'static str,
operands_in: Option<Vec<Operand>>,
operands_out: Option<Vec<Operand>>,
constraints: Option<Vec<Constraint>>,
// 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<Constraint>) -> 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<Operand>,
operands_out: &Vec<Operand>,
format: &InstructionFormat,
value_opnums: &Vec<usize>,
) -> Option<PolymorphicInfo> {
// 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<usize>,
operands_in: &Vec<Operand>,
operands_out: &Vec<Operand>,
) -> Result<Vec<TypeVar>, 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)
}

View File

@@ -1,16 +1,24 @@
use super::regs::IsaRegs; use crate::cdsl::inst::InstructionGroup;
use super::settings::SettingGroup; use crate::cdsl::regs::IsaRegs;
use crate::cdsl::settings::SettingGroup;
pub struct TargetIsa { pub struct TargetIsa {
pub name: &'static str, pub name: &'static str,
pub instructions: InstructionGroup,
pub settings: SettingGroup, pub settings: SettingGroup,
pub regs: IsaRegs, pub regs: IsaRegs,
} }
impl TargetIsa { 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 { Self {
name, name,
instructions,
settings, settings,
regs, regs,
} }

View File

@@ -4,10 +4,12 @@
//! instructions and other entities. //! instructions and other entities.
pub mod formats; pub mod formats;
pub mod inst;
pub mod isa; pub mod isa;
pub mod operands; pub mod operands;
pub mod regs; pub mod regs;
pub mod settings; pub mod settings;
pub mod type_inference;
pub mod types; pub mod types;
pub mod typevar; pub mod typevar;

View File

@@ -0,0 +1,5 @@
use crate::cdsl::typevar::TypeVar;
pub enum Constraint {
WiderOrEq(TypeVar, TypeVar),
}

View File

@@ -1,6 +1,8 @@
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::isa::TargetIsa; use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder}; use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
use crate::shared::Definitions as SharedDefinitions;
fn define_settings(_shared: &SettingGroup) -> SettingGroup { fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let setting = SettingGroupBuilder::new("arm32"); let setting = SettingGroupBuilder::new("arm32");
@@ -44,8 +46,11 @@ fn define_regs() -> IsaRegs {
regs.finish() regs.finish()
} }
pub fn define(shared_settings: &SettingGroup) -> TargetIsa { pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let settings = define_settings(shared_settings); let settings = define_settings(&shared_defs.settings);
let regs = define_regs(); 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)
} }

View File

@@ -1,6 +1,8 @@
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::isa::TargetIsa; use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder}; use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder};
use crate::shared::Definitions as SharedDefinitions;
fn define_settings(_shared: &SettingGroup) -> SettingGroup { fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let setting = SettingGroupBuilder::new("arm64"); let setting = SettingGroupBuilder::new("arm64");
@@ -40,8 +42,11 @@ fn define_registers() -> IsaRegs {
regs.finish() regs.finish()
} }
pub fn define(shared_settings: &SettingGroup) -> TargetIsa { pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let settings = define_settings(shared_settings); let settings = define_settings(&shared_defs.settings);
let regs = define_registers(); 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)
} }

View File

@@ -1,5 +1,5 @@
use crate::cdsl::isa::TargetIsa; use crate::cdsl::isa::TargetIsa;
use crate::cdsl::settings::SettingGroup; use crate::shared::Definitions as SharedDefinitions;
use std::fmt; use std::fmt;
mod arm32; mod arm32;
@@ -55,13 +55,13 @@ impl fmt::Display for Isa {
} }
} }
pub fn define(isas: &Vec<Isa>, shared_settings: &SettingGroup) -> Vec<TargetIsa> { pub fn define(isas: &Vec<Isa>, shared_defs: &mut SharedDefinitions) -> Vec<TargetIsa> {
isas.iter() isas.iter()
.map(|isa| match isa { .map(|isa| match isa {
Isa::Riscv => riscv::define(shared_settings), Isa::Riscv => riscv::define(shared_defs),
Isa::X86 => x86::define(shared_settings), Isa::X86 => x86::define(shared_defs),
Isa::Arm32 => arm32::define(shared_settings), Isa::Arm32 => arm32::define(shared_defs),
Isa::Arm64 => arm64::define(shared_settings), Isa::Arm64 => arm64::define(shared_defs),
}) })
.collect() .collect()
} }

View File

@@ -1,6 +1,8 @@
use crate::cdsl::inst::InstructionGroup;
use crate::cdsl::isa::TargetIsa; use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
use crate::shared::Definitions as SharedDefinitions;
fn define_settings(shared: &SettingGroup) -> SettingGroup { fn define_settings(shared: &SettingGroup) -> SettingGroup {
let mut setting = SettingGroupBuilder::new("riscv"); let mut setting = SettingGroupBuilder::new("riscv");
@@ -76,8 +78,11 @@ fn define_registers() -> IsaRegs {
regs.finish() regs.finish()
} }
pub fn define(shared_settings: &SettingGroup) -> TargetIsa { pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let settings = define_settings(shared_settings); let settings = define_settings(&shared_defs.settings);
let regs = define_registers(); 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)
} }

View File

@@ -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
}

View File

@@ -1,7 +1,11 @@
mod instructions;
use crate::cdsl::isa::TargetIsa; use crate::cdsl::isa::TargetIsa;
use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder}; use crate::cdsl::regs::{IsaRegs, IsaRegsBuilder, RegBankBuilder, RegClassBuilder};
use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
use crate::shared::Definitions as SharedDefinitions;
fn define_settings(_shared: &SettingGroup) -> SettingGroup { fn define_settings(_shared: &SettingGroup) -> SettingGroup {
let mut settings = SettingGroupBuilder::new("x86"); let mut settings = SettingGroupBuilder::new("x86");
@@ -109,8 +113,11 @@ fn define_registers() -> IsaRegs {
regs.finish() regs.finish()
} }
pub fn define(shared_settings: &SettingGroup) -> TargetIsa { pub fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let settings = define_settings(shared_settings); let settings = define_settings(&shared_defs.settings);
let regs = define_registers(); 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)
} }

View File

@@ -20,10 +20,10 @@ pub fn isa_from_arch(arch: &str) -> Result<isa::Isa, String> {
/// Generates all the Rust source files used in Cranelift from the meta-language. /// 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> { pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error> {
// Common definitions. // Common definitions.
let settings = shared::settings::define(); let mut shared_defs = shared::define();
gen_settings::generate( gen_settings::generate(
&settings, &shared_defs.settings,
gen_settings::ParentGroup::None, gen_settings::ParentGroup::None,
"new_settings.rs", "new_settings.rs",
&out_dir, &out_dir,
@@ -31,7 +31,7 @@ pub fn generate(isas: &Vec<isa::Isa>, out_dir: &str) -> Result<(), error::Error>
gen_types::generate("types.rs", &out_dir)?; gen_types::generate("types.rs", &out_dir)?;
// Per ISA definitions. // Per ISA definitions.
let isas = isa::define(isas, &settings); let isas = isa::define(isas, &mut shared_defs);
for isa in isas { for isa in isas {
gen_registers::generate(&isa, &format!("registers-{}.rs", isa.name), &out_dir)?; gen_registers::generate(&isa, &format!("registers-{}.rs", isa.name), &out_dir)?;