From f8545574b5bdd5ea7edd93a071d98e03e3adec93 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Tue, 8 Nov 2016 09:40:19 -0800 Subject: [PATCH] Move ValueType into a new cdsl top-level module. We want to separate the Python classes that make up the DSL used to define the Cretonne language from the concrete definitions. - cdsl.types defines the ValueType class hierarchy. - base.types defines the concrete types. --- docs/cton_domain.py | 6 +- docs/metaref.rst | 25 +-- lib/cretonne/meta/base/__init__.py | 1 + lib/cretonne/meta/{cretonne => base}/types.py | 4 +- lib/cretonne/meta/cdsl/__init__.py | 6 + lib/cretonne/meta/cdsl/types.py | 160 ++++++++++++++ lib/cretonne/meta/check.sh | 2 +- lib/cretonne/meta/cretonne/__init__.py | 198 ++---------------- lib/cretonne/meta/cretonne/base.py | 2 +- lib/cretonne/meta/gen_instr.py | 5 +- lib/cretonne/meta/gen_types.py | 3 +- 11 files changed, 210 insertions(+), 202 deletions(-) create mode 100644 lib/cretonne/meta/base/__init__.py rename lib/cretonne/meta/{cretonne => base}/types.py (88%) create mode 100644 lib/cretonne/meta/cdsl/__init__.py create mode 100644 lib/cretonne/meta/cdsl/types.py diff --git a/docs/cton_domain.py b/docs/cton_domain.py index b45aa5e677..00242ff565 100644 --- a/docs/cton_domain.py +++ b/docs/cton_domain.py @@ -245,7 +245,7 @@ class TypeDocumenter(sphinx.ext.autodoc.Documenter): return False def resolve_name(self, modname, parents, path, base): - return 'cretonne.types', [base] + return 'base.types', [base] def add_content(self, more_content, no_docstring=False): super(TypeDocumenter, self).add_content(more_content, no_docstring) @@ -280,11 +280,11 @@ class InstDocumenter(sphinx.ext.autodoc.Documenter): op = inst.ins[0] sig += ' ' + op.name # If the first input is variable-args, this is 'return'. No parens. - if op.typ.operand_kind().name == 'variable_args': + if op.kind.name == 'variable_args': sig += '...'.format(op.name) for op in inst.ins[1:]: # This is a call or branch with args in (...). - if op.typ.operand_kind().name == 'variable_args': + if op.kind.name == 'variable_args': sig += '({}...)'.format(op.name) else: sig += ', ' + op.name diff --git a/docs/metaref.rst b/docs/metaref.rst index fa3226c51f..6d8d8ad2b1 100644 --- a/docs/metaref.rst +++ b/docs/metaref.rst @@ -4,7 +4,7 @@ Cretonne Meta Language Reference .. default-domain:: py .. highlight:: python -.. module:: cretonne +.. module:: cdsl The Cretonne meta language is used to define instructions for Cretonne. It is a domain specific language embedded in Python. This document describes the Python @@ -16,8 +16,8 @@ steps: 1. The Python modules are imported. This has the effect of building static data structures in global variables in the modules. These static data structures - use the classes in the :mod:`cretonne` module to describe instruction sets - and other properties. + in the :mod:`base` and :mod:`isa` packages use the classes in the + :mod:`cdsl` module to describe instruction sets and other properties. 2. The static data structures are processed to produce Rust source code and constant tables. @@ -41,6 +41,7 @@ ISA, and defined in a `settings` module under the appropriate Settings can take boolean on/off values, small numbers, or explicitly enumerated symbolic values. Each type is represented by a sub-class of :class:`Setting`: +.. currentmodule:: cretonne .. inheritance-diagram:: Setting BoolSetting NumSetting EnumSetting :parts: 1 @@ -125,36 +126,36 @@ Immediate operands Immediate instruction operands don't correspond to SSA values, but have values that are encoded directly in the instruction. Immediate operands don't -have types from the :class:`cretonne.ValueType` type system; they often have +have types from the :class:`cdsl.types.ValueType` type system; they often have enumerated values of a specific type. The type of an immediate operand is indicated with an instance of :class:`ImmediateKind`. +.. currentmodule:: cretonne .. autoclass:: ImmediateKind .. automodule:: cretonne.immediates :members: -.. currentmodule:: cretonne - Entity references ----------------- Instruction operands can also refer to other entities in the same function. This can be extended basic blocks, or entities declared in the function preamble. +.. currentmodule:: cretonne + .. autoclass:: EntityRefKind .. automodule:: cretonne.entities :members: -.. currentmodule:: cretonne - Value types ----------- -Concrete value types are represented as instances of :class:`cretonne.ValueType`. There are +Concrete value types are represented as instances of :class:`cdsl.types.ValueType`. There are subclasses to represent scalar and vector types. +.. currentmodule:: cdsl.types .. autoclass:: ValueType .. inheritance-diagram:: ValueType ScalarType VectorType IntType FloatType BoolType :parts: 1 @@ -169,11 +170,9 @@ subclasses to represent scalar and vector types. .. autoclass:: BoolType :members: -.. automodule:: cretonne.types +.. automodule:: base.types :members: -.. currentmodule:: cretonne - There are no predefined vector types, but they can be created as needed with the :func:`ScalarType.by` function. @@ -181,6 +180,8 @@ the :func:`ScalarType.by` function. Instruction representation ========================== +.. currentmodule:: cretonne + The Rust in-memory representation of instructions is derived from the instruction descriptions. Part of the representation is generated, and part is written as Rust code in the `cretonne.instructions` module. The instruction diff --git a/lib/cretonne/meta/base/__init__.py b/lib/cretonne/meta/base/__init__.py new file mode 100644 index 0000000000..132b469ee6 --- /dev/null +++ b/lib/cretonne/meta/base/__init__.py @@ -0,0 +1 @@ +"""Definitions for the base Cretonne language.""" diff --git a/lib/cretonne/meta/cretonne/types.py b/lib/cretonne/meta/base/types.py similarity index 88% rename from lib/cretonne/meta/cretonne/types.py rename to lib/cretonne/meta/base/types.py index 05dcb53e16..a2eb1054b9 100644 --- a/lib/cretonne/meta/cretonne/types.py +++ b/lib/cretonne/meta/base/types.py @@ -1,8 +1,8 @@ """ -The cretonne.types module predefines all the Cretonne scalar types. +The base.types module predefines all the Cretonne scalar types. """ from __future__ import absolute_import -from . import ScalarType, IntType, FloatType, BoolType +from cdsl.types import ScalarType, IntType, FloatType, BoolType #: Boolean. b1 = ScalarType( diff --git a/lib/cretonne/meta/cdsl/__init__.py b/lib/cretonne/meta/cdsl/__init__.py new file mode 100644 index 0000000000..417f800950 --- /dev/null +++ b/lib/cretonne/meta/cdsl/__init__.py @@ -0,0 +1,6 @@ +""" +Cretonne DSL classes. + +This module defines the classes that are used to define Cretonne instructions +and other entitties. +""" diff --git a/lib/cretonne/meta/cdsl/types.py b/lib/cretonne/meta/cdsl/types.py new file mode 100644 index 0000000000..3d5a5dcd38 --- /dev/null +++ b/lib/cretonne/meta/cdsl/types.py @@ -0,0 +1,160 @@ +"""Cretonne ValueType hierarchy""" +from __future__ import absolute_import +import math + + +# ValueType instances (i8, i32, ...) are provided in the cretonne.types module. +class ValueType(object): + """ + A concrete SSA value type. + + All SSA values have a type that is described by an instance of `ValueType` + or one of its subclasses. + """ + + # Map name -> ValueType. + _registry = dict() # type: Dict[str, ValueType] + + # List of all the scalar types. + all_scalars = list() # type: List[ValueType] + + def __init__(self, name, membytes, doc): + # type: (str, int, str) -> None + self.name = name + self.membytes = membytes + self.__doc__ = doc + assert name not in ValueType._registry + ValueType._registry[name] = self + + def __str__(self): + # type: () -> str + return self.name + + def free_typevar(self): + return None + + @staticmethod + def by_name(name): + # type: (str) -> ValueType + if name in ValueType._registry: + return ValueType._registry[name] + else: + raise AttributeError("No type named '{}'".format(name)) + + +class ScalarType(ValueType): + """ + A concrete scalar (not vector) type. + + Also tracks a unique set of :py:class:`VectorType` instances with this type + as the lane type. + """ + + def __init__(self, name, membytes, doc): + # type: (str, int, str) -> None + super(ScalarType, self).__init__(name, membytes, doc) + self._vectors = dict() # type: Dict[int, VectorType] + # Assign numbers starting from 1. (0 is VOID). + ValueType.all_scalars.append(self) + self.number = len(ValueType.all_scalars) + assert self.number < 16, 'Too many scalar types' + + def __repr__(self): + # type: () -> str + return 'ScalarType({})'.format(self.name) + + def rust_name(self): + # type: () -> str + return 'types::' + self.name.upper() + + def by(self, lanes): + # type: (int) -> VectorType + """ + Get a vector type with this type as the lane type. + + For example, ``i32.by(4)`` returns the :obj:`i32x4` type. + """ + if lanes in self._vectors: + return self._vectors[lanes] + else: + v = VectorType(self, lanes) + self._vectors[lanes] = v + return v + + +class VectorType(ValueType): + """ + A concrete SIMD vector type. + + A vector type has a lane type which is an instance of :class:`ScalarType`, + and a positive number of lanes. + """ + + def __init__(self, base, lanes): + # type: (ScalarType, int) -> None + assert isinstance(base, ScalarType), 'SIMD lanes must be scalar types' + super(VectorType, self).__init__( + name='{}x{}'.format(base.name, lanes), + membytes=lanes*base.membytes, + doc=""" + A SIMD vector with {} lanes containing a `{}` each. + """.format(lanes, base.name)) + self.base = base + self.lanes = lanes + self.number = 16*int(math.log(lanes, 2)) + base.number + + def __repr__(self): + # type: () -> str + return ('VectorType(base={}, lanes={})' + .format(self.base.name, self.lanes)) + + +class IntType(ScalarType): + """A concrete scalar integer type.""" + + def __init__(self, bits): + # type: (int) -> None + assert bits > 0, 'IntType must have positive number of bits' + super(IntType, self).__init__( + name='i{:d}'.format(bits), + membytes=bits // 8, + doc="An integer type with {} bits.".format(bits)) + self.bits = bits + + def __repr__(self): + # type: () -> str + return 'IntType(bits={})'.format(self.bits) + + +class FloatType(ScalarType): + """A concrete scalar floating point type.""" + + def __init__(self, bits, doc): + # type: (int, str) -> None + assert bits > 0, 'FloatType must have positive number of bits' + super(FloatType, self).__init__( + name='f{:d}'.format(bits), + membytes=bits // 8, + doc=doc) + self.bits = bits + + def __repr__(self): + # type: () -> str + return 'FloatType(bits={})'.format(self.bits) + + +class BoolType(ScalarType): + """A concrete scalar boolean type.""" + + def __init__(self, bits): + # type: (int) -> None + assert bits > 0, 'BoolType must have positive number of bits' + super(BoolType, self).__init__( + name='b{:d}'.format(bits), + membytes=bits // 8, + doc="A boolean type with {} bits.".format(bits)) + self.bits = bits + + def __repr__(self): + # type: () -> str + return 'BoolType(bits={})'.format(self.bits) diff --git a/lib/cretonne/meta/check.sh b/lib/cretonne/meta/check.sh index b0a6c853db..2077716eb7 100755 --- a/lib/cretonne/meta/check.sh +++ b/lib/cretonne/meta/check.sh @@ -14,7 +14,7 @@ runif() { # Check Python sources for Python 3 compatibility using pylint. # # Install pylint with 'pip install pylint'. -runif pylint --py3k --reports=no -- *.py cretonne isa +runif pylint --py3k --reports=no -- *.py cdsl base cretonne isa # Style linting. runif flake8 . diff --git a/lib/cretonne/meta/cretonne/__init__.py b/lib/cretonne/meta/cretonne/__init__.py index 7463f47d69..b1cc0322bd 100644 --- a/lib/cretonne/meta/cretonne/__init__.py +++ b/lib/cretonne/meta/cretonne/__init__.py @@ -6,10 +6,10 @@ instructions. """ from __future__ import absolute_import import re -import math import importlib from collections import OrderedDict from .predicates import And, Predicate, FieldPredicate # noqa +import cdsl.types # The typing module is only required by mypy, and we don't use these imports # outside type comments. @@ -17,7 +17,7 @@ try: from typing import Tuple, Union, Any, Iterable, Sequence, TYPE_CHECKING # noqa MaybeBoundInst = Union['Instruction', 'BoundInstruction'] AnyPredicate = Union['Predicate', 'FieldPredicate'] - OperandSpec = Union['OperandKind', 'ValueType', 'TypeVar'] + OperandSpec = Union['OperandKind', 'cdsl.types.ValueType', 'TypeVar'] except ImportError: TYPE_CHECKING = False @@ -391,171 +391,6 @@ class EntityRefKind(OperandKind): return 'EntityRefKind({})'.format(self.name) -# ValueType instances (i8, i32, ...) are provided in the cretonne.types module. -class ValueType(object): - """ - A concrete SSA value type. - - All SSA values have a type that is described by an instance of `ValueType` - or one of its subclasses. - """ - - # Map name -> ValueType. - _registry = dict() # type: Dict[str, ValueType] - - # List of all the scalar types. - all_scalars = list() # type: List[ValueType] - - def __init__(self, name, membytes, doc): - # type: (str, int, str) -> None - self.name = name - self.membytes = membytes - self.__doc__ = doc - assert name not in ValueType._registry - ValueType._registry[name] = self - - def __str__(self): - # type: () -> str - return self.name - - def operand_kind(self): - # type: () -> OperandKind - """ - When a `ValueType` 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): - return None - - @staticmethod - def by_name(name): - # type: (str) -> ValueType - if name in ValueType._registry: - return ValueType._registry[name] - else: - raise AttributeError("No type named '{}'".format(name)) - - -class ScalarType(ValueType): - """ - A concrete scalar (not vector) type. - - Also tracks a unique set of :py:class:`VectorType` instances with this type - as the lane type. - """ - - def __init__(self, name, membytes, doc): - # type: (str, int, str) -> None - super(ScalarType, self).__init__(name, membytes, doc) - self._vectors = dict() # type: Dict[int, VectorType] - # Assign numbers starting from 1. (0 is VOID). - ValueType.all_scalars.append(self) - self.number = len(ValueType.all_scalars) - assert self.number < 16, 'Too many scalar types' - - def __repr__(self): - # type: () -> str - return 'ScalarType({})'.format(self.name) - - def rust_name(self): - # type: () -> str - return 'types::' + self.name.upper() - - def by(self, lanes): - # type: (int) -> VectorType - """ - Get a vector type with this type as the lane type. - - For example, ``i32.by(4)`` returns the :obj:`i32x4` type. - """ - if lanes in self._vectors: - return self._vectors[lanes] - else: - v = VectorType(self, lanes) - self._vectors[lanes] = v - return v - - -class VectorType(ValueType): - """ - A concrete SIMD vector type. - - A vector type has a lane type which is an instance of :class:`ScalarType`, - and a positive number of lanes. - """ - - def __init__(self, base, lanes): - # type: (ScalarType, int) -> None - assert isinstance(base, ScalarType), 'SIMD lanes must be scalar types' - super(VectorType, self).__init__( - name='{}x{}'.format(base.name, lanes), - membytes=lanes*base.membytes, - doc=""" - A SIMD vector with {} lanes containing a `{}` each. - """.format(lanes, base.name)) - self.base = base - self.lanes = lanes - self.number = 16*int(math.log(lanes, 2)) + base.number - - def __repr__(self): - # type: () -> str - return ('VectorType(base={}, lanes={})' - .format(self.base.name, self.lanes)) - - -class IntType(ScalarType): - """A concrete scalar integer type.""" - - def __init__(self, bits): - # type: (int) -> None - assert bits > 0, 'IntType must have positive number of bits' - super(IntType, self).__init__( - name='i{:d}'.format(bits), - membytes=bits // 8, - doc="An integer type with {} bits.".format(bits)) - self.bits = bits - - def __repr__(self): - # type: () -> str - return 'IntType(bits={})'.format(self.bits) - - -class FloatType(ScalarType): - """A concrete scalar floating point type.""" - - def __init__(self, bits, doc): - # type: (int, str) -> None - assert bits > 0, 'FloatType must have positive number of bits' - super(FloatType, self).__init__( - name='f{:d}'.format(bits), - membytes=bits // 8, - doc=doc) - self.bits = bits - - def __repr__(self): - # type: () -> str - return 'FloatType(bits={})'.format(self.bits) - - -class BoolType(ScalarType): - """A concrete scalar boolean type.""" - - def __init__(self, bits): - # type: (int) -> None - assert bits > 0, 'BoolType must have positive number of bits' - super(BoolType, self).__init__( - name='b{:d}'.format(bits), - membytes=bits // 8, - doc="A boolean type with {} bits.".format(bits)) - self.bits = bits - - def __repr__(self): - # type: () -> str - return 'BoolType(bits={})'.format(self.bits) - - # Defining instructions. @@ -633,9 +468,12 @@ class Operand(object): def __init__(self, name, typ, doc=''): # type: (str, OperandSpec, str) -> None self.name = name - self.typ = typ self.__doc__ = doc - self.kind = typ.operand_kind() + self.typ = typ + if isinstance(typ, cdsl.types.ValueType): + self.kind = value + else: + self.kind = typ.operand_kind() def get_doc(self): # type: () -> str @@ -688,7 +526,7 @@ class InstructionFormat(object): """ # Map (multiple_results, kind, kind, ...) -> InstructionFormat - _registry = dict() # type: Dict[Tuple, InstructionFormat] + _registry = dict() # type: Dict[Tuple[bool, Tuple[OperandKind, ...]], InstructionFormat] # noqa # All existing formats. all_formats = list() # type: List[InstructionFormat] @@ -715,7 +553,7 @@ class InstructionFormat(object): self.typevar_operand = self.value_operands[0] # Compute a signature for the global registry. - sig = (self.multiple_results,) + self.kinds + sig = (self.multiple_results, self.kinds) if sig in InstructionFormat._registry: raise RuntimeError( "Format '{}' has the same signature as existing format '{}'" @@ -782,11 +620,11 @@ class InstructionFormat(object): multiple_results = outs[0].kind == variable_args else: multiple_results = len(outs) > 1 - sig = (multiple_results,) + tuple(op.kind for op in ins) + sig = (multiple_results, tuple(op.kind for op in ins)) if sig not in InstructionFormat._registry: raise RuntimeError( "No instruction format matches ins = ({}){}".format( - ", ".join(map(str, sig[1:])), + ", ".join(map(str, sig[1])), "[multiple results]" if multiple_results else "")) return InstructionFormat._registry[sig] @@ -991,7 +829,7 @@ class Instruction(object): return x def bind(self, *args): - # type: (*ValueType) -> BoundInstruction + # type: (*cdsl.types.ValueType) -> BoundInstruction """ Bind a polymorphic instruction to a concrete list of type variable values. @@ -1007,10 +845,10 @@ class Instruction(object): >>> iadd.i32 """ - return self.bind(ValueType.by_name(name)) + return self.bind(cdsl.types.ValueType.by_name(name)) def fully_bound(self): - # type: () -> Tuple[Instruction, Tuple[ValueType, ...]] + # type: () -> Tuple[Instruction, Tuple[cdsl.types.ValueType, ...]] """ Verify that all typevars have been bound, and return a `(inst, typevars)` pair. @@ -1036,7 +874,7 @@ class BoundInstruction(object): """ def __init__(self, inst, typevars): - # type: (Instruction, Tuple[ValueType, ...]) -> None + # type: (Instruction, Tuple[cdsl.types.ValueType, ...]) -> None self.inst = inst self.typevars = typevars assert len(typevars) <= 1 + len(inst.other_typevars) @@ -1045,7 +883,7 @@ class BoundInstruction(object): return '.'.join([self.inst.name, ] + list(map(str, self.typevars))) def bind(self, *args): - # type: (*ValueType) -> BoundInstruction + # type: (*cdsl.types.ValueType) -> BoundInstruction """ Bind additional typevars. """ @@ -1058,10 +896,10 @@ class BoundInstruction(object): >>> uext.i32.i8 """ - return self.bind(ValueType.by_name(name)) + return self.bind(cdsl.types.ValueType.by_name(name)) def fully_bound(self): - # type: () -> Tuple[Instruction, Tuple[ValueType, ...]] + # type: () -> Tuple[Instruction, Tuple[cdsl.types.ValueType, ...]] """ Verify that all typevars have been bound, and return a `(inst, typevars)` pair. diff --git a/lib/cretonne/meta/cretonne/base.py b/lib/cretonne/meta/cretonne/base.py index adbbf68d28..d991228872 100644 --- a/lib/cretonne/meta/cretonne/base.py +++ b/lib/cretonne/meta/cretonne/base.py @@ -7,7 +7,7 @@ support. from __future__ import absolute_import from . import Operand, Instruction, InstructionGroup, variable_args from .typevar import TypeVar -from .types import i8, f32, f64, b1 +from base.types import i8, f32, f64, b1 from .immediates import imm64, uimm8, ieee32, ieee64, immvector, intcc, floatcc from . import entities diff --git a/lib/cretonne/meta/gen_instr.py b/lib/cretonne/meta/gen_instr.py index 569eca71b7..d7afcf1887 100644 --- a/lib/cretonne/meta/gen_instr.py +++ b/lib/cretonne/meta/gen_instr.py @@ -5,6 +5,7 @@ from __future__ import absolute_import import srcgen import constant_hash from unique_table import UniqueTable, UniqueSeqTable +import cdsl.types import cretonne @@ -310,11 +311,11 @@ def get_constraint(op, ctrl_typevar, type_sets): - `Free(idx)` where `idx` is an index into `type_sets`. - `Same`, `Lane`, `AsBool` for controlling typevar-derived constraints. """ + assert op.kind is cretonne.value t = op.typ - assert t.operand_kind() is cretonne.value # A concrete value type. - if isinstance(t, cretonne.ValueType): + if isinstance(t, cdsl.types.ValueType): return 'Concrete({})'.format(t.rust_name()) if t.free_typevar() is not ctrl_typevar: diff --git a/lib/cretonne/meta/gen_types.py b/lib/cretonne/meta/gen_types.py index 2b284d135b..5ffed14cf4 100644 --- a/lib/cretonne/meta/gen_types.py +++ b/lib/cretonne/meta/gen_types.py @@ -9,7 +9,8 @@ This ensures that Python and Rust use the same type numbering. """ from __future__ import absolute_import import srcgen -from cretonne import ValueType +from cdsl.types import ValueType +import base.types # noqa def emit_type(ty, fmt):