Generate value type constraints.
Add an Opcode::constraints() method which returns an OpcodeConstraints object. This object provides information on instruction polymorphism and how many results is produced. Generate a list of TypeSet objects for checking free type variables. The type sets are parametrized rather than being represented as fully general sets. Add UniqueTable and UniqueSeqTable classes to the meta code generator. Use for compressing tabular data by removing duplicates.
This commit is contained in:
@@ -11,7 +11,7 @@ use std::str::FromStr;
|
||||
|
||||
use entities::*;
|
||||
use immediates::*;
|
||||
use types::Type;
|
||||
use types::{self, Type};
|
||||
|
||||
// Include code generated by `meta/gen_instr.py`. This file contains:
|
||||
//
|
||||
@@ -21,6 +21,12 @@ use types::Type;
|
||||
// - 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 {
|
||||
@@ -38,6 +44,12 @@ impl Opcode {
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
// A primitive hash function for matching opcodes.
|
||||
@@ -259,6 +271,155 @@ impl InstructionData {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Since there can be a lot of opcodes, the `OpcodeConstraints` object is encoded as a bit field
|
||||
/// by the `meta/gen_instr.py` script.
|
||||
///
|
||||
/// The bit field bits are:
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// Bits 4-7:
|
||||
/// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`.
|
||||
///
|
||||
/// Bits 8-15:
|
||||
/// 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 isdetermined by the instruction
|
||||
/// format.
|
||||
///
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OpcodeConstraints(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.0 & 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.0 & 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.0 & 0xff) >> 4) 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.0 >> 8) 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])
|
||||
}
|
||||
}
|
||||
|
||||
/// A value type set describes the permitted set of types for a type variable.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ValueTypeSet {
|
||||
allow_scalars: bool,
|
||||
allow_simd: bool,
|
||||
|
||||
base: Type,
|
||||
all_ints: bool,
|
||||
all_floats: bool,
|
||||
all_bools: bool,
|
||||
}
|
||||
|
||||
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 {
|
||||
scalar == self.base || (self.all_ints && scalar.is_int()) ||
|
||||
(self.all_floats && scalar.is_float()) || (self.all_bools && scalar.is_bool())
|
||||
}
|
||||
|
||||
/// Does `typ` belong to this set?
|
||||
pub fn contains(&self, typ: Type) -> bool {
|
||||
let allowed = if typ.is_scalar() {
|
||||
self.allow_scalars
|
||||
} else {
|
||||
self.allow_simd
|
||||
};
|
||||
allowed && self.is_base_type(typ.lane_type())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()`.
|
||||
Lane,
|
||||
|
||||
/// This operand is `ctrlType.as_bool()`.
|
||||
AsBool,
|
||||
}
|
||||
|
||||
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),
|
||||
Lane => Some(ctrl_type.lane_type()),
|
||||
AsBool => Some(ctrl_type.as_bool()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -83,6 +83,20 @@ impl Type {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a type with the same number of lanes as this type, but with the lanes replaces 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))
|
||||
}
|
||||
|
||||
/// Is this the VOID type?
|
||||
pub fn is_void(self) -> bool {
|
||||
self == VOID
|
||||
|
||||
Reference in New Issue
Block a user