def test_cleanup_concrete_rtl_ireduce_bad(self): # type: () -> None x = Var('x') y = Var('y') x.set_typevar(TypeVar.singleton(i16.by(1))) r = Rtl( y << ireduce(x), ) with self.assertRaises(AssertionError): r.cleanup_concrete_rtl()
def setUp(self): # type: () -> None 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_nonscalar", "", 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)
RV32.legalize_type( default=narrow, i32=expand, f32=expand, f64=expand) RV64.legalize_monomorphic(expand) RV64.legalize_type( default=narrow, i32=expand, i64=expand, f32=expand, f64=expand) # Dummies for instruction predicates. x = Var('x') y = Var('y') dest = Var('dest') args = Var('args') # Basic arithmetic binary instructions are encoded in an R-type instruction. for inst, inst_imm, f3, f7 in [ (base.iadd, base.iadd_imm, 0b000, 0b0000000), (base.isub, None, 0b000, 0b0100000), (base.bxor, base.bxor_imm, 0b100, 0b0000000), (base.bor, base.bor_imm, 0b110, 0b0000000), (base.band, base.band_imm, 0b111, 0b0000000) ]: RV32.enc(inst.i32, R, OP(f3, f7)) RV64.enc(inst.i64, R, OP(f3, f7))
expand.custom_legalize(insts.global_addr, 'expand_global_addr') expand.custom_legalize(insts.heap_addr, 'expand_heap_addr') # Custom expansions that need to change the CFG. # TODO: Add sufficient XForm syntax that we don't need to hand-code these. expand.custom_legalize(insts.trapz, 'expand_cond_trap') expand.custom_legalize(insts.trapnz, 'expand_cond_trap') expand.custom_legalize(insts.br_table, 'expand_br_table') expand.custom_legalize(insts.select, 'expand_select') # Custom expansions for floating point constants. # These expansions require bit-casting or creating constant pool entries. expand.custom_legalize(insts.f32const, 'expand_fconst') expand.custom_legalize(insts.f64const, 'expand_fconst') x = Var('x') y = Var('y') a = Var('a') a1 = Var('a1') a2 = Var('a2') b = Var('b') b1 = Var('b1') b2 = Var('b2') b_in = Var('b_in') b_int = Var('b_int') c = Var('c') c1 = Var('c1') c2 = Var('c2') c_in = Var('c_in') c_int = Var('c_int') xl = Var('xl')
class TestRuntimeChecks(TestCase): def setUp(self): # type: () -> None 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_nonscalar", "", 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) def check_yo_check(self, xform, expected_f): # type: (XForm, CheckProducer) -> None fmt = Formatter() type_sets = UniqueTable() for check in get_runtime_typechecks(xform): emit_runtime_typecheck(check, fmt, type_sets) # Remove comments got = "".join([l for l in fmt.lines if not l.strip().startswith("//")]) expected = expected_f(type_sets) self.assertEqual(got, expected) def test_width_check(self): # type: () -> None x = XForm(Rtl(self.v0 << copy(self.v1)), Rtl((self.v2, self.v3) << isplit(self.v1), self.v0 << iconcat(self.v2, self.v3))) WideInt = TypeSet(lanes=(1, 256), ints=(16, 64)) self.check_yo_check(x, typeset_check(self.v1, WideInt)) def test_lanes_check(self): # type: () -> None x = XForm(Rtl(self.v0 << copy(self.v1)), Rtl((self.v2, self.v3) << vsplit(self.v1), self.v0 << vconcat(self.v2, self.v3))) WideVec = TypeSet(lanes=(2, 256), ints=(8, 64), floats=(32, 64), bools=(1, 64)) self.check_yo_check(x, typeset_check(self.v1, WideVec)) def test_vselect_imm(self): # type: () -> None ts = TypeSet(lanes=(2, 256), ints=True, floats=True, bools=(8, 64)) 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), ) x = XForm(r, r) tv2_exp = 'Some({}).map(|t: Type| -> t.as_bool())'\ .format(self.v2.get_typevar().name) tv3_exp = 'Some({}).map(|t: Type| -> t.as_bool())'\ .format(self.v3.get_typevar().name) self.check_yo_check( x, sequence(typeset_check(self.v3, ts), equiv_check(tv2_exp, tv3_exp))) def test_reduce_extend(self): # type: () -> None r = Rtl( self.v1 << uextend(self.v0), self.v2 << ireduce(self.v1), self.v3 << sextend(self.v2), ) x = XForm(r, r) tv0_exp = 'Some({})'.format(self.v0.get_typevar().name) tv1_exp = 'Some({})'.format(self.v1.get_typevar().name) tv2_exp = 'Some({})'.format(self.v2.get_typevar().name) tv3_exp = 'Some({})'.format(self.v3.get_typevar().name) self.check_yo_check( x, sequence(wider_check(tv1_exp, tv0_exp), wider_check(tv1_exp, tv2_exp), wider_check(tv3_exp, tv2_exp))) def test_demote_promote(self): # type: () -> None r = Rtl( self.v1 << fpromote(self.v0), self.v2 << fdemote(self.v1), self.v3 << fpromote(self.v2), ) x = XForm(r, r) tv0_exp = 'Some({})'.format(self.v0.get_typevar().name) tv1_exp = 'Some({})'.format(self.v1.get_typevar().name) tv2_exp = 'Some({})'.format(self.v2.get_typevar().name) tv3_exp = 'Some({})'.format(self.v3.get_typevar().name) self.check_yo_check( x, sequence(wider_check(tv1_exp, tv0_exp), wider_check(tv1_exp, tv2_exp), wider_check(tv3_exp, tv2_exp)))
the primitives. """ from __future__ import absolute_import from cdsl.operands import Operand from cdsl.typevar import TypeVar from cdsl.instructions import Instruction, InstructionGroup from base.types import b1 from base.immediates import imm64 from cdsl.ast import Var from cdsl.xform import Rtl from semantics.primitives import bv_from_imm64, bvite import base.formats # noqa GROUP = InstructionGroup("primitive_macros", "Semantic macros instruction set") AnyBV = TypeVar('AnyBV', bitvecs=True, doc="") x = Var('x') y = Var('y') imm = Var('imm') a = Var('a') # # Bool-to-bv1 # BV1 = TypeVar("BV1", bitvecs=(1, 1), doc="") bv1_op = Operand('bv1_op', BV1, doc="") cond_op = Operand("cond", b1, doc="") bool2bv = Instruction('bool2bv', r"""Convert a b1 value to a 1-bit BV""", ins=cond_op, outs=bv1_op)
# TODO: Add sufficient XForm syntax that we don't need to hand-code these. expand.custom_legalize(insts.trapz, 'expand_cond_trap') expand.custom_legalize(insts.trapnz, 'expand_cond_trap') expand.custom_legalize(insts.br_table, 'expand_br_table') expand.custom_legalize(insts.select, 'expand_select') # Custom expansions for floating point constants. # These expansions require bit-casting or creating constant pool entries. expand.custom_legalize(insts.f32const, 'expand_fconst') expand.custom_legalize(insts.f64const, 'expand_fconst') # Custom expansions for stack memory accesses. expand.custom_legalize(insts.stack_load, 'expand_stack_load') expand.custom_legalize(insts.stack_store, 'expand_stack_store') x = Var('x') y = Var('y') a = Var('a') a1 = Var('a1') a2 = Var('a2') b = Var('b') b1 = Var('b1') b2 = Var('b2') b_in = Var('b_in') b_int = Var('b_int') c = Var('c') c1 = Var('c1') c2 = Var('c2') c_in = Var('c_in') c_int = Var('c_int') d = Var('d')
from base.immediates import imm64, intcc, floatcc from base import legalize as shared from base import instructions as insts from . import instructions as x86 from .defs import ISA x86_expand = XFormGroup('x86_expand', """ Legalize instructions by expansion. Use x86-specific instructions if needed. """, isa=ISA, chain=shared.expand_flags) a = Var('a') dead = Var('dead') x = Var('x') xhi = Var('xhi') y = Var('y') a1 = Var('a1') a2 = Var('a2') # # Division and remainder. # # The srem expansion requires custom code because srem INT_MIN, -1 is not # allowed to trap. The other ops need to check avoid_div_traps. x86_expand.custom_legalize(insts.sdiv, 'expand_sdivrem') x86_expand.custom_legalize(insts.srem, 'expand_sdivrem') x86_expand.custom_legalize(insts.udiv, 'expand_udivrem')
class TestRuntimeChecks(TestCase): def setUp(self): # type: () -> None 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_nonscalar", "", 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) def check_yo_check(self, xform, expected_f): # type: (XForm, CheckProducer) -> None fmt = Formatter() type_sets = UniqueTable() for check in get_runtime_typechecks(xform): emit_runtime_typecheck(check, fmt, type_sets) # Remove comments got = "".join([l for l in fmt.lines if not l.strip().startswith("//")]) expected = expected_f(type_sets) self.assertEqual(got, expected) def test_width_check(self): # type: () -> None x = XForm( Rtl(self.v0 << copy(self.v1)), Rtl((self.v2, self.v3) << isplit(self.v1), self.v0 << iconcat(self.v2, self.v3))) WideInt = TypeSet(lanes=(1, 256), ints=(16, 64)) self.check_yo_check(x, typeset_check(self.v1, WideInt)) def test_lanes_check(self): # type: () -> None x = XForm( Rtl(self.v0 << copy(self.v1)), Rtl((self.v2, self.v3) << vsplit(self.v1), self.v0 << vconcat(self.v2, self.v3))) WideVec = TypeSet(lanes=(2, 256), ints=(8, 64), floats=(32, 64), bools=(1, 64)) self.check_yo_check(x, typeset_check(self.v1, WideVec)) def test_vselect_imm(self): # type: () -> None ts = TypeSet(lanes=(2, 256), ints=True, floats=True, bools=(8, 64)) 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), ) x = XForm(r, r) tv2_exp = 'Some({}).map(|t: crate::ir::Type| t.as_bool())'\ .format(self.v2.get_typevar().name) tv3_exp = 'Some({}).map(|t: crate::ir::Type| t.as_bool())'\ .format(self.v3.get_typevar().name) self.check_yo_check( x, sequence(typeset_check(self.v3, ts), equiv_check(tv2_exp, tv3_exp))) def test_reduce_extend(self): # type: () -> None r = Rtl( self.v1 << uextend(self.v0), self.v2 << ireduce(self.v1), self.v3 << sextend(self.v2), ) x = XForm(r, r) tv0_exp = 'Some({})'.format(self.v0.get_typevar().name) tv1_exp = 'Some({})'.format(self.v1.get_typevar().name) tv2_exp = 'Some({})'.format(self.v2.get_typevar().name) tv3_exp = 'Some({})'.format(self.v3.get_typevar().name) self.check_yo_check( x, sequence(wider_check(tv1_exp, tv0_exp), wider_check(tv1_exp, tv2_exp), wider_check(tv3_exp, tv2_exp))) def test_demote_promote(self): # type: () -> None r = Rtl( self.v1 << fpromote(self.v0), self.v2 << fdemote(self.v1), self.v3 << fpromote(self.v2), ) x = XForm(r, r) tv0_exp = 'Some({})'.format(self.v0.get_typevar().name) tv1_exp = 'Some({})'.format(self.v1.get_typevar().name) tv2_exp = 'Some({})'.format(self.v2.get_typevar().name) tv3_exp = 'Some({})'.format(self.v3.get_typevar().name) self.check_yo_check( x, sequence(wider_check(tv1_exp, tv0_exp), wider_check(tv1_exp, tv2_exp), wider_check(tv3_exp, tv2_exp)))
def equivalent(r1, r2, inp_m, out_m): # type: (Rtl, Rtl, VarAtomMap, VarAtomMap) -> List[ExprRef] """ Given: - concrete source Rtl r1 - concrete dest Rtl r2 - VarAtomMap inp_m mapping r1's non-bitvector inputs to r2 - VarAtomMap out_m mapping r1's non-bitvector outputs to r2 Build a query checking whether r1 and r2 are semantically equivalent. If the returned query is unsatisfiable, then r1 and r2 are equivalent. Otherwise, the satisfying example for the query gives us values for which the two Rtls disagree. """ # Sanity - inp_m is a bijection from the set of inputs of r1 to the set of # inputs of r2 assert set(r1.free_vars()) == set(inp_m.keys()) assert set(r2.free_vars()) == set(inp_m.values()) # Note that the same rule is not expected to hold for out_m due to # temporaries/intermediates. out_m specified which values are enough for # equivalence. # Rename the vars in r1 and r2 with unique suffixes to avoid conflicts src_m = {v: Var(v.name + ".a", v.get_typevar()) for v in r1.vars()} # type: VarAtomMap # noqa dst_m = {v: Var(v.name + ".b", v.get_typevar()) for v in r2.vars()} # type: VarAtomMap # noqa r1 = r1.copy(src_m) r2 = r2.copy(dst_m) def _translate(m, k_m, v_m): # type: (VarAtomMap, VarAtomMap, VarAtomMap) -> VarAtomMap """Obtain a new map from m, by mapping m's keys with k_m and m's values with v_m""" res = {} # type: VarAtomMap for (k, v) in m1.items(): new_k = k_m[k] new_v = v_m[v] assert isinstance(new_k, Var) res[new_k] = new_v return res # Convert inp_m, out_m in terms of variables with the .a/.b suffixes inp_m = _translate(inp_m, src_m, dst_m) out_m = _translate(out_m, src_m, dst_m) # Encode r1 and r2 as SMT queries (q1, m1) = to_smt(r1) (q2, m2) = to_smt(r2) # Build an expression for the equality of real Cranelift inputs of # r1 and r2 args_eq_exp = [] # type: List[ExprRef] for (v1, v2) in inp_m.items(): assert isinstance(v2, Var) args_eq_exp.append(mk_eq(m1[v1], m2[v2])) # Build an expression for the equality of real Cranelift outputs of # r1 and r2 results_eq_exp = [] # type: List[ExprRef] for (v1, v2) in out_m.items(): assert isinstance(v2, Var) results_eq_exp.append(mk_eq(m1[v1], m2[v2])) # Put the whole query toghether return q1 + q2 + args_eq_exp + [Not(And(*results_eq_exp))]
# TODO: Add sufficient XForm syntax that we don't need to hand-code these. expand.custom_legalize(insts.trapz, 'expand_cond_trap') expand.custom_legalize(insts.trapnz, 'expand_cond_trap') expand.custom_legalize(insts.br_table, 'expand_br_table') expand.custom_legalize(insts.select, 'expand_select') # Custom expansions for floating point constants. # These expansions require bit-casting or creating constant pool entries. expand.custom_legalize(insts.f32const, 'expand_fconst') expand.custom_legalize(insts.f64const, 'expand_fconst') # Custom expansions for stack memory accesses. expand.custom_legalize(insts.stack_load, 'expand_stack_load') expand.custom_legalize(insts.stack_store, 'expand_stack_store') x = Var('x') y = Var('y') z = Var('z') a = Var('a') a1 = Var('a1') a2 = Var('a2') a3 = Var('a3') a4 = Var('a4') b = Var('b') b1 = Var('b1') b2 = Var('b2') b3 = Var('b3') b4 = Var('b4') b_in = Var('b_in') b_int = Var('b_int') c = Var('c')
def test_elaborate_iadd_cout_simple(self): # type: () -> None x = Var('x') y = Var('y') a = Var('a') c_out = Var('c_out') bvc_out = Var('bvc_out') bc_out = Var('bc_out') bvx = Var('bvx') bvy = Var('bvy') bva = Var('bva') bvone = Var('bvone') bvzero = Var('bvzero') r = Rtl( (a, c_out) << iadd_cout.i32(x, y), ) r.cleanup_concrete_rtl() sem = elaborate(r) exp = Rtl( bvx << prim_to_bv.i32(x), bvy << prim_to_bv.i32(y), bva << bvadd.bv32(bvx, bvy), bc_out << bvult.bv32(bva, bvx), bvone << bv_from_imm64(imm64(1)), bvzero << bv_from_imm64(imm64(0)), bvc_out << bvite(bc_out, bvone, bvzero), a << prim_from_bv.i32(bva), c_out << prim_from_bv.b1(bvc_out) ) exp.cleanup_concrete_rtl() assert concrete_rtls_eq(sem, exp)
def test_elaborate_iadd_elaborate_2(self): # type: () -> None i8.by(4) # Make sure i32x2 exists. r = Rtl( self.v0 << iadd.i8x4(self.v1, self.v2), ) r.cleanup_concrete_rtl() sem = elaborate(r) x = Var('x') y = Var('y') a = Var('a') bvx_1 = Var('bvx_1') bvx_2 = Var('bvx_2') bvx_5 = Var('bvx_5') bvx_10 = Var('bvx_10') bvx_15 = Var('bvx_15') bvlo_1 = Var('bvlo_1') bvlo_2 = Var('bvlo_2') bvlo_6 = Var('bvlo_6') bvlo_7 = Var('bvlo_7') bvlo_11 = Var('bvlo_11') bvlo_12 = Var('bvlo_12') bvhi_1 = Var('bvhi_1') bvhi_2 = Var('bvhi_2') bvhi_6 = Var('bvhi_6') bvhi_7 = Var('bvhi_7') bvhi_11 = Var('bvhi_11') bvhi_12 = Var('bvhi_12') bva_8 = Var('bva_8') bva_9 = Var('bva_9') bva_13 = Var('bva_13') bva_14 = Var('bva_14') exp = Rtl( bvx_1 << prim_to_bv.i8x4(x), (bvlo_1, bvhi_1) << bvsplit.bv32(bvx_1), bvx_2 << prim_to_bv.i8x4(y), (bvlo_2, bvhi_2) << bvsplit.bv32(bvx_2), (bvlo_6, bvhi_6) << bvsplit.bv16(bvlo_1), (bvlo_7, bvhi_7) << bvsplit.bv16(bvlo_2), bva_8 << bvadd.bv8(bvlo_6, bvlo_7), bva_9 << bvadd.bv8(bvhi_6, bvhi_7), bvx_10 << bvconcat.bv8(bva_8, bva_9), (bvlo_11, bvhi_11) << bvsplit.bv16(bvhi_1), (bvlo_12, bvhi_12) << bvsplit.bv16(bvhi_2), bva_13 << bvadd.bv8(bvlo_11, bvlo_12), bva_14 << bvadd.bv8(bvhi_11, bvhi_12), bvx_15 << bvconcat.bv8(bva_13, bva_14), bvx_5 << bvconcat.bv16(bvx_10, bvx_15), a << prim_from_bv.i8x4(bvx_5) ) exp.cleanup_concrete_rtl() assert concrete_rtls_eq(sem, exp)
def test_elaborate_iadd_elaborate_1(self): # type: () -> None i32.by(2) # Make sure i32x2 exists. r = Rtl( self.v0 << iadd.i32x2(self.v1, self.v2), ) r.cleanup_concrete_rtl() sem = elaborate(r) x = Var('x') y = Var('y') a = Var('a') bvx_1 = Var('bvx_1') bvx_2 = Var('bvx_2') bvx_5 = Var('bvx_5') bvlo_1 = Var('bvlo_1') bvlo_2 = Var('bvlo_2') bvhi_1 = Var('bvhi_1') bvhi_2 = Var('bvhi_2') bva_3 = Var('bva_3') bva_4 = Var('bva_4') exp = Rtl( bvx_1 << prim_to_bv.i32x2(x), (bvlo_1, bvhi_1) << bvsplit.bv64(bvx_1), bvx_2 << prim_to_bv.i32x2(y), (bvlo_2, bvhi_2) << bvsplit.bv64(bvx_2), bva_3 << bvadd.bv32(bvlo_1, bvlo_2), bva_4 << bvadd.bv32(bvhi_1, bvhi_2), bvx_5 << bvconcat.bv32(bva_3, bva_4), a << prim_from_bv.i32x2(bvx_5) ) exp.cleanup_concrete_rtl() assert concrete_rtls_eq(sem, exp)