Split out instruction formats.
- cdsl.formats provides classes for describing instruction formats. - base.formats provides concrete instruction format definitions.
This commit is contained in:
@@ -6,8 +6,8 @@ Rust representation of cretonne IL, so all instruction formats must be defined
|
|||||||
in this module.
|
in this module.
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
from cdsl.formats import InstructionFormat
|
||||||
from cdsl.operands import VALUE, VARIABLE_ARGS
|
from cdsl.operands import VALUE, VARIABLE_ARGS
|
||||||
from cretonne import InstructionFormat
|
|
||||||
from .immediates import imm64, uimm8, ieee32, ieee64, immvector, intcc, floatcc
|
from .immediates import imm64, uimm8, ieee32, ieee64, immvector, intcc, floatcc
|
||||||
from .entities import ebb, sig_ref, func_ref, jump_table
|
from .entities import ebb, sig_ref, func_ref, jump_table
|
||||||
|
|
||||||
|
|||||||
189
lib/cretonne/meta/cdsl/formats.py
Normal file
189
lib/cretonne/meta/cdsl/formats.py
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
"""Classes for describing instruction formats."""
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from .operands import OperandKind, VALUE, VARIABLE_ARGS
|
||||||
|
from .operands import Operand # noqa
|
||||||
|
|
||||||
|
# The typing module is only required by mypy, and we don't use these imports
|
||||||
|
# outside type comments.
|
||||||
|
try:
|
||||||
|
from typing import Tuple, Union, Any, Sequence, Iterable # noqa
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InstructionFormat(object):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
the kinds of operands used in its declaration.
|
||||||
|
|
||||||
|
Most instruction formats produce a single result, or no result at all. If
|
||||||
|
an instruction can produce more than one result, the `multiple_results`
|
||||||
|
flag must be set on its format. All results are of the `value` kind, and
|
||||||
|
the instruction format does not keep track of how many results are
|
||||||
|
produced. Some instructions, like `call`, may have a variable number of
|
||||||
|
results.
|
||||||
|
|
||||||
|
All instruction formats must be predefined in the
|
||||||
|
:py:mod:`cretonne.formats` module.
|
||||||
|
|
||||||
|
:param kinds: List of `OperandKind` objects describing the operands.
|
||||||
|
:param name: Instruction format name in CamelCase. This is used as a Rust
|
||||||
|
variant name in both the `InstructionData` and `InstructionFormat`
|
||||||
|
enums.
|
||||||
|
:param multiple_results: Set to `True` if this instruction format allows
|
||||||
|
more than one result to be produced.
|
||||||
|
:param boxed_storage: Set to `True` is this instruction format requires a
|
||||||
|
`data: Box<...>` pointer to additional storage in its `InstructionData`
|
||||||
|
variant.
|
||||||
|
:param typevar_operand: Index of the input operand that is used to infer
|
||||||
|
the controlling type variable. By default, this is the first `value`
|
||||||
|
operand.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Map (multiple_results, kind, kind, ...) -> InstructionFormat
|
||||||
|
_registry = dict() # type: Dict[Tuple[bool, Tuple[OperandKind, ...]], InstructionFormat] # noqa
|
||||||
|
|
||||||
|
# All existing formats.
|
||||||
|
all_formats = list() # type: List[InstructionFormat]
|
||||||
|
|
||||||
|
def __init__(self, *kinds, **kwargs):
|
||||||
|
# type: (*Union[OperandKind, Tuple[str, OperandKind]], **Any) -> None # noqa
|
||||||
|
self.name = kwargs.get('name', None) # type: str
|
||||||
|
self.multiple_results = kwargs.get('multiple_results', False)
|
||||||
|
self.boxed_storage = kwargs.get('boxed_storage', False)
|
||||||
|
self.members = list() # type: List[str]
|
||||||
|
self.kinds = tuple(self._process_member_names(kinds))
|
||||||
|
|
||||||
|
# Which of self.kinds are `value`?
|
||||||
|
self.value_operands = tuple(
|
||||||
|
i for i, k in enumerate(self.kinds) if k is VALUE)
|
||||||
|
|
||||||
|
# The typevar_operand argument must point to a 'value' operand.
|
||||||
|
self.typevar_operand = kwargs.get('typevar_operand', None) # type: int
|
||||||
|
if self.typevar_operand is not None:
|
||||||
|
assert self.kinds[self.typevar_operand] is VALUE, \
|
||||||
|
"typevar_operand must indicate a 'value' operand"
|
||||||
|
elif len(self.value_operands) > 0:
|
||||||
|
# Default to the first 'value' operand, if there is one.
|
||||||
|
self.typevar_operand = self.value_operands[0]
|
||||||
|
|
||||||
|
# Compute a signature for the global registry.
|
||||||
|
sig = (self.multiple_results, self.kinds)
|
||||||
|
if sig in InstructionFormat._registry:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Format '{}' has the same signature as existing format '{}'"
|
||||||
|
.format(self.name, InstructionFormat._registry[sig]))
|
||||||
|
InstructionFormat._registry[sig] = self
|
||||||
|
InstructionFormat.all_formats.append(self)
|
||||||
|
|
||||||
|
def _process_member_names(self, kinds):
|
||||||
|
# type: (Sequence[Union[OperandKind, Tuple[str, OperandKind]]]) -> Iterable[OperandKind] # noqa
|
||||||
|
"""
|
||||||
|
Extract names of all the immediate operands in the kinds tuple.
|
||||||
|
|
||||||
|
Each entry is either an `OperandKind` instance, or a `(member, kind)`
|
||||||
|
pair. The member names correspond to members in the Rust
|
||||||
|
`InstructionData` data structure.
|
||||||
|
|
||||||
|
Yields the operand kinds.
|
||||||
|
"""
|
||||||
|
for arg in kinds:
|
||||||
|
if isinstance(arg, OperandKind):
|
||||||
|
member = arg.default_member
|
||||||
|
k = arg
|
||||||
|
else:
|
||||||
|
member, k = arg
|
||||||
|
self.members.append(member)
|
||||||
|
yield k
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# type: () -> str
|
||||||
|
args = ', '.join('{}: {}'.format(m, k) if m else str(k)
|
||||||
|
for m, k in zip(self.members, self.kinds))
|
||||||
|
return '{}({})'.format(self.name, args)
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
# type: (str) -> FormatField
|
||||||
|
"""
|
||||||
|
Make instruction format members available as attributes.
|
||||||
|
|
||||||
|
Each non-value format member becomes a corresponding `FormatField`
|
||||||
|
attribute.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
i = self.members.index(attr)
|
||||||
|
except ValueError:
|
||||||
|
raise AttributeError(
|
||||||
|
'{} is neither a {} member or a '
|
||||||
|
.format(attr, self.name) +
|
||||||
|
'normal InstructionFormat attribute')
|
||||||
|
field = FormatField(self, i, attr)
|
||||||
|
setattr(self, attr, field)
|
||||||
|
return field
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def lookup(ins, outs):
|
||||||
|
# type: (Sequence[Operand], Sequence[Operand]) -> InstructionFormat
|
||||||
|
"""
|
||||||
|
Find an existing instruction format that matches the given lists of
|
||||||
|
instruction inputs and outputs.
|
||||||
|
|
||||||
|
The `ins` and `outs` arguments correspond to the
|
||||||
|
:py:class:`Instruction` arguments of the same name, except they must be
|
||||||
|
tuples of :py:`Operand` objects.
|
||||||
|
"""
|
||||||
|
if len(outs) == 1:
|
||||||
|
multiple_results = outs[0].kind == VARIABLE_ARGS
|
||||||
|
else:
|
||||||
|
multiple_results = len(outs) > 1
|
||||||
|
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])),
|
||||||
|
"[multiple results]" if multiple_results else ""))
|
||||||
|
return InstructionFormat._registry[sig]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extract_names(globs):
|
||||||
|
"""
|
||||||
|
Given a dict mapping name -> object as returned by `globals()`, find
|
||||||
|
all the InstructionFormat objects and set their name from the dict key.
|
||||||
|
This is used to name a bunch of global variables in a module.
|
||||||
|
"""
|
||||||
|
for name, obj in globs.items():
|
||||||
|
if isinstance(obj, InstructionFormat):
|
||||||
|
assert obj.name is None
|
||||||
|
obj.name = name
|
||||||
|
|
||||||
|
|
||||||
|
class FormatField(object):
|
||||||
|
"""
|
||||||
|
A field in an instruction format.
|
||||||
|
|
||||||
|
This corresponds to a single member of a variant of the `InstructionData`
|
||||||
|
data type.
|
||||||
|
|
||||||
|
:param format: Parent `InstructionFormat`.
|
||||||
|
:param operand: Operand number in parent.
|
||||||
|
:param name: Member name in `InstructionData` variant.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, format, operand, name):
|
||||||
|
# type: (InstructionFormat, int, str) -> None
|
||||||
|
self.format = format
|
||||||
|
self.operand = operand
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# type: () -> str
|
||||||
|
return '{}.{}'.format(self.format.name, self.name)
|
||||||
|
|
||||||
|
def rust_name(self):
|
||||||
|
# type: () -> str
|
||||||
|
if self.format.boxed_storage:
|
||||||
|
return 'data.' + self.name
|
||||||
|
else:
|
||||||
|
return self.name
|
||||||
@@ -10,7 +10,8 @@ from cdsl import camel_case
|
|||||||
from cdsl.predicates import And
|
from cdsl.predicates import And
|
||||||
from cdsl.types import ValueType
|
from cdsl.types import ValueType
|
||||||
from cdsl.typevar import TypeVar
|
from cdsl.typevar import TypeVar
|
||||||
from cdsl.operands import VALUE, VARIABLE_ARGS, OperandKind, Operand
|
from cdsl.operands import Operand
|
||||||
|
from cdsl.formats import InstructionFormat
|
||||||
|
|
||||||
# The typing module is only required by mypy, and we don't use these imports
|
# The typing module is only required by mypy, and we don't use these imports
|
||||||
# outside type comments.
|
# outside type comments.
|
||||||
@@ -19,7 +20,6 @@ try:
|
|||||||
from cdsl.predicates import Predicate, FieldPredicate # noqa
|
from cdsl.predicates import Predicate, FieldPredicate # noqa
|
||||||
MaybeBoundInst = Union['Instruction', 'BoundInstruction']
|
MaybeBoundInst = Union['Instruction', 'BoundInstruction']
|
||||||
AnyPredicate = Union['Predicate', 'FieldPredicate']
|
AnyPredicate = Union['Predicate', 'FieldPredicate']
|
||||||
OperandSpec = Union['OperandKind', ValueType, TypeVar]
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
TYPE_CHECKING = False
|
TYPE_CHECKING = False
|
||||||
|
|
||||||
@@ -80,182 +80,6 @@ class InstructionGroup(object):
|
|||||||
InstructionGroup._current.instructions.append(inst)
|
InstructionGroup._current.instructions.append(inst)
|
||||||
|
|
||||||
|
|
||||||
class InstructionFormat(object):
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
the kinds of operands used in its declaration.
|
|
||||||
|
|
||||||
Most instruction formats produce a single result, or no result at all. If
|
|
||||||
an instruction can produce more than one result, the `multiple_results`
|
|
||||||
flag must be set on its format. All results are of the `value` kind, and
|
|
||||||
the instruction format does not keep track of how many results are
|
|
||||||
produced. Some instructions, like `call`, may have a variable number of
|
|
||||||
results.
|
|
||||||
|
|
||||||
All instruction formats must be predefined in the
|
|
||||||
:py:mod:`cretonne.formats` module.
|
|
||||||
|
|
||||||
:param kinds: List of `OperandKind` objects describing the operands.
|
|
||||||
:param name: Instruction format name in CamelCase. This is used as a Rust
|
|
||||||
variant name in both the `InstructionData` and `InstructionFormat`
|
|
||||||
enums.
|
|
||||||
:param multiple_results: Set to `True` if this instruction format allows
|
|
||||||
more than one result to be produced.
|
|
||||||
:param boxed_storage: Set to `True` is this instruction format requires a
|
|
||||||
`data: Box<...>` pointer to additional storage in its `InstructionData`
|
|
||||||
variant.
|
|
||||||
:param typevar_operand: Index of the input operand that is used to infer
|
|
||||||
the controlling type variable. By default, this is the first `value`
|
|
||||||
operand.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Map (multiple_results, kind, kind, ...) -> InstructionFormat
|
|
||||||
_registry = dict() # type: Dict[Tuple[bool, Tuple[OperandKind, ...]], InstructionFormat] # noqa
|
|
||||||
|
|
||||||
# All existing formats.
|
|
||||||
all_formats = list() # type: List[InstructionFormat]
|
|
||||||
|
|
||||||
def __init__(self, *kinds, **kwargs):
|
|
||||||
# type: (*Union[OperandKind, Tuple[str, OperandKind]], **Any) -> None # noqa
|
|
||||||
self.name = kwargs.get('name', None) # type: str
|
|
||||||
self.multiple_results = kwargs.get('multiple_results', False)
|
|
||||||
self.boxed_storage = kwargs.get('boxed_storage', False)
|
|
||||||
self.members = list() # type: List[str]
|
|
||||||
self.kinds = tuple(self._process_member_names(kinds))
|
|
||||||
|
|
||||||
# Which of self.kinds are `value`?
|
|
||||||
self.value_operands = tuple(
|
|
||||||
i for i, k in enumerate(self.kinds) if k is VALUE)
|
|
||||||
|
|
||||||
# The typevar_operand argument must point to a 'value' operand.
|
|
||||||
self.typevar_operand = kwargs.get('typevar_operand', None) # type: int
|
|
||||||
if self.typevar_operand is not None:
|
|
||||||
assert self.kinds[self.typevar_operand] is VALUE, \
|
|
||||||
"typevar_operand must indicate a 'value' operand"
|
|
||||||
elif len(self.value_operands) > 0:
|
|
||||||
# Default to the first 'value' operand, if there is one.
|
|
||||||
self.typevar_operand = self.value_operands[0]
|
|
||||||
|
|
||||||
# Compute a signature for the global registry.
|
|
||||||
sig = (self.multiple_results, self.kinds)
|
|
||||||
if sig in InstructionFormat._registry:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Format '{}' has the same signature as existing format '{}'"
|
|
||||||
.format(self.name, InstructionFormat._registry[sig]))
|
|
||||||
InstructionFormat._registry[sig] = self
|
|
||||||
InstructionFormat.all_formats.append(self)
|
|
||||||
|
|
||||||
def _process_member_names(self, kinds):
|
|
||||||
# type: (Sequence[Union[OperandKind, Tuple[str, OperandKind]]]) -> Iterable[OperandKind] # noqa
|
|
||||||
"""
|
|
||||||
Extract names of all the immediate operands in the kinds tuple.
|
|
||||||
|
|
||||||
Each entry is either an `OperandKind` instance, or a `(member, kind)`
|
|
||||||
pair. The member names correspond to members in the Rust
|
|
||||||
`InstructionData` data structure.
|
|
||||||
|
|
||||||
Yields the operand kinds.
|
|
||||||
"""
|
|
||||||
for arg in kinds:
|
|
||||||
if isinstance(arg, OperandKind):
|
|
||||||
member = arg.default_member
|
|
||||||
k = arg
|
|
||||||
else:
|
|
||||||
member, k = arg
|
|
||||||
self.members.append(member)
|
|
||||||
yield k
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
args = ', '.join('{}: {}'.format(m, k) if m else str(k)
|
|
||||||
for m, k in zip(self.members, self.kinds))
|
|
||||||
return '{}({})'.format(self.name, args)
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
# type: (str) -> FormatField
|
|
||||||
"""
|
|
||||||
Make instruction format members available as attributes.
|
|
||||||
|
|
||||||
Each non-value format member becomes a corresponding `FormatField`
|
|
||||||
attribute.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
i = self.members.index(attr)
|
|
||||||
except ValueError:
|
|
||||||
raise AttributeError(
|
|
||||||
'{} is neither a {} member or a '
|
|
||||||
.format(attr, self.name) +
|
|
||||||
'normal InstructionFormat attribute')
|
|
||||||
field = FormatField(self, i, attr)
|
|
||||||
setattr(self, attr, field)
|
|
||||||
return field
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def lookup(ins, outs):
|
|
||||||
# type: (Sequence[Operand], Sequence[Operand]) -> InstructionFormat
|
|
||||||
"""
|
|
||||||
Find an existing instruction format that matches the given lists of
|
|
||||||
instruction inputs and outputs.
|
|
||||||
|
|
||||||
The `ins` and `outs` arguments correspond to the
|
|
||||||
:py:class:`Instruction` arguments of the same name, except they must be
|
|
||||||
tuples of :py:`Operand` objects.
|
|
||||||
"""
|
|
||||||
if len(outs) == 1:
|
|
||||||
multiple_results = outs[0].kind == VARIABLE_ARGS
|
|
||||||
else:
|
|
||||||
multiple_results = len(outs) > 1
|
|
||||||
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])),
|
|
||||||
"[multiple results]" if multiple_results else ""))
|
|
||||||
return InstructionFormat._registry[sig]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def extract_names(globs):
|
|
||||||
"""
|
|
||||||
Given a dict mapping name -> object as returned by `globals()`, find
|
|
||||||
all the InstructionFormat objects and set their name from the dict key.
|
|
||||||
This is used to name a bunch of global variables in a module.
|
|
||||||
"""
|
|
||||||
for name, obj in globs.items():
|
|
||||||
if isinstance(obj, InstructionFormat):
|
|
||||||
assert obj.name is None
|
|
||||||
obj.name = name
|
|
||||||
|
|
||||||
|
|
||||||
class FormatField(object):
|
|
||||||
"""
|
|
||||||
A field in an instruction format.
|
|
||||||
|
|
||||||
This corresponds to a single member of a variant of the `InstructionData`
|
|
||||||
data type.
|
|
||||||
|
|
||||||
:param format: Parent `InstructionFormat`.
|
|
||||||
:param operand: Operand number in parent.
|
|
||||||
:param name: Member name in `InstructionData` variant.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, format, operand, name):
|
|
||||||
# type: (InstructionFormat, int, str) -> None
|
|
||||||
self.format = format
|
|
||||||
self.operand = operand
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{}.{}'.format(self.format.name, self.name)
|
|
||||||
|
|
||||||
def rust_name(self):
|
|
||||||
# type: () -> str
|
|
||||||
if self.format.boxed_storage:
|
|
||||||
return 'data.' + self.name
|
|
||||||
else:
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class Instruction(object):
|
class Instruction(object):
|
||||||
"""
|
"""
|
||||||
The operands to the instruction are specified as two tuples: ``ins`` and
|
The operands to the instruction are specified as two tuples: ``ins`` and
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from base.types import i8, f32, f64, b1
|
|||||||
from base.immediates import imm64, uimm8, ieee32, ieee64, immvector
|
from base.immediates import imm64, uimm8, ieee32, ieee64, immvector
|
||||||
from base.immediates import intcc, floatcc
|
from base.immediates import intcc, floatcc
|
||||||
from base import entities
|
from base import entities
|
||||||
|
import base.formats # noqa
|
||||||
|
|
||||||
instructions = InstructionGroup("base", "Shared base instruction set")
|
instructions = InstructionGroup("base", "Shared base instruction set")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user