""" Type variables for Parametric polymorphism. Cretonne instructions and instruction transformations can be specified to be polymorphic by using type variables. """ from __future__ import absolute_import import math from . import value MAX_LANES = 256 MAX_BITS = 64 def is_power_of_two(x): return x > 0 and x & (x-1) == 0 def int_log2(x): return int(math.log(x, 2)) class TypeSet(object): """ A set of types. We don't allow arbitrary subsets of types, but use a parametrized approach instead. Objects of this class can be used as dictionary keys. Parametrized type sets are specified in terms of ranges: - The permitted range of vector lanes, where 1 indicates a scalar type. - The permitted range of integer types. - The permitted range of floating point types, and - The permitted range of boolean types. The ranges are inclusive from smallest bit-width to largest bit-width. :param lanes: `(min, max)` inclusive range of permitted vector lane counts. :param ints: `(min, max)` inclusive range of permitted scalar integer widths. :param floats: `(min, max)` inclusive range of permitted scalar floating point widths. :param bools: `(min, max)` inclusive range of permitted scalar boolean widths. """ def __init__(self, lanes, ints=None, floats=None, bools=None): self.min_lanes, self.max_lanes = lanes assert is_power_of_two(self.min_lanes) assert is_power_of_two(self.max_lanes) assert self.max_lanes <= MAX_LANES if ints: if ints is True: ints = (8, MAX_BITS) self.min_int, self.max_int = ints assert is_power_of_two(self.min_int) assert is_power_of_two(self.max_int) assert self.max_int <= MAX_BITS else: self.min_int = None self.max_int = None if floats: if floats is True: floats = (32, 64) self.min_float, self.max_float = floats assert is_power_of_two(self.min_float) assert self.min_float >= 32 assert is_power_of_two(self.max_float) assert self.max_float <= 64 else: self.min_float = None self.max_float = None if bools: if bools is True: bools = (1, MAX_BITS) self.min_bool, self.max_bool = bools assert is_power_of_two(self.min_bool) assert is_power_of_two(self.max_bool) assert self.max_bool <= MAX_BITS else: self.min_bool = None self.max_bool = None def typeset_key(self): """Key tuple used for hashing and equality.""" return (self.min_lanes, self.max_lanes, self.min_int, self.max_int, self.min_float, self.max_float, self.min_bool, self.max_bool) def __hash__(self): return hash(self.typeset_key()) def __eq__(self, other): return self.typeset_key() == other.typeset_key() def __repr__(self): s = 'TypeSet(lanes=({}, {})'.format(self.min_lanes, self.max_lanes) if self.min_int is not None: s += ', ints=({}, {})'.format(self.min_int, self.max_int) if self.min_float is not None: s += ', floats=({}, {})'.format(self.min_float, self.max_float) if self.min_bool is not None: s += ', bools=({}, {})'.format(self.min_bool, self.max_bool) return s + ')' def emit_fields(self, fmt): """Emit field initializers for this typeset.""" fmt.comment(repr(self)) fields = ('lanes', 'int', 'float', 'bool') for field in fields: min_val = getattr(self, 'min_' + field) max_val = getattr(self, 'max_' + field) if min_val is None: fmt.line('min_{}: 0,'.format(field)) fmt.line('max_{}: 0,'.format(field)) else: fmt.line('min_{}: {},'.format( field, int_log2(min_val))) fmt.line('max_{}: {},'.format( field, int_log2(max_val) + 1)) class TypeVar(object): """ Type variables can be used in place of concrete types when defining instructions. This makes the instructions *polymorphic*. A type variable is restricted to vary over a subset of the value types. This subset is specified by a set of flags that control the permitted base types and whether the type variable can assume scalar or vector types, or both. :param name: Short name of type variable used in instruction descriptions. :param doc: Documentation string. :param ints: Allow all integer base types, or `(min, max)` bit-range. :param floats: Allow all floating point base types, or `(min, max)` bit-range. :param bools: Allow all boolean base types, or `(min, max)` bit-range. :param scalars: Allow type variable to assume scalar types. :param simd: Allow type variable to assume vector types, or `(min, max)` lane count range. """ def __init__( self, name, doc, ints=False, floats=False, bools=False, scalars=True, simd=False, base=None, derived_func=None): self.name = name self.__doc__ = doc self.is_derived = isinstance(base, TypeVar) if base: assert self.is_derived assert derived_func self.base = base self.derived_func = derived_func self.name = '{}({})'.format(derived_func, base.name) else: min_lanes = 1 if scalars else 2 if simd: if simd is True: max_lanes = MAX_LANES else: min_lanes, max_lanes = simd assert not scalars or min_lanes <= 2 else: max_lanes = 1 self.type_set = TypeSet( lanes=(min_lanes, max_lanes), ints=ints, floats=floats, bools=bools) def __str__(self): return "`{}`".format(self.name) def lane_of(self): """ Return a derived type variable that is the scalar lane type of this type variable. When this type variable assumes a scalar type, the derived type will be the same scalar type. """ return TypeVar(None, None, base=self, derived_func='LaneOf') def as_bool(self): """ Return a derived type variable that has the same vector geometry as this type variable, but with boolean lanes. Scalar types map to `b1`. """ return TypeVar(None, None, base=self, derived_func='AsBool') def operand_kind(self): # When a `TypeVar` object is used to describe the type of an `Operand` # in an instruction definition, the kind of that operand is an SSA # value. return value def free_typevar(self): if self.is_derived: return self.base else: return self