diff --git a/docs/metaref.rst b/docs/metaref.rst index fe46e8af4a..2e47535492 100644 --- a/docs/metaref.rst +++ b/docs/metaref.rst @@ -4,65 +4,79 @@ Cretonne Meta Language Reference .. default-domain:: py .. highlight:: python - -The Cretonne meta language is used to define instructions for Cretonne. It is a -domain specific language embedded in Python. - -An instruction set is described by a Python module under the :file:`meta` -directory that has a global variable called ``instructions``. The basic -Cretonne instruction set described in :doc:`langref` is defined by the Python -module :mod:`cretonne.base`. - .. module:: cretonne -Value Types -=========== +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 +modules that form the embedded DSL. -Concrete value types are represented as instances of :class:`cretonne.ValueType`. There are -subclasses to represent scalar and vector types. +The meta language descriptions are Python modules under the :file:`meta` +top-level directory. The descriptions are processed in two steps: -.. inheritance-diagram:: ValueType ScalarType VectorType IntType FloatType - :parts: 1 -.. autoclass:: ValueType -.. autoclass:: ScalarType - :members: -.. autoclass:: VectorType - :members: -.. autoclass:: IntType - :members: -.. autoclass:: FloatType - :members: +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. -Predefined types ----------------- -.. automodule:: cretonne.types - :members: +2. The static data structures are processed to produce Rust source code and + constant dables tables. -.. currentmodule:: cretonne +The main driver for this source code generation process is the +:file:`meta/build.py` script which is invoked as part of the build process if +anything in the :file:`meta` directory has changed since the last build. -Parametric polymorphism ------------------------ +Instruction descriptions +======================== -Instruction operands can be defined with *type variables* instead of concrete -types for their operands. This makes the instructions polymorphic. +New instructions are defined as instances of the :class:`Instruction` +class. As instruction instances are created, they are added to the currently +open :class:`InstructionGroup`. -.. autoclass:: TypeVar - -Instructions -============ - -New instructions are defined as instances of the :class:`cretonne.Instruction` -class. - -.. autoclass:: Instruction -.. autoclass:: Operand -.. autoclass:: OperandKind .. autoclass:: InstructionGroup :members: +The basic Cretonne instruction set described in :doc:`langref` is defined by the +Python module :mod:`cretonne.base`. This module has a global variable +:data:`cretonne.base.instructions` which is an :class:`InstructionGroup` +instance containing all the base instructions. -Immediates ----------- +.. autoclass:: Instruction + +An instruction is defined with a set of distinct input and output operands which +must be instances of the :class:`Operand` class. + +.. autoclass:: Operand + +Cretonne uses two separate type systems for immediate operands and SSA values. + +Type variables +-------------- + +Instruction descriptions can be made polymorphic by using :class:`Operand` +instances that refer to a *type variable* instead of a concrete value type. +Polymorphism only works for SSA value operands. Immediate operands have a fixed +operand kind. + +.. autoclass:: TypeVar + +If multiple operands refer to the same type variable they will be required to +have the same concrete type. For example, this defines an integer addition +instruction:: + + Int = TypeVar('Int', 'A scalar or vector integer type', ints=True, simd=True) + a = Operand('a', Int) + x = Operand('x', Int) + y = Operand('y', Int) + + iadd = Instruction('iadd', 'Integer addition', ins=(x, y), outs=a) + +The type variable `Int` is allowed to vary over all scalar and vector integer +value types, but in a given instance of the `iadd` instruction, the two +operands must have the same type, and the result will be the same type as the +inputs. + +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 @@ -77,6 +91,59 @@ indicated with an instance of :class:`ImmediateKind`. .. currentmodule:: cretonne + +Value types +----------- + +Concrete value types are represented as instances of :class:`cretonne.ValueType`. There are +subclasses to represent scalar and vector types. + +.. autoclass:: ValueType +.. inheritance-diagram:: ValueType ScalarType VectorType IntType FloatType + :parts: 1 +.. autoclass:: ScalarType + :members: +.. autoclass:: VectorType + :members: +.. autoclass:: IntType + :members: +.. autoclass:: FloatType + :members: + +.. automodule:: cretonne.types + :members: + +.. currentmodule:: cretonne + +There are no predefined vector types, but they can be created as needed with +the :func:`ScalarType.by` function. + + +Instruction representation +========================== + +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 +representation depends on the input operand kinds and whether the instruction +can produce multiple results. + +.. autoclass:: OperandKind + +Since all SSA value operands are represented as a `Value` in Rust code, value +types don't affect the representation. Two special operand kinds are used to +represent SSA values: + +.. autodata:: value +.. autodata:: args + +When an instruction description is created, it is automatically assigned a +predefined instruction format which is an instance of +:class:`InstructionFormat`: + +.. autoclass:: InstructionFormat + + Targets ======= diff --git a/meta/cretonne/__init__.py b/meta/cretonne/__init__.py index cbad9185fc..7e6f61d6bf 100644 --- a/meta/cretonne/__init__.py +++ b/meta/cretonne/__init__.py @@ -24,8 +24,6 @@ def camel_case(s): # operands and the kind of each operand. class OperandKind(object): """ - The kind of an operand. - An instance of the `OperandKind` class corresponds to a kind of operand. Each operand kind has a corresponding type in the Rust representation of an instruction. @@ -55,8 +53,8 @@ value = OperandKind( operand. """) -#: A variable-sizes list of value operands. Use for Ebb and function call -#: arguemnts. +#: A variable-sized list of value operands. Use for Ebb and function call +#: arguments. args = OperandKind( 'args', """ A variable size list of `value` operands. @@ -71,7 +69,7 @@ args = OperandKind( # module. class ImmediateKind(OperandKind): """ - The type of an immediate instruction operand. + The kind of an immediate instruction operand. """ def __init__(self, name, doc): @@ -215,13 +213,30 @@ class BoolType(ScalarType): class TypeVar(object): """ - A Type Variable. - 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 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 scalars: Allow type variable to assume scalar types. + :param simd: Allow type variable to assume vector types. """ - def __init__(self, name, doc): + def __init__( + self, name, doc, base=None, + ints=False, floats=False, bools=False, + scalars=True, simd=False): self.name = name self.__doc__ = doc @@ -238,8 +253,6 @@ class TypeVar(object): class InstructionGroup(object): """ - An instruction group. - Every instruction must belong to exactly one instruction group. A given target architecture can support instructions from multiple groups, and it does not necessarily support all instructions in a group. @@ -286,8 +299,6 @@ class InstructionGroup(object): class Operand(object): """ - An instruction operand. - An instruction operand can be either an *immediate* or an *SSA value*. The type of the operand is one of: @@ -318,8 +329,6 @@ class Operand(object): class InstructionFormat(object): """ - An instruction format. - Every instruction opcode has a corresponding instruction format which determines the number of operands and their kinds. Instruction formats are identified structurally, i.e., the format of an instruction is derived from @@ -396,8 +405,6 @@ class InstructionFormat(object): class Instruction(object): """ - An instruction description. - The operands to the instruction are specified as two tuples: ``ins`` and ``outs``. Since the Python singleton tuple syntax is a bit awkward, it is allowed to specify a singleton as just the operand itself, i.e., `ins=x` diff --git a/meta/cretonne/base.py b/meta/cretonne/base.py index 412e7f3abd..3fd8554cf3 100644 --- a/meta/cretonne/base.py +++ b/meta/cretonne/base.py @@ -10,9 +10,11 @@ from immediates import imm64, ieee32, ieee64, immvector instructions = InstructionGroup("base", "Shared base instruction set") -Int = TypeVar('Int', 'A scalar or vector integer type') -iB = TypeVar('iB', 'A scalar integer type') -TxN = TypeVar('%Tx%N', 'A SIMD vector type') +Int = TypeVar('Int', 'A scalar or vector integer type', ints=True, simd=True) +iB = TypeVar('iB', 'A scalar integer type', ints=True) +TxN = TypeVar( + '%Tx%N', 'A SIMD vector type', + ints=True, floats=True, bools=True, scalars=False, simd=True) # # Materializing constants. @@ -217,7 +219,10 @@ isub_imm = Instruction( # # TODO: Which types should permit boolean operations? Any reason to restrict? -bits = TypeVar('bits', 'Any integer, float, or boolean scalar or vector type') +bits = TypeVar( + 'bits', 'Any integer, float, or boolean scalar or vector type', + ints=True, floats=True, bools=True, scalars=True, simd=True) + x = Operand('x', bits) y = Operand('y', bits) a = Operand('a', bits)