Represent type sets with ranges.
Allow limits on the smallest and largest integer type in the set, the highest and lowest number of lanes etc.
This commit is contained in:
@@ -5,20 +5,127 @@ Cretonne instructions and instruction transformations can be specified to be
|
||||
polymorphic by using type variables.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from collections import namedtuple
|
||||
import math
|
||||
from . import value
|
||||
|
||||
#: A `TypeSet` represents a set of types. We don't allow arbitrary subsets of
|
||||
#: types, but use a parametrized approach instead.
|
||||
#: This is represented as a named tuple so it can be used as a dictionary key.
|
||||
TypeSet = namedtuple(
|
||||
'TypeSet', [
|
||||
'allow_scalars',
|
||||
'allow_simd',
|
||||
'base',
|
||||
'all_ints',
|
||||
'all_floats',
|
||||
'all_bools'])
|
||||
|
||||
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):
|
||||
@@ -33,37 +140,45 @@ class TypeVar(object):
|
||||
|
||||
:param name: Short name of type variable used in instruction descriptions.
|
||||
:param doc: Documentation string.
|
||||
:param base: Single base type or list of base types. Use this to specify an
|
||||
exact set of base types if the general categories below are not good
|
||||
enough.
|
||||
:param ints: Allow all integer base types.
|
||||
:param floats: Allow all floating point base types.
|
||||
:param bools: Allow all boolean base types.
|
||||
: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.
|
||||
:param simd: Allow type variable to assume vector types, or `(min, max)`
|
||||
lane count range.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, name, doc, base=None,
|
||||
self, name, doc,
|
||||
ints=False, floats=False, bools=False,
|
||||
scalars=True, simd=False,
|
||||
derived_func=None):
|
||||
base=None, derived_func=None):
|
||||
self.name = name
|
||||
self.__doc__ = doc
|
||||
self.base = base
|
||||
self.is_derived = isinstance(base, TypeVar)
|
||||
if self.is_derived:
|
||||
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(
|
||||
allow_scalars=scalars,
|
||||
allow_simd=simd,
|
||||
base=base,
|
||||
all_ints=ints,
|
||||
all_floats=floats,
|
||||
all_bools=bools)
|
||||
lanes=(min_lanes, max_lanes),
|
||||
ints=ints,
|
||||
floats=floats,
|
||||
bools=bools)
|
||||
|
||||
def __str__(self):
|
||||
return "`{}`".format(self.name)
|
||||
@@ -92,7 +207,7 @@ class TypeVar(object):
|
||||
return value
|
||||
|
||||
def free_typevar(self):
|
||||
if isinstance(self.base, TypeVar):
|
||||
if self.is_derived:
|
||||
return self.base
|
||||
else:
|
||||
return self
|
||||
|
||||
Reference in New Issue
Block a user