Move library crates under 'lib/'.

Give these crates each a more standard directory layout with sources in
a 'src' sub-sirectory and Cargo.toml in the top lib/foo directory.

Add license and description fields to each.

The build script for the cretonne crate now lives in
'lib/cretonne/build.rs' separating it from the normal library sources
under 'lib/cretonne/src'.
This commit is contained in:
Jakob Stoklund Olesen
2016-10-17 14:44:43 -07:00
parent af6355e2ef
commit 846db00a21
52 changed files with 64 additions and 64 deletions

View File

@@ -0,0 +1,55 @@
//! Cretonne instruction builder.
//!
//! A `Builder` provides a convenient interface for inserting instructions into a Cretonne
//! function. Many of its methods are generated from the meta language instruction definitions.
use ir::{types, instructions};
use ir::{InstructionData, DataFlowGraph, Cursor};
use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, VariableArgs, FuncRef};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, ImmVector};
use ir::condcodes::{IntCC, FloatCC};
/// Instruction builder.
///
/// A `Builder` holds mutable references to a data flow graph and a layout cursor. It provides
/// convenience method for creating and inserting instructions at the current cursor position.
pub struct Builder<'a> {
pub dfg: &'a mut DataFlowGraph,
pub pos: &'a mut Cursor<'a>,
}
impl<'a> Builder<'a> {
/// Create a new builder which inserts instructions at `pos`.
/// The `dfg` and `pos.layout` references should be from the same `Function`.
pub fn new(dfg: &'a mut DataFlowGraph, pos: &'a mut Cursor<'a>) -> Builder<'a> {
Builder {
dfg: dfg,
pos: pos,
}
}
/// Create and insert an EBB. Further instructions will be inserted into the new EBB.
pub fn ebb(&mut self) -> Ebb {
let ebb = self.dfg.make_ebb();
self.insert_ebb(ebb);
ebb
}
/// Insert an existing EBB at the current position. Further instructions will be inserted into
/// the new EBB.
pub fn insert_ebb(&mut self, ebb: Ebb) {
self.pos.insert_ebb(ebb);
}
// Create and insert an instruction.
// This method is used by the generated format-specific methods.
fn insert_inst(&mut self, data: InstructionData) -> Inst {
let inst = self.dfg.make_inst(data);
self.pos.insert_inst(inst);
inst
}
}
// Include code generated by `meta/gen_instr.py`. This file includes `Builder` methods per
// instruction format and per opcode for inserting instructions.
include!(concat!(env!("OUT_DIR"), "/builder.rs"));

View File

@@ -0,0 +1,325 @@
//! Condition codes for the Cretonne code generator.
//!
//! A condition code here is an enumerated type that determined how to compare two numbers. There
//! are different rules for comparing integers and floating point numbers, so they use different
//! condition codes.
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
/// Common traits of condition codes.
pub trait CondCode: Copy {
/// Get the inverse condition code of `self`.
///
/// The inverse condition code produces the opposite result for all comparisons.
/// That is, `cmp CC, x, y` is true if and only if `cmp CC.inverse(), x, y` is false.
fn inverse(self) -> Self;
/// Get the reversed condition code for `self`.
///
/// The reversed condition code produces the same result as swapping `x` and `y` in the
/// comparison. That is, `cmp CC, x, y` is the same as `cmp CC.reverse(), y, x`.
fn reverse(self) -> Self;
}
/// Condition code for comparing integers.
///
/// This condition code is used by the `icmp` instruction to compare integer values. There are
/// separate codes for comparing the integers as signed or unsigned numbers where it makes a
/// difference.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum IntCC {
Equal,
NotEqual,
SignedLessThan,
SignedGreaterThanOrEqual,
SignedGreaterThan,
SignedLessThanOrEqual,
UnsignedLessThan,
UnsignedGreaterThanOrEqual,
UnsignedGreaterThan,
UnsignedLessThanOrEqual,
}
impl CondCode for IntCC {
fn inverse(self) -> Self {
use self::IntCC::*;
match self {
Equal => NotEqual,
NotEqual => Equal,
SignedLessThan => SignedGreaterThanOrEqual,
SignedGreaterThanOrEqual => SignedLessThan,
SignedGreaterThan => SignedLessThanOrEqual,
SignedLessThanOrEqual => SignedGreaterThan,
UnsignedLessThan => UnsignedGreaterThanOrEqual,
UnsignedGreaterThanOrEqual => UnsignedLessThan,
UnsignedGreaterThan => UnsignedLessThanOrEqual,
UnsignedLessThanOrEqual => UnsignedGreaterThan,
}
}
fn reverse(self) -> Self {
use self::IntCC::*;
match self {
Equal => Equal,
NotEqual => NotEqual,
SignedGreaterThan => SignedLessThan,
SignedGreaterThanOrEqual => SignedLessThanOrEqual,
SignedLessThan => SignedGreaterThan,
SignedLessThanOrEqual => SignedGreaterThanOrEqual,
UnsignedGreaterThan => UnsignedLessThan,
UnsignedGreaterThanOrEqual => UnsignedLessThanOrEqual,
UnsignedLessThan => UnsignedGreaterThan,
UnsignedLessThanOrEqual => UnsignedGreaterThanOrEqual,
}
}
}
impl Display for IntCC {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use self::IntCC::*;
f.write_str(match self {
&Equal => "eq",
&NotEqual => "ne",
&SignedGreaterThan => "sgt",
&SignedGreaterThanOrEqual => "sge",
&SignedLessThan => "slt",
&SignedLessThanOrEqual => "sle",
&UnsignedGreaterThan => "ugt",
&UnsignedGreaterThanOrEqual => "uge",
&UnsignedLessThan => "ult",
&UnsignedLessThanOrEqual => "ule",
})
}
}
impl FromStr for IntCC {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::IntCC::*;
match s {
"eq" => Ok(Equal),
"ne" => Ok(NotEqual),
"sge" => Ok(SignedGreaterThanOrEqual),
"sgt" => Ok(SignedGreaterThan),
"sle" => Ok(SignedLessThanOrEqual),
"slt" => Ok(SignedLessThan),
"uge" => Ok(UnsignedGreaterThanOrEqual),
"ugt" => Ok(UnsignedGreaterThan),
"ule" => Ok(UnsignedLessThanOrEqual),
"ult" => Ok(UnsignedLessThan),
_ => Err(()),
}
}
}
/// Condition code for comparing floating point numbers.
///
/// This condition code is used by the `fcmp` instruction to compare floating point values. Two
/// IEEE floating point values relate in exactly one of four ways:
///
/// 1. `UN` - unordered when either value is NaN.
/// 2. `EQ` - equal numerical value.
/// 3. `LT` - `x` is less than `y`.
/// 4. `GT` - `x` is greater than `y`.
///
/// Note that `0.0` and `-0.0` relate as `EQ` because they both represent the number 0.
///
/// The condition codes described here are used to produce a single boolean value from the
/// comparison. The 14 condition codes here cover every possible combination of the relation above
/// except the impossible `!UN & !EQ & !LT & !GT` and the always true `UN | EQ | LT | GT`.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum FloatCC {
Ordered, // EQ | LT | GT
Unordered, // UN
Equal, // EQ
// The C '!=' operator is the inverse of '==': NotEqual.
NotEqual, // UN | LT | GT
OrderedNotEqual, // LT | GT
UnorderedOrEqual, // UN | EQ
LessThan, // LT
LessThanOrEqual, // LT | EQ
GreaterThan, // GT
GreaterThanOrEqual, // GT | EQ
UnorderedOrLessThan, // UN | LT
UnorderedOrLessThanOrEqual, // UN | LT | EQ
UnorderedOrGreaterThan, // UN | GT
UnorderedOrGreaterThanOrEqual, // UN | GT | EQ
}
impl CondCode for FloatCC {
fn inverse(self) -> Self {
use self::FloatCC::*;
match self {
Ordered => Unordered,
Unordered => Ordered,
Equal => NotEqual,
NotEqual => Equal,
OrderedNotEqual => UnorderedOrEqual,
UnorderedOrEqual => OrderedNotEqual,
LessThan => UnorderedOrGreaterThanOrEqual,
LessThanOrEqual => UnorderedOrGreaterThan,
GreaterThan => UnorderedOrLessThanOrEqual,
GreaterThanOrEqual => UnorderedOrLessThan,
UnorderedOrLessThan => GreaterThanOrEqual,
UnorderedOrLessThanOrEqual => GreaterThan,
UnorderedOrGreaterThan => LessThanOrEqual,
UnorderedOrGreaterThanOrEqual => LessThan,
}
}
fn reverse(self) -> Self {
use self::FloatCC::*;
match self {
Ordered => Ordered,
Unordered => Unordered,
Equal => Equal,
NotEqual => NotEqual,
OrderedNotEqual => OrderedNotEqual,
UnorderedOrEqual => UnorderedOrEqual,
LessThan => GreaterThan,
LessThanOrEqual => GreaterThanOrEqual,
GreaterThan => LessThan,
GreaterThanOrEqual => LessThanOrEqual,
UnorderedOrLessThan => UnorderedOrGreaterThan,
UnorderedOrLessThanOrEqual => UnorderedOrGreaterThanOrEqual,
UnorderedOrGreaterThan => UnorderedOrLessThan,
UnorderedOrGreaterThanOrEqual => UnorderedOrLessThanOrEqual,
}
}
}
impl Display for FloatCC {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use self::FloatCC::*;
f.write_str(match self {
&Ordered => "ord",
&Unordered => "uno",
&Equal => "eq",
&NotEqual => "ne",
&OrderedNotEqual => "one",
&UnorderedOrEqual => "ueq",
&LessThan => "lt",
&LessThanOrEqual => "le",
&GreaterThan => "gt",
&GreaterThanOrEqual => "ge",
&UnorderedOrLessThan => "ult",
&UnorderedOrLessThanOrEqual => "ule",
&UnorderedOrGreaterThan => "ugt",
&UnorderedOrGreaterThanOrEqual => "uge",
})
}
}
impl FromStr for FloatCC {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::FloatCC::*;
match s {
"ord" => Ok(Ordered),
"uno" => Ok(Unordered),
"eq" => Ok(Equal),
"ne" => Ok(NotEqual),
"one" => Ok(OrderedNotEqual),
"ueq" => Ok(UnorderedOrEqual),
"lt" => Ok(LessThan),
"le" => Ok(LessThanOrEqual),
"gt" => Ok(GreaterThan),
"ge" => Ok(GreaterThanOrEqual),
"ult" => Ok(UnorderedOrLessThan),
"ule" => Ok(UnorderedOrLessThanOrEqual),
"ugt" => Ok(UnorderedOrGreaterThan),
"uge" => Ok(UnorderedOrGreaterThanOrEqual),
_ => Err(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
static INT_ALL: [IntCC; 10] = [IntCC::Equal,
IntCC::NotEqual,
IntCC::SignedLessThan,
IntCC::SignedGreaterThanOrEqual,
IntCC::SignedGreaterThan,
IntCC::SignedLessThanOrEqual,
IntCC::UnsignedLessThan,
IntCC::UnsignedGreaterThanOrEqual,
IntCC::UnsignedGreaterThan,
IntCC::UnsignedLessThanOrEqual];
#[test]
fn int_inverse() {
for r in &INT_ALL {
let cc = *r;
let inv = cc.inverse();
assert!(cc != inv);
assert_eq!(inv.inverse(), cc);
}
}
#[test]
fn int_reverse() {
for r in &INT_ALL {
let cc = *r;
let rev = cc.reverse();
assert_eq!(rev.reverse(), cc);
}
}
#[test]
fn int_display() {
for r in &INT_ALL {
let cc = *r;
assert_eq!(cc.to_string().parse(), Ok(cc));
}
}
static FLOAT_ALL: [FloatCC; 14] = [FloatCC::Ordered,
FloatCC::Unordered,
FloatCC::Equal,
FloatCC::NotEqual,
FloatCC::OrderedNotEqual,
FloatCC::UnorderedOrEqual,
FloatCC::LessThan,
FloatCC::LessThanOrEqual,
FloatCC::GreaterThan,
FloatCC::GreaterThanOrEqual,
FloatCC::UnorderedOrLessThan,
FloatCC::UnorderedOrLessThanOrEqual,
FloatCC::UnorderedOrGreaterThan,
FloatCC::UnorderedOrGreaterThanOrEqual];
#[test]
fn float_inverse() {
for r in &FLOAT_ALL {
let cc = *r;
let inv = cc.inverse();
assert!(cc != inv);
assert_eq!(inv.inverse(), cc);
}
}
#[test]
fn float_reverse() {
for r in &FLOAT_ALL {
let cc = *r;
let rev = cc.reverse();
assert_eq!(rev.reverse(), cc);
}
}
#[test]
fn float_display() {
for r in &FLOAT_ALL {
let cc = *r;
assert_eq!(cc.to_string().parse(), Ok(cc));
}
}
}

443
lib/cretonne/src/ir/dfg.rs Normal file
View File

@@ -0,0 +1,443 @@
//! Data flow graph tracking Instructions, Values, and EBBs.
use ir::{Ebb, Inst, Value, Type};
use ir::entities::{NO_VALUE, ExpandedValue};
use ir::instructions::InstructionData;
use entity_map::{EntityMap, PrimaryEntityData};
use std::ops::{Index, IndexMut};
use std::u16;
/// A data flow graph defines all instuctions and extended basic blocks in a function as well as
/// the data flow dependencies between them. The DFG also tracks values which can be either
/// instruction results or EBB arguments.
///
/// The layout of EBBs in the function and of instructions in each EBB is recorded by the
/// `FunctionLayout` data structure which form the other half of the function representation.
///
#[derive(Clone)]
pub struct DataFlowGraph {
/// Data about all of the instructions in the function, including opcodes and operands.
/// The instructions in this map are not in program order. That is tracked by `Layout`, along
/// with the EBB containing each instruction.
insts: EntityMap<Inst, InstructionData>,
/// Extended basic blocks in the function and their arguments.
/// This map is not in program order. That is handled by `Layout`, and so is the sequence of
/// instructions contained in each EBB.
ebbs: EntityMap<Ebb, EbbData>,
/// Extended value table. Most `Value` references refer directly to their defining instruction.
/// Others index into this table.
///
/// This is implemented directly with a `Vec` rather than an `EntityMap<Value, ...>` because
/// the Value entity references can refer to two things -- an instruction or an extended value.
extended_values: Vec<ValueData>,
}
impl PrimaryEntityData for InstructionData {}
impl PrimaryEntityData for EbbData {}
impl DataFlowGraph {
/// Create a new empty `DataFlowGraph`.
pub fn new() -> DataFlowGraph {
DataFlowGraph {
insts: EntityMap::new(),
ebbs: EntityMap::new(),
extended_values: Vec::new(),
}
}
/// Get the total number of instructions created in this function, whether they are currently
/// inserted in the layout or not.
///
/// This is intended for use with `EntityMap::with_capacity`.
pub fn num_insts(&self) -> usize {
self.insts.len()
}
/// Get the total number of extended basic blocks created in this function, whether they are
/// currently inserted in the layout or not.
///
/// This is intended for use with `EntityMap::with_capacity`.
pub fn num_ebbs(&self) -> usize {
self.ebbs.len()
}
}
/// Handling values.
///
/// Values are either EBB arguments or instruction results.
impl DataFlowGraph {
// Allocate an extended value entry.
fn make_value(&mut self, data: ValueData) -> Value {
let vref = Value::new_table(self.extended_values.len());
self.extended_values.push(data);
vref
}
/// Get the type of a value.
pub fn value_type(&self, v: Value) -> Type {
use ir::entities::ExpandedValue::*;
match v.expand() {
Direct(i) => self.insts[i].first_type(),
Table(i) => {
match self.extended_values[i] {
ValueData::Inst { ty, .. } => ty,
ValueData::Arg { ty, .. } => ty,
}
}
None => panic!("NO_VALUE has no type"),
}
}
/// Get the definition of a value.
///
/// This is either the instruction that defined it or the Ebb that has the value as an
/// argument.
pub fn value_def(&self, v: Value) -> ValueDef {
use ir::entities::ExpandedValue::*;
match v.expand() {
Direct(inst) => ValueDef::Res(inst, 0),
Table(idx) => {
match self.extended_values[idx] {
ValueData::Inst { inst, num, .. } => ValueDef::Res(inst, num as usize),
ValueData::Arg { ebb, num, .. } => ValueDef::Arg(ebb, num as usize),
}
}
None => panic!("NO_VALUE has no def"),
}
}
}
/// Where did a value come from?
#[derive(Debug, PartialEq, Eq)]
pub enum ValueDef {
/// Value is the n'th result of an instruction.
Res(Inst, usize),
/// Value is the n'th argument to an EBB.
Arg(Ebb, usize),
}
// Internal table storage for extended values.
#[derive(Clone)]
enum ValueData {
// Value is defined by an instruction, but it is not the first result.
Inst {
ty: Type,
num: u16, // Result number starting from 0.
inst: Inst,
next: Value, // Next result defined by `def`.
},
// Value is an EBB argument.
Arg {
ty: Type,
num: u16, // Argument number, starting from 0.
ebb: Ebb,
next: Value, // Next argument to `ebb`.
},
}
/// Iterate through a list of related value references, such as:
///
/// - All results defined by an instruction. See `DataFlowGraph::inst_results`.
/// - All arguments to an EBB. See `DataFlowGraph::ebb_args`.
///
/// A value iterator borrows a `DataFlowGraph` reference.
pub struct Values<'a> {
dfg: &'a DataFlowGraph,
cur: Value,
}
impl<'a> Iterator for Values<'a> {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
let prev = self.cur;
// Advance self.cur to the next value, or NO_VALUE.
self.cur = match prev.expand() {
ExpandedValue::Direct(inst) => self.dfg.insts[inst].second_result().unwrap_or_default(),
ExpandedValue::Table(index) => {
match self.dfg.extended_values[index] {
ValueData::Inst { next, .. } => next,
ValueData::Arg { next, .. } => next,
}
}
ExpandedValue::None => return None,
};
Some(prev)
}
}
/// Instructions.
///
impl DataFlowGraph {
/// Create a new instruction.
///
/// The type of the first result is indicated by `data.ty`. If the instruction produces
/// multiple results, also call `make_inst_results` to allocate value table entries.
pub fn make_inst(&mut self, data: InstructionData) -> Inst {
self.insts.push(data)
}
/// Create result values for an instruction that produces multiple results.
///
/// Instructions that produce 0 or 1 result values only need to be created with `make_inst`. If
/// the instruction may produce more than 1 result, call `make_inst_results` to allocate
/// value table entries for the additional results.
///
/// The result value types are determined from the instruction's value type constraints and the
/// provided `ctrl_typevar` type for polymorphic instructions. For non-polymorphic
/// instructions, `ctrl_typevar` is ignored, and `VOID` can be used.
///
/// The type of the first result value is also set, even if it was already set in the
/// `InstructionData` passed to `make_inst`. If this function is called with a single-result
/// instruction, that is the only effect.
///
/// Returns the number of results produced by the instruction.
pub fn make_inst_results(&mut self, inst: Inst, ctrl_typevar: Type) -> usize {
let constraints = self.insts[inst].opcode().constraints();
let fixed_results = constraints.fixed_results();
// Additional values form a linked list starting from the second result value. Generate
// the list backwards so we don't have to modify value table entries in place. (This
// causes additional result values to be numbered backwards which is not the aestetic
// choice, but since it is only visible in extremely rare instructions with 3+ results,
// we don't care).
let mut head = NO_VALUE;
let mut first_type = Type::default();
// TBD: Function call return values for direct and indirect function calls.
if fixed_results > 0 {
for res_idx in (1..fixed_results).rev() {
head = self.make_value(ValueData::Inst {
ty: constraints.result_type(res_idx, ctrl_typevar),
num: res_idx as u16,
inst: inst,
next: head,
});
}
first_type = constraints.result_type(0, ctrl_typevar);
}
// Update the second_result pointer in `inst`.
if head != NO_VALUE {
*self.insts[inst]
.second_result_mut()
.expect("instruction format doesn't allow multiple results") = head;
}
*self.insts[inst].first_type_mut() = first_type;
fixed_results
}
/// Get the first result of an instruction.
///
/// If `Inst` doesn't produce any results, this returns a `Value` with a `VOID` type.
pub fn first_result(&self, inst: Inst) -> Value {
Value::new_direct(inst)
}
/// Iterate through all the results of an instruction.
pub fn inst_results<'a>(&'a self, inst: Inst) -> Values<'a> {
Values {
dfg: self,
cur: if self.insts[inst].first_type().is_void() {
NO_VALUE
} else {
Value::new_direct(inst)
},
}
}
}
/// Allow immutable access to instructions via indexing.
impl Index<Inst> for DataFlowGraph {
type Output = InstructionData;
fn index<'a>(&'a self, inst: Inst) -> &'a InstructionData {
&self.insts[inst]
}
}
/// Allow mutable access to instructions via indexing.
impl IndexMut<Inst> for DataFlowGraph {
fn index_mut<'a>(&'a mut self, inst: Inst) -> &'a mut InstructionData {
&mut self.insts[inst]
}
}
/// Extended basic blocks.
impl DataFlowGraph {
/// Create a new basic block.
pub fn make_ebb(&mut self) -> Ebb {
self.ebbs.push(EbbData::new())
}
/// Get the number of arguments on `ebb`.
pub fn num_ebb_args(&self, ebb: Ebb) -> usize {
let last_arg = self.ebbs[ebb].last_arg;
match last_arg.expand() {
ExpandedValue::None => 0,
ExpandedValue::Table(idx) => {
if let ValueData::Arg { num, .. } = self.extended_values[idx] {
num as usize + 1
} else {
panic!("inconsistent value table entry for EBB arg");
}
}
ExpandedValue::Direct(_) => panic!("inconsistent value table entry for EBB arg"),
}
}
/// Append an argument with type `ty` to `ebb`.
pub fn append_ebb_arg(&mut self, ebb: Ebb, ty: Type) -> Value {
let num_args = self.num_ebb_args(ebb);
assert!(num_args <= u16::MAX as usize, "Too many arguments to EBB");
let val = self.make_value(ValueData::Arg {
ty: ty,
ebb: ebb,
num: num_args as u16,
next: NO_VALUE,
});
let last_arg = self.ebbs[ebb].last_arg;
match last_arg.expand() {
// If last_arg is NO_VALUE, we're adding the first EBB argument.
ExpandedValue::None => {
self.ebbs[ebb].first_arg = val;
}
// Append to linked list of arguments.
ExpandedValue::Table(idx) => {
if let ValueData::Arg { ref mut next, .. } = self.extended_values[idx] {
*next = val;
} else {
panic!("inconsistent value table entry for EBB arg");
}
}
ExpandedValue::Direct(_) => panic!("inconsistent value table entry for EBB arg"),
};
self.ebbs[ebb].last_arg = val;
val
}
/// Iterate through the arguments to an EBB.
pub fn ebb_args<'a>(&'a self, ebb: Ebb) -> Values<'a> {
Values {
dfg: self,
cur: self.ebbs[ebb].first_arg,
}
}
}
// Contents of an extended basic block.
//
// Arguments for an extended basic block are values that dominate everything in the EBB. All
// branches to this EBB must provide matching arguments, and the arguments to the entry EBB must
// match the function arguments.
#[derive(Clone)]
struct EbbData {
// First argument to this EBB, or `NO_VALUE` if the block has no arguments.
//
// The arguments are all ValueData::Argument entries that form a linked list from `first_arg`
// to `last_arg`.
first_arg: Value,
// Last argument to this EBB, or `NO_VALUE` if the block has no arguments.
last_arg: Value,
}
impl EbbData {
fn new() -> EbbData {
EbbData {
first_arg: NO_VALUE,
last_arg: NO_VALUE,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ir::types;
use ir::{Opcode, InstructionData};
#[test]
fn make_inst() {
let mut dfg = DataFlowGraph::new();
let idata = InstructionData::Nullary {
opcode: Opcode::Iconst,
ty: types::I32,
};
let inst = dfg.make_inst(idata);
assert_eq!(inst.to_string(), "inst0");
// Immutable reference resolution.
{
let immdfg = &dfg;
let ins = &immdfg[inst];
assert_eq!(ins.opcode(), Opcode::Iconst);
assert_eq!(ins.first_type(), types::I32);
}
// Result iterator.
let mut res = dfg.inst_results(inst);
let val = res.next().unwrap();
assert!(res.next().is_none());
assert_eq!(dfg.value_def(val), ValueDef::Res(inst, 0));
assert_eq!(dfg.value_type(val), types::I32);
}
#[test]
fn no_results() {
let mut dfg = DataFlowGraph::new();
let idata = InstructionData::Nullary {
opcode: Opcode::Trap,
ty: types::VOID,
};
let inst = dfg.make_inst(idata);
// Result iterator should be empty.
let mut res = dfg.inst_results(inst);
assert_eq!(res.next(), None);
}
#[test]
fn ebb() {
let mut dfg = DataFlowGraph::new();
let ebb = dfg.make_ebb();
assert_eq!(ebb.to_string(), "ebb0");
assert_eq!(dfg.num_ebb_args(ebb), 0);
assert_eq!(dfg.ebb_args(ebb).next(), None);
let arg1 = dfg.append_ebb_arg(ebb, types::F32);
assert_eq!(arg1.to_string(), "vx0");
assert_eq!(dfg.num_ebb_args(ebb), 1);
{
let mut args1 = dfg.ebb_args(ebb);
assert_eq!(args1.next(), Some(arg1));
assert_eq!(args1.next(), None);
}
let arg2 = dfg.append_ebb_arg(ebb, types::I16);
assert_eq!(arg2.to_string(), "vx1");
assert_eq!(dfg.num_ebb_args(ebb), 2);
{
let mut args2 = dfg.ebb_args(ebb);
assert_eq!(args2.next(), Some(arg1));
assert_eq!(args2.next(), Some(arg2));
assert_eq!(args2.next(), None);
}
assert_eq!(dfg.value_def(arg1), ValueDef::Arg(ebb, 0));
assert_eq!(dfg.value_def(arg2), ValueDef::Arg(ebb, 1));
assert_eq!(dfg.value_type(arg1), types::F32);
assert_eq!(dfg.value_type(arg2), types::I16);
}
}

View File

@@ -0,0 +1,408 @@
//! IL entity references.
//!
//! Instructions in Cretonne IL need to reference other entities in the function. This can be other
//! parts of the function like extended basic blocks or stack slots, or it can be external entities
//! that are declared in the function preamble in the text format.
//!
//! These entity references in instruction operands are not implemented as Rust references both
//! because Rust's ownership and mutability rules make it difficult, and because 64-bit pointers
//! take up a lot of space, and we want a compact in-memory representation. Instead, entity
//! references are structs wrapping a `u32` index into a table in the `Function` main data
//! structure. There is a separate index type for each entity type, so we don't lose type safety.
//!
//! The `entities` module defines public types for the entity references along with constants
//! representing an invalid reference. We prefer to use `Option<EntityRef>` whenever possible, but
//! unfortunately that type is twice as large as the 32-bit index type on its own. Thus, compact
//! data structures use the sentinen constant, while function arguments and return values prefer
//! the more Rust-like `Option<EntityRef>` variant.
//!
//! The entity references all implement the `Display` trait in a way that matches the textual IL
//! format.
use entity_map::EntityRef;
use std::default::Default;
use std::fmt::{self, Display, Formatter};
use std::u32;
/// An opaque reference to an extended basic block in a function.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Ebb(u32);
impl EntityRef for Ebb {
fn new(index: usize) -> Self {
assert!(index < (u32::MAX as usize));
Ebb(index as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
impl Ebb {
/// Create a new EBB reference from its number. This corresponds to the ebbNN representation.
pub fn with_number(n: u32) -> Option<Ebb> {
if n < u32::MAX { Some(Ebb(n)) } else { None }
}
}
/// Display an `Ebb` reference as "ebb12".
impl Display for Ebb {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "ebb{}", self.0)
}
}
/// A guaranteed invalid EBB reference.
pub const NO_EBB: Ebb = Ebb(u32::MAX);
impl Default for Ebb {
fn default() -> Ebb {
NO_EBB
}
}
/// An opaque reference to an instruction in a function.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Inst(u32);
impl EntityRef for Inst {
fn new(index: usize) -> Self {
assert!(index < (u32::MAX as usize));
Inst(index as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
/// Display an `Inst` reference as "inst7".
impl Display for Inst {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "inst{}", self.0)
}
}
/// A guaranteed invalid instruction reference.
pub const NO_INST: Inst = Inst(u32::MAX);
impl Default for Inst {
fn default() -> Inst {
NO_INST
}
}
/// An opaque reference to an SSA value.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Value(u32);
// Value references can either reference an instruction directly, or they can refer to the extended
// value table.
pub enum ExpandedValue {
// This is the first value produced by the referenced instruction.
Direct(Inst),
// This value is described in the extended value table.
Table(usize),
// This is NO_VALUE.
None,
}
impl Value {
/// Create a `Direct` value from its number representation.
/// This is the number in the vNN notation.
pub fn direct_with_number(n: u32) -> Option<Value> {
if n < u32::MAX / 2 {
let encoding = n * 2;
assert!(encoding < u32::MAX);
Some(Value(encoding))
} else {
None
}
}
/// Create a `Table` value from its number representation.
/// This is the number in the vxNN notation.
pub fn table_with_number(n: u32) -> Option<Value> {
if n < u32::MAX / 2 {
let encoding = n * 2 + 1;
assert!(encoding < u32::MAX);
Some(Value(encoding))
} else {
None
}
}
pub fn new_direct(i: Inst) -> Value {
let encoding = i.index() * 2;
assert!(encoding < u32::MAX as usize);
Value(encoding as u32)
}
pub fn new_table(index: usize) -> Value {
let encoding = index * 2 + 1;
assert!(encoding < u32::MAX as usize);
Value(encoding as u32)
}
// Expand the internal representation into something useful.
pub fn expand(&self) -> ExpandedValue {
use self::ExpandedValue::*;
if *self == NO_VALUE {
return None;
}
let index = (self.0 / 2) as usize;
if self.0 % 2 == 0 {
Direct(Inst::new(index))
} else {
Table(index)
}
}
}
/// Display a `Value` reference as "v7" or "v2x".
impl Display for Value {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
use self::ExpandedValue::*;
match self.expand() {
Direct(i) => write!(fmt, "v{}", i.0),
Table(i) => write!(fmt, "vx{}", i),
None => write!(fmt, "NO_VALUE"),
}
}
}
/// A guaranteed invalid value reference.
pub const NO_VALUE: Value = Value(u32::MAX);
impl Default for Value {
fn default() -> Value {
NO_VALUE
}
}
/// An opaque reference to a stack slot.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct StackSlot(u32);
impl EntityRef for StackSlot {
fn new(index: usize) -> StackSlot {
assert!(index < (u32::MAX as usize));
StackSlot(index as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
/// Display a `StackSlot` reference as "ss12".
impl Display for StackSlot {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "ss{}", self.0)
}
}
/// A guaranteed invalid stack slot reference.
pub const NO_STACK_SLOT: StackSlot = StackSlot(u32::MAX);
impl Default for StackSlot {
fn default() -> StackSlot {
NO_STACK_SLOT
}
}
/// An opaque reference to a jump table.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct JumpTable(u32);
impl EntityRef for JumpTable {
fn new(index: usize) -> JumpTable {
assert!(index < (u32::MAX as usize));
JumpTable(index as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
/// Display a `JumpTable` reference as "jt12".
impl Display for JumpTable {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "jt{}", self.0)
}
}
/// A guaranteed invalid jump table reference.
pub const NO_JUMP_TABLE: JumpTable = JumpTable(u32::MAX);
impl Default for JumpTable {
fn default() -> JumpTable {
NO_JUMP_TABLE
}
}
/// A reference to an external function.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct FuncRef(u32);
impl EntityRef for FuncRef {
fn new(index: usize) -> FuncRef {
assert!(index < (u32::MAX as usize));
FuncRef(index as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
/// Display a `FuncRef` reference as "fn12".
impl Display for FuncRef {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "fn{}", self.0)
}
}
/// A guaranteed invalid function reference.
pub const NO_FUNC_REF: FuncRef = FuncRef(u32::MAX);
impl Default for FuncRef {
fn default() -> FuncRef {
NO_FUNC_REF
}
}
/// A reference to a function signature.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct SigRef(u32);
impl EntityRef for SigRef {
fn new(index: usize) -> SigRef {
assert!(index < (u32::MAX as usize));
SigRef(index as u32)
}
fn index(self) -> usize {
self.0 as usize
}
}
/// Display a `SigRef` reference as "sig12".
impl Display for SigRef {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "sig{}", self.0)
}
}
/// A guaranteed invalid function reference.
pub const NO_SIG_REF: SigRef = SigRef(u32::MAX);
impl Default for SigRef {
fn default() -> SigRef {
NO_SIG_REF
}
}
/// A reference to any of the entities defined in this module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum AnyEntity {
/// The whole function.
Function,
Ebb(Ebb),
Inst(Inst),
Value(Value),
StackSlot(StackSlot),
JumpTable(JumpTable),
FuncRef(FuncRef),
SigRef(SigRef),
}
impl Display for AnyEntity {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
match *self {
AnyEntity::Function => write!(fmt, "function"),
AnyEntity::Ebb(r) => r.fmt(fmt),
AnyEntity::Inst(r) => r.fmt(fmt),
AnyEntity::Value(r) => r.fmt(fmt),
AnyEntity::StackSlot(r) => r.fmt(fmt),
AnyEntity::JumpTable(r) => r.fmt(fmt),
AnyEntity::FuncRef(r) => r.fmt(fmt),
AnyEntity::SigRef(r) => r.fmt(fmt),
}
}
}
impl From<Ebb> for AnyEntity {
fn from(r: Ebb) -> AnyEntity {
AnyEntity::Ebb(r)
}
}
impl From<Inst> for AnyEntity {
fn from(r: Inst) -> AnyEntity {
AnyEntity::Inst(r)
}
}
impl From<Value> for AnyEntity {
fn from(r: Value) -> AnyEntity {
AnyEntity::Value(r)
}
}
impl From<StackSlot> for AnyEntity {
fn from(r: StackSlot) -> AnyEntity {
AnyEntity::StackSlot(r)
}
}
impl From<JumpTable> for AnyEntity {
fn from(r: JumpTable) -> AnyEntity {
AnyEntity::JumpTable(r)
}
}
impl From<FuncRef> for AnyEntity {
fn from(r: FuncRef) -> AnyEntity {
AnyEntity::FuncRef(r)
}
}
impl From<SigRef> for AnyEntity {
fn from(r: SigRef) -> AnyEntity {
AnyEntity::SigRef(r)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::u32;
use entity_map::EntityRef;
#[test]
fn value_with_number() {
assert_eq!(Value::direct_with_number(0).unwrap().to_string(), "v0");
assert_eq!(Value::direct_with_number(1).unwrap().to_string(), "v1");
assert_eq!(Value::table_with_number(0).unwrap().to_string(), "vx0");
assert_eq!(Value::table_with_number(1).unwrap().to_string(), "vx1");
assert_eq!(Value::direct_with_number(u32::MAX / 2), None);
assert_eq!(match Value::direct_with_number(u32::MAX / 2 - 1).unwrap().expand() {
ExpandedValue::Direct(i) => i.index() as u32,
_ => u32::MAX,
},
u32::MAX / 2 - 1);
assert_eq!(Value::table_with_number(u32::MAX / 2), None);
assert_eq!(match Value::table_with_number(u32::MAX / 2 - 1).unwrap().expand() {
ExpandedValue::Table(i) => i as u32,
_ => u32::MAX,
},
u32::MAX / 2 - 1);
}
}

View File

@@ -0,0 +1,135 @@
//! External function calls.
//!
//! To a Cretonne function, all functions are "external". Directly called functions must be
//! declared in the preamble, and all function calls must have a signature.
//!
//! This module declares the data types used to represent external functions and call signatures.
use std::fmt::{self, Display, Formatter};
use ir::Type;
/// Function signature.
///
/// The function signature describes the types of arguments and return values along with other
/// details that are needed to call a function correctly.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Signature {
pub argument_types: Vec<ArgumentType>,
pub return_types: Vec<ArgumentType>,
}
impl Signature {
pub fn new() -> Signature {
Signature {
argument_types: Vec::new(),
return_types: Vec::new(),
}
}
}
fn write_list(f: &mut Formatter, args: &Vec<ArgumentType>) -> fmt::Result {
match args.split_first() {
None => {}
Some((first, rest)) => {
try!(write!(f, "{}", first));
for arg in rest {
try!(write!(f, ", {}", arg));
}
}
}
Ok(())
}
impl Display for Signature {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
try!(write!(f, "("));
try!(write_list(f, &self.argument_types));
try!(write!(f, ")"));
if !self.return_types.is_empty() {
try!(write!(f, " -> "));
try!(write_list(f, &self.return_types));
}
Ok(())
}
}
/// Function argument or return value type.
///
/// This describes the value type being passed to or from a function along with flags that affect
/// how the argument is passed.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct ArgumentType {
pub value_type: Type,
pub extension: ArgumentExtension,
/// Place this argument in a register if possible.
pub inreg: bool,
}
impl ArgumentType {
pub fn new(vt: Type) -> ArgumentType {
ArgumentType {
value_type: vt,
extension: ArgumentExtension::None,
inreg: false,
}
}
}
impl Display for ArgumentType {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
try!(write!(f, "{}", self.value_type));
match self.extension {
ArgumentExtension::None => {}
ArgumentExtension::Uext => try!(write!(f, " uext")),
ArgumentExtension::Sext => try!(write!(f, " sext")),
}
if self.inreg {
try!(write!(f, " inreg"));
}
Ok(())
}
}
/// Function argument extension options.
///
/// On some architectures, small integer function arguments are extended to the width of a
/// general-purpose register.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum ArgumentExtension {
/// No extension, high bits are indeterminate.
None,
/// Unsigned extension: high bits in register are 0.
Uext,
/// Signed extension: high bits in register replicate sign bit.
Sext,
}
#[cfg(test)]
mod tests {
use super::*;
use ir::types::{I32, F32, B8};
#[test]
fn argument_type() {
let mut t = ArgumentType::new(I32);
assert_eq!(t.to_string(), "i32");
t.extension = ArgumentExtension::Uext;
assert_eq!(t.to_string(), "i32 uext");
t.inreg = true;
assert_eq!(t.to_string(), "i32 uext inreg");
}
#[test]
fn signatures() {
let mut sig = Signature::new();
assert_eq!(sig.to_string(), "()");
sig.argument_types.push(ArgumentType::new(I32));
assert_eq!(sig.to_string(), "(i32)");
sig.return_types.push(ArgumentType::new(F32));
assert_eq!(sig.to_string(), "(i32) -> f32");
sig.argument_types.push(ArgumentType::new(I32.by(4).unwrap()));
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32");
sig.return_types.push(ArgumentType::new(B8));
assert_eq!(sig.to_string(), "(i32, i32x4) -> f32, b8");
}
}

View File

@@ -0,0 +1,77 @@
//! Function names.
//!
//! The name of a function doesn't have any meaning to Cretonne which compiles functions
//! independently.
use std::fmt::{self, Write};
use std::ascii::AsciiExt;
/// The name of a function can be any UTF-8 string.
///
/// Function names are mostly a testing and debugging tool.
/// In particular, `.cton` files use function names to identify functions.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct FunctionName(String);
impl FunctionName {
pub fn new<S: Into<String>>(s: S) -> FunctionName {
FunctionName(s.into())
}
}
fn is_id_start(c: char) -> bool {
c.is_ascii() && (c == '_' || c.is_alphabetic())
}
fn is_id_continue(c: char) -> bool {
c.is_ascii() && (c == '_' || c.is_alphanumeric())
}
// The function name may need quotes if it doesn't parse as an identifier.
fn needs_quotes(name: &str) -> bool {
let mut iter = name.chars();
if let Some(ch) = iter.next() {
!is_id_start(ch) || !iter.all(is_id_continue)
} else {
// A blank function name needs quotes.
true
}
}
impl fmt::Display for FunctionName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if needs_quotes(&self.0) {
try!(f.write_char('"'));
for c in self.0.chars().flat_map(char::escape_default) {
try!(f.write_char(c));
}
f.write_char('"')
} else {
f.write_str(&self.0)
}
}
}
#[cfg(test)]
mod tests {
use super::{needs_quotes, FunctionName};
#[test]
fn quoting() {
assert_eq!(needs_quotes(""), true);
assert_eq!(needs_quotes("x"), false);
assert_eq!(needs_quotes(" "), true);
assert_eq!(needs_quotes("0"), true);
assert_eq!(needs_quotes("x0"), false);
}
#[test]
fn escaping() {
assert_eq!(FunctionName::new("").to_string(), "\"\"");
assert_eq!(FunctionName::new("x").to_string(), "x");
assert_eq!(FunctionName::new(" ").to_string(), "\" \"");
assert_eq!(FunctionName::new(" \n").to_string(), "\" \\n\"");
assert_eq!(FunctionName::new("a\u{1000}v").to_string(),
"\"a\\u{1000}v\"");
}
}

View File

@@ -0,0 +1,80 @@
//! Intermediate representation of a function.
//!
//! The `Function` struct defined in this module owns all of its extended basic blocks and
//! instructions.
use std::fmt::{self, Display, Debug, Formatter};
use ir::{FunctionName, Signature, Inst, StackSlot, StackSlotData, JumpTable, JumpTableData,
DataFlowGraph, Layout};
use isa::Encoding;
use entity_map::{EntityMap, PrimaryEntityData};
use write::write_function;
/// A function.
///
/// Functions can be cloned, but it is not a very fast operation.
/// The clone will have all the same entity numbers as the original.
#[derive(Clone)]
pub struct Function {
/// Name of this function. Mostly used by `.cton` files.
pub name: FunctionName,
/// Signature of this function.
signature: Signature,
/// Stack slots allocated in this function.
pub stack_slots: EntityMap<StackSlot, StackSlotData>,
/// Jump tables used in this function.
pub jump_tables: EntityMap<JumpTable, JumpTableData>,
/// Data flow graph containing the primary definition of all instructions, EBBs and values.
pub dfg: DataFlowGraph,
/// Layout of EBBs and instructions in the function body.
pub layout: Layout,
/// Encoding recipe and bits for the legal instructions.
/// Illegal instructions have the `Encoding::default()` value.
pub encodings: EntityMap<Inst, Encoding>,
}
impl PrimaryEntityData for StackSlotData {}
impl PrimaryEntityData for JumpTableData {}
impl Function {
/// Create a function with the given name and signature.
pub fn with_name_signature(name: FunctionName, sig: Signature) -> Function {
Function {
name: name,
signature: sig,
stack_slots: EntityMap::new(),
jump_tables: EntityMap::new(),
dfg: DataFlowGraph::new(),
layout: Layout::new(),
encodings: EntityMap::new(),
}
}
/// Create a new empty, anomymous function.
pub fn new() -> Function {
Self::with_name_signature(FunctionName::default(), Signature::new())
}
/// Get the signature of this function.
pub fn own_signature(&self) -> &Signature {
&self.signature
}
}
impl Display for Function {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write_function(fmt, self, None)
}
}
impl Debug for Function {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write_function(fmt, self, None)
}
}

View File

@@ -0,0 +1,717 @@
//! Immediate operands for Cretonne instructions
//!
//! This module defines the types of immediate operands that can appear on Cretonne instructions.
//! Each type here should have a corresponding definition in the `cretonne.immediates` Python
//! module in the meta language.
use std::fmt::{self, Display, Formatter};
use std::mem;
use std::str::FromStr;
/// 64-bit immediate integer operand.
///
/// An `Imm64` operand can also be used to represent immediate values of smaller integer types by
/// sign-extending to `i64`.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Imm64(i64);
impl Imm64 {
pub fn new(x: i64) -> Imm64 {
Imm64(x)
}
}
impl Into<i64> for Imm64 {
fn into(self) -> i64 {
self.0
}
}
impl Display for Imm64 {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let x = self.0;
if -10_000 < x && x < 10_000 {
// Use decimal for small numbers.
write!(f, "{}", x)
} else {
// Hexadecimal with a multiple of 4 digits and group separators:
//
// 0xfff0
// 0x0001_ffff
// 0xffff_ffff_fff8_4400
//
let mut pos = (64 - x.leading_zeros() - 1) & 0xf0;
try!(write!(f, "0x{:04x}", (x >> pos) & 0xffff));
while pos > 0 {
pos -= 16;
try!(write!(f, "_{:04x}", (x >> pos) & 0xffff));
}
Ok(())
}
}
}
impl FromStr for Imm64 {
type Err = &'static str;
// Parse a decimal or hexadecimal Imm64, formatted as above.
fn from_str(s: &str) -> Result<Imm64, &'static str> {
let mut value: u64 = 0;
let mut digits = 0;
let negative = s.starts_with('-');
let s2 = if negative { &s[1..] } else { s };
if s2.starts_with("0x") {
// Hexadecimal.
for ch in s2[2..].chars() {
match ch.to_digit(16) {
Some(digit) => {
digits += 1;
if digits > 16 {
return Err("Too many hexadecimal digits in Imm64");
}
// This can't overflow given the digit limit.
value = (value << 4) | digit as u64;
}
None => {
// Allow embedded underscores, but fail on anything else.
if ch != '_' {
return Err("Invalid character in hexadecimal Imm64");
}
}
}
}
} else {
// Decimal number, possibly negative.
for ch in s2.chars() {
match ch.to_digit(16) {
Some(digit) => {
digits += 1;
match value.checked_mul(10) {
None => return Err("Too large decimal Imm64"),
Some(v) => value = v,
}
match value.checked_add(digit as u64) {
None => return Err("Too large decimal Imm64"),
Some(v) => value = v,
}
}
None => {
// Allow embedded underscores, but fail on anything else.
if ch != '_' {
return Err("Invalid character in decimal Imm64");
}
}
}
}
}
if digits == 0 {
return Err("No digits in Imm64");
}
// We support the range-and-a-half from -2^63 .. 2^64-1.
if negative {
value = value.wrapping_neg();
// Don't allow large negative values to wrap around and become positive.
if value as i64 > 0 {
return Err("Negative number too small for Imm64");
}
}
Ok(Imm64::new(value as i64))
}
}
/// 8-bit unsigned integer immediate operand.
///
/// This is used to indicate lane indexes typically.
pub type Uimm8 = u8;
/// An IEEE binary32 immediate floating point value.
///
/// All bit patterns are allowed.
#[derive(Copy, Clone, Debug)]
pub struct Ieee32(f32);
/// An IEEE binary64 immediate floating point value.
///
/// All bit patterns are allowed.
#[derive(Copy, Clone, Debug)]
pub struct Ieee64(f64);
// Format a floating point number in a way that is reasonably human-readable, and that can be
// converted back to binary without any rounding issues. The hexadecimal formatting of normal and
// subnormal numbers is compatible with C99 and the printf "%a" format specifier. The NaN and Inf
// formats are not supported by C99.
//
// The encoding parameters are:
//
// w - exponent field width in bits
// t - trailing significand field width in bits
//
fn format_float(bits: u64, w: u8, t: u8, f: &mut Formatter) -> fmt::Result {
debug_assert!(w > 0 && w <= 16, "Invalid exponent range");
debug_assert!(1 + w + t <= 64, "Too large IEEE format for u64");
debug_assert!((t + w + 1).is_power_of_two(), "Unexpected IEEE format size");
let max_e_bits = (1u64 << w) - 1;
let t_bits = bits & ((1u64 << t) - 1); // Trailing significand.
let e_bits = (bits >> t) & max_e_bits; // Biased exponent.
let sign_bit = (bits >> w + t) & 1;
let bias: i32 = (1 << (w - 1)) - 1;
let e = e_bits as i32 - bias; // Unbiased exponent.
let emin = 1 - bias; // Minimum exponent.
// How many hexadecimal digits are needed for the trailing significand?
let digits = (t + 3) / 4;
// Trailing significand left-aligned in `digits` hexadecimal digits.
let left_t_bits = t_bits << (4 * digits - t);
// All formats share the leading sign.
if sign_bit != 0 {
try!(write!(f, "-"));
}
if e_bits == 0 {
if t_bits == 0 {
// Zero.
write!(f, "0.0")
} else {
// Subnormal.
write!(f, "0x0.{0:01$x}p{2}", left_t_bits, digits as usize, emin)
}
} else if e_bits == max_e_bits {
if t_bits == 0 {
// Infinity.
write!(f, "Inf")
} else {
// NaN.
let payload = t_bits & ((1 << (t - 1)) - 1);
if t_bits & (1 << (t - 1)) != 0 {
// Quiet NaN.
if payload != 0 {
write!(f, "NaN:0x{:x}", payload)
} else {
write!(f, "NaN")
}
} else {
// Signaling NaN.
write!(f, "sNaN:0x{:x}", payload)
}
}
} else {
// Normal number.
write!(f, "0x1.{0:01$x}p{2}", left_t_bits, digits as usize, e)
}
}
// Parse a float using the same format as `format_float` above.
//
// The encoding parameters are:
//
// w - exponent field width in bits
// t - trailing significand field width in bits
//
fn parse_float(s: &str, w: u8, t: u8) -> Result<u64, &'static str> {
debug_assert!(w > 0 && w <= 16, "Invalid exponent range");
debug_assert!(1 + w + t <= 64, "Too large IEEE format for u64");
debug_assert!((t + w + 1).is_power_of_two(), "Unexpected IEEE format size");
let (sign_bit, s2) = if s.starts_with('-') {
(1u64 << t + w, &s[1..])
} else {
(0, s)
};
if !s2.starts_with("0x") {
let max_e_bits = ((1u64 << w) - 1) << t;
let quiet_bit = 1u64 << (t - 1);
// The only decimal encoding allowed is 0.
if s2 == "0.0" {
return Ok(sign_bit);
}
if s2 == "Inf" {
// +/- infinity: e = max, t = 0.
return Ok(sign_bit | max_e_bits);
}
if s2 == "NaN" {
// Canonical quiet NaN: e = max, t = quiet.
return Ok(sign_bit | max_e_bits | quiet_bit);
}
if s2.starts_with("NaN:0x") {
// Quiet NaN with payload.
return match u64::from_str_radix(&s2[6..], 16) {
Ok(payload) if payload < quiet_bit => {
Ok(sign_bit | max_e_bits | quiet_bit | payload)
}
_ => Err("Invalid NaN payload"),
};
}
if s2.starts_with("sNaN:0x") {
// Signaling NaN with payload.
return match u64::from_str_radix(&s2[7..], 16) {
Ok(payload) if 0 < payload && payload < quiet_bit => {
Ok(sign_bit | max_e_bits | payload)
}
_ => Err("Invalid sNaN payload"),
};
}
return Err("Float must be hexadecimal");
}
let s3 = &s2[2..];
let mut digits = 0u8;
let mut digits_before_period: Option<u8> = None;
let mut significand = 0u64;
let mut exponent = 0i32;
for (idx, ch) in s3.char_indices() {
match ch {
'.' => {
// This is the radix point. There can only be one.
if digits_before_period != None {
return Err("Multiple radix points");
} else {
digits_before_period = Some(digits);
}
}
'p' => {
// The following exponent is a decimal number.
let exp_str = &s3[1 + idx..];
match exp_str.parse::<i16>() {
Ok(e) => {
exponent = e as i32;
break;
}
Err(_) => return Err("Bad exponent"),
}
}
_ => {
match ch.to_digit(16) {
Some(digit) => {
digits += 1;
if digits > 16 {
return Err("Too many digits");
}
significand = (significand << 4) | digit as u64;
}
None => return Err("Invalid character"),
}
}
}
}
if digits == 0 {
return Err("No digits");
}
if significand == 0 {
// This is +/- 0.0.
return Ok(sign_bit);
}
// Number of bits appearing after the radix point.
match digits_before_period {
None => {} // No radix point present.
Some(d) => exponent -= 4 * (digits - d) as i32,
};
// Normalize the significand and exponent.
let significant_bits = (64 - significand.leading_zeros()) as u8;
if significant_bits > t + 1 {
let adjust = significant_bits - (t + 1);
if significand & ((1u64 << adjust) - 1) != 0 {
return Err("Too many significant bits");
}
// Adjust significand down.
significand >>= adjust;
exponent += adjust as i32;
} else {
let adjust = t + 1 - significant_bits;
significand <<= adjust;
exponent -= adjust as i32;
}
assert_eq!(significand >> t, 1);
// Trailing significand excludes the high bit.
let t_bits = significand & ((1 << t) - 1);
let max_exp = (1i32 << w) - 2;
let bias: i32 = (1 << (w - 1)) - 1;
exponent += bias + t as i32;
if exponent > max_exp {
Err("Magnitude too large")
} else if exponent > 0 {
// This is a normal number.
let e_bits = (exponent as u64) << t;
Ok(sign_bit | e_bits | t_bits)
} else if 1 - exponent <= t as i32 {
// This is a subnormal number: e = 0, t = significand bits.
// Renormalize significand for exponent = 1.
let adjust = 1 - exponent;
if significand & ((1u64 << adjust) - 1) != 0 {
Err("Subnormal underflow")
} else {
significand >>= adjust;
Ok(sign_bit | significand)
}
} else {
Err("Magnitude too small")
}
}
impl Ieee32 {
pub fn new(x: f32) -> Ieee32 {
Ieee32(x)
}
/// Construct Ieee32 immediate from raw bits.
pub fn from_bits(x: u32) -> Ieee32 {
Ieee32(unsafe { mem::transmute(x) })
}
}
impl Display for Ieee32 {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let bits: u32 = unsafe { mem::transmute(self.0) };
format_float(bits as u64, 8, 23, f)
}
}
impl FromStr for Ieee32 {
type Err = &'static str;
fn from_str(s: &str) -> Result<Ieee32, &'static str> {
match parse_float(s, 8, 23) {
Ok(b) => Ok(Ieee32::from_bits(b as u32)),
Err(s) => Err(s),
}
}
}
impl Ieee64 {
pub fn new(x: f64) -> Ieee64 {
Ieee64(x)
}
/// Construct Ieee64 immediate from raw bits.
pub fn from_bits(x: u64) -> Ieee64 {
Ieee64(unsafe { mem::transmute(x) })
}
}
impl Display for Ieee64 {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let bits: u64 = unsafe { mem::transmute(self.0) };
format_float(bits, 11, 52, f)
}
}
impl FromStr for Ieee64 {
type Err = &'static str;
fn from_str(s: &str) -> Result<Ieee64, &'static str> {
match parse_float(s, 11, 52) {
Ok(b) => Ok(Ieee64::from_bits(b)),
Err(s) => Err(s),
}
}
}
/// Arbitrary vector immediate.
///
/// This kind of immediate can represent any kind of SIMD vector constant.
/// The representation is simply the sequence of bytes that would be used to store the vector.
pub type ImmVector = Vec<u8>;
#[cfg(test)]
mod tests {
use super::*;
use std::{f32, f64};
use std::str::FromStr;
use std::fmt::Display;
#[test]
fn format_imm64() {
assert_eq!(Imm64(0).to_string(), "0");
assert_eq!(Imm64(9999).to_string(), "9999");
assert_eq!(Imm64(10000).to_string(), "0x2710");
assert_eq!(Imm64(-9999).to_string(), "-9999");
assert_eq!(Imm64(-10000).to_string(), "0xffff_ffff_ffff_d8f0");
assert_eq!(Imm64(0xffff).to_string(), "0xffff");
assert_eq!(Imm64(0x10000).to_string(), "0x0001_0000");
}
// Verify that `text` can be parsed as a `T` into a value that displays as `want`.
fn parse_ok<T: FromStr + Display>(text: &str, want: &str)
where <T as FromStr>::Err: Display
{
match text.parse::<T>() {
Err(s) => panic!("\"{}\".parse() error: {}", text, s),
Ok(x) => assert_eq!(x.to_string(), want),
}
}
// Verify that `text` fails to parse as `T` with the error `msg`.
fn parse_err<T: FromStr + Display>(text: &str, msg: &str)
where <T as FromStr>::Err: Display
{
match text.parse::<T>() {
Err(s) => assert_eq!(s.to_string(), msg),
Ok(x) => panic!("Wanted Err({}), but got {}", msg, x),
}
}
#[test]
fn parse_imm64() {
parse_ok::<Imm64>("0", "0");
parse_ok::<Imm64>("1", "1");
parse_ok::<Imm64>("-0", "0");
parse_ok::<Imm64>("-1", "-1");
parse_ok::<Imm64>("0x0", "0");
parse_ok::<Imm64>("0xf", "15");
parse_ok::<Imm64>("-0x9", "-9");
// Probe limits.
parse_ok::<Imm64>("0xffffffff_ffffffff", "-1");
parse_ok::<Imm64>("0x80000000_00000000", "0x8000_0000_0000_0000");
parse_ok::<Imm64>("-0x80000000_00000000", "0x8000_0000_0000_0000");
parse_err::<Imm64>("-0x80000000_00000001",
"Negative number too small for Imm64");
parse_ok::<Imm64>("18446744073709551615", "-1");
parse_ok::<Imm64>("-9223372036854775808", "0x8000_0000_0000_0000");
// Overflow both the checked_add and checked_mul.
parse_err::<Imm64>("18446744073709551616", "Too large decimal Imm64");
parse_err::<Imm64>("184467440737095516100", "Too large decimal Imm64");
parse_err::<Imm64>("-9223372036854775809",
"Negative number too small for Imm64");
// Underscores are allowed where digits go.
parse_ok::<Imm64>("0_0", "0");
parse_ok::<Imm64>("-_10_0", "-100");
parse_ok::<Imm64>("_10_", "10");
parse_ok::<Imm64>("0x97_88_bb", "0x0097_88bb");
parse_ok::<Imm64>("0x_97_", "151");
parse_err::<Imm64>("", "No digits in Imm64");
parse_err::<Imm64>("-", "No digits in Imm64");
parse_err::<Imm64>("_", "No digits in Imm64");
parse_err::<Imm64>("0x", "No digits in Imm64");
parse_err::<Imm64>("0x_", "No digits in Imm64");
parse_err::<Imm64>("-0x", "No digits in Imm64");
parse_err::<Imm64>(" ", "Invalid character in decimal Imm64");
parse_err::<Imm64>("0 ", "Invalid character in decimal Imm64");
parse_err::<Imm64>(" 0", "Invalid character in decimal Imm64");
parse_err::<Imm64>("--", "Invalid character in decimal Imm64");
parse_err::<Imm64>("-0x-", "Invalid character in hexadecimal Imm64");
// Hex count overflow.
parse_err::<Imm64>("0x0_0000_0000_0000_0000",
"Too many hexadecimal digits in Imm64");
}
#[test]
fn format_ieee32() {
assert_eq!(Ieee32::new(0.0).to_string(), "0.0");
assert_eq!(Ieee32::new(-0.0).to_string(), "-0.0");
assert_eq!(Ieee32::new(1.0).to_string(), "0x1.000000p0");
assert_eq!(Ieee32::new(1.5).to_string(), "0x1.800000p0");
assert_eq!(Ieee32::new(0.5).to_string(), "0x1.000000p-1");
assert_eq!(Ieee32::new(f32::EPSILON).to_string(), "0x1.000000p-23");
assert_eq!(Ieee32::new(f32::MIN).to_string(), "-0x1.fffffep127");
assert_eq!(Ieee32::new(f32::MAX).to_string(), "0x1.fffffep127");
// Smallest positive normal number.
assert_eq!(Ieee32::new(f32::MIN_POSITIVE).to_string(),
"0x1.000000p-126");
// Subnormals.
assert_eq!(Ieee32::new(f32::MIN_POSITIVE / 2.0).to_string(),
"0x0.800000p-126");
assert_eq!(Ieee32::new(f32::MIN_POSITIVE * f32::EPSILON).to_string(),
"0x0.000002p-126");
assert_eq!(Ieee32::new(f32::INFINITY).to_string(), "Inf");
assert_eq!(Ieee32::new(f32::NEG_INFINITY).to_string(), "-Inf");
assert_eq!(Ieee32::new(f32::NAN).to_string(), "NaN");
assert_eq!(Ieee32::new(-f32::NAN).to_string(), "-NaN");
// Construct some qNaNs with payloads.
assert_eq!(Ieee32::from_bits(0x7fc00001).to_string(), "NaN:0x1");
assert_eq!(Ieee32::from_bits(0x7ff00001).to_string(), "NaN:0x300001");
// Signaling NaNs.
assert_eq!(Ieee32::from_bits(0x7f800001).to_string(), "sNaN:0x1");
assert_eq!(Ieee32::from_bits(0x7fa00001).to_string(), "sNaN:0x200001");
}
#[test]
fn parse_ieee32() {
parse_ok::<Ieee32>("0.0", "0.0");
parse_ok::<Ieee32>("-0.0", "-0.0");
parse_ok::<Ieee32>("0x0", "0.0");
parse_ok::<Ieee32>("0x0.0", "0.0");
parse_ok::<Ieee32>("0x.0", "0.0");
parse_ok::<Ieee32>("0x0.", "0.0");
parse_ok::<Ieee32>("0x1", "0x1.000000p0");
parse_ok::<Ieee32>("-0x1", "-0x1.000000p0");
parse_ok::<Ieee32>("0x10", "0x1.000000p4");
parse_ok::<Ieee32>("0x10.0", "0x1.000000p4");
parse_err::<Ieee32>("0.", "Float must be hexadecimal");
parse_err::<Ieee32>(".0", "Float must be hexadecimal");
parse_err::<Ieee32>("0", "Float must be hexadecimal");
parse_err::<Ieee32>("-0", "Float must be hexadecimal");
parse_err::<Ieee32>(".", "Float must be hexadecimal");
parse_err::<Ieee32>("", "Float must be hexadecimal");
parse_err::<Ieee32>("-", "Float must be hexadecimal");
parse_err::<Ieee32>("0x", "No digits");
parse_err::<Ieee32>("0x..", "Multiple radix points");
// Check significant bits.
parse_ok::<Ieee32>("0x0.ffffff", "0x1.fffffep-1");
parse_ok::<Ieee32>("0x1.fffffe", "0x1.fffffep0");
parse_ok::<Ieee32>("0x3.fffffc", "0x1.fffffep1");
parse_ok::<Ieee32>("0x7.fffff8", "0x1.fffffep2");
parse_ok::<Ieee32>("0xf.fffff0", "0x1.fffffep3");
parse_err::<Ieee32>("0x1.ffffff", "Too many significant bits");
parse_err::<Ieee32>("0x1.fffffe0000000000", "Too many digits");
// Exponents.
parse_ok::<Ieee32>("0x1p3", "0x1.000000p3");
parse_ok::<Ieee32>("0x1p-3", "0x1.000000p-3");
parse_ok::<Ieee32>("0x1.0p3", "0x1.000000p3");
parse_ok::<Ieee32>("0x2.0p3", "0x1.000000p4");
parse_ok::<Ieee32>("0x1.0p127", "0x1.000000p127");
parse_ok::<Ieee32>("0x1.0p-126", "0x1.000000p-126");
parse_ok::<Ieee32>("0x0.1p-122", "0x1.000000p-126");
parse_err::<Ieee32>("0x2.0p127", "Magnitude too large");
// Subnormals.
parse_ok::<Ieee32>("0x1.0p-127", "0x0.800000p-126");
parse_ok::<Ieee32>("0x1.0p-149", "0x0.000002p-126");
parse_ok::<Ieee32>("0x0.000002p-126", "0x0.000002p-126");
parse_err::<Ieee32>("0x0.100001p-126", "Subnormal underflow");
parse_err::<Ieee32>("0x1.8p-149", "Subnormal underflow");
parse_err::<Ieee32>("0x1.0p-150", "Magnitude too small");
// NaNs and Infs.
parse_ok::<Ieee32>("Inf", "Inf");
parse_ok::<Ieee32>("-Inf", "-Inf");
parse_ok::<Ieee32>("NaN", "NaN");
parse_ok::<Ieee32>("-NaN", "-NaN");
parse_ok::<Ieee32>("NaN:0x0", "NaN");
parse_err::<Ieee32>("NaN:", "Float must be hexadecimal");
parse_err::<Ieee32>("NaN:0", "Float must be hexadecimal");
parse_err::<Ieee32>("NaN:0x", "Invalid NaN payload");
parse_ok::<Ieee32>("NaN:0x000001", "NaN:0x1");
parse_ok::<Ieee32>("NaN:0x300001", "NaN:0x300001");
parse_err::<Ieee32>("NaN:0x400001", "Invalid NaN payload");
parse_ok::<Ieee32>("sNaN:0x1", "sNaN:0x1");
parse_err::<Ieee32>("sNaN:0x0", "Invalid sNaN payload");
parse_ok::<Ieee32>("sNaN:0x200001", "sNaN:0x200001");
parse_err::<Ieee32>("sNaN:0x400001", "Invalid sNaN payload");
}
#[test]
fn format_ieee64() {
assert_eq!(Ieee64::new(0.0).to_string(), "0.0");
assert_eq!(Ieee64::new(-0.0).to_string(), "-0.0");
assert_eq!(Ieee64::new(1.0).to_string(), "0x1.0000000000000p0");
assert_eq!(Ieee64::new(1.5).to_string(), "0x1.8000000000000p0");
assert_eq!(Ieee64::new(0.5).to_string(), "0x1.0000000000000p-1");
assert_eq!(Ieee64::new(f64::EPSILON).to_string(),
"0x1.0000000000000p-52");
assert_eq!(Ieee64::new(f64::MIN).to_string(), "-0x1.fffffffffffffp1023");
assert_eq!(Ieee64::new(f64::MAX).to_string(), "0x1.fffffffffffffp1023");
// Smallest positive normal number.
assert_eq!(Ieee64::new(f64::MIN_POSITIVE).to_string(),
"0x1.0000000000000p-1022");
// Subnormals.
assert_eq!(Ieee64::new(f64::MIN_POSITIVE / 2.0).to_string(),
"0x0.8000000000000p-1022");
assert_eq!(Ieee64::new(f64::MIN_POSITIVE * f64::EPSILON).to_string(),
"0x0.0000000000001p-1022");
assert_eq!(Ieee64::new(f64::INFINITY).to_string(), "Inf");
assert_eq!(Ieee64::new(f64::NEG_INFINITY).to_string(), "-Inf");
assert_eq!(Ieee64::new(f64::NAN).to_string(), "NaN");
assert_eq!(Ieee64::new(-f64::NAN).to_string(), "-NaN");
// Construct some qNaNs with payloads.
assert_eq!(Ieee64::from_bits(0x7ff8000000000001).to_string(), "NaN:0x1");
assert_eq!(Ieee64::from_bits(0x7ffc000000000001).to_string(),
"NaN:0x4000000000001");
// Signaling NaNs.
assert_eq!(Ieee64::from_bits(0x7ff0000000000001).to_string(),
"sNaN:0x1");
assert_eq!(Ieee64::from_bits(0x7ff4000000000001).to_string(),
"sNaN:0x4000000000001");
}
#[test]
fn parse_ieee64() {
parse_ok::<Ieee64>("0.0", "0.0");
parse_ok::<Ieee64>("-0.0", "-0.0");
parse_ok::<Ieee64>("0x0", "0.0");
parse_ok::<Ieee64>("0x0.0", "0.0");
parse_ok::<Ieee64>("0x.0", "0.0");
parse_ok::<Ieee64>("0x0.", "0.0");
parse_ok::<Ieee64>("0x1", "0x1.0000000000000p0");
parse_ok::<Ieee64>("-0x1", "-0x1.0000000000000p0");
parse_ok::<Ieee64>("0x10", "0x1.0000000000000p4");
parse_ok::<Ieee64>("0x10.0", "0x1.0000000000000p4");
parse_err::<Ieee64>("0.", "Float must be hexadecimal");
parse_err::<Ieee64>(".0", "Float must be hexadecimal");
parse_err::<Ieee64>("0", "Float must be hexadecimal");
parse_err::<Ieee64>("-0", "Float must be hexadecimal");
parse_err::<Ieee64>(".", "Float must be hexadecimal");
parse_err::<Ieee64>("", "Float must be hexadecimal");
parse_err::<Ieee64>("-", "Float must be hexadecimal");
parse_err::<Ieee64>("0x", "No digits");
parse_err::<Ieee64>("0x..", "Multiple radix points");
// Check significant bits.
parse_ok::<Ieee64>("0x0.fffffffffffff8", "0x1.fffffffffffffp-1");
parse_ok::<Ieee64>("0x1.fffffffffffff", "0x1.fffffffffffffp0");
parse_ok::<Ieee64>("0x3.ffffffffffffe", "0x1.fffffffffffffp1");
parse_ok::<Ieee64>("0x7.ffffffffffffc", "0x1.fffffffffffffp2");
parse_ok::<Ieee64>("0xf.ffffffffffff8", "0x1.fffffffffffffp3");
parse_err::<Ieee64>("0x3.fffffffffffff", "Too many significant bits");
parse_err::<Ieee64>("0x001.fffffe00000000", "Too many digits");
// Exponents.
parse_ok::<Ieee64>("0x1p3", "0x1.0000000000000p3");
parse_ok::<Ieee64>("0x1p-3", "0x1.0000000000000p-3");
parse_ok::<Ieee64>("0x1.0p3", "0x1.0000000000000p3");
parse_ok::<Ieee64>("0x2.0p3", "0x1.0000000000000p4");
parse_ok::<Ieee64>("0x1.0p1023", "0x1.0000000000000p1023");
parse_ok::<Ieee64>("0x1.0p-1022", "0x1.0000000000000p-1022");
parse_ok::<Ieee64>("0x0.1p-1018", "0x1.0000000000000p-1022");
parse_err::<Ieee64>("0x2.0p1023", "Magnitude too large");
// Subnormals.
parse_ok::<Ieee64>("0x1.0p-1023", "0x0.8000000000000p-1022");
parse_ok::<Ieee64>("0x1.0p-1074", "0x0.0000000000001p-1022");
parse_ok::<Ieee64>("0x0.0000000000001p-1022", "0x0.0000000000001p-1022");
parse_err::<Ieee64>("0x0.10000000000008p-1022", "Subnormal underflow");
parse_err::<Ieee64>("0x1.8p-1074", "Subnormal underflow");
parse_err::<Ieee64>("0x1.0p-1075", "Magnitude too small");
// NaNs and Infs.
parse_ok::<Ieee64>("Inf", "Inf");
parse_ok::<Ieee64>("-Inf", "-Inf");
parse_ok::<Ieee64>("NaN", "NaN");
parse_ok::<Ieee64>("-NaN", "-NaN");
parse_ok::<Ieee64>("NaN:0x0", "NaN");
parse_err::<Ieee64>("NaN:", "Float must be hexadecimal");
parse_err::<Ieee64>("NaN:0", "Float must be hexadecimal");
parse_err::<Ieee64>("NaN:0x", "Invalid NaN payload");
parse_ok::<Ieee64>("NaN:0x000001", "NaN:0x1");
parse_ok::<Ieee64>("NaN:0x4000000000001", "NaN:0x4000000000001");
parse_err::<Ieee64>("NaN:0x8000000000001", "Invalid NaN payload");
parse_ok::<Ieee64>("sNaN:0x1", "sNaN:0x1");
parse_err::<Ieee64>("sNaN:0x0", "Invalid sNaN payload");
parse_ok::<Ieee64>("sNaN:0x4000000000001", "sNaN:0x4000000000001");
parse_err::<Ieee64>("sNaN:0x8000000000001", "Invalid sNaN payload");
}
}

View File

@@ -0,0 +1,695 @@
//! Instruction formats and opcodes.
//!
//! The `instructions` module contains definitions for instruction formats, opcodes, and the
//! in-memory representation of IL instructions.
//!
//! A large part of this module is auto-generated from the instruction descriptions in the meta
//! directory.
use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use std::ops::{Deref, DerefMut};
use ir::{Value, Type, Ebb, JumpTable, FuncRef};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, ImmVector};
use ir::condcodes::*;
use ir::types;
// Include code generated by `meta/gen_instr.py`. This file contains:
//
// - The `pub enum InstructionFormat` enum with all the instruction formats.
// - The `pub enum Opcode` definition with all known opcodes,
// - The `const OPCODE_FORMAT: [InstructionFormat; N]` table.
// - The private `fn opcode_name(Opcode) -> &'static str` function, and
// - The hash table `const OPCODE_HASH_TABLE: [Opcode; N]`.
//
// For value type constraints:
//
// - The `const OPCODE_CONSTRAINTS : [OpcodeConstraints; N]` table.
// - The `const TYPE_SETS : [ValueTypeSet; N]` table.
// - The `const OPERAND_CONSTRAINTS : [OperandConstraint; N]` table.
//
include!(concat!(env!("OUT_DIR"), "/opcodes.rs"));
impl Display for Opcode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", opcode_name(*self))
}
}
impl Opcode {
/// Get the instruction format for this opcode.
pub fn format(self) -> Option<InstructionFormat> {
if self == Opcode::NotAnOpcode {
None
} else {
Some(OPCODE_FORMAT[self as usize - 1])
}
}
/// Get the constraint descriptor for this opcode.
/// Panic if this is called on `NotAnOpcode`.
pub fn constraints(self) -> OpcodeConstraints {
OPCODE_CONSTRAINTS[self as usize - 1]
}
}
// This trait really belongs in libreader where it is used by the .cton file parser, but since it
// critically depends on the `opcode_name()` function which is needed here anyway, it lives in this
// module. This also saves us from runing the build script twice to generate code for the two
// separate crates.
impl FromStr for Opcode {
type Err = &'static str;
/// Parse an Opcode name from a string.
fn from_str(s: &str) -> Result<Opcode, &'static str> {
use constant_hash::{Table, simple_hash, probe};
impl<'a> Table<&'a str> for [Opcode] {
fn len(&self) -> usize {
self.len()
}
fn key(&self, idx: usize) -> Option<&'a str> {
if self[idx] == Opcode::NotAnOpcode {
None
} else {
Some(opcode_name(self[idx]))
}
}
}
match probe::<&str, [Opcode]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) {
None => Err("Unknown opcode"),
Some(i) => Ok(OPCODE_HASH_TABLE[i]),
}
}
}
/// Contents on an instruction.
///
/// Every variant must contain `opcode` and `ty` fields. An instruction that doesn't produce a
/// value should have its `ty` field set to `VOID`. 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
/// `Box<AuxData>` to store the additional information out of line.
#[derive(Clone, Debug)]
pub enum InstructionData {
Nullary { opcode: Opcode, ty: Type },
Unary {
opcode: Opcode,
ty: Type,
arg: Value,
},
UnaryImm {
opcode: Opcode,
ty: Type,
imm: Imm64,
},
UnaryIeee32 {
opcode: Opcode,
ty: Type,
imm: Ieee32,
},
UnaryIeee64 {
opcode: Opcode,
ty: Type,
imm: Ieee64,
},
UnaryImmVector {
opcode: Opcode,
ty: Type,
data: Box<UnaryImmVectorData>,
},
UnarySplit {
opcode: Opcode,
ty: Type,
second_result: Value,
arg: Value,
},
Binary {
opcode: Opcode,
ty: Type,
args: [Value; 2],
},
BinaryImm {
opcode: Opcode,
ty: Type,
arg: Value,
imm: Imm64,
},
// Same as BinaryImm, but the immediate is the lhs operand.
BinaryImmRev {
opcode: Opcode,
ty: Type,
arg: Value,
imm: Imm64,
},
BinaryOverflow {
opcode: Opcode,
ty: Type,
second_result: Value,
args: [Value; 2],
},
Ternary {
opcode: Opcode,
ty: Type,
args: [Value; 3],
},
TernaryOverflow {
opcode: Opcode,
ty: Type,
second_result: Value,
data: Box<TernaryOverflowData>,
},
InsertLane {
opcode: Opcode,
ty: Type,
lane: Uimm8,
args: [Value; 2],
},
ExtractLane {
opcode: Opcode,
ty: Type,
lane: Uimm8,
arg: Value,
},
IntCompare {
opcode: Opcode,
ty: Type,
cond: IntCC,
args: [Value; 2],
},
FloatCompare {
opcode: Opcode,
ty: Type,
cond: FloatCC,
args: [Value; 2],
},
Jump {
opcode: Opcode,
ty: Type,
data: Box<JumpData>,
},
Branch {
opcode: Opcode,
ty: Type,
data: Box<BranchData>,
},
BranchTable {
opcode: Opcode,
ty: Type,
arg: Value,
table: JumpTable,
},
Call {
opcode: Opcode,
ty: Type,
second_result: Value,
data: Box<CallData>,
},
Return {
opcode: Opcode,
ty: Type,
data: Box<ReturnData>,
},
}
/// A variable list of `Value` operands used for function call arguments and passing arguments to
/// basic blocks.
#[derive(Clone, Debug)]
pub struct VariableArgs(Vec<Value>);
impl VariableArgs {
pub fn new() -> VariableArgs {
VariableArgs(Vec::new())
}
pub fn push(&mut self, v: Value) {
self.0.push(v)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
// Coerce VariableArgs into a &[Value] slice.
impl Deref for VariableArgs {
type Target = [Value];
fn deref<'a>(&'a self) -> &'a [Value] {
&self.0
}
}
impl DerefMut for VariableArgs {
fn deref_mut<'a>(&'a mut self) -> &'a mut [Value] {
&mut self.0
}
}
impl Display for VariableArgs {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
for (i, val) in self.0.iter().enumerate() {
if i == 0 {
try!(write!(fmt, "{}", val));
} else {
try!(write!(fmt, ", {}", val));
}
}
Ok(())
}
}
impl Default for VariableArgs {
fn default() -> VariableArgs {
VariableArgs::new()
}
}
/// Payload data for `vconst`.
#[derive(Clone, Debug)]
pub struct UnaryImmVectorData {
pub imm: ImmVector,
}
impl Display for UnaryImmVectorData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
try!(write!(f, "#"));
for b in &self.imm {
try!(write!(f, "{:02x}", b));
}
Ok(())
}
}
/// Payload data for ternary instructions with multiple results, such as `iadd_carry`.
#[derive(Clone, Debug)]
pub struct TernaryOverflowData {
pub args: [Value; 3],
}
impl Display for TernaryOverflowData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}, {}, {}", self.args[0], self.args[1], self.args[2])
}
}
/// Payload data for jump instructions. These need to carry lists of EBB arguments that won't fit
/// in the allowed InstructionData size.
#[derive(Clone, Debug)]
pub struct JumpData {
pub destination: Ebb,
pub varargs: VariableArgs,
}
impl Display for JumpData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.varargs.is_empty() {
write!(f, "{}", self.destination)
} else {
write!(f, "{}({})", self.destination, self.varargs)
}
}
}
/// Payload data for branch instructions. These need to carry lists of EBB arguments that won't fit
/// in the allowed InstructionData size.
#[derive(Clone, Debug)]
pub struct BranchData {
pub arg: Value,
pub destination: Ebb,
pub varargs: VariableArgs,
}
impl Display for BranchData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
try!(write!(f, "{}, {}", self.arg, self.destination));
if !self.varargs.is_empty() {
try!(write!(f, "({})", self.varargs));
}
Ok(())
}
}
/// Payload of a call instruction.
#[derive(Clone, Debug)]
pub struct CallData {
/// Callee function.
pub func_ref: FuncRef,
/// Dynamically sized array containing call argument values.
pub varargs: VariableArgs,
}
impl Display for CallData {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "TBD({})", self.varargs)
}
}
/// Payload of a return instruction.
#[derive(Clone, Debug)]
pub struct ReturnData {
// Dynamically sized array containing return values.
pub varargs: VariableArgs,
}
/// Analyzing an instruction.
///
/// Avoid large matches on instruction formats by using the methods efined here to examine
/// instructions.
impl InstructionData {
/// Return information about the destination of a branch or jump instruction.
///
/// Any instruction that can transfer control to another EBB reveals its possible destinations
/// here.
pub fn analyze_branch<'a>(&'a self) -> BranchInfo<'a> {
match self {
&InstructionData::Jump { ref data, .. } => {
BranchInfo::SingleDest(data.destination, &data.varargs)
}
&InstructionData::Branch { ref data, .. } => {
BranchInfo::SingleDest(data.destination, &data.varargs)
}
&InstructionData::BranchTable { table, .. } => BranchInfo::Table(table),
_ => BranchInfo::NotABranch,
}
}
/// Return true if an instruction is terminating, or false otherwise.
pub fn is_terminating<'a>(&'a self) -> bool {
match self {
&InstructionData::Jump { .. } => true,
&InstructionData::Return { .. } => true,
&InstructionData::Nullary { .. } => true,
_ => false,
}
}
}
/// Information about branch and jump instructions.
pub enum BranchInfo<'a> {
/// This is not a branch or jump instruction.
/// This instruction will not transfer control to another EBB in the function, but it may still
/// affect control flow by returning or trapping.
NotABranch,
/// This is a branch or jump to a single destination EBB, possibly taking value arguments.
SingleDest(Ebb, &'a [Value]),
/// This is a jump table branch which can have many destination EBBs.
Table(JumpTable),
}
/// Value type constraints for a given opcode.
///
/// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and
/// results are not determined by the format. Every `Opcode` has an associated
/// `OpcodeConstraints` object that provides the missing details.
#[derive(Clone, Copy)]
pub struct OpcodeConstraints {
/// Flags for this opcode encoded as a bit field:
///
/// Bits 0-2:
/// Number of fixed result values. This does not include `variable_args` results as are
/// produced by call instructions.
///
/// Bit 3:
/// This opcode is polymorphic and the controlling type variable can be inferred from the
/// designated input operand. This is the `typevar_operand` index given to the
/// `InstructionFormat` meta language object. When bit 0 is not set, the controlling type
/// variable must be the first output value instead.
flags: u8,
/// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`.
typeset_offset: u8,
/// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first
/// `fixed_results()` entries describe the result constraints, then follows constraints for the
/// fixed `Value` input operands. The number of `Value` inputs is determined by the instruction
/// format.
constraint_offset: u16,
}
impl OpcodeConstraints {
/// Can the controlling type variable for this opcode be inferred from the designated value
/// input operand?
/// This also implies that this opcode is polymorphic.
pub fn use_typevar_operand(self) -> bool {
(self.flags & 0x8) != 0
}
/// Get the number of *fixed* result values produced by this opcode.
/// This does not include `variable_args` produced by calls.
pub fn fixed_results(self) -> usize {
(self.flags & 0x7) as usize
}
/// Get the offset into `TYPE_SETS` for the controlling type variable.
/// Returns `None` if the instruction is not polymorphic.
fn typeset_offset(self) -> Option<usize> {
let offset = self.typeset_offset as usize;
if offset < TYPE_SETS.len() {
Some(offset)
} else {
None
}
}
/// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin.
fn constraint_offset(self) -> usize {
self.constraint_offset as usize
}
/// Get the value type of result number `n`, having resolved the controlling type variable to
/// `ctrl_type`.
pub fn result_type(self, n: usize, ctrl_type: Type) -> Type {
assert!(n < self.fixed_results(), "Invalid result index");
OPERAND_CONSTRAINTS[self.constraint_offset() + n]
.resolve(ctrl_type)
.expect("Result constraints can't be free")
}
/// Get the typeset of allowed types for the controlling type variable in a polymorphic
/// instruction.
pub fn ctrl_typeset(self) -> Option<ValueTypeSet> {
self.typeset_offset().map(|offset| TYPE_SETS[offset])
}
/// Is this instruction polymorphic?
pub fn is_polymorphic(self) -> bool {
self.ctrl_typeset().is_some()
}
}
/// A value type set describes the permitted set of types for a type variable.
#[derive(Clone, Copy)]
pub struct ValueTypeSet {
min_lanes: u8,
max_lanes: u8,
min_int: u8,
max_int: u8,
min_float: u8,
max_float: u8,
min_bool: u8,
max_bool: u8,
}
impl ValueTypeSet {
/// Is `scalar` part of the base type set?
///
/// Note that the base type set does not have to be included in the type set proper.
fn is_base_type(&self, scalar: Type) -> bool {
let l2b = scalar.log2_lane_bits();
if scalar.is_int() {
self.min_int <= l2b && l2b < self.max_int
} else if scalar.is_float() {
self.min_float <= l2b && l2b < self.max_float
} else if scalar.is_bool() {
self.min_bool <= l2b && l2b < self.max_bool
} else {
false
}
}
/// Does `typ` belong to this set?
pub fn contains(&self, typ: Type) -> bool {
let l2l = typ.log2_lane_count();
self.min_lanes <= l2l && l2l < self.max_lanes && self.is_base_type(typ.lane_type())
}
/// Get an example member of this type set.
///
/// This is used for error messages to avoid suggesting invalid types.
pub fn example(&self) -> Type {
let t = if self.max_int > 5 {
types::I32
} else if self.max_float > 5 {
types::F32
} else if self.max_bool > 5 {
types::B32
} else {
types::B1
};
t.by(1 << self.min_lanes).unwrap()
}
}
/// Operand constraints. This describes the value type constraints on a single `Value` operand.
enum OperandConstraint {
/// This operand has a concrete value type.
Concrete(Type),
/// This operand can vary freely within the given type set.
/// The type set is identified by its index into the TYPE_SETS constant table.
Free(u8),
/// This operand is the same type as the controlling type variable.
Same,
/// This operand is `ctrlType.lane_type()`.
LaneOf,
/// This operand is `ctrlType.as_bool()`.
AsBool,
/// This operand is `ctrlType.half_width()`.
HalfWidth,
/// This operand is `ctrlType.double_width()`.
DoubleWidth,
}
impl OperandConstraint {
/// Resolve this operand constraint into a concrete value type, given the value of the
/// controlling type variable.
/// Returns `None` if this is a free operand which is independent of the controlling type
/// variable.
pub fn resolve(&self, ctrl_type: Type) -> Option<Type> {
use self::OperandConstraint::*;
match *self {
Concrete(t) => Some(t),
Free(_) => None,
Same => Some(ctrl_type),
LaneOf => Some(ctrl_type.lane_type()),
AsBool => Some(ctrl_type.as_bool()),
HalfWidth => Some(ctrl_type.half_width().expect("invalid type for half_width")),
DoubleWidth => Some(ctrl_type.double_width().expect("invalid type for double_width")),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn opcodes() {
let x = Opcode::Iadd;
let mut y = Opcode::Isub;
assert!(x != y);
y = Opcode::Iadd;
assert_eq!(x, y);
assert_eq!(x.format(), Some(InstructionFormat::Binary));
assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm");
assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm");
// Check the matcher.
assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd));
assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm));
assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode"));
assert_eq!("".parse::<Opcode>(), Err("Unknown opcode"));
assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode"));
}
#[test]
fn instruction_data() {
use std::mem;
// The size of the InstructionData enum is important for performance. It should not exceed
// 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that require
// more space than that.
// It would be fine with a data structure smaller than 16 bytes, but what are the odds of
// that?
assert_eq!(mem::size_of::<InstructionData>(), 16);
}
#[test]
fn value_set() {
use ir::types::*;
let vts = ValueTypeSet {
min_lanes: 0,
max_lanes: 8,
min_int: 3,
max_int: 7,
min_float: 0,
max_float: 0,
min_bool: 3,
max_bool: 7,
};
assert!(vts.contains(I32));
assert!(vts.contains(I64));
assert!(vts.contains(I32X4));
assert!(!vts.contains(F32));
assert!(!vts.contains(B1));
assert!(vts.contains(B8));
assert!(vts.contains(B64));
assert_eq!(vts.example().to_string(), "i32");
let vts = ValueTypeSet {
min_lanes: 0,
max_lanes: 8,
min_int: 0,
max_int: 0,
min_float: 5,
max_float: 7,
min_bool: 3,
max_bool: 7,
};
assert_eq!(vts.example().to_string(), "f32");
let vts = ValueTypeSet {
min_lanes: 1,
max_lanes: 8,
min_int: 0,
max_int: 0,
min_float: 5,
max_float: 7,
min_bool: 3,
max_bool: 7,
};
assert_eq!(vts.example().to_string(), "f32x2");
let vts = ValueTypeSet {
min_lanes: 2,
max_lanes: 8,
min_int: 0,
max_int: 0,
min_float: 0,
max_float: 0,
min_bool: 3,
max_bool: 7,
};
assert!(!vts.contains(B32X2));
assert!(vts.contains(B32X4));
assert_eq!(vts.example().to_string(), "b32x4");
let vts = ValueTypeSet {
// TypeSet(lanes=(1, 256), ints=(8, 64))
min_lanes: 0,
max_lanes: 9,
min_int: 3,
max_int: 7,
min_float: 0,
max_float: 0,
min_bool: 0,
max_bool: 0,
};
assert!(vts.contains(I32));
assert!(vts.contains(I32X4));
}
}

View File

@@ -0,0 +1,157 @@
//! Jump table representation.
//!
//! Jump tables are declared in the preamble and assigned an `ir::entities::JumpTable` reference.
//! The actual table of destinations is stored in a `JumpTableData` struct defined in this module.
use ir::entities::{Ebb, NO_EBB};
use std::iter;
use std::slice;
use std::fmt::{self, Display, Formatter};
/// Contents of a jump table.
///
/// All jump tables use 0-based indexing and are expected to be densely populated. They don't need
/// to be completely populated, though. Individual entries can be missing.
#[derive(Clone)]
pub struct JumpTableData {
// Table entries, using NO_EBB as a placeholder for missing entries.
table: Vec<Ebb>,
// How many `NO_EBB` holes in table?
holes: usize,
}
impl JumpTableData {
/// Create a new empty jump table.
pub fn new() -> JumpTableData {
JumpTableData {
table: Vec::new(),
holes: 0,
}
}
/// Set a table entry.
///
/// The table will grow as needed to fit 'idx'.
pub fn set_entry(&mut self, idx: usize, dest: Ebb) {
assert!(dest != NO_EBB);
// Resize table to fit `idx`.
if idx >= self.table.len() {
self.holes += idx - self.table.len();
self.table.resize(idx + 1, NO_EBB);
} else if self.table[idx] == NO_EBB {
// We're filling in an existing hole.
self.holes -= 1;
}
self.table[idx] = dest;
}
/// Clear a table entry.
///
/// The `br_table` instruction will fall through if given an index corresponding to a cleared
/// table entry.
pub fn clear_entry(&mut self, idx: usize) {
if idx < self.table.len() && self.table[idx] != NO_EBB {
self.holes += 1;
self.table[idx] = NO_EBB;
}
}
/// Get the entry for `idx`, or `None`.
pub fn get_entry(&self, idx: usize) -> Option<Ebb> {
if idx < self.table.len() && self.table[idx] != NO_EBB {
Some(self.table[idx])
} else {
None
}
}
/// Enumerate over all `(idx, dest)` pairs in the table in order.
///
/// This returns an iterator that skips any empty slots in the table.
pub fn entries<'a>(&'a self) -> Entries {
Entries(self.table.iter().cloned().enumerate())
}
/// Access the whole table as a mutable slice.
pub fn as_mut_slice(&mut self) -> &mut [Ebb] {
self.table.as_mut_slice()
}
}
/// Enumerate `(idx, dest)` pairs in order.
pub struct Entries<'a>(iter::Enumerate<iter::Cloned<slice::Iter<'a, Ebb>>>);
impl<'a> Iterator for Entries<'a> {
type Item = (usize, Ebb);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some((idx, dest)) = self.0.next() {
if dest != NO_EBB {
return Some((idx, dest));
}
} else {
return None;
}
}
}
}
impl Display for JumpTableData {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
let first = self.table.first().cloned().unwrap_or_default();
if first == NO_EBB {
try!(write!(fmt, "jump_table 0"));
} else {
try!(write!(fmt, "jump_table {}", first));
}
for dest in self.table.iter().cloned().skip(1) {
if dest == NO_EBB {
try!(write!(fmt, ", 0"));
} else {
try!(write!(fmt, ", {}", dest));
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::JumpTableData;
use ir::Ebb;
use entity_map::EntityRef;
#[test]
fn empty() {
let jt = JumpTableData::new();
assert_eq!(jt.get_entry(0), None);
assert_eq!(jt.get_entry(10), None);
assert_eq!(jt.to_string(), "jump_table 0");
let v: Vec<(usize, Ebb)> = jt.entries().collect();
assert_eq!(v, []);
}
#[test]
fn insert() {
let e1 = Ebb::new(1);
let e2 = Ebb::new(2);
let mut jt = JumpTableData::new();
jt.set_entry(0, e1);
jt.set_entry(0, e2);
jt.set_entry(10, e1);
assert_eq!(jt.to_string(),
"jump_table ebb2, 0, 0, 0, 0, 0, 0, 0, 0, 0, ebb1");
let v: Vec<(usize, Ebb)> = jt.entries().collect();
assert_eq!(v, [(0, e2), (10, e1)]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
//! Representation of Cretonne IL functions.
pub mod types;
pub mod entities;
pub mod condcodes;
pub mod immediates;
pub mod instructions;
pub mod stackslot;
pub mod jumptable;
pub mod dfg;
pub mod layout;
pub mod function;
mod funcname;
mod extfunc;
mod builder;
pub use ir::funcname::FunctionName;
pub use ir::extfunc::{Signature, ArgumentType, ArgumentExtension};
pub use ir::types::Type;
pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable, FuncRef, SigRef};
pub use ir::instructions::{Opcode, InstructionData, VariableArgs};
pub use ir::stackslot::StackSlotData;
pub use ir::jumptable::JumpTableData;
pub use ir::dfg::{DataFlowGraph, ValueDef};
pub use ir::layout::{Layout, Cursor};
pub use ir::function::Function;
pub use ir::builder::Builder;

View File

@@ -0,0 +1,45 @@
//! Stack slots.
//!
//! The `StackSlotData` struct keeps track of a single stack slot in a function.
//!
use std::fmt::{self, Display, Formatter};
/// Contents of a stack slot.
#[derive(Clone, Debug)]
pub struct StackSlotData {
/// Size of stack slot in bytes.
pub size: u32,
}
impl StackSlotData {
/// Create a stack slot with the specified byte size.
pub fn new(size: u32) -> StackSlotData {
StackSlotData { size: size }
}
}
impl Display for StackSlotData {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "stack_slot {}", self.size)
}
}
#[cfg(test)]
mod tests {
use ir::Function;
use super::StackSlotData;
#[test]
fn stack_slot() {
let mut func = Function::new();
let ss0 = func.stack_slots.push(StackSlotData::new(4));
let ss1 = func.stack_slots.push(StackSlotData::new(8));
assert_eq!(ss0.to_string(), "ss0");
assert_eq!(ss1.to_string(), "ss1");
assert_eq!(func.stack_slots[ss0].size, 4);
assert_eq!(func.stack_slots[ss1].size, 8);
}
}

View File

@@ -0,0 +1,342 @@
//! Common types for the Cretonne code generator.
use std::default::Default;
use std::fmt::{self, Display, Formatter};
// ====--------------------------------------------------------------------------------------====//
//
// Value types
//
// ====--------------------------------------------------------------------------------------====//
/// The type of an SSA value.
///
/// The `VOID` type is only used for instructions that produce no value. It can't be part of a SIMD
/// vector.
///
/// Basic integer types: `I8`, `I16`, `I32`, and `I64`. These types are sign-agnostic.
///
/// Basic floating point types: `F32` and `F64`. IEEE single and double precision.
///
/// Boolean types: `B1`, `B8`, `B16`, `B32`, and `B64`. These all encode 'true' or 'false'. The
/// larger types use redundant bits.
///
/// SIMD vector types have power-of-two lanes, up to 256. Lanes can be any int/float/bool type.
///
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Type(u8);
/// No type. Used for functions without a return value. Can't be loaded or stored. Can't be part of
/// a SIMD vector.
pub const VOID: Type = Type(0);
// Include code generated by `meta/gen_types.py`. This file contains constant definitions for all
// the scalar types as well as common vector types for 64, 128, 256, and 512-bit SID vectors.
include!(concat!(env!("OUT_DIR"), "/types.rs"));
impl Type {
/// Get the lane type of this SIMD vector type.
///
/// A scalar type is the same as a SIMD vector type with one lane, so it returns itself.
pub fn lane_type(self) -> Type {
Type(self.0 & 0x0f)
}
/// Get log2 of the number of bits in a lane.
pub fn log2_lane_bits(self) -> u8 {
match self.lane_type() {
B1 => 0,
B8 | I8 => 3,
B16 | I16 => 4,
B32 | I32 | F32 => 5,
B64 | I64 | F64 => 6,
_ => 0,
}
}
/// Get the number of bits in a lane.
pub fn lane_bits(self) -> u8 {
match self.lane_type() {
B1 => 1,
B8 | I8 => 8,
B16 | I16 => 16,
B32 | I32 | F32 => 32,
B64 | I64 | F64 => 64,
_ => 0,
}
}
/// Get a type with the same number of lanes as this type, but with the lanes replaced by
/// booleans of the same size.
pub fn as_bool(self) -> Type {
// Replace the low 4 bits with the boolean version, preserve the high 4 bits.
let lane = match self.lane_type() {
B8 | I8 => B8,
B16 | I16 => B16,
B32 | I32 | F32 => B32,
B64 | I64 | F64 => B64,
_ => B1,
};
Type(lane.0 | (self.0 & 0xf0))
}
/// Get a type with the same number of lanes as this type, but with lanes that are half the
/// number of bits.
pub fn half_width(self) -> Option<Type> {
let lane = match self.lane_type() {
I16 => I8,
I32 => I16,
I64 => I32,
F64 => F32,
B16 => B8,
B32 => B16,
B64 => B32,
_ => return None,
};
Some(Type(lane.0 | (self.0 & 0xf0)))
}
/// Get a type with the same number of lanes as this type, but with lanes that are twice the
/// number of bits.
pub fn double_width(self) -> Option<Type> {
let lane = match self.lane_type() {
I8 => I16,
I16 => I32,
I32 => I64,
F32 => F64,
B8 => B16,
B16 => B32,
B32 => B64,
_ => return None,
};
Some(Type(lane.0 | (self.0 & 0xf0)))
}
/// Is this the VOID type?
pub fn is_void(self) -> bool {
self == VOID
}
/// Is this a scalar boolean type?
pub fn is_bool(self) -> bool {
match self {
B1 | B8 | B16 | B32 | B64 => true,
_ => false,
}
}
/// Is this a scalar integer type?
pub fn is_int(self) -> bool {
match self {
I8 | I16 | I32 | I64 => true,
_ => false,
}
}
/// Is this a scalar floating point type?
pub fn is_float(self) -> bool {
match self {
F32 | F64 => true,
_ => false,
}
}
/// Get log2 of the number of lanes in this SIMD vector type.
///
/// All SIMD types have a lane count that is a power of two and no larger than 256, so this
/// will be a number in the range 0-8.
///
/// A scalar type is the same as a SIMD vector type with one lane, so it return 0.
pub fn log2_lane_count(self) -> u8 {
self.0 >> 4
}
/// Is this a scalar type? (That is, not a SIMD vector type).
///
/// A scalar type is the same as a SIMD vector type with one lane.
pub fn is_scalar(self) -> bool {
self.log2_lane_count() == 0
}
/// Get the number of lanes in this SIMD vector type.
///
/// A scalar type is the same as a SIMD vector type with one lane, so it returns 1.
pub fn lane_count(self) -> u16 {
1 << self.log2_lane_count()
}
/// Get the total number of bits used to represent this type.
pub fn bits(self) -> u16 {
self.lane_bits() as u16 * self.lane_count()
}
/// Get a SIMD vector type with `n` times more lanes than this one.
///
/// If this is a scalar type, this produces a SIMD type with this as a lane type and `n` lanes.
///
/// If this is already a SIMD vector type, this produces a SIMD vector type with `n *
/// self.lane_count()` lanes.
pub fn by(self, n: u16) -> Option<Type> {
if self.lane_bits() == 0 || !n.is_power_of_two() {
return None;
}
let log2_lanes: u32 = n.trailing_zeros();
let new_type = self.0 as u32 + (log2_lanes << 4);
if new_type < 0x90 {
Some(Type(new_type as u8))
} else {
None
}
}
/// Get a SIMD vector with half the number of lanes.
pub fn half_vector(self) -> Option<Type> {
if self.is_scalar() {
None
} else {
Some(Type(self.0 - 0x10))
}
}
/// Index of this type, for use with hash tables etc.
pub fn index(self) -> usize {
self.0 as usize
}
}
impl Display for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.is_void() {
write!(f, "void")
} else if self.is_bool() {
write!(f, "b{}", self.lane_bits())
} else if self.is_int() {
write!(f, "i{}", self.lane_bits())
} else if self.is_float() {
write!(f, "f{}", self.lane_bits())
} else if !self.is_scalar() {
write!(f, "{}x{}", self.lane_type(), self.lane_count())
} else {
panic!("Invalid Type(0x{:x})", self.0)
}
}
}
impl Default for Type {
fn default() -> Type {
VOID
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_scalars() {
assert_eq!(VOID, VOID.lane_type());
assert_eq!(0, VOID.bits());
assert_eq!(B1, B1.lane_type());
assert_eq!(B8, B8.lane_type());
assert_eq!(B16, B16.lane_type());
assert_eq!(B32, B32.lane_type());
assert_eq!(B64, B64.lane_type());
assert_eq!(I8, I8.lane_type());
assert_eq!(I16, I16.lane_type());
assert_eq!(I32, I32.lane_type());
assert_eq!(I64, I64.lane_type());
assert_eq!(F32, F32.lane_type());
assert_eq!(F64, F64.lane_type());
assert_eq!(VOID.lane_bits(), 0);
assert_eq!(B1.lane_bits(), 1);
assert_eq!(B8.lane_bits(), 8);
assert_eq!(B16.lane_bits(), 16);
assert_eq!(B32.lane_bits(), 32);
assert_eq!(B64.lane_bits(), 64);
assert_eq!(I8.lane_bits(), 8);
assert_eq!(I16.lane_bits(), 16);
assert_eq!(I32.lane_bits(), 32);
assert_eq!(I64.lane_bits(), 64);
assert_eq!(F32.lane_bits(), 32);
assert_eq!(F64.lane_bits(), 64);
}
#[test]
fn typevar_functions() {
assert_eq!(VOID.half_width(), None);
assert_eq!(B1.half_width(), None);
assert_eq!(B8.half_width(), None);
assert_eq!(B16.half_width(), Some(B8));
assert_eq!(B32.half_width(), Some(B16));
assert_eq!(B64.half_width(), Some(B32));
assert_eq!(I8.half_width(), None);
assert_eq!(I16.half_width(), Some(I8));
assert_eq!(I32.half_width(), Some(I16));
assert_eq!(I32X4.half_width(), Some(I16X4));
assert_eq!(I64.half_width(), Some(I32));
assert_eq!(F32.half_width(), None);
assert_eq!(F64.half_width(), Some(F32));
assert_eq!(VOID.double_width(), None);
assert_eq!(B1.double_width(), None);
assert_eq!(B8.double_width(), Some(B16));
assert_eq!(B16.double_width(), Some(B32));
assert_eq!(B32.double_width(), Some(B64));
assert_eq!(B64.double_width(), None);
assert_eq!(I8.double_width(), Some(I16));
assert_eq!(I16.double_width(), Some(I32));
assert_eq!(I32.double_width(), Some(I64));
assert_eq!(I32X4.double_width(), Some(I64X4));
assert_eq!(I64.double_width(), None);
assert_eq!(F32.double_width(), Some(F64));
assert_eq!(F64.double_width(), None);
}
#[test]
fn vectors() {
let big = F64.by(256).unwrap();
assert_eq!(big.lane_bits(), 64);
assert_eq!(big.lane_count(), 256);
assert_eq!(big.bits(), 64 * 256);
assert_eq!(big.half_vector().unwrap().to_string(), "f64x128");
assert_eq!(B1.by(2).unwrap().half_vector().unwrap().to_string(), "b1");
assert_eq!(I32.half_vector(), None);
assert_eq!(VOID.half_vector(), None);
// Check that the generated constants match the computed vector types.
assert_eq!(I32.by(4), Some(I32X4));
assert_eq!(F64.by(8), Some(F64X8));
}
#[test]
fn format_scalars() {
assert_eq!(VOID.to_string(), "void");
assert_eq!(B1.to_string(), "b1");
assert_eq!(B8.to_string(), "b8");
assert_eq!(B16.to_string(), "b16");
assert_eq!(B32.to_string(), "b32");
assert_eq!(B64.to_string(), "b64");
assert_eq!(I8.to_string(), "i8");
assert_eq!(I16.to_string(), "i16");
assert_eq!(I32.to_string(), "i32");
assert_eq!(I64.to_string(), "i64");
assert_eq!(F32.to_string(), "f32");
assert_eq!(F64.to_string(), "f64");
}
#[test]
fn format_vectors() {
assert_eq!(B1.by(8).unwrap().to_string(), "b1x8");
assert_eq!(B8.by(1).unwrap().to_string(), "b8");
assert_eq!(B16.by(256).unwrap().to_string(), "b16x256");
assert_eq!(B32.by(4).unwrap().by(2).unwrap().to_string(), "b32x8");
assert_eq!(B64.by(8).unwrap().to_string(), "b64x8");
assert_eq!(I8.by(64).unwrap().to_string(), "i8x64");
assert_eq!(F64.by(2).unwrap().to_string(), "f64x2");
assert_eq!(I8.by(3), None);
assert_eq!(I8.by(512), None);
assert_eq!(VOID.by(4), None);
}
}