Documentation nits; Sematnics syntax cleanup
This commit is contained in:
committed by
Jakob Stoklund Olesen
parent
a5fe64440f
commit
74f72a3b43
@@ -2,11 +2,10 @@ from __future__ import absolute_import
|
|||||||
from semantics.primitives import prim_to_bv, prim_from_bv, bvsplit, bvconcat,\
|
from semantics.primitives import prim_to_bv, prim_from_bv, bvsplit, bvconcat,\
|
||||||
bvadd
|
bvadd
|
||||||
from .instructions import vsplit, vconcat, iadd
|
from .instructions import vsplit, vconcat, iadd
|
||||||
from cdsl.xform import XForm, Rtl
|
from cdsl.xform import Rtl
|
||||||
from cdsl.ast import Var
|
from cdsl.ast import Var
|
||||||
from cdsl.typevar import TypeSet
|
from cdsl.typevar import TypeSet
|
||||||
from cdsl.ti import InTypeset
|
from cdsl.ti import InTypeset
|
||||||
import semantics.types # noqa
|
|
||||||
|
|
||||||
x = Var('x')
|
x = Var('x')
|
||||||
y = Var('y')
|
y = Var('y')
|
||||||
@@ -28,30 +27,32 @@ bvhi = Var('bvhi')
|
|||||||
ScalarTS = TypeSet(lanes=(1, 1), ints=True, floats=True, bools=True)
|
ScalarTS = TypeSet(lanes=(1, 1), ints=True, floats=True, bools=True)
|
||||||
|
|
||||||
vsplit.set_semantics(
|
vsplit.set_semantics(
|
||||||
XForm(Rtl((lo, hi) << vsplit(x)),
|
(lo, hi) << vsplit(x),
|
||||||
Rtl(bvx << prim_to_bv(x),
|
Rtl(
|
||||||
(bvlo, bvhi) << bvsplit(bvx),
|
bvx << prim_to_bv(x),
|
||||||
lo << prim_from_bv(bvlo),
|
(bvlo, bvhi) << bvsplit(bvx),
|
||||||
hi << prim_from_bv(bvhi))))
|
lo << prim_from_bv(bvlo),
|
||||||
|
hi << prim_from_bv(bvhi)
|
||||||
|
))
|
||||||
|
|
||||||
vconcat.set_semantics(
|
vconcat.set_semantics(
|
||||||
XForm(Rtl(x << vconcat(lo, hi)),
|
x << vconcat(lo, hi),
|
||||||
Rtl(bvlo << prim_to_bv(lo),
|
Rtl(
|
||||||
bvhi << prim_to_bv(hi),
|
bvlo << prim_to_bv(lo),
|
||||||
bvx << bvconcat(bvlo, bvhi),
|
bvhi << prim_to_bv(hi),
|
||||||
x << prim_from_bv(bvx))))
|
bvx << bvconcat(bvlo, bvhi),
|
||||||
|
x << prim_from_bv(bvx)
|
||||||
|
))
|
||||||
|
|
||||||
iadd.set_semantics([
|
iadd.set_semantics(
|
||||||
XForm(Rtl(a << iadd(x, y)),
|
a << iadd(x, y),
|
||||||
Rtl(bvx << prim_to_bv(x),
|
(Rtl(bvx << prim_to_bv(x),
|
||||||
bvy << prim_to_bv(y),
|
bvy << prim_to_bv(y),
|
||||||
bva << bvadd(bvx, bvy),
|
bva << bvadd(bvx, bvy),
|
||||||
a << prim_from_bv(bva)),
|
a << prim_from_bv(bva)),
|
||||||
constraints=[InTypeset(x.get_typevar(), ScalarTS)]),
|
[InTypeset(x.get_typevar(), ScalarTS)]),
|
||||||
XForm(Rtl(a << iadd(x, y)),
|
Rtl((xlo, xhi) << vsplit(x),
|
||||||
Rtl((xlo, xhi) << vsplit(x),
|
(ylo, yhi) << vsplit(y),
|
||||||
(ylo, yhi) << vsplit(y),
|
alo << iadd(xlo, ylo),
|
||||||
alo << iadd(xlo, ylo),
|
ahi << iadd(xhi, yhi),
|
||||||
ahi << iadd(xhi, yhi),
|
a << vconcat(alo, ahi)))
|
||||||
a << vconcat(alo, ahi)))
|
|
||||||
])
|
|
||||||
|
|||||||
@@ -102,15 +102,17 @@ class Def(object):
|
|||||||
|
|
||||||
def vars(self):
|
def vars(self):
|
||||||
# type: () -> Set[Var]
|
# type: () -> Set[Var]
|
||||||
""" Return the set of all Vars that appear in self"""
|
"""Return the set of all Vars in self that correspond to SSA values"""
|
||||||
return self.definitions().union(self.uses())
|
return self.definitions().union(self.uses())
|
||||||
|
|
||||||
def substitution(self, other, s):
|
def substitution(self, other, s):
|
||||||
# type: (Def, VarMap) -> Optional[VarMap]
|
# type: (Def, VarMap) -> Optional[VarMap]
|
||||||
"""
|
"""
|
||||||
If the Defs self and other agree structurally, return a variable
|
If the Defs self and other agree structurally, return a variable
|
||||||
substitution to transform self ot other. Two Defs agree structurally
|
substitution to transform self to other. Otherwise return None. Two
|
||||||
if the contained Apply's agree structurally.
|
Defs agree structurally if there exists a Var substitution, that can
|
||||||
|
transform one into the other. See Apply.substitution() for more
|
||||||
|
details.
|
||||||
"""
|
"""
|
||||||
s = self.expr.substitution(other.expr, s)
|
s = self.expr.substitution(other.expr, s)
|
||||||
|
|
||||||
@@ -378,7 +380,7 @@ class Apply(Expr):
|
|||||||
|
|
||||||
def vars(self):
|
def vars(self):
|
||||||
# type: () -> Set[Var]
|
# type: () -> Set[Var]
|
||||||
""" Return the set of all Vars that appear in self"""
|
"""Return the set of all Vars in self that correspond to SSA values"""
|
||||||
res = set()
|
res = set()
|
||||||
for i in self.inst.value_opnums:
|
for i in self.inst.value_opnums:
|
||||||
arg = self.args[i]
|
arg = self.args[i]
|
||||||
@@ -390,8 +392,8 @@ class Apply(Expr):
|
|||||||
# type: (Apply, VarMap) -> Optional[VarMap]
|
# type: (Apply, VarMap) -> Optional[VarMap]
|
||||||
"""
|
"""
|
||||||
If the application self and other agree structurally, return a variable
|
If the application self and other agree structurally, return a variable
|
||||||
substitution to transform self ot other. Two applications agree
|
substitution to transform self to other. Otherwise return None. Two
|
||||||
structurally if:
|
applications agree structurally if:
|
||||||
1) They are over the same instruction
|
1) They are over the same instruction
|
||||||
2) Every Var v in self, maps to a single Var w in other. I.e for
|
2) Every Var v in self, maps to a single Var w in other. I.e for
|
||||||
each use of v in self, w is used in the corresponding place in
|
each use of v in self, w is used in the corresponding place in
|
||||||
|
|||||||
@@ -9,15 +9,16 @@ try:
|
|||||||
from typing import Union, Sequence, List, Tuple, Any, TYPE_CHECKING # noqa
|
from typing import Union, Sequence, List, Tuple, Any, TYPE_CHECKING # noqa
|
||||||
from typing import Dict # noqa
|
from typing import Dict # noqa
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .ast import Expr, Apply, Var # noqa
|
from .ast import Expr, Apply, Var, Def # noqa
|
||||||
from .typevar import TypeVar # noqa
|
from .typevar import TypeVar # noqa
|
||||||
from .ti import TypeConstraint # noqa
|
from .ti import TypeConstraint # noqa
|
||||||
from .xform import XForm
|
from .xform import XForm, Rtl
|
||||||
# List of operands for ins/outs:
|
# List of operands for ins/outs:
|
||||||
OpList = Union[Sequence[Operand], Operand]
|
OpList = Union[Sequence[Operand], Operand]
|
||||||
ConstrList = Union[Sequence[TypeConstraint], TypeConstraint]
|
ConstrList = Union[Sequence[TypeConstraint], TypeConstraint]
|
||||||
MaybeBoundInst = Union['Instruction', 'BoundInstruction']
|
MaybeBoundInst = Union['Instruction', 'BoundInstruction']
|
||||||
InstructionSemantics = List[XForm]
|
InstructionSemantics = Sequence[XForm]
|
||||||
|
RtlCase = Union[Rtl, Tuple[Rtl, Sequence[TypeConstraint]]]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -336,15 +337,23 @@ class Instruction(object):
|
|||||||
from .ast import Apply # noqa
|
from .ast import Apply # noqa
|
||||||
return Apply(self, args)
|
return Apply(self, args)
|
||||||
|
|
||||||
def set_semantics(self, sem):
|
def set_semantics(self, src, *dsts):
|
||||||
# type: (Union[XForm, InstructionSemantics]) -> None
|
# type: (Union[Def, Apply], *RtlCase) -> None
|
||||||
"""Set our semantics."""
|
"""Set our semantics."""
|
||||||
from semantics import verify_semantics
|
from semantics import verify_semantics
|
||||||
|
from .xform import XForm, Rtl
|
||||||
|
|
||||||
if not isinstance(sem, list):
|
sem = [] # type: List[XForm]
|
||||||
sem = [sem]
|
for dst in dsts:
|
||||||
|
if isinstance(dst, Rtl):
|
||||||
|
sem.append(XForm(Rtl(src).copy({}), dst))
|
||||||
|
else:
|
||||||
|
assert isinstance(dst, tuple)
|
||||||
|
sem.append(XForm(Rtl(src).copy({}), dst[0],
|
||||||
|
constraints=dst[1]))
|
||||||
|
|
||||||
|
verify_semantics(self, Rtl(src), sem)
|
||||||
|
|
||||||
verify_semantics(self, sem)
|
|
||||||
self.semantics = sem
|
self.semantics = sem
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -89,10 +89,7 @@ class ScalarType(ValueType):
|
|||||||
self._vectors = dict() # type: Dict[int, VectorType]
|
self._vectors = dict() # type: Dict[int, VectorType]
|
||||||
# Assign numbers starting from 1. (0 is VOID).
|
# Assign numbers starting from 1. (0 is VOID).
|
||||||
ValueType.all_scalars.append(self)
|
ValueType.all_scalars.append(self)
|
||||||
# Numbers are only valid for Cretone types that get emitted to Rust.
|
self.number = len(ValueType.all_scalars)
|
||||||
# This excludes BVTypes
|
|
||||||
self.number = len([x for x in ValueType.all_scalars
|
|
||||||
if not isinstance(x, BVType)])
|
|
||||||
assert self.number < 16, 'Too many scalar types'
|
assert self.number < 16, 'Too many scalar types'
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -249,7 +246,7 @@ class BoolType(ScalarType):
|
|||||||
return self.bits
|
return self.bits
|
||||||
|
|
||||||
|
|
||||||
class BVType(ScalarType):
|
class BVType(ValueType):
|
||||||
"""A flat bitvector type. Used for semantics description only."""
|
"""A flat bitvector type. Used for semantics description only."""
|
||||||
|
|
||||||
def __init__(self, bits):
|
def __init__(self, bits):
|
||||||
@@ -268,7 +265,11 @@ class BVType(ScalarType):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def with_bits(bits):
|
def with_bits(bits):
|
||||||
# type: (int) -> BVType
|
# type: (int) -> BVType
|
||||||
typ = ValueType.by_name('bv{:d}'.format(bits))
|
name = 'bv{:d}'.format(bits)
|
||||||
|
if name not in ValueType._registry:
|
||||||
|
return BVType(bits)
|
||||||
|
|
||||||
|
typ = ValueType.by_name(name)
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
return cast(BVType, typ)
|
return cast(BVType, typ)
|
||||||
else:
|
else:
|
||||||
@@ -278,3 +279,8 @@ class BVType(ScalarType):
|
|||||||
# type: () -> int
|
# type: () -> int
|
||||||
"""Return the number of bits in a lane."""
|
"""Return the number of bits in a lane."""
|
||||||
return self.bits
|
return self.bits
|
||||||
|
|
||||||
|
def lane_count(self):
|
||||||
|
# type: () -> int
|
||||||
|
"""Return the number of lane. For BVtypes always 1."""
|
||||||
|
return 1
|
||||||
|
|||||||
@@ -501,7 +501,8 @@ class TypeSet(object):
|
|||||||
for bits in self.bools:
|
for bits in self.bools:
|
||||||
yield by(types.BoolType.with_bits(bits), nlanes)
|
yield by(types.BoolType.with_bits(bits), nlanes)
|
||||||
for bits in self.bitvecs:
|
for bits in self.bitvecs:
|
||||||
yield by(types.BVType.with_bits(bits), nlanes)
|
assert nlanes == 1
|
||||||
|
yield types.BVType.with_bits(bits)
|
||||||
|
|
||||||
def get_singleton(self):
|
def get_singleton(self):
|
||||||
# type: () -> types.ValueType
|
# type: () -> types.ValueType
|
||||||
@@ -572,12 +573,17 @@ class TypeVar(object):
|
|||||||
def singleton(typ):
|
def singleton(typ):
|
||||||
# type: (types.ValueType) -> TypeVar
|
# type: (types.ValueType) -> TypeVar
|
||||||
"""Create a type variable that can only assume a single type."""
|
"""Create a type variable that can only assume a single type."""
|
||||||
|
scalar = None # type: ValueType
|
||||||
if isinstance(typ, types.VectorType):
|
if isinstance(typ, types.VectorType):
|
||||||
scalar = typ.base
|
scalar = typ.base
|
||||||
lanes = (typ.lanes, typ.lanes)
|
lanes = (typ.lanes, typ.lanes)
|
||||||
elif isinstance(typ, types.ScalarType):
|
elif isinstance(typ, types.ScalarType):
|
||||||
scalar = typ
|
scalar = typ
|
||||||
lanes = (1, 1)
|
lanes = (1, 1)
|
||||||
|
else:
|
||||||
|
assert isinstance(typ, types.BVType)
|
||||||
|
scalar = typ
|
||||||
|
lanes = (1, 1)
|
||||||
|
|
||||||
ints = None
|
ints = None
|
||||||
floats = None
|
floats = None
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class Rtl(object):
|
|||||||
|
|
||||||
def vars(self):
|
def vars(self):
|
||||||
# type: () -> Set[Var]
|
# type: () -> Set[Var]
|
||||||
""" Return the set of all Vars that appear in self"""
|
"""Return the set of all Vars in self that correspond to SSA values"""
|
||||||
return reduce(lambda x, y: x.union(y),
|
return reduce(lambda x, y: x.union(y),
|
||||||
[d.vars() for d in self.rtl],
|
[d.vars() for d in self.rtl],
|
||||||
set([]))
|
set([]))
|
||||||
|
|||||||
@@ -4,52 +4,45 @@ from cdsl.ti import TypeEnv, ti_rtl, get_type_env
|
|||||||
try:
|
try:
|
||||||
from typing import List, Dict, Tuple # noqa
|
from typing import List, Dict, Tuple # noqa
|
||||||
from cdsl.ast import Var # noqa
|
from cdsl.ast import Var # noqa
|
||||||
from cdsl.xform import XForm # noqa
|
from cdsl.xform import XForm, Rtl # noqa
|
||||||
from cdsl.ti import VarTyping # noqa
|
from cdsl.ti import VarTyping # noqa
|
||||||
from cdsl.instructions import Instruction, InstructionSemantics # noqa
|
from cdsl.instructions import Instruction, InstructionSemantics # noqa
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def verify_semantics(inst, sem):
|
def verify_semantics(inst, src, xforms):
|
||||||
# type: (Instruction, InstructionSemantics) -> None
|
# type: (Instruction, Rtl, InstructionSemantics) -> None
|
||||||
"""
|
"""
|
||||||
Verify that the semantics sem correctly describes the instruction inst.
|
Verify that the semantics transforms in xforms correctly describe the
|
||||||
This involves checking that:
|
instruction described by the src Rtl. This involves checking that:
|
||||||
1) For all XForms x \in sem, x.src consists of a single instance of
|
1) For all XForms x \in xforms, there is a Var substitution form src to
|
||||||
inst
|
x.src
|
||||||
2) For any possible concrete typing of inst there is exactly 1 XForm x
|
2) For any possible concrete typing of src there is exactly 1 XForm x
|
||||||
in sem that applies.
|
in xforms that applies.
|
||||||
"""
|
"""
|
||||||
# 1) The source rtl is always a single instance of inst.
|
# 0) The source rtl is always a single instruction
|
||||||
for xform in sem:
|
assert len(src.rtl) == 1
|
||||||
assert len(xform.src.rtl) == 1 and\
|
|
||||||
xform.src.rtl[0].expr.inst == inst,\
|
# 1) For all XForms x, x.src is structurally equivalent to src
|
||||||
"XForm {} doesn't describe instruction {}."\
|
for x in xforms:
|
||||||
.format(xform, inst)
|
assert src.substitution(x.src, {}) is not None,\
|
||||||
|
"XForm {} doesn't describe instruction {}.".format(x, src)
|
||||||
|
|
||||||
# 2) Any possible typing for the instruction should be covered by
|
# 2) Any possible typing for the instruction should be covered by
|
||||||
# exactly ONE semantic XForm
|
# exactly ONE semantic XForm
|
||||||
inst_rtl = sem[0].src
|
typenv = get_type_env(ti_rtl(src, TypeEnv()))
|
||||||
typenv = get_type_env(ti_rtl(inst_rtl, TypeEnv()))
|
typenv.normalize()
|
||||||
|
typenv = typenv.extract()
|
||||||
# This bit is awkward. Concrete typing is defined in terms of the vars
|
|
||||||
# of one Rtl. We arbitrarily picked that Rtl to be sem[0].src. For any
|
|
||||||
# other XForms in sem, we must build a substitution form
|
|
||||||
# sem[0].src->sem[N].src, before we can check if sem[N] permits one of
|
|
||||||
# the concrete typings of our Rtl.
|
|
||||||
# TODO: Can this be made cleaner?
|
|
||||||
subst = [inst_rtl.substitution(x.src, {}) for x in sem]
|
|
||||||
assert not any(x is None for x in subst)
|
|
||||||
sub_sem = list(zip(subst, sem)) # type: List[Tuple[Dict[Var, Var], XForm]] # noqa
|
|
||||||
|
|
||||||
def subst_typing(typing, sub):
|
|
||||||
# type: (VarTyping, Dict[Var, Var]) -> VarTyping
|
|
||||||
return {sub[v]: tv for (v, tv) in typing.items()}
|
|
||||||
|
|
||||||
for t in typenv.concrete_typings():
|
for t in typenv.concrete_typings():
|
||||||
matching_xforms = [x for (s, x) in sub_sem
|
matching_xforms = [] # type: List[XForm]
|
||||||
if x.ti.permits(subst_typing(t, s))]
|
for x in xforms:
|
||||||
|
# Translate t using x.symtab
|
||||||
|
t = {x.symtab[str(v)]: tv for (v, tv) in t.items()}
|
||||||
|
if (x.ti.permits(t)):
|
||||||
|
matching_xforms.append(x)
|
||||||
|
|
||||||
assert len(matching_xforms) == 1,\
|
assert len(matching_xforms) == 1,\
|
||||||
("Possible typing {} of {} not matched by exactly one case " +
|
("Possible typing {} of {} not matched by exactly one case " +
|
||||||
": {}").format(t, inst, matching_xforms)
|
": {}").format(t, inst, matching_xforms)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ def find_matching_xform(d):
|
|||||||
|
|
||||||
for x in d.expr.inst.semantics:
|
for x in d.expr.inst.semantics:
|
||||||
subst = d.substitution(x.src.rtl[0], {})
|
subst = d.substitution(x.src.rtl[0], {})
|
||||||
|
assert subst is not None
|
||||||
|
|
||||||
if x.ti.permits({subst[v]: tv for (v, tv) in typing.items()}):
|
if x.ti.permits({subst[v]: tv for (v, tv) in typing.items()}):
|
||||||
res.append(x)
|
res.append(x)
|
||||||
|
|||||||
@@ -232,16 +232,16 @@ class TestElaborate(TestCase):
|
|||||||
def test_elaborate_iadd_simple(self):
|
def test_elaborate_iadd_simple(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
i32.by(2) # Make sure i32x2 exists.
|
i32.by(2) # Make sure i32x2 exists.
|
||||||
r = Rtl(
|
|
||||||
self.v0 << iadd.i32(self.v1, self.v2),
|
|
||||||
)
|
|
||||||
sem = elaborate(cleanup_concrete_rtl(r))
|
|
||||||
x = Var('x')
|
x = Var('x')
|
||||||
y = Var('y')
|
y = Var('y')
|
||||||
a = Var('a')
|
a = Var('a')
|
||||||
bvx = Var('bvx')
|
bvx = Var('bvx')
|
||||||
bvy = Var('bvy')
|
bvy = Var('bvy')
|
||||||
bva = Var('bva')
|
bva = Var('bva')
|
||||||
|
r = Rtl(
|
||||||
|
a << iadd.i32(x, y),
|
||||||
|
)
|
||||||
|
sem = elaborate(cleanup_concrete_rtl(r))
|
||||||
|
|
||||||
assert concrete_rtls_eq(sem, cleanup_concrete_rtl(Rtl(
|
assert concrete_rtls_eq(sem, cleanup_concrete_rtl(Rtl(
|
||||||
bvx << prim_to_bv.i32(x),
|
bvx << prim_to_bv.i32(x),
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
"""
|
|
||||||
The semantics.types module predefines all the Cretone primitive bitvector
|
|
||||||
types.
|
|
||||||
"""
|
|
||||||
from cdsl.types import BVType
|
|
||||||
from cdsl.typevar import MAX_BITVEC, int_log2
|
|
||||||
|
|
||||||
for width in range(0, int_log2(MAX_BITVEC)+1):
|
|
||||||
BVType(2**width)
|
|
||||||
Reference in New Issue
Block a user