Split out operand descriptions.

- cdsl.operands has the Operand and OperandKind classes.
This commit is contained in:
Jakob Stoklund Olesen
2016-11-08 10:58:23 -08:00
parent 2fe61e83f6
commit bb28dc6686
10 changed files with 167 additions and 156 deletions

View File

@@ -127,13 +127,14 @@ There are some practical restrictions on the use of type variables, see
Immediate operands
------------------
.. currentmodule:: cdsl.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:`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
@@ -145,8 +146,7 @@ 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
.. currentmodule:: cdsl.operands
.. autoclass:: EntityRefKind
.. automodule:: cretonne.entities
@@ -183,7 +183,7 @@ the :func:`ScalarType.by` function.
Instruction representation
==========================
.. currentmodule:: cretonne
.. module:: cdsl.operands
The Rust in-memory representation of instructions is derived from the
instruction descriptions. Part of the representation is generated, and part is
@@ -198,8 +198,10 @@ 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:: variable_args
.. autodata:: VALUE
.. autodata:: VARIABLE_ARGS
.. currentmodule:: cretonne
When an instruction description is created, it is automatically assigned a
predefined instruction format which is an instance of

View File

@@ -4,3 +4,14 @@ Cretonne DSL classes.
This module defines the classes that are used to define Cretonne instructions
and other entitties.
"""
from __future__ import absolute_import
import re
camel_re = re.compile('(^|_)([a-z])')
def camel_case(s):
# type: (str) -> str
"""Convert the string s to CamelCase"""
return camel_re.sub(lambda m: m.group(2).upper(), s)

View File

@@ -0,0 +1,104 @@
"""Classes for describing instruction operands."""
from __future__ import absolute_import
from . import camel_case
# Kinds of operands.
#
# Each instruction has an opcode and a number of operands. The opcode
# determines the instruction format, and the format determines the number of
# operands and the kind of each operand.
class OperandKind(object):
"""
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.
"""
def __init__(self, name, doc, default_member=None, rust_type=None):
# type: (str, str, str, str) -> None
self.name = name
self.__doc__ = doc
self.default_member = default_member
# The camel-cased name of an operand kind is also the Rust type used to
# represent it.
self.rust_type = rust_type or camel_case(name)
def __str__(self):
# type: () -> str
return self.name
def __repr__(self):
# type: () -> str
return 'OperandKind({})'.format(self.name)
def operand_kind(self):
# type: () -> OperandKind
"""
An `OperandKind` instance can be used directly as the type of an
`Operand` when defining an instruction.
"""
return self
def free_typevar(self):
# Return the free typevariable controlling the type of this operand.
return None
#: An SSA value operand. This is a value defined by another instruction.
VALUE = OperandKind(
'value', """
An SSA value defined by another instruction.
This kind of operand can represent any SSA value type, but the
instruction format may restrict the valid value types for a given
operand.
""")
#: A variable-sized list of value operands. Use for Ebb and function call
#: arguments.
VARIABLE_ARGS = OperandKind(
'variable_args', """
A variable size list of `value` operands.
Use this to represent arguemtns passed to a function call, arguments
passed to an extended basic block, or a variable number of results
returned from an instruction.
""",
default_member='varargs')
# Instances of immediate operand types are provided in the
# `cretonne.immediates` module.
class ImmediateKind(OperandKind):
"""
The kind of an immediate instruction operand.
:param default_member: The default member name of this kind the
`InstructionData` data structure.
"""
def __init__(self, name, doc, default_member='imm', rust_type=None):
# type: (str, str, str, str) -> None
super(ImmediateKind, self).__init__(
name, doc, default_member, rust_type)
def __repr__(self):
# type: () -> str
return 'ImmediateKind({})'.format(self.name)
# Instances of entity reference operand types are provided in the
# `cretonne.entities` module.
class EntityRefKind(OperandKind):
"""
The kind of an entity reference instruction operand.
"""
def __init__(self, name, doc, default_member=None, rust_type=None):
# type: (str, str, str, str) -> None
super(EntityRefKind, self).__init__(
name, doc, default_member or name, rust_type)
def __repr__(self):
# type: () -> str
return 'EntityRefKind({})'.format(self.name)

View File

@@ -5,10 +5,11 @@ This module provides classes and functions used to describe Cretonne
instructions.
"""
from __future__ import absolute_import
import re
import importlib
from cdsl import camel_case
from cdsl.predicates import And
import cdsl.types
from cdsl.operands import VALUE, VARIABLE_ARGS, OperandKind
# The typing module is only required by mypy, and we don't use these imports
# outside type comments.
@@ -25,116 +26,6 @@ if TYPE_CHECKING:
from .typevar import TypeVar # noqa
camel_re = re.compile('(^|_)([a-z])')
def camel_case(s):
# type: (str) -> str
"""Convert the string s to CamelCase"""
return camel_re.sub(lambda m: m.group(2).upper(), s)
# Kinds of operands.
#
# Each instruction has an opcode and a number of operands. The opcode
# determines the instruction format, and the format determines the number of
# operands and the kind of each operand.
class OperandKind(object):
"""
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.
"""
def __init__(self, name, doc, default_member=None, rust_type=None):
# type: (str, str, str, str) -> None
self.name = name
self.__doc__ = doc
self.default_member = default_member
# The camel-cased name of an operand kind is also the Rust type used to
# represent it.
self.rust_type = rust_type or camel_case(name)
def __str__(self):
# type: () -> str
return self.name
def __repr__(self):
# type: () -> str
return 'OperandKind({})'.format(self.name)
def operand_kind(self):
# type: () -> OperandKind
"""
An `OperandKind` instance can be used directly as the type of an
`Operand` when defining an instruction.
"""
return self
def free_typevar(self):
# Return the free typevariable controlling the type of this operand.
return None
#: An SSA value operand. This is a value defined by another instruction.
value = OperandKind(
'value', """
An SSA value defined by another instruction.
This kind of operand can represent any SSA value type, but the
instruction format may restrict the valid value types for a given
operand.
""")
#: A variable-sized list of value operands. Use for Ebb and function call
#: arguments.
variable_args = OperandKind(
'variable_args', """
A variable size list of `value` operands.
Use this to represent arguemtns passed to a function call, arguments
passed to an extended basic block, or a variable number of results
returned from an instruction.
""",
default_member='varargs')
# Instances of immediate operand types are provided in the
# `cretonne.immediates` module.
class ImmediateKind(OperandKind):
"""
The kind of an immediate instruction operand.
:param default_member: The default member name of this kind the
`InstructionData` data structure.
"""
def __init__(self, name, doc, default_member='imm', rust_type=None):
# type: (str, str, str, str) -> None
super(ImmediateKind, self).__init__(
name, doc, default_member, rust_type)
def __repr__(self):
# type: () -> str
return 'ImmediateKind({})'.format(self.name)
# Instances of entity reference operand types are provided in the
# `cretonne.entities` module.
class EntityRefKind(OperandKind):
"""
The kind of an entity reference instruction operand.
"""
def __init__(self, name, doc, default_member=None, rust_type=None):
# type: (str, str, str, str) -> None
super(EntityRefKind, self).__init__(
name, doc, default_member or name, rust_type)
def __repr__(self):
# type: () -> str
return 'EntityRefKind({})'.format(self.name)
# Defining instructions.
@@ -215,7 +106,7 @@ class Operand(object):
self.__doc__ = doc
self.typ = typ
if isinstance(typ, cdsl.types.ValueType):
self.kind = value
self.kind = VALUE
else:
self.kind = typ.operand_kind()
@@ -235,7 +126,7 @@ class Operand(object):
"""
Is this an SSA value operand?
"""
return self.kind is value
return self.kind is cdsl.operands.VALUE
class InstructionFormat(object):
@@ -285,12 +176,12 @@ class InstructionFormat(object):
# Which of self.kinds are `value`?
self.value_operands = tuple(
i for i, k in enumerate(self.kinds) if k is value)
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, \
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.
@@ -361,7 +252,7 @@ class InstructionFormat(object):
tuples of :py:`Operand` objects.
"""
if len(outs) == 1:
multiple_results = outs[0].kind == variable_args
multiple_results = outs[0].kind == VARIABLE_ARGS
else:
multiple_results = len(outs) > 1
sig = (multiple_results, tuple(op.kind for op in ins))

View File

@@ -5,7 +5,8 @@ This module defines the basic Cretonne instruction set that all targets
support.
"""
from __future__ import absolute_import
from . import Operand, Instruction, InstructionGroup, variable_args
from cdsl.operands import VARIABLE_ARGS
from . import Operand, Instruction, InstructionGroup
from .typevar import TypeVar
from base.types import i8, f32, f64, b1
from .immediates import imm64, uimm8, ieee32, ieee64, immvector, intcc, floatcc
@@ -31,7 +32,7 @@ Any = TypeVar(
#
c = Operand('c', Testable, doc='Controlling value to test')
EBB = Operand('EBB', entities.ebb, doc='Destination extended basic block')
args = Operand('args', variable_args, doc='EBB arguments')
args = Operand('args', VARIABLE_ARGS, doc='EBB arguments')
jump = Instruction(
'jump', r"""
@@ -98,7 +99,7 @@ trapnz = Instruction(
""",
ins=c)
rvals = Operand('rvals', variable_args, doc='return values')
rvals = Operand('rvals', VARIABLE_ARGS, doc='return values')
x_return = Instruction(
'return', r"""
@@ -114,7 +115,7 @@ FN = Operand(
'FN',
entities.func_ref,
doc='function to call, declared by :inst:`function`')
args = Operand('args', variable_args, doc='call arguments')
args = Operand('args', VARIABLE_ARGS, doc='call arguments')
call = Instruction(
'call', r"""

View File

@@ -4,7 +4,7 @@ operand types. There are corresponding definitions in the `cretonne.entities`
Rust module.
"""
from __future__ import absolute_import
from . import EntityRefKind
from cdsl.operands import EntityRefKind
#: A reference to an extended basic block in the same function.

View File

@@ -6,51 +6,52 @@ Rust representation of cretonne IL, so all instruction formats must be defined
in this module.
"""
from __future__ import absolute_import
from . import InstructionFormat, value, variable_args
from cdsl.operands import VALUE, VARIABLE_ARGS
from . import InstructionFormat
from .immediates import imm64, uimm8, ieee32, ieee64, immvector, intcc, floatcc
from .entities import ebb, sig_ref, func_ref, jump_table
Nullary = InstructionFormat()
Unary = InstructionFormat(value)
Unary = InstructionFormat(VALUE)
UnaryImm = InstructionFormat(imm64)
UnaryIeee32 = InstructionFormat(ieee32)
UnaryIeee64 = InstructionFormat(ieee64)
UnaryImmVector = InstructionFormat(immvector, boxed_storage=True)
UnarySplit = InstructionFormat(value, multiple_results=True)
UnarySplit = InstructionFormat(VALUE, multiple_results=True)
Binary = InstructionFormat(value, value)
BinaryImm = InstructionFormat(value, imm64)
BinaryImmRev = InstructionFormat(imm64, value)
Binary = InstructionFormat(VALUE, VALUE)
BinaryImm = InstructionFormat(VALUE, imm64)
BinaryImmRev = InstructionFormat(imm64, VALUE)
# Generate result + overflow flag.
BinaryOverflow = InstructionFormat(value, value, multiple_results=True)
BinaryOverflow = InstructionFormat(VALUE, VALUE, multiple_results=True)
# The select instructions are controlled by the second value operand.
# The first value operand is the controlling flag which has a derived type.
# The select instructions are controlled by the second VALUE operand.
# The first VALUE operand is the controlling flag which has a derived type.
# The fma instruction has the same constraint on all inputs.
Ternary = InstructionFormat(value, value, value, typevar_operand=1)
Ternary = InstructionFormat(VALUE, VALUE, VALUE, typevar_operand=1)
# Carry in *and* carry out for `iadd_carry` and friends.
TernaryOverflow = InstructionFormat(
value, value, value, multiple_results=True, boxed_storage=True)
VALUE, VALUE, VALUE, multiple_results=True, boxed_storage=True)
InsertLane = InstructionFormat(value, ('lane', uimm8), value)
ExtractLane = InstructionFormat(value, ('lane', uimm8))
InsertLane = InstructionFormat(VALUE, ('lane', uimm8), VALUE)
ExtractLane = InstructionFormat(VALUE, ('lane', uimm8))
IntCompare = InstructionFormat(intcc, value, value)
FloatCompare = InstructionFormat(floatcc, value, value)
IntCompare = InstructionFormat(intcc, VALUE, VALUE)
FloatCompare = InstructionFormat(floatcc, VALUE, VALUE)
Jump = InstructionFormat(ebb, variable_args, boxed_storage=True)
Branch = InstructionFormat(value, ebb, variable_args, boxed_storage=True)
BranchTable = InstructionFormat(value, jump_table)
Jump = InstructionFormat(ebb, VARIABLE_ARGS, boxed_storage=True)
Branch = InstructionFormat(VALUE, ebb, VARIABLE_ARGS, boxed_storage=True)
BranchTable = InstructionFormat(VALUE, jump_table)
Call = InstructionFormat(
func_ref, variable_args, multiple_results=True, boxed_storage=True)
func_ref, VARIABLE_ARGS, multiple_results=True, boxed_storage=True)
IndirectCall = InstructionFormat(
sig_ref, value, variable_args,
sig_ref, VALUE, VARIABLE_ARGS,
multiple_results=True, boxed_storage=True)
Return = InstructionFormat(variable_args, boxed_storage=True)
Return = InstructionFormat(VARIABLE_ARGS, boxed_storage=True)
# Finally extract the names of global variables in this module.

View File

@@ -3,7 +3,7 @@ The `cretonne.immediates` module predefines all the Cretonne immediate operand
types.
"""
from __future__ import absolute_import
from . import ImmediateKind
from cdsl.operands import ImmediateKind
#: A 64-bit immediate integer operand.
#:

View File

@@ -6,7 +6,7 @@ polymorphic by using type variables.
"""
from __future__ import absolute_import
import math
import cretonne
import cdsl.operands
try:
from typing import Tuple, Union # noqa
@@ -315,11 +315,11 @@ class TypeVar(object):
return TypeVar(None, None, base=self, derived_func='DoubleWidth')
def operand_kind(self):
# type: () -> cretonne.OperandKind
# type: () -> cdsl.operands.OperandKind
# 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 cretonne.value # type: ignore
return cdsl.operands.VALUE
def free_typevar(self):
# type: () -> TypeVar

View File

@@ -6,6 +6,7 @@ import srcgen
import constant_hash
from unique_table import UniqueTable, UniqueSeqTable
import cdsl.types
import cdsl.operands
import cretonne
@@ -56,7 +57,7 @@ def gen_arguments_method(fmt, is_mut):
with fmt.indented('match *self {', '}'):
for f in cretonne.InstructionFormat.all_formats:
n = 'InstructionData::' + f.name
has_varargs = cretonne.variable_args in f.kinds
has_varargs = cdsl.operands.VARIABLE_ARGS in f.kinds
# Formats with both fixed and variable arguments delegate to
# the data struct. We need to work around borrow checker quirks
# when extracting two mutable references.
@@ -84,7 +85,7 @@ def gen_arguments_method(fmt, is_mut):
capture = 'ref {}args, '.format(mut)
arg = 'args'
# Varargs.
if cretonne.variable_args in f.kinds:
if cdsl.operands.VARIABLE_ARGS in f.kinds:
varg = '&{}data.varargs'.format(mut)
capture = 'ref {}data, '.format(mut)
else:
@@ -311,7 +312,7 @@ 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
assert op.kind is cdsl.operands.VALUE
t = op.typ
# A concrete value type.
@@ -504,7 +505,7 @@ def gen_inst_builder(inst, fmt):
tmpl_types = list()
into_args = list()
for op in inst.ins:
if isinstance(op.kind, cretonne.ImmediateKind):
if isinstance(op.kind, cdsl.operands.ImmediateKind):
t = 'T{}{}'.format(1 + len(tmpl_types), op.kind.name)
tmpl_types.append('{}: Into<{}>'.format(t, op.kind.rust_type))
into_args.append(op.name)