from __future__ import absolute_import
from base.instructions import vselect, vsplit, vconcat, iconst, iadd, bint,\
b1, icmp, iadd_cout, iadd_cin, uextend, sextend, ireduce, fpromote, \
fdemote
from base.legalize import narrow, expand
from base.immediates import intcc
from base.types import i32, i8
from .typevar import TypeVar
from .ast import Var, Def
from .xform import Rtl, XForm
from .ti import ti_rtl, subst, TypeEnv, get_type_env, TypesEqual, WiderOrEq
from unittest import TestCase
from functools import reduce
try:
from .ti import TypeMap, ConstraintList, VarTyping, TypingOrError from typing import List, Dict, Tuple, TYPE_CHECKING, cast except ImportError:
TYPE_CHECKING = False
def agree(me, other):
m = {} for tv in me.type_map:
if (me[tv] not in m):
m[me[tv]] = other[tv]
if me[tv].get_typeset() != other[tv].get_typeset():
return False
else:
if m[me[tv]] != other[tv]:
return False
me_equiv_constr = sorted([constr.translate(m)
for constr in me.constraints], key=repr)
other_equiv_constr = sorted([constr.translate(other)
for constr in other.constraints], key=repr)
return me_equiv_constr == other_equiv_constr
def check_typing(got_or_err, expected, symtab=None):
(m, c) = expected
got = get_type_env(got_or_err)
if (symtab is not None):
subst_m = {k.get_typevar(): symtab[str(k)].get_typevar()
for k in m.keys()}
tv_m = {subst(k.get_typevar(), subst_m): v for (k, v) in m.items()}
c = [constr.translate(subst_m) for constr in c]
else:
tv_m = {k.get_typevar(): v for (k, v) in m.items()}
expected_typ = TypeEnv((tv_m, c))
assert agree(expected_typ, got), \
"typings disagree:\n {} \n {}".format(got.dot(),
expected_typ.dot())
def check_concrete_typing_rtl(var_types, rtl):
for d in rtl.rtl:
assert isinstance(d, Def)
inst = d.expr.inst
actual_tvs = [var_types[d.defs[i]] for i in inst.value_results]
for v in [d.expr.args[i] for i in inst.value_opnums]:
assert isinstance(v, Var)
actual_tvs.append(var_types[v])
formal_tvs = [inst.outs[i].typevar for i in inst.value_results] +\
[inst.ins[i].typevar for i in inst.value_opnums]
m = {}
for (actual_tv, formal_tv) in zip(actual_tvs, formal_tvs):
assert actual_tv.singleton_type() is not None
formal_tv = subst(formal_tv, m)
assert actual_tv.get_typeset().issubset(formal_tv.get_typeset())
if formal_tv not in m and not formal_tv.is_derived:
m[formal_tv] = actual_tv
def check_concrete_typing_xform(var_types, xform):
check_concrete_typing_rtl(var_types, xform.src)
check_concrete_typing_rtl(var_types, xform.dst)
class TypeCheckingBaseTest(TestCase):
def setUp(self):
self.v0 = Var("v0")
self.v1 = Var("v1")
self.v2 = Var("v2")
self.v3 = Var("v3")
self.v4 = Var("v4")
self.v5 = Var("v5")
self.v6 = Var("v6")
self.v7 = Var("v7")
self.v8 = Var("v8")
self.v9 = Var("v9")
self.imm0 = Var("imm0")
self.IxN_nonscalar = TypeVar("IxN", "", ints=True, scalars=False,
simd=True)
self.TxN = TypeVar("TxN", "", ints=True, bools=True, floats=True,
scalars=False, simd=True)
self.b1 = TypeVar.singleton(b1)
class TestRTL(TypeCheckingBaseTest):
def test_bad_rtl1(self):
r = Rtl(
(self.v0, self.v1) << vsplit(self.v2),
self.v3 << vconcat(self.v0, self.v2),
)
ti = TypeEnv()
self.assertEqual(ti_rtl(r, ti),
"On line 1: fail ti on `typeof_v2` <: `1`: " +
"Error: empty type created when unifying " +
"`typeof_v2` and `half_vector(typeof_v2)`")
def test_vselect(self):
r = Rtl(
self.v0 << vselect(self.v1, self.v2, self.v3),
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
txn = self.TxN.get_fresh_copy("TxN1")
check_typing(typing, ({
self.v0: txn,
self.v1: txn.as_bool(),
self.v2: txn,
self.v3: txn
}, []))
def test_vselect_icmpimm(self):
r = Rtl(
self.v0 << iconst(self.imm0),
self.v1 << icmp(intcc.eq, self.v2, self.v0),
self.v5 << vselect(self.v1, self.v3, self.v4),
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
ixn = self.IxN_nonscalar.get_fresh_copy("IxN1")
txn = self.TxN.get_fresh_copy("TxN1")
check_typing(typing, ({
self.v0: ixn,
self.v1: ixn.as_bool(),
self.v2: ixn,
self.v3: txn,
self.v4: txn,
self.v5: txn,
}, [TypesEqual(ixn.as_bool(), txn.as_bool())]))
def test_vselect_vsplits(self):
r = Rtl(
self.v3 << vselect(self.v0, self.v1, self.v2),
(self.v4, self.v5) << vsplit(self.v3),
(self.v6, self.v7) << vsplit(self.v4),
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
t = TypeVar("t", "", ints=True, bools=True, floats=True,
simd=(4, 256))
check_typing(typing, ({
self.v0: t.as_bool(),
self.v1: t,
self.v2: t,
self.v3: t,
self.v4: t.half_vector(),
self.v5: t.half_vector(),
self.v6: t.half_vector().half_vector(),
self.v7: t.half_vector().half_vector(),
}, []))
def test_vselect_vconcats(self):
r = Rtl(
self.v3 << vselect(self.v0, self.v1, self.v2),
self.v8 << vconcat(self.v3, self.v3),
self.v9 << vconcat(self.v8, self.v8),
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
t = TypeVar("t", "", ints=True, bools=True, floats=True,
simd=(2, 64))
check_typing(typing, ({
self.v0: t.as_bool(),
self.v1: t,
self.v2: t,
self.v3: t,
self.v8: t.double_vector(),
self.v9: t.double_vector().double_vector(),
}, []))
def test_vselect_vsplits_vconcats(self):
r = Rtl(
self.v3 << vselect(self.v0, self.v1, self.v2),
(self.v4, self.v5) << vsplit(self.v3),
(self.v6, self.v7) << vsplit(self.v4),
self.v8 << vconcat(self.v3, self.v3),
self.v9 << vconcat(self.v8, self.v8),
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
t = TypeVar("t", "", ints=True, bools=True, floats=True,
simd=(4, 64))
check_typing(typing, ({
self.v0: t.as_bool(),
self.v1: t,
self.v2: t,
self.v3: t,
self.v4: t.half_vector(),
self.v5: t.half_vector(),
self.v6: t.half_vector().half_vector(),
self.v7: t.half_vector().half_vector(),
self.v8: t.double_vector(),
self.v9: t.double_vector().double_vector(),
}, []))
def test_bint(self):
r = Rtl(
self.v4 << iadd(self.v1, self.v2),
self.v5 << bint(self.v3),
self.v0 << iadd(self.v4, self.v5)
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
itype = TypeVar("t", "", ints=True, simd=(1, 256))
btype = TypeVar("b", "", bools=True, simd=True)
check_typing(typing, ({
self.v1: itype,
self.v2: itype,
self.v4: itype,
self.v5: itype,
self.v3: btype,
self.v0: itype,
}, []))
def test_fully_bound_inst_inference_bad(self):
r = Rtl(
self.v3 << uextend.i32(self.v1),
self.v4 << uextend.i16(self.v2),
self.v5 << iadd(self.v3, self.v4),
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
self.assertEqual(typing,
"On line 2: fail ti on `typeof_v4` <: `4`: " +
"Error: empty type created when unifying " +
"`i16` and `i32`")
def test_extend_reduce(self):
r = Rtl(
self.v1 << uextend(self.v0),
self.v2 << ireduce(self.v1),
self.v3 << sextend(self.v2),
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
typing = typing.extract()
itype0 = TypeVar("t", "", ints=True, simd=(1, 256))
itype1 = TypeVar("t1", "", ints=True, simd=(1, 256))
itype2 = TypeVar("t2", "", ints=True, simd=(1, 256))
itype3 = TypeVar("t3", "", ints=True, simd=(1, 256))
check_typing(typing, ({
self.v0: itype0,
self.v1: itype1,
self.v2: itype2,
self.v3: itype3,
}, [WiderOrEq(itype1, itype0),
WiderOrEq(itype1, itype2),
WiderOrEq(itype3, itype2)]))
def test_extend_reduce_enumeration(self):
for op in (uextend, sextend, ireduce):
r = Rtl(
self.v1 << op(self.v0),
)
ti = TypeEnv()
typing = ti_rtl(r, ti).extract()
lst = [(t[self.v0], t[self.v1]) for t in typing.concrete_typings()]
assert (len(lst) == len(set(lst)) and len(lst) == 90)
for (tv0, tv1) in lst:
typ0, typ1 = (tv0.singleton_type(), tv1.singleton_type())
if (op == ireduce):
assert typ0.wider_or_equal(typ1)
else:
assert typ1.wider_or_equal(typ0)
def test_fpromote_fdemote(self):
r = Rtl(
self.v1 << fpromote(self.v0),
self.v2 << fdemote(self.v1),
)
ti = TypeEnv()
typing = ti_rtl(r, ti)
typing = typing.extract()
ftype0 = TypeVar("t", "", floats=True, simd=(1, 256))
ftype1 = TypeVar("t1", "", floats=True, simd=(1, 256))
ftype2 = TypeVar("t2", "", floats=True, simd=(1, 256))
check_typing(typing, ({
self.v0: ftype0,
self.v1: ftype1,
self.v2: ftype2,
}, [WiderOrEq(ftype1, ftype0),
WiderOrEq(ftype1, ftype2)]))
def test_fpromote_fdemote_enumeration(self):
for op in (fpromote, fdemote):
r = Rtl(
self.v1 << op(self.v0),
)
ti = TypeEnv()
typing = ti_rtl(r, ti).extract()
lst = [(t[self.v0], t[self.v1]) for t in typing.concrete_typings()]
assert (len(lst) == len(set(lst)) and len(lst) == 27)
for (tv0, tv1) in lst:
(typ0, typ1) = (tv0.singleton_type(), tv1.singleton_type())
if (op == fdemote):
assert typ0.wider_or_equal(typ1)
else:
assert typ1.wider_or_equal(typ0)
class TestXForm(TypeCheckingBaseTest):
def test_iadd_cout(self):
x = XForm(Rtl((self.v0, self.v1) << iadd_cout(self.v2, self.v3),),
Rtl(
self.v0 << iadd(self.v2, self.v3),
self.v1 << icmp(intcc.ult, self.v0, self.v2)
))
itype = TypeVar("t", "", ints=True, simd=(1, 1))
check_typing(x.ti, ({
self.v0: itype,
self.v2: itype,
self.v3: itype,
self.v1: itype.as_bool(),
}, []), x.symtab)
def test_iadd_cin(self):
x = XForm(Rtl(self.v0 << iadd_cin(self.v1, self.v2, self.v3)),
Rtl(
self.v4 << iadd(self.v1, self.v2),
self.v5 << bint(self.v3),
self.v0 << iadd(self.v4, self.v5)
))
itype = TypeVar("t", "", ints=True, simd=(1, 1))
check_typing(x.ti, ({
self.v0: itype,
self.v1: itype,
self.v2: itype,
self.v3: self.b1,
self.v4: itype,
self.v5: itype,
}, []), x.symtab)
def test_enumeration_with_constraints(self):
xform = XForm(
Rtl(
self.v0 << iconst(self.imm0),
self.v1 << icmp(intcc.eq, self.v2, self.v0),
self.v5 << vselect(self.v1, self.v3, self.v4)
),
Rtl(
self.v0 << iconst(self.imm0),
self.v1 << icmp(intcc.eq, self.v2, self.v0),
self.v5 << vselect(self.v1, self.v3, self.v4)
))
assert len(xform.ti.constraints) > 0
concrete_var_assigns = list(xform.ti.concrete_typings())
v0 = xform.symtab[str(self.v0)]
v1 = xform.symtab[str(self.v1)]
v2 = xform.symtab[str(self.v2)]
v3 = xform.symtab[str(self.v3)]
v4 = xform.symtab[str(self.v4)]
v5 = xform.symtab[str(self.v5)]
for var_m in concrete_var_assigns:
assert var_m[v0] == var_m[v2] and \
var_m[v3] == var_m[v4] and\
var_m[v5] == var_m[v3] and\
var_m[v1] == var_m[v2].as_bool() and\
var_m[v1].get_typeset() == var_m[v3].as_bool().get_typeset()
check_concrete_typing_xform(var_m, xform)
assert len(concrete_var_assigns) == 80
def test_base_legalizations_enumeration(self):
for xform in narrow.xforms + expand.xforms:
concrete_typings_list = list(xform.ti.concrete_typings())
assert len(concrete_typings_list) > 0
if (len(xform.ti.free_typevars()) == 0):
assert len(concrete_typings_list) == 1
continue
if (len(xform.ti.constraints) > 0):
theoretical_num_typings =\
reduce(lambda x, y: x*y,
[tv.get_typeset().size()
for tv in xform.ti.free_typevars()], 1)
assert len(concrete_typings_list) < theoretical_num_typings
for concrete_typing in concrete_typings_list:
check_concrete_typing_xform(concrete_typing, xform)
def test_bound_inst_inference(self):
x = XForm(
Rtl(
self.v0 << iadd(self.v1, self.v2),
),
Rtl(
self.v3 << uextend.i32(self.v1),
self.v4 << uextend.i32(self.v2),
self.v5 << iadd(self.v3, self.v4),
self.v0 << ireduce(self.v5)
))
itype = TypeVar("t", "", ints=True, simd=True)
i32t = TypeVar.singleton(i32)
check_typing(x.ti, ({
self.v0: itype,
self.v1: itype,
self.v2: itype,
self.v3: i32t,
self.v4: i32t,
self.v5: i32t,
}, [WiderOrEq(i32t, itype)]), x.symtab)
def test_bound_inst_inference1(self):
x = XForm(
Rtl(
self.v0 << iadd(self.v1, self.v2),
),
Rtl(
self.v3 << uextend(self.v1),
self.v4 << uextend(self.v2),
self.v5 << iadd.i32(self.v3, self.v4),
self.v0 << ireduce(self.v5)
))
itype = TypeVar("t", "", ints=True, simd=True)
i32t = TypeVar.singleton(i32)
check_typing(x.ti, ({
self.v0: itype,
self.v1: itype,
self.v2: itype,
self.v3: i32t,
self.v4: i32t,
self.v5: i32t,
}, [WiderOrEq(i32t, itype)]), x.symtab)
def test_fully_bound_inst_inference(self):
x = XForm(
Rtl(
self.v0 << iadd(self.v1, self.v2),
),
Rtl(
self.v3 << uextend.i32.i8(self.v1),
self.v4 << uextend.i32.i8(self.v2),
self.v5 << iadd(self.v3, self.v4),
self.v0 << ireduce(self.v5)
))
i8t = TypeVar.singleton(i8)
i32t = TypeVar.singleton(i32)
check_typing(x.ti, ({
self.v0: i8t,
self.v1: i8t,
self.v2: i8t,
self.v3: i32t,
self.v4: i32t,
self.v5: i32t,
}, []), x.symtab)
def test_fully_bound_inst_inference_bad(self):
with self.assertRaises(AssertionError):
XForm(
Rtl(
self.v0 << iadd(self.v1, self.v2),
),
Rtl(
self.v3 << uextend.i32.i8(self.v1),
self.v4 << uextend.i32.i16(self.v2),
self.v5 << iadd(self.v3, self.v4),
self.v0 << ireduce(self.v5)
))