Rename Dict[Var, TypeVar] to VarTyping; Add VarMap (Dict[Var,Var]). Add {Ast, Def, Rtl}.{vars(), substitution()} and Def.uses(), Def.definitions() - these enable checking structural equivalence between Rtls and doing variable substitutions between compatible Rtls; Add TypeEnv.permits() routine - allows checking if a given TypeEnv allows a given concrete typing without enumerating all typings (will be useful for determing which semantic transform applies to a given concrete typing).
This commit is contained in:
committed by
Jakob Stoklund Olesen
parent
716cd26fbf
commit
605886a277
@@ -11,15 +11,17 @@ from .predicates import IsEqual, And
|
||||
|
||||
try:
|
||||
from typing import Union, Tuple, Sequence, TYPE_CHECKING, Dict, List # noqa
|
||||
from typing import Optional, Set # noqa
|
||||
if TYPE_CHECKING:
|
||||
from .operands import ImmediateKind # noqa
|
||||
from .predicates import PredNode # noqa
|
||||
VarMap = Dict["Var", "Var"]
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def replace_var(arg, m):
|
||||
# type: (Expr, Dict[Var, Var]) -> Expr
|
||||
# type: (Expr, VarMap) -> Expr
|
||||
"""
|
||||
Given a var v return either m[v] or a new variable v' (and remember
|
||||
m[v]=v'). Otherwise return the argument unchanged
|
||||
@@ -74,7 +76,7 @@ class Def(object):
|
||||
', '.join(map(str, self.defs)), self.expr)
|
||||
|
||||
def copy(self, m):
|
||||
# type: (Dict[Var, Var]) -> Def
|
||||
# type: (VarMap) -> Def
|
||||
"""
|
||||
Return a copy of this Def with vars replaced with fresh variables,
|
||||
in accordance with the map m. Update m as neccessary.
|
||||
@@ -88,6 +90,40 @@ class Def(object):
|
||||
|
||||
return Def(tuple(new_defs), new_expr)
|
||||
|
||||
def definitions(self):
|
||||
# type: () -> Set[Var]
|
||||
""" Return the set of all Vars that are defined by self"""
|
||||
return set(self.defs)
|
||||
|
||||
def uses(self):
|
||||
# type: () -> Set[Var]
|
||||
""" Return the set of all Vars that are used(read) by self"""
|
||||
return set(self.expr.vars())
|
||||
|
||||
def vars(self):
|
||||
# type: () -> Set[Var]
|
||||
""" Return the set of all Vars that appear in self"""
|
||||
return self.definitions().union(self.uses())
|
||||
|
||||
def substitution(self, other, s):
|
||||
# type: (Def, VarMap) -> Optional[VarMap]
|
||||
"""
|
||||
If the Defs self and other agree structurally, return a variable
|
||||
substitution to transform self ot other. Two Defs agree structurally
|
||||
if the contained Apply's agree structurally.
|
||||
"""
|
||||
s = self.expr.substitution(other.expr, s)
|
||||
|
||||
if (s is None):
|
||||
return s
|
||||
|
||||
assert len(self.defs) == len(other.defs)
|
||||
for (self_d, other_d) in zip(self.defs, other.defs):
|
||||
assert self_d not in s # Guaranteed by SSA form
|
||||
s[self_d] = other_d
|
||||
|
||||
return s
|
||||
|
||||
|
||||
class Expr(object):
|
||||
"""
|
||||
@@ -332,7 +368,7 @@ class Apply(Expr):
|
||||
return pred
|
||||
|
||||
def copy(self, m):
|
||||
# type: (Dict[Var, Var]) -> Apply
|
||||
# type: (VarMap) -> Apply
|
||||
"""
|
||||
Return a copy of this Expr with vars replaced with fresh variables,
|
||||
in accordance with the map m. Update m as neccessary.
|
||||
@@ -340,6 +376,43 @@ class Apply(Expr):
|
||||
return Apply(self.inst, tuple(map(lambda e: replace_var(e, m),
|
||||
self.args)))
|
||||
|
||||
def vars(self):
|
||||
# type: () -> Set[Var]
|
||||
""" Return the set of all Vars that appear in self"""
|
||||
res = set()
|
||||
for i in self.inst.value_opnums:
|
||||
arg = self.args[i]
|
||||
assert isinstance(arg, Var)
|
||||
res.add(arg)
|
||||
return res
|
||||
|
||||
def substitution(self, other, s):
|
||||
# type: (Apply, VarMap) -> Optional[VarMap]
|
||||
"""
|
||||
If the application self and other agree structurally, return a variable
|
||||
substitution to transform self ot other. Two applications agree
|
||||
structurally if:
|
||||
1) They are over the same instruction
|
||||
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
|
||||
other.
|
||||
"""
|
||||
if self.inst != other.inst:
|
||||
return None
|
||||
|
||||
# TODO: Should we check imm/cond codes here as well?
|
||||
for i in self.inst.value_opnums:
|
||||
self_a = self.args[i]
|
||||
other_a = other.args[i]
|
||||
|
||||
assert isinstance(self_a, Var) and isinstance(other_a, Var)
|
||||
if (self_a not in s):
|
||||
s[self_a] = other_a
|
||||
else:
|
||||
if (s[self_a] != other_a):
|
||||
return None
|
||||
return s
|
||||
|
||||
|
||||
class Enumerator(Expr):
|
||||
"""
|
||||
|
||||
@@ -7,14 +7,16 @@ from .formats import InstructionFormat
|
||||
|
||||
try:
|
||||
from typing import Union, Sequence, List, Tuple, Any, TYPE_CHECKING # noqa
|
||||
from typing import Dict # noqa
|
||||
if TYPE_CHECKING:
|
||||
from .ast import Expr, Apply # noqa
|
||||
from .ast import Expr, Apply, Var # noqa
|
||||
from .typevar import TypeVar # noqa
|
||||
from .ti import TypeConstraint # noqa
|
||||
# List of operands for ins/outs:
|
||||
OpList = Union[Sequence[Operand], Operand]
|
||||
ConstrList = Union[Sequence[TypeConstraint], TypeConstraint]
|
||||
MaybeBoundInst = Union['Instruction', 'BoundInstruction']
|
||||
VarTyping = Dict[Var, TypeVar]
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from unittest import TestCase
|
||||
from functools import reduce
|
||||
|
||||
try:
|
||||
from .ti import TypeMap, ConstraintList, VarMap, TypingOrError # noqa
|
||||
from .ti import TypeMap, ConstraintList, VarTyping, TypingOrError # noqa
|
||||
from typing import List, Dict, Tuple, TYPE_CHECKING, cast # noqa
|
||||
except ImportError:
|
||||
TYPE_CHECKING = False
|
||||
@@ -61,7 +61,7 @@ def agree(me, other):
|
||||
|
||||
|
||||
def check_typing(got_or_err, expected, symtab=None):
|
||||
# type: (TypingOrError, Tuple[VarMap, ConstraintList], Dict[str, Var]) -> None # noqa
|
||||
# type: (TypingOrError, Tuple[VarTyping, ConstraintList], Dict[str, Var]) -> None # noqa
|
||||
"""
|
||||
Check that a the typing we received (got_or_err) complies with the
|
||||
expected typing (expected). If symtab is specified, substitute the Vars in
|
||||
@@ -93,7 +93,7 @@ def check_typing(got_or_err, expected, symtab=None):
|
||||
|
||||
|
||||
def check_concrete_typing_rtl(var_types, rtl):
|
||||
# type: (VarMap, Rtl) -> None
|
||||
# type: (VarTyping, Rtl) -> None
|
||||
"""
|
||||
Check that a concrete type assignment var_types (Dict[Var, TypeVar]) is
|
||||
valid for an Rtl rtl. Specifically check that:
|
||||
@@ -136,7 +136,7 @@ def check_concrete_typing_rtl(var_types, rtl):
|
||||
|
||||
|
||||
def check_concrete_typing_xform(var_types, xform):
|
||||
# type: (VarMap, XForm) -> None
|
||||
# type: (VarTyping, XForm) -> None
|
||||
"""
|
||||
Check a concrete type assignment var_types for an XForm xform
|
||||
"""
|
||||
|
||||
@@ -15,7 +15,7 @@ try:
|
||||
from .typevar import TypeSet # noqa
|
||||
if TYPE_CHECKING:
|
||||
TypeMap = Dict[TypeVar, TypeVar]
|
||||
VarMap = Dict[Var, TypeVar]
|
||||
VarTyping = Dict[Var, TypeVar]
|
||||
except ImportError:
|
||||
TYPE_CHECKING = False
|
||||
pass
|
||||
@@ -257,6 +257,7 @@ class TypeEnv(object):
|
||||
Lookup the canonical representative for a Var/TypeVar.
|
||||
"""
|
||||
if (isinstance(arg, Var)):
|
||||
assert arg in self.vars
|
||||
tv = arg.get_typevar()
|
||||
else:
|
||||
assert (isinstance(arg, TypeVar))
|
||||
@@ -290,8 +291,16 @@ class TypeEnv(object):
|
||||
"""
|
||||
Add a new constraint
|
||||
"""
|
||||
if (constr not in self.constraints):
|
||||
self.constraints.append(constr)
|
||||
if (constr in self.constraints):
|
||||
return
|
||||
|
||||
# InTypeset constraints can be expressed by constraining the typeset of
|
||||
# a variable. No need to add them to self.constraints
|
||||
if (isinstance(constr, InTypeset)):
|
||||
self[constr.tv].constrain_types_by_ts(constr.ts)
|
||||
return
|
||||
|
||||
self.constraints.append(constr)
|
||||
|
||||
def get_uid(self):
|
||||
# type: () -> str
|
||||
@@ -436,7 +445,7 @@ class TypeEnv(object):
|
||||
return t
|
||||
|
||||
def concrete_typings(self):
|
||||
# type: () -> Iterable[VarMap]
|
||||
# type: () -> Iterable[VarTyping]
|
||||
"""
|
||||
Return an iterable over all possible concrete typings permitted by this
|
||||
TypeEnv.
|
||||
@@ -464,6 +473,35 @@ class TypeEnv(object):
|
||||
|
||||
yield concrete_var_map
|
||||
|
||||
def permits(self, concrete_typing):
|
||||
# type: (VarTyping) -> bool
|
||||
"""
|
||||
Return true iff this TypeEnv permits the (possibly partial) concrete
|
||||
variable type mapping concrete_typing.
|
||||
"""
|
||||
# Each variable has a concrete type, that is a subset of its inferred
|
||||
# typeset.
|
||||
for (v, typ) in concrete_typing.items():
|
||||
assert typ.singleton_type() is not None
|
||||
if not typ.get_typeset().issubset(self[v].get_typeset()):
|
||||
return False
|
||||
|
||||
m = {self[v]: typ for (v, typ) in concrete_typing.items()}
|
||||
|
||||
# Constraints involving vars in concrete_typing are satisfied
|
||||
for constr in self.constraints:
|
||||
try:
|
||||
# If the constraint includes only vars in concrete_typing, we
|
||||
# can translate it using m. Otherwise we encounter a KeyError
|
||||
# and ignore it
|
||||
constr = constr.translate(m)
|
||||
if not constr.eval():
|
||||
return False
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
def dot(self):
|
||||
# type: () -> str
|
||||
"""
|
||||
|
||||
@@ -4,10 +4,12 @@ Instruction transformations.
|
||||
from __future__ import absolute_import
|
||||
from .ast import Def, Var, Apply
|
||||
from .ti import ti_xform, TypeEnv, get_type_env
|
||||
from functools import reduce
|
||||
|
||||
try:
|
||||
from typing import Union, Iterator, Sequence, Iterable, List, Dict # noqa
|
||||
from .ast import Expr # noqa
|
||||
from typing import Optional, Set # noqa
|
||||
from .ast import Expr, VarMap # noqa
|
||||
DefApply = Union[Def, Apply]
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -42,13 +44,45 @@ class Rtl(object):
|
||||
self.rtl = tuple(map(canonicalize_defapply, args))
|
||||
|
||||
def copy(self, m):
|
||||
# type: (Dict[Var, Var]) -> Rtl
|
||||
# type: (VarMap) -> Rtl
|
||||
"""
|
||||
Return a copy of this rtl with all Vars substituted with copies or
|
||||
according to m. Update m as neccessary.
|
||||
"""
|
||||
return Rtl(*[d.copy(m) for d in self.rtl])
|
||||
|
||||
def vars(self):
|
||||
# type: () -> Set[Var]
|
||||
""" Return the set of all Vars that appear in self"""
|
||||
return reduce(lambda x, y: x.union(y),
|
||||
[d.vars() for d in self.rtl],
|
||||
set([]))
|
||||
|
||||
def definitions(self):
|
||||
# type: () -> Set[Var]
|
||||
""" Return the set of all Vars defined in self"""
|
||||
return reduce(lambda x, y: x.union(y),
|
||||
[d.definitions() for d in self.rtl],
|
||||
set([]))
|
||||
|
||||
def substitution(self, other, s):
|
||||
# type: (Rtl, VarMap) -> Optional[VarMap]
|
||||
"""
|
||||
If the Rtl self agrees structurally with the Rtl other, return a
|
||||
substitution to transform self to other. Two Rtls agree structurally if
|
||||
they have the same sequence of Defs, that agree structurally.
|
||||
"""
|
||||
if len(self.rtl) != len(other.rtl):
|
||||
return None
|
||||
|
||||
for i in range(len(self.rtl)):
|
||||
s = self.rtl[i].substitution(other.rtl[i], s)
|
||||
|
||||
if s is None:
|
||||
return None
|
||||
|
||||
return s
|
||||
|
||||
|
||||
class XForm(object):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user