Exemple #1
0
 def codegen(self, e : Exp, concretization_functions : { str : Exp }, out : EVar) -> Stm:
     if isinstance(e, EMakeMinHeap) or isinstance(e, EMakeMaxHeap):
         out_raw = EVar(out.id).with_type(self.rep_type(e.type))
         l = fresh_var(INT, "alloc_len")
         x = fresh_var(e.type.elem_type, "x")
         return seq([
             SDecl(l.id, ELen(e.e)),
             SArrayAlloc(out_raw, l),
             SCall(out, "add_all", (ZERO, e.e))])
     elif isinstance(e, EHeapElems):
         elem_type = e.type.t
         if isinstance(e.e, EMakeMinHeap) or isinstance(e.e, EMakeMaxHeap):
             x = fresh_var(elem_type, "x")
             return SForEach(x, e.e.e, SCall(out, "add", (x,)))
         i = fresh_var(INT, "i")
         return seq([
             SDecl(i.id, ZERO),
             SWhile(ELt(i, EArrayLen(e.e).with_type(INT)), seq([
                 SCall(out, "add", (EArrayGet(e.e, i).with_type(elem_type),)),
                 SAssign(i, EBinOp(i, "+", ONE).with_type(INT))]))])
     elif isinstance(e, EHeapPeek):
         raise NotImplementedError()
     elif isinstance(e, EHeapPeek2):
         from cozy.evaluation import construct_value
         best = EArgMin if isinstance(e.e.type, TMinHeap) else EArgMax
         f = heap_func(e.e, concretization_functions)
         return SSwitch(e.n, (
             (ZERO, SAssign(out, construct_value(e.type))),
             (ONE,  SAssign(out, construct_value(e.type))),
             (TWO,  SAssign(out, EArrayGet(e.e, ONE).with_type(e.type)))),
             SAssign(out, best(EBinOp(ESingleton(EArrayGet(e.e, ONE).with_type(e.type)).with_type(TBag(out.type)), "+", ESingleton(EArrayGet(e.e, TWO).with_type(e.type)).with_type(TBag(out.type))).with_type(TBag(out.type)), f).with_type(out.type)))
     else:
         raise NotImplementedError(e)
Exemple #2
0
def optimized_distinct(xs, args):
    if isinstance(xs, EEmptyList) or isinstance(xs, ESingleton):
        yield xs
        return
    if isinstance(xs, EStateVar):
        yield EStateVar(EUnaryOp(UOp.Distinct,
                                 xs.e).with_type(xs.type)).with_type(xs.type)
    if isinstance(xs, EBinOp):
        if xs.op == "+":
            v = fresh_var(xs.type.elem_type)
            for a in optimized_distinct(xs.e1, args):
                for b in optimized_distinct(xs.e2, args):
                    for b_prime in optimized_filter(
                            b, ELambda(v, ENot(optimized_in(v, a))), args):
                        yield EBinOp(a, "+", b_prime).with_type(xs.type)
        if xs.op == "-":
            v = fresh_var(xs.type.elem_type)
            for a in optimized_distinct(xs.e1, args):
                for b in optimized_distinct(xs.e2, args):
                    yield from optimized_filter(
                        a, ELambda(v, ENot(optimized_in(v, b))), args)
    if isinstance(xs, EFilter):
        for ee in optimized_distinct(xs.e, args):
            yield EFilter(ee, xs.predicate).with_type(xs.type)
    yield EUnaryOp(UOp.Distinct, xs).with_type(xs.type)
Exemple #3
0
def uses_intrusive_data(e: target_syntax.Exp,
                        handle: target_syntax.Exp) -> target_syntax.Exp:
    if isinstance(e, target_syntax.EMakeMap):
        if isinstance(e.e.type,
                      target_syntax.TBag) and e.e.type.t == handle.type:
            k = e.key.apply_to(handle)
            kk = syntax_tools.fresh_var(k.type, "k")
            return uses_intrusive_data(
                e.value.apply_to(
                    target_syntax.EFilter(
                        e.e,
                        target_syntax.ELambda(handle,
                                              syntax_tools.equal(k, kk)))),
                handle)
        return target_syntax.F
    elif isinstance(e, target_syntax.EMakeMap2):
        if e.e.type.t == handle.type:
            k = syntax_tools.fresh_var(e.type.k)
            return target_syntax.EImplies(
                target_syntax.EBinOp(k, target_syntax.BOp.In, e.e),
                uses_intrusive_data(e.value.apply_to(k), handle))
        return target_syntax.F
    elif isinstance(e, target_syntax.EFilter):
        return target_syntax.EAll(
            [uses_intrusive_data(e.e, handle),
             e.p.apply_to(handle)])
    elif isinstance(e, target_syntax.EEmptyList):
        return target_syntax.F
    elif isinstance(e, target_syntax.EMap):
        return uses_intrusive_data(e.e, handle)
    elif isinstance(e, target_syntax.EUnaryOp):
        return uses_intrusive_data(e.e, handle)
    elif isinstance(e, target_syntax.EBinOp):
        return uses_intrusive_data(e.e1, handle) or uses_intrusive_data(
            e.e2, handle)
    elif isinstance(e, target_syntax.ECond):
        return target_syntax.ECond(e.cond,
                                   uses_intrusive_data(e.then_branch, handle),
                                   uses_intrusive_data(e.else_branch,
                                                       handle)).with_type(
                                                           target_syntax.BOOL)
    elif isinstance(e, target_syntax.ESingleton):
        if e.type.t == handle.type:
            return target_syntax.EEq(e.e, handle)
        return target_syntax.F
    elif isinstance(e, target_syntax.ETuple):
        return target_syntax.EAny(
            uses_intrusive_data(ee, handle) for ee in e.es)
    elif isinstance(e, target_syntax.EVar):
        if isinstance(e.type, target_syntax.TBag) and e.type.t == handle.type:
            return target_syntax.EBinOp(handle, target_syntax.BOp.In,
                                        e).with_type(target_syntax.BOOL)
        return target_syntax.F
    elif type(e) in [
            target_syntax.ENum, target_syntax.EBool, target_syntax.EEnumEntry
    ]:
        return target_syntax.F
    else:
        raise NotImplementedError(e)
Exemple #4
0
 def test_edeepin(self):
     ht = THandle("H", INT)
     hb = EVar("hb").with_type(TBag(ht))
     h = fresh_var(ht, omit=free_vars(hb))
     arg = fresh_var(ht, omit=free_vars(h)|free_vars(hb))
     f1 = EDeepIn(h, hb)
     f2 = EUnaryOp(UOp.Any, EMap(hb, ELambda(arg, EBinOp(arg, "===", h).with_type(BOOL))).with_type(BOOL_BAG)).with_type(BOOL)
     self.assert_same(f1, f2)
Exemple #5
0
 def test_edeepin(self):
     ht = THandle("H", INT)
     hb = EVar("hb").with_type(TBag(ht))
     h = fresh_var(ht, omit=free_vars(hb))
     arg = fresh_var(ht, omit=free_vars(h)|free_vars(hb))
     f1 = EDeepIn(h, hb)
     f2 = EUnaryOp(UOp.Any, EMap(hb, ELambda(arg, EBinOp(arg, "===", h).with_type(BOOL))).with_type(BOOL_BAG)).with_type(BOOL)
     self.assert_same(f1, f2)
Exemple #6
0
 def visit_EMakeMap2(self, e):
     res = fresh_var(e.type, "map")
     self.stms.append(SDecl(res, EEmptyMap().with_type(e.type)))
     k = e.value_function.arg
     v = fresh_var(e.type.v, "v")
     self.stms.append(simplify_and_optimize(SForEach(k, e.e,
         SMapUpdate(res, k, v,
             SAssign(v, e.value_function.body)))))
     return EMove(res).with_type(res.type)
Exemple #7
0
    def test_distinct_foreach(self):
        with io.StringIO() as f:
            codgen = CxxPrinter(out=f)

            bag = EFilter(EVar("v").with_type(TBag(INT)), mk_lambda(INT, lambda x: EBinOp(x, ">", ZERO))).with_type(TBag(INT))
            x = fresh_var(INT)
            v = fresh_var(INT)
            stm = SForEach(x, EUnaryOp(UOp.Distinct, bag).with_type(TSet(INT)), SAssign(v, x))
            codgen.visit(stm)
Exemple #8
0
 def find_one(self, iterable):
     v = fresh_var(iterable.type.elem_type, "v")
     label = fresh_name("label")
     x = fresh_var(iterable.type.elem_type, "x")
     decl = SDecl(v, evaluation.construct_value(v.type))
     find = SEscapableBlock(label,
         SForEach(x, iterable, seq([
             SAssign(v, x),
             SEscapeBlock(label)])))
     self.stms.append(simplify_and_optimize(seq([decl, find])))
     return v
Exemple #9
0
    def visit_EUnaryOp(self, e):
        op = e.op
        if op == UOp.Distinct:
            return self.visit_iterable(e)
        elif op == UOp.The:
            return self.find_one(e.e)
        elif op == UOp.Sum:
            sum_var = fresh_var(e.type, "sum")
            loop_var = fresh_var(e.e.type.elem_type, "x")
            self.stms.append(simplify_and_optimize(seq([
                SDecl(sum_var, ENum(0).with_type(e.type)),
                SForEach(loop_var, e.e,
                    SAssign(sum_var, EBinOp(sum_var, "+", loop_var).with_type(INT)))])))
            return sum_var
        elif op == UOp.Length:
            arg = EVar("x").with_type(e.e.type.elem_type)
            return self.visit(EUnaryOp(UOp.Sum, EMap(e.e, ELambda(arg, ONE)).with_type(INT_BAG)).with_type(INT))
        elif op == UOp.All:
            arg = EVar("x").with_type(e.e.type.elem_type)
            return self.visit(EUnaryOp(UOp.Empty, EFilter(e.e, ELambda(arg, ENot(arg))).with_type(INT_BAG)).with_type(INT))
        elif op == UOp.Any:
            arg = EVar("x").with_type(e.e.type.elem_type)
            return self.visit(EUnaryOp(UOp.Exists, EFilter(e.e, ELambda(arg, arg)).with_type(INT_BAG)).with_type(INT))
        elif op == UOp.Empty:
            iterable = e.e
            v = fresh_var(BOOL, "v")
            label = fresh_name("label")
            x = fresh_var(iterable.type.elem_type, "x")
            decl = SDecl(v, ETRUE)
            find = SEscapableBlock(label,
                SForEach(x, iterable, seq([
                    SAssign(v, EFALSE),
                    SEscapeBlock(label)])))
            self.stms.append(simplify_and_optimize(seq([decl, find])))
            return v
        elif op == UOp.Exists:
            return self.visit(ENot(EUnaryOp(UOp.Empty, e.e).with_type(BOOL)))
        # elif op == UOp.AreUnique:
        #     s = fresh_var(TSet(e.e.type.elem_type), "unique_elems")
        #     u = fresh_var(BOOL, "is_unique")
        #     x = fresh_var(e.e.type.elem_type)
        #     label = fresh_name("label")
        #     self.visit(seq([
        #         SDecl(s, EEmptyList().with_type(s.type)),
        #         SDecl(u, ETRUE),
        #         SEscapableBlock(label,
        #             SForEach(x, e.e,
        #                 SIf(EEscape("{s}.find({x}) != {s}.end()", ("s", "x"), (s, x)).with_type(BOOL),
        #                     seq([SAssign(u, EFALSE), SEscapeBlock(label)]),
        #                     SEscape("{indent}{s}.insert({x});\n", ("s", "x"), (s, x)))))]))
        #     return u.id

        return self.visit_Exp(e)
Exemple #10
0
def storage_size(e, freebies: [Exp] = []):
    h = extension_handler(type(e.type))
    if h is not None:
        return h.storage_size(e, storage_size=storage_size)

    if e in freebies:
        return ZERO
    elif e.type == BOOL:
        return ONE
    elif is_numeric(e.type) or isinstance(e.type, THandle):
        return FOUR
    elif isinstance(e.type, TEnum):
        return TWO
    elif isinstance(e.type, TNative):
        return FOUR
    elif isinstance(e.type, TString):
        return TWENTY
    elif isinstance(e.type, TTuple):
        return ESum([
            storage_size(ETupleGet(e, n).with_type(t))
            for (n, t) in enumerate(e.type.ts)
        ])
    elif isinstance(e.type, TRecord):
        return ESum([
            storage_size(EGetField(e, f).with_type(t))
            for (f, t) in e.type.fields
        ])
    elif is_collection(e.type):
        v = fresh_var(e.type.t, omit=free_vars(e))
        return ESum([
            FOUR,
            EUnaryOp(UOp.Sum,
                     EMap(e, ELambda(
                         v,
                         storage_size(v))).with_type(INT_BAG)).with_type(INT)
        ])
    elif isinstance(e.type, TMap):
        k = fresh_var(e.type.k, omit=free_vars(e))
        return ESum([
            FOUR,
            EUnaryOp(
                UOp.Sum,
                EMap(
                    EMapKeys(e).with_type(TBag(e.type.k)),
                    ELambda(
                        k,
                        ESum([
                            storage_size(k),
                            storage_size(EMapGet(e, k).with_type(e.type.v))
                        ]))).with_type(INT_BAG)).with_type(INT)
        ])
    else:
        raise NotImplementedError(e.type)
Exemple #11
0
    def codegen(self, e : Exp, concretization_functions : { str : Exp }, out : EVar) -> Stm:
        """Return statements that write the result of `e` to `out`.

        The returned statements must declare the variable `out`; it will not be
        declared by the caller.

        This function also requires the `concretization_functions` that
        describe the invariants for variables in `e`.
        """
        if isinstance(e, EMakeMinHeap) or isinstance(e, EMakeMaxHeap):
            assert out.type == self.rep_type(e.type)
            elem_type = e.type.elem_type
            extended_concretization_functions = dict(concretization_functions)
            extended_concretization_functions[out.id] = e
            dummy_out = EVar(out.id).with_type(e.type)
            a = fresh_var(TArray(elem_type), "heap_elems")
            return seq([
                SArrayAlloc(a, ZERO),
                SDecl(out, ETuple((ZERO, a)).with_type(out.type)),
                self.implement_stmt(SCall(dummy_out, "add_all", (ZERO, e.e)), extended_concretization_functions)])
        elif isinstance(e, EHeapElems):
            elem_type = e.type.elem_type
            if isinstance(e.e, EMakeMinHeap) or isinstance(e.e, EMakeMaxHeap):
                x = fresh_var(elem_type, "x")
                return seq([
                    SDecl(out, EEmptyList().with_type(out.type)),
                    SForEach(x, e.e.e, SCall(out, "add", (x,)))])
            i = fresh_var(INT, "i") # the array index
            return seq([
                SDecl(out, EEmptyList().with_type(out.type)),
                SDecl(i, ZERO),
                SWhile(ELt(i, ETupleGet(e.e, 0).with_type(INT)), seq([
                    SCall(out, "add", (EArrayGet(ETupleGet(e.e, 1), i).with_type(elem_type),)),
                    SAssign(i, EBinOp(i, "+", ONE).with_type(INT))]))])
        elif isinstance(e, EHeapPeek):
            raise NotImplementedError()
        elif isinstance(e, EHeapPeek2):
            from cozy.evaluation import construct_value
            best = EArgMin if isinstance(e.e.type, TMinHeap) else EArgMax
            f = heap_func(e.e, concretization_functions)
            return seq([
                SDecl(out, construct_value(out.type)),
                SSwitch(ETupleGet(e.e, 0), (
                    (ZERO, SAssign(out, construct_value(e.type))),
                    (ONE,  SAssign(out, construct_value(e.type))),
                    (TWO,  SAssign(out, EArrayGet(ETupleGet(e.e, 1), ONE).with_type(e.type)))),
                    SAssign(out, best(EBinOp(ESingleton(EArrayGet(ETupleGet(e.e, 1), ONE).with_type(e.type)).with_type(TBag(out.type)), "+",
                                             ESingleton(EArrayGet(ETupleGet(e.e, 1), TWO).with_type(e.type)).with_type(TBag(out.type))).with_type(TBag(out.type)), f).with_type(out.type)))])
        else:
            raise NotImplementedError(e)
Exemple #12
0
    def codegen(self, e : Exp, concretization_functions : { str : Exp }, out : EVar) -> Stm:
        """Return statements that write the result of `e` to `out`.

        The returned statements must declare the variable `out`; it will not be
        declared by the caller.

        This function also requires the `concretization_functions` that
        describe the invariants for variables in `e`.
        """
        if isinstance(e, EMakeMinTreeMultiset) or isinstance(e, EMakeMaxTreeMultiset):
            assert out.type == self.rep_type(e.type)
            extended_concretization_functions = dict(concretization_functions)
            extended_concretization_functions[out.id] = e
            dummy_out = EVar(out.id).with_type(e.type)
            return seq([
                SDecl(out, None),
                self.implement_stmt(SCall(dummy_out, "add_all", (e.e,)), extended_concretization_functions)])
        elif isinstance(e, ETreeMultisetElems):
            elem_type = e.type.elem_type
            x = fresh_var(elem_type, "x")
            from cozy.syntax_tools import shallow_copy
            xs = shallow_copy(e.e).replace_type(e.type)
            return seq([
                SDecl(out, EEmptyList().with_type(out.type)),
                SForEach(x, xs, SCall(out, "add", (x,)))])
        elif isinstance(e, ETreeMultisetPeek):
            return SDecl(out, e)
        else:
            raise NotImplementedError(e)
Exemple #13
0
def optimized_sum(xs, args):
    elem_type = xs.type.elem_type
    if isinstance(xs, EStateVar):
        yield EStateVar(sum_of(strip_EStateVar(xs))).with_type(elem_type)
    if isinstance(xs, EBinOp) and xs.op == "+":
        for a in optimized_sum(xs.e1, args=args):
            for b in optimized_sum(xs.e2, args=args):
                yield EBinOp(a, "+", b).with_type(elem_type)
    if isinstance(xs, EBinOp) and xs.op == "-":
        arg = fresh_var(elem_type)
        for a in optimized_sum(xs.e1, args=args):
            for e2 in _simple_filter(xs.e2, ELambda(arg, optimized_in(arg, xs.e1)), args):
                for b in optimized_sum(e2, args=args):
                    yield EBinOp(a, "-", b).with_type(elem_type)
    x = excluded_element(xs, args)
    if x is not None:
        bag, x = x
        for s in optimized_sum(bag, args):
            yield EBinOp(s, "-", x).with_type(x.type)
    if isinstance(xs, ESingleton):
        yield xs.e
    if isinstance(xs, EFlatMap):
        f = xs.transform_function
        if isinstance(f.body, EBinOp) and f.body.op == "+":
            for e1 in optimized_flatmap(xs.e, ELambda(f.arg, f.body.e1), args):
                for e2 in optimized_flatmap(xs.e, ELambda(f.arg, f.body.e2), args):
                    for e in optimized_sum(EBinOp(e1, "+", e2).with_type(e1.type), args):
                        yield e

    yield sum_of(xs)
Exemple #14
0
    def mutate_in_place(self, lval, e, op, assumptions, invariants, make_subgoal):
        from cozy.state_maintenance import mutate

        old_value = e
        new_value = mutate(e, op)

        # added/removed elements
        t = TBag(lval.type.elem_type)
        old_elems = EHeapElems(old_value).with_type(t)
        new_elems = EHeapElems(new_value).with_type(t)
        initial_count = make_subgoal(ELen(old_elems))
        to_add = make_subgoal(EBinOp(new_elems, "-", old_elems).with_type(t), docstring="additions to {}".format(pprint(lval)))
        to_del_spec = EBinOp(old_elems, "-", new_elems).with_type(t)
        removed_count = make_subgoal(ELen(to_del_spec))
        to_del = make_subgoal(to_del_spec, docstring="deletions from {}".format(pprint(lval)))

        # modified elements
        f1 = heap_func(old_value)
        f2 = heap_func(new_value)
        v = fresh_var(t.elem_type)
        old_v_key = f1.apply_to(v)
        new_v_key = f2.apply_to(v)
        mod_spec = EFilter(old_elems, ELambda(v, EAll([EIn(v, new_elems), ENot(EEq(new_v_key, old_v_key))]))).with_type(new_elems.type)
        modified = make_subgoal(mod_spec)
        intermediate_count = make_subgoal(EBinOp(ELen(old_elems), "-", ELen(to_del_spec)).with_type(INT))
        return seq([
            SCall(lval, "remove_all", (initial_count, to_del)),
            SCall(lval, "add_all",    (intermediate_count, to_add)),
            SForEach(v, modified, SCall(lval, "update", (v, make_subgoal(new_v_key, a=[EIn(v, mod_spec)]))))])
Exemple #15
0
def reachable_handles_by_type(root : Exp) -> {THandle:Exp}:
    """
    Compute a mapping from handle types to bags of all handle objects of that
    type reachable from the given root.

    Note that the bags may contain duplicate handles.  This can happen in two
    ways:
     - there is a bag of handles reachable from the root that contains
       duplicate handles, or
     - the same handle is reachable from the root via two different paths
    """
    if isinstance(root.type, THandle):
        return _merge(
            { root.type : ESingleton(root).with_type(TBag(root.type)) },
            reachable_handles_by_type(EGetField(root, "val").with_type(root.type.value_type)))
    elif is_collection(root.type):
        v = fresh_var(root.type.elem_type)
        res = reachable_handles_by_type(v)
        for k, bag in list(res.items()):
            res[k] = EFlatMap(root, ELambda(v, bag)).with_type(bag.type)
        return res
    elif isinstance(root.type, TTuple):
        res = OrderedDict()
        for i, t in enumerate(root.type.ts):
            res = _merge(res, reachable_handles_by_type(ETupleGet(root, i).with_type(t)))
        return res
    elif isinstance(root.type, TRecord):
        res = OrderedDict()
        for f, t in root.type.fields:
            res = _merge(res, reachable_handles_by_type(EGetField(root, f).with_type(t)))
        return res
    elif isinstance(root.type, TMap):
        raise NotImplementedError()
    else:
        return OrderedDict()
Exemple #16
0
def reachable_values_of_type(root: Exp, t: Type) -> Exp:
    """
    Find all values of the given type reachable from the given root.
    """
    if root.type == t:
        return ESingleton(root).with_type(TBag(t))
    elif is_collection(root.type):
        v = fresh_var(root.type.t)
        res = reachable_values_of_type(v, t)
        return MkFlatMap(root, ELambda(v, res))
    elif isinstance(root.type, THandle):
        return reachable_values_of_type(
            EGetField(root, "val").with_type(root.type.value_type), t)
    elif isinstance(root.type, TTuple):
        sub = [
            reachable_values_of_type(ETupleGet(root, i).with_type(tt), t)
            for (i, tt) in enumerate(root.type.ts)
        ]
        return EUnion(sub, t)
    elif isinstance(root.type, TRecord):
        sub = [
            reachable_values_of_type(EGetField(root, f).with_type(ft), t)
            for (f, ft) in root.type.fields
        ]
        return EUnion(sub, t)
    elif isinstance(root.type, TMap):
        raise NotImplementedError()
    else:
        return EEmptyList().with_type(TBag(t))
Exemple #17
0
    def mutate_in_place(self, lval, e, op, assumptions, make_subgoal):
        from cozy.state_maintenance import mutate

        old_value = e
        new_value = mutate(e, op)

        # added/removed elements
        t = TBag(lval.type.elem_type)
        old_elems = EHeapElems(old_value).with_type(t)
        new_elems = EHeapElems(new_value).with_type(t)
        initial_count = make_subgoal(ELen(old_elems))
        to_add = make_subgoal(EBinOp(new_elems, "-", old_elems).with_type(t), docstring="additions to {}".format(pprint(lval)))
        to_del_spec = EBinOp(old_elems, "-", new_elems).with_type(t)
        removed_count = make_subgoal(ELen(to_del_spec))
        to_del = make_subgoal(to_del_spec, docstring="deletions from {}".format(pprint(lval)))

        # modified elements
        f1 = heap_func(old_value)
        f2 = heap_func(new_value)
        v = fresh_var(t.t)
        old_v_key = f1.apply_to(v)
        new_v_key = f2.apply_to(v)
        mod_spec = EFilter(old_elems, ELambda(v, EAll([EIn(v, new_elems), ENot(EEq(new_v_key, old_v_key))]))).with_type(new_elems.type)
        modified = make_subgoal(mod_spec)
        return seq([
            SCall(lval, "remove_all", (initial_count, to_del)),
            SCall(lval, "add_all",    (EBinOp(initial_count, "-", removed_count).with_type(INT), to_add)),
            SForEach(v, modified, SCall(lval, "update", (v, make_subgoal(new_v_key, a=[EIn(v, mod_spec)]))))])
Exemple #18
0
def histogram(e : Exp) -> (Stm, EVar):
    """Compute a histogram of the elements in the iterable `e`.

    Returns an unoptimized statement that declares and constructs a histogram
    map and the fresh variable that got declared.
    """
    elem_type = e.type.elem_type
    h = fresh_var(TMap(elem_type, INT), "histogram")
    x = fresh_var(elem_type, "x")
    count = fresh_var(INT, "count")
    stm = seq([
        SDecl(h, EEmptyMap().with_type(h.type)),
        SForEach(x, e,
            SMapUpdate(h, x, count,
                SAssign(count, EBinOp(count, "+", ONE).with_type(INT))))])
    return (stm, h)
Exemple #19
0
def pull_temps(s: Stm, decls_out: [SDecl], exp_is_bad) -> Stm:
    def pull(e: Exp) -> Exp:
        if exp_is_bad(e):
            v = fresh_var(e.type)
            decls_out.append(SDecl(v.id, e))
            return v
        return e

    if isinstance(s, SNoOp):
        return s
    if isinstance(s, SSeq):
        s1 = pull_temps(s.s1, decls_out, exp_is_bad)
        s2 = pull_temps(s.s2, decls_out, exp_is_bad)
        return SSeq(s1, s2)
    if isinstance(s, SIf):
        cond = pull(s.cond)
        s1 = pull_temps(s.then_branch, decls_out, exp_is_bad)
        s2 = pull_temps(s.else_branch, decls_out, exp_is_bad)
        return SIf(cond, s1, s2)
    if isinstance(s, SForEach):
        bag = pull(s.iter)
        d_tmp = []
        body = pull_temps(s.body, d_tmp, exp_is_bad)
        to_fix, ok = partition(d_tmp, lambda d: s.id in free_vars(d.val))
        decls_out.extend(ok)
        for d in to_fix:
            v = EVar(d.id).with_type(d.val.type)
            mt = TMap(s.id.type, v.type)
            m = EMakeMap2(bag, ELambda(s.id, d.val)).with_type(mt)
            mv = fresh_var(m.type)
            md = SDecl(mv.id, m)
            decls_out.append(md)
            body = subst(body, {v.id: EMapGet(mv, s.id).with_type(v.type)})
        return SForEach(s.id, bag, body)
    if isinstance(s, SAssign):
        return SAssign(s.lhs, pull(s.rhs))
    if isinstance(s, SCall):
        return SCall(s.target, s.func, tuple(pull(arg) for arg in s.args))
    if isinstance(s, SMapDel):
        return SMapDel(s.map, pull(s.key))
    if isinstance(s, SMapPut):
        return SMapPut(s.map, pull(s.key), pull(s.value))
    if isinstance(s, SMapUpdate):
        key = pull(s.key)
        d_tmp = []
        change = pull_temps(s.change, d_tmp, exp_is_bad)
        for d in d_tmp:
            if s.val_var in free_vars(d.val):
                decls_out.append(
                    SDecl(
                        d.id,
                        subst(
                            d.val, {
                                s.val_var.id:
                                EMapGet(s.map, key).with_type(s.val_var.type)
                            })))
            else:
                decls_out.append(d)
        return SMapUpdate(s.map, key, s.val_var, change)
    raise NotImplementedError(s)
Exemple #20
0
    def codegen(self, e: Exp, concretization_functions: {str: Exp},
                out: EVar) -> Stm:
        """Return statements that write the result of `e` to `out`.

        The returned statements must declare the variable `out`; it will not be
        declared by the caller.

        This function also requires the `concretization_functions` that
        describe the invariants for variables in `e`.
        """
        if isinstance(e, EMakeMinTreeMultiset) or isinstance(
                e, EMakeMaxTreeMultiset):
            assert out.type == self.rep_type(e.type)
            extended_concretization_functions = dict(concretization_functions)
            extended_concretization_functions[out.id] = e
            dummy_out = EVar(out.id).with_type(e.type)
            return seq([
                SDecl(out, None),
                self.implement_stmt(SCall(dummy_out, "add_all", (e.e, )),
                                    extended_concretization_functions)
            ])
        elif isinstance(e, ETreeMultisetElems):
            elem_type = e.type.elem_type
            x = fresh_var(elem_type, "x")
            from cozy.syntax_tools import shallow_copy
            xs = shallow_copy(e.e).replace_type(e.type)
            return seq([
                SDecl(out,
                      EEmptyList().with_type(out.type)),
                SForEach(x, xs, SCall(out, "add", (x, )))
            ])
        elif isinstance(e, ETreeMultisetPeek):
            return SDecl(out, e)
        else:
            raise NotImplementedError(e)
Exemple #21
0
def reachable_handles_by_type(root : Exp) -> {THandle:Exp}:
    """
    Compute a mapping from handle types to bags of all handle objects of that
    type reachable from the given root.

    Note that the bags may contain duplicate handles.  This can happen in two
    ways:
     - there is a bag of handles reachable from the root that contains
       duplicate handles, or
     - the same handle is reachable from the root via two different paths
    """
    if isinstance(root.type, THandle):
        return _merge(
            { root.type : ESingleton(root).with_type(TBag(root.type)) },
            reachable_handles_by_type(EGetField(root, "val").with_type(root.type.value_type)))
    elif is_collection(root.type):
        v = fresh_var(root.type.t)
        res = reachable_handles_by_type(v)
        for k, bag in list(res.items()):
            res[k] = EFlatMap(root, ELambda(v, bag)).with_type(bag.type)
        return res
    elif isinstance(root.type, TTuple):
        res = OrderedDict()
        for i, t in enumerate(root.type.ts):
            res = _merge(res, reachable_handles_by_type(ETupleGet(root, i).with_type(t)))
        return res
    elif isinstance(root.type, TRecord):
        res = OrderedDict()
        for f, t in root.type.fields:
            res = _merge(res, reachable_handles_by_type(EGetField(root, f).with_type(t)))
        return res
    elif isinstance(root.type, TMap):
        raise NotImplementedError()
    else:
        return OrderedDict()
Exemple #22
0
def EDeepIn(e1, e2):
    from cozy.syntax_tools import free_vars, fresh_var
    arg = fresh_var(e1.type, omit=free_vars(e1))
    return EUnaryOp(
        UOp.Any,
        EMap(e2, ELambda(arg,
                         EDeepEq(arg,
                                 e1))).with_type(BOOL_BAG)).with_type(BOOL)
Exemple #23
0
 def visit_EBinOp(self, e):
     if e.op in ("+", "-") and is_collection(e.type):
         return self.visit_iterable(e)
     elif e.op == BOp.In and not isinstance(e.e2.type, TSet):
         t = BOOL
         res = fresh_var(t, "found")
         x = fresh_var(e.e1.type, "x")
         label = fresh_name("label")
         self.stms.append(simplify_and_optimize(seq([
             SDecl(res, EFALSE),
             SEscapableBlock(label,
                 SForEach(x, e.e2, SIf(
                     EBinOp(x, "==", e.e1).with_type(BOOL),
                     seq([SAssign(res, ETRUE), SEscapeBlock(label)]),
                     SNoOp())))])))
         return res
     return self.visit_Exp(e)
Exemple #24
0
    def _setup_handle_updates(self):
        """
        This method creates update code for handle objects modified by each op.
        Must be called once after all user-specified queries have been added.
        """
        for op in self.op_specs:
            print("Setting up handle updates for {}...".format(op.name))
            handles = reachable_handles_at_method(self.spec, op)
            # print("-"*60)
            for t, bag in handles.items():
                # print("  {} : {}".format(pprint(t), pprint(bag)))
                h = fresh_var(t)
                lval = EGetField(h, "val").with_type(t.value_type)
                new_val = inc.mutate(lval, op.body)

                # get set of modified handles
                modified_handles = Query(
                    fresh_name("modified_handles"), Visibility.Internal, [],
                    op.assumptions,
                    EFilter(
                        EUnaryOp(UOp.Distinct, bag).with_type(bag.type),
                        ELambda(h, ENot(EEq(lval,
                                            new_val)))).with_type(bag.type),
                    "[{}] modified handles of type {}".format(
                        op.name, pprint(t)))
                query_vars = [
                    v for v in free_vars(modified_handles)
                    if v not in self.abstract_state
                ]
                modified_handles.args = [(arg.id, arg.type)
                                         for arg in query_vars]

                # modify each one
                subqueries = []
                state_update_stm = inc.mutate_in_place(
                    lval,
                    lval,
                    op.body,
                    abstract_state=self.abstract_state,
                    assumptions=list(op.assumptions) +
                    [EDeepIn(h, bag),
                     EIn(h, modified_handles.ret)],
                    invariants=self.abstract_invariants,
                    subgoals_out=subqueries)
                for sub_q in subqueries:
                    sub_q.docstring = "[{}] {}".format(op.name,
                                                       sub_q.docstring)
                    state_update_stm = self._add_subquery(
                        sub_q=sub_q, used_by=state_update_stm)
                if state_update_stm != SNoOp():
                    state_update_stm = SForEach(
                        h,
                        ECall(modified_handles.name,
                              query_vars).with_type(bag.type),
                        state_update_stm)
                    state_update_stm = self._add_subquery(
                        sub_q=modified_handles, used_by=state_update_stm)
                self.handle_updates[(t, op.name)] = state_update_stm
Exemple #25
0
 def visit_Exp(self, e):
     h = extension_handler(type(e))
     if h is not None:
         v = fresh_var(self.visit(e.type))
         new_stm = h.codegen(e, self.concretization_functions, out=v)
         return EStm(new_stm, v).with_type(v.type)
     e = self.visit_ADT(e)
     if hasattr(e, "type"):
         e = shallow_copy(e).with_type(self.visit(e.type))
     return e
Exemple #26
0
    def implement_stmt(self, s : Stm, concretization_functions : { str : Exp }) -> Stm:
        """Convert a call to a ordered function into simpler statements.

        This function also requires the `concretization_functions` that
        describe the invariants for variables in `e`.
        """
        if isinstance(s, SCall):
            elem_type = s.target.type.elem_type
            target = EVar(s.target.id).with_type(self.rep_type(s.target.type))
            if s.func == "add_all":
                x = fresh_var(elem_type, "x")
                return SForEach(x, s.args[0], SInsert(target, x))
            elif s.func == "remove_all":
                x = fresh_var(elem_type, "x")
                return SForEach(x, s.args[0], SErase(target, x))
            else:
                raise ValueError("ordereds do not support the function {}".format(s.func))
        else:
            raise ValueError("the statement {} is not an update to a ordered variable".format(pprint(s)))
Exemple #27
0
    def _setup_handle_updates(self):
        """
        This method creates update code for handle objects modified by each op.
        Must be called once after all user-specified queries have been added.
        """
        for op in self.op_specs:
            handles = reachable_handles_at_method(self.spec, op)
            # print("-"*60)
            for t, bag in handles.items():
                # print("  {} : {}".format(pprint(t), pprint(bag)))
                h = fresh_var(t)
                delta = inc.delta_form(
                    self.spec.statevars + op.args + [(h.id, h.type)], op)
                lval = EGetField(h, "val").with_type(t.value_type)
                new_val = simplify(subst(lval, delta))

                # get set of modified handles
                modified_handles = Query(
                    fresh_name("modified_handles"), Visibility.Internal, [],
                    op.assumptions,
                    EFilter(
                        EUnaryOp(UOp.Distinct, bag).with_type(bag.type),
                        ELambda(h, ENot(EEq(lval,
                                            new_val)))).with_type(bag.type),
                    "[{}] modified handles of type {}".format(
                        op.name, pprint(t)))
                query_vars = [
                    v for v in free_vars(modified_handles)
                    if v not in self.abstract_state
                ]
                modified_handles.args = [(arg.id, arg.type)
                                         for arg in query_vars]

                # modify each one
                (state_update_stm, subqueries) = inc.sketch_update(
                    lval, lval, new_val, self.abstract_state,
                    list(op.assumptions) +
                    [EDeepIn(h, bag),
                     EIn(h, modified_handles.ret)])
                # print("  got {} subqueries".format(len(subqueries)))
                # print("  to update {} in {}, use\n{}".format(pprint(t), op.name, pprint(state_update_stm)))
                for sub_q in subqueries:
                    sub_q.docstring = "[{}] {}".format(op.name,
                                                       sub_q.docstring)
                    state_update_stm = self._add_subquery(
                        sub_q=sub_q, used_by=state_update_stm)
                if state_update_stm != SNoOp():
                    state_update_stm = SForEach(
                        h,
                        ECall(modified_handles.name,
                              query_vars).with_type(bag.type),
                        state_update_stm)
                    state_update_stm = self._add_subquery(
                        sub_q=modified_handles, used_by=state_update_stm)
                self.handle_updates[(t, op.name)] = state_update_stm
Exemple #28
0
    def pull(e: Exp) -> Exp:
        """Pull an expression into a temporary.

        Creates a fresh variable for `e`, writes a declaration into `decls_out`,
        and returns the fresh variable.
        """
        if exp_is_bad(e):
            v = fresh_var(e.type)
            decls_out.append(SDecl(v, e))
            return v
        return e
Exemple #29
0
def ECountIn(e, collection):
    """Count the number of times e occurs in the collection"""
    from cozy.syntax_tools import free_vars, fresh_var
    assert e.type == collection.type.t
    arg = fresh_var(e.type, omit=free_vars(e))
    return EUnaryOp(
        UOp.Length,
        EFilter(collection,
                ELambda(arg,
                        EEq(arg,
                            e))).with_type(collection.type)).with_type(INT)
Exemple #30
0
    def pull(e : Exp) -> Exp:
        """Pull an expression into a temporary.

        Creates a fresh variable for `e`, writes a declaration into `decls_out`,
        and returns the fresh variable.
        """
        if exp_is_bad(e):
            v = fresh_var(e.type)
            decls_out.append(SDecl(v, e))
            return v
        return e
Exemple #31
0
 def min_or_max(self, op, e, f):
     if isinstance(e, EBinOp) and e.op == "+" and isinstance(e.e1, ESingleton) and isinstance(e.e2, ESingleton):
         # argmin_f ([a] + [b]) ---> f(a) < f(b) ? a : b
         return self.visit(ECond(
             EBinOp(f.apply_to(e.e1.e), op, f.apply_to(e.e2.e)).with_type(BOOL),
             e.e1.e,
             e.e2.e).with_type(e.e1.e.type))
     out = fresh_var(e.type.elem_type, "min" if op == "<" else "max")
     first = fresh_var(BOOL, "first")
     x = fresh_var(e.type.elem_type, "x")
     decl1 = SDecl(out, evaluation.construct_value(out.type))
     decl2 = SDecl(first, ETRUE)
     find = SForEach(x, e,
         SIf(EBinOp(
                 first,
                 BOp.Or,
                 EBinOp(f.apply_to(x), op, f.apply_to(out)).with_type(BOOL)).with_type(BOOL),
             seq([SAssign(first, EFALSE), SAssign(out, x)]),
             SNoOp()))
     self.stms.append(simplify_and_optimize(seq([decl1, decl2, find])))
     return out
Exemple #32
0
def compute_sharing(state_map: dict, true_types: dict) -> dict:
    """
    Takes a dictionary mapping { state_var_id : state_exp } and a
    dictionary mapping { state_var_id : refined_type } and returns
    a dictionary { ht : groups } for each handle type ht. Each group
    is a list of implementation types whose intrusive data will
    never be used at the same time.
    """

    types = set(t for e in state_map.values()
                for t in syntax_tools.all_types(e.type))
    handle_types = set(t for t in types
                       if isinstance(t, target_syntax.THandle))
    out = {}

    # for (var, exp) in state_map.items():
    #     print(" --> {} = {}".format(var, syntax_tools.pprint(exp)))

    for ht in handle_types:
        groups = []
        handle = syntax_tools.fresh_var(ht, "handle")
        # print(ht)
        # for (var, exp) in state_map.items():
        #     print(" --> {} iff {}".format(var, syntax_tools.pprint(uses_intrusive_data(exp, handle))))

        type_uses_intrusive_data = {}
        for (var, exp) in state_map.items():
            use = uses_intrusive_data(exp, handle)
            for t in syntax_tools.all_types(true_types[var]):
                # print(syntax_tools.pprint(t))
                if hasattr(t, "intrusive_data"):
                    type_uses_intrusive_data[t] = use
                # else:
                #     print("     no intrusive data for " + syntax_tools.pprint(t))

        # print(type_uses_intrusive_data)

        for t, cond in type_uses_intrusive_data.items():
            found = False
            for g in groups:
                if all(not solver.satisfy(
                        target_syntax.EAll([cond, type_uses_intrusive_data[t]
                                            ])) for t in g):
                    found = True
                    g.append(t)
                    break
            if not found:
                groups.append([t])

        # print("    --> {}".format(groups))
        out[ht] = groups

    return out
Exemple #33
0
 def compare_to(self, other, assumptions : Exp = T, solver : IncrementalSolver = None):
     assert isinstance(other, SymbolicCost)
     if False:
         s = IncrementalSolver()
         v1, v2 = fresh_var(BOOL), fresh_var(BOOL)
         s.add_assumption(EAll([
             self.order_cardinalities(other, assumptions, solver),
             EEq(v1, EBinOp(self.formula, "<=", other.formula).with_type(BOOL)),
             EEq(v2, EBinOp(other.formula, "<=", self.formula).with_type(BOOL))]))
         o1 = s.valid(v1)
         o2 = s.valid(v2)
     else:
         cards = self.order_cardinalities(other, assumptions, solver)
         o1 = self.always("<=", other, cards=cards)
         o2 = other.always("<=", self, cards=cards)
     if o1 and not o2:
         return Cost.BETTER
     elif o2 and not o1:
         return Cost.WORSE
     else:
         return Cost.UNORDERED
Exemple #34
0
 def compute_hash_1(self, hc: Exp, e : Exp) -> Stm:
     if is_scalar(e.type):
         return SAssign(hc, self.compute_hash_scalar(e))
     elif isinstance(e.type, TArray):
         x = fresh_var(e.type.elem_type, "x")
         s = SSeq(SAssign(hc, ZERO.with_type(hc.type)),
                  SForEach(x, e,
                      SAssign(hc, EEscape("({hc} * 31) ^ ({h})", ("hc", "h"),
                                          (hc, self.compute_hash_scalar(x))).with_type(INT))))
         return s
     else:
         raise NotImplementedError("can't compute hash for type {}".format(e.type))
Exemple #35
0
    def order_cardinalities(self, other, assumptions : Exp = T, solver : IncrementalSolver = None) -> Exp:
        if solver is None:
            solver = IncrementalSolver()
        if incremental:
            solver.push()
            solver.add_assumption(assumptions)

        cardinalities = OrderedDict()
        for m in (self.cardinalities, other.cardinalities):
            for k, v in m.items():
                cardinalities[v] = k

        conds = []
        res = []
        for (v1, c1) in cardinalities.items():
            res.append(EBinOp(v1, ">=", ZERO).with_type(BOOL))
            for (v2, c2) in cardinalities.items():
                if v1 == v2:
                    continue
                if alpha_equivalent(c1, c2):
                    res.append(EEq(v1, v2))
                    continue

                if incremental and use_indicators:
                    conds.append((v1, v2, fresh_var(BOOL), cardinality_le(c1, c2, as_f=True)))
                else:
                    if incremental:
                        le = cardinality_le(c1, c2, solver=solver)
                    else:
                        # print("CMP {}: {} / {}".format("<-" if v1 < v2 else "->", pprint(c1), pprint(c2)))
                        le = cardinality_le(c1, c2, assumptions=assumptions, solver=solver)
                    if le:
                        res.append(EBinOp(v1, "<=", v2).with_type(BOOL))

        if incremental and use_indicators:
            solver.add_assumption(EAll(
                [EEq(indicator, f) for (v1, v2, indicator, f) in conds]))
            for (v1, v2, indicator, f) in conds:
                if solver.valid(indicator):
                    res.append(EBinOp(v1, "<=", v2).with_type(BOOL))

        if incremental:
            solver.pop()

        if assume_large_cardinalities.value:
            min_cardinality = ENum(assume_large_cardinalities.value).with_type(INT)
            for cvar, exp in cardinalities.items():
                if isinstance(exp, EVar):
                    res.append(EBinOp(cvar, ">", min_cardinality).with_type(BOOL))

        # print("cards: {}".format(pprint(EAll(res))))
        return EAll(res)
Exemple #36
0
 def implement_stmt(self, s : Stm, concretization_functions : { str : Exp }) -> Stm:
     op = "<=" if isinstance(s.target.type, TMinHeap) else ">="
     f = heap_func(s.target, concretization_functions)
     if isinstance(s, SCall):
         elem_type = s.target.type.elem_type
         target_raw = EVar(s.target.id).with_type(self.rep_type(s.target.type))
         if s.func == "add_all":
             size = fresh_var(INT, "heap_size")
             i = fresh_var(INT, "i")
             x = fresh_var(elem_type, "x")
             return seq([
                 SDecl(size.id, s.args[0]),
                 SEnsureCapacity(target_raw, EBinOp(size, "+", ELen(s.args[1])).with_type(INT)),
                 SForEach(x, s.args[1], seq([
                     SAssign(EArrayGet(target_raw, size), x),
                     SDecl(i.id, size),
                     SWhile(EAll([
                         EBinOp(i, ">", ZERO).with_type(BOOL),
                         ENot(EBinOp(f.apply_to(EArrayGet(target_raw, _parent(i))), op, f.apply_to(EArrayGet(target_raw, i))).with_type(BOOL))]),
                         seq([
                             SSwap(EArrayGet(target_raw, _parent(i)), EArrayGet(target_raw, i)),
                             SAssign(i, _parent(i))])),
                     SAssign(size, EBinOp(size, "+", ONE).with_type(INT))]))])
         elif s.func == "remove_all":
             size = fresh_var(INT, "heap_size")
             size_minus_one = EBinOp(size, "-", ONE).with_type(INT)
             i = fresh_var(INT, "i")
             x = fresh_var(elem_type, "x")
             label = fresh_name("stop_bubble_down")
             child_index = fresh_var(INT, "child_index")
             return seq([
                 SDecl(size.id, s.args[0]),
                 SForEach(x, s.args[1], seq([
                     # find the element to remove
                     SDecl(i.id, EArrayIndexOf(target_raw, x).with_type(INT)),
                     # swap with last element in heap
                     SSwap(EArrayGet(target_raw, i), EArrayGet(target_raw, size_minus_one)),
                     # bubble down
                     SEscapableBlock(label, SWhile(_has_left_child(i, size_minus_one), seq([
                         SDecl(child_index.id, _left_child(i)),
                         SIf(EAll([_has_right_child(i, size_minus_one), ENot(EBinOp(f.apply_to(EArrayGet(target_raw, _left_child(i))), op, f.apply_to(EArrayGet(target_raw, _right_child(i)))))]),
                             SAssign(child_index, _right_child(i)),
                             SNoOp()),
                         SIf(ENot(EBinOp(f.apply_to(EArrayGet(target_raw, i)), op, f.apply_to(EArrayGet(target_raw, child_index)))),
                             seq([
                                 SSwap(EArrayGet(target_raw, i), EArrayGet(target_raw, child_index)),
                                 SAssign(i, child_index)]),
                             SEscapeBlock(label))]))),
                     # dec. size
                     SAssign(size, size_minus_one)]))])
         else:
             raise NotImplementedError()
     else:
         raise NotImplementedError(pprint(s))
Exemple #37
0
def can_elim_vars(spec: Exp, assumptions: Exp, vs: [EVar]):
    """Does any execution of `spec` actually depend on any of `vs`?

    It is possible for a variable to appear in an expression like `spec`
    without affecting its value.  This function uses the solver to
    determine whether any of the given variables can affect the output of
    `spec`.
    """
    spec = strip_EStateVar(spec)
    sub = {v.id: fresh_var(v.type) for v in vs}
    return valid(
        EImplies(EAll([assumptions, subst(assumptions, sub)]),
                 EEq(spec, subst(spec, sub))))
Exemple #38
0
def optimized_distinct(xs, args):
    if isinstance(xs, EEmptyList) or isinstance(xs, ESingleton):
        yield xs
        return
    if isinstance(xs, EStateVar):
        yield EStateVar(EUnaryOp(UOp.Distinct, xs.e).with_type(xs.type)).with_type(xs.type)
    if isinstance(xs, EBinOp):
        if xs.op == "+":
            v = fresh_var(xs.type.elem_type)
            for a in optimized_distinct(xs.e1, args):
                for b in optimized_distinct(xs.e2, args):
                    for b_prime in optimized_filter(b, ELambda(v, ENot(optimized_in(v, a))), args):
                        yield EBinOp(a, "+", b_prime).with_type(xs.type)
        if xs.op == "-":
            v = fresh_var(xs.type.elem_type)
            for a in optimized_distinct(xs.e1, args):
                for b in optimized_distinct(xs.e2, args):
                    yield from optimized_filter(a, ELambda(v, ENot(optimized_in(v, b))), args)
    if isinstance(xs, EFilter):
        for ee in optimized_distinct(xs.e, args):
            yield EFilter(ee, xs.predicate).with_type(xs.type)
    yield EUnaryOp(UOp.Distinct, xs).with_type(xs.type)
Exemple #39
0
def can_elim_vars(spec : Exp, assumptions : Exp, vs : [EVar]):
    """Does any execution of `spec` actually depend on any of `vs`?

    It is possible for a variable to appear in an expression like `spec`
    without affecting its value.  This function uses the solver to
    determine whether any of the given variables can affect the output of
    `spec`.
    """
    spec = strip_EStateVar(spec)
    sub = { v.id : fresh_var(v.type) for v in vs }
    return valid(EImplies(
        EAll([assumptions, subst(assumptions, sub)]),
        EEq(spec, subst(spec, sub))))
Exemple #40
0
 def impls(self, e: Exp, assumptions: Exp):
     ty = e.type
     if type(ty) is TMap:
         k = fresh_var(ty.k)
         for v in self.impls(
                 EMapGet(e, k).with_type(e.type.v), assumptions):
             if is_enumerable(ty.k):
                 yield TVectorMap(ty.k, v)
             else:
                 yield TNativeMap(ty.k, v)
     elif type(ty) is TSet or (type(ty) is TBag and valid(
             EImplies(assumptions,
                      EUnaryOp(UOp.AreUnique, e).with_type(BOOL)),
             model_callback=print)):
         if isinstance(ty.t, THandle):
             yield TIntrusiveLinkedList(ty.t)
         x = fresh_var(ty.t)
         for t in self.impls(x, EAll((assumptions, EIn(x, e)))):
             yield TNativeSet(t)
     elif type(ty) is TBag:
         x = fresh_var(ty.t)
         for t in self.impls(x, EAll((assumptions, EIn(x, e)))):
             yield TNativeList(t)
     elif type(ty) is TList:
         if isinstance(ty.t, THandle) and valid(EImplies(
                 assumptions,
                 EUnaryOp(UOp.AreUnique, e).with_type(BOOL)),
                                                model_callback=print):
             yield TIntrusiveLinkedList(ty.t)
         yield TNativeList(ty.t)
     elif type(ty) is TTuple:
         for refinements in cross_product([
                 self.impls(
                     ETupleGet(e, i).with_type(ty.ts[i]), assumptions)
                 for i in range(len(ty.ts))
         ]):
             yield TTuple(refinements)
     else:
         yield ty
Exemple #41
0
def heap_func(e : Exp, concretization_functions : { str : Exp } = None) -> ELambda:
    """
    Assuming 'e' produces a heap, this returns the function used to sort its elements.
    """
    if isinstance(e, EMakeMinHeap) or isinstance(e, EMakeMaxHeap):
        return e.key_function
    if isinstance(e, EVar) and concretization_functions:
        ee = concretization_functions.get(e.id)
        if ee is not None:
            return heap_func(ee)
    if isinstance(e, ECond):
        h1 = heap_func(e.then_branch)
        h2 = heap_func(e.else_branch)
        if alpha_equivalent(h1, h2):
            return h1
        v = fresh_var(h1.arg.type)
        return ELambda(v, ECond(e.cond, h1.apply_to(v), h2.apply_to(v)).with_type(h1.body.type))
    raise NotImplementedError(repr(e))
Exemple #42
0
def fold_into_map(e, context):
    fvs = free_vars(e)
    state_vars = [v for v, p in context.vars() if p == STATE_POOL]
    for subexp, subcontext, subpool in all_subexpressions_with_context_information(e, context, RUNTIME_POOL):
        if isinstance(subexp, EMapGet) and isinstance(subexp.map, EStateVar):
            map = subexp.map.e
            key = subexp.key
            key_type = key.type
            value_type = subexp.type
            # e is of the form `... EStateVar(map)[key] ...`
            arg = fresh_var(subexp.type, omit=fvs)
            func = ELambda(arg, replace(
                e, context, RUNTIME_POOL,
                subexp, subcontext, subpool,
                arg))
            if not all(v in state_vars for v in free_vars(func)):
                continue
            func = strip_EStateVar(func)
            new_map = map_values(map, func.apply_to)
            yield EMapGet(EStateVar(new_map).with_type(new_map.type), key).with_type(e.type)
Exemple #43
0
    def _setup_handle_updates(self):
        """
        This method creates update code for handle objects modified by each op.
        Must be called once after all user-specified queries have been added.
        """
        for op in self.op_specs:
            print("Setting up handle updates for {}...".format(op.name))
            handles = reachable_handles_at_method(self.spec, op)
            # print("-"*60)
            for t, bag in handles.items():
                # print("  {} : {}".format(pprint(t), pprint(bag)))
                h = fresh_var(t)
                lval = EGetField(h, "val").with_type(t.value_type)
                new_val = inc.mutate(lval, op.body)

                # get set of modified handles
                modified_handles = Query(
                    fresh_name("modified_handles"),
                    Visibility.Internal, [], op.assumptions,
                    EFilter(EUnaryOp(UOp.Distinct, bag).with_type(bag.type), ELambda(h, ENot(EEq(lval, new_val)))).with_type(bag.type),
                    "[{}] modified handles of type {}".format(op.name, pprint(t)))
                query_vars = [v for v in free_vars(modified_handles) if v not in self.abstract_state]
                modified_handles.args = [(arg.id, arg.type) for arg in query_vars]

                # modify each one
                subqueries = []
                state_update_stm = inc.mutate_in_place(
                    lval,
                    lval,
                    op.body,
                    abstract_state=self.abstract_state,
                    assumptions=list(op.assumptions) + [EDeepIn(h, bag), EIn(h, modified_handles.ret)],
                    invariants=self.abstract_invariants,
                    subgoals_out=subqueries)
                for sub_q in subqueries:
                    sub_q.docstring = "[{}] {}".format(op.name, sub_q.docstring)
                    state_update_stm = self._add_subquery(sub_q=sub_q, used_by=state_update_stm)
                if state_update_stm != SNoOp():
                    state_update_stm = SForEach(h, ECall(modified_handles.name, query_vars).with_type(bag.type), state_update_stm)
                    state_update_stm = self._add_subquery(sub_q=modified_handles, used_by=state_update_stm)
                self.handle_updates[(t, op.name)] = state_update_stm
Exemple #44
0
def optimized_sum(xs, args):
    elem_type = xs.type.elem_type
    if isinstance(xs, EStateVar):
        yield EStateVar(sum_of(xs)).with_type(elem_type)
    if isinstance(xs, EBinOp) and xs.op == "+":
        for a in optimized_sum(xs.e1, args=args):
            for b in optimized_sum(xs.e2, args=args):
                yield EBinOp(a, "+", b).with_type(elem_type)
    if isinstance(xs, EBinOp) and xs.op == "-":
        arg = fresh_var(elem_type)
        for a in optimized_sum(xs.e1, args=args):
            for e2 in _simple_filter(xs.e2, ELambda(arg, optimized_in(arg, xs.e1)), args):
                for b in optimized_sum(e2, args=args):
                    yield EBinOp(a, "-", b).with_type(elem_type)
    x = excluded_element(xs, args)
    if x is not None:
        bag, x = x
        for s in optimized_sum(bag, args):
            yield EBinOp(s, "-", x).with_type(x.type)
    if isinstance(xs, ESingleton):
        yield xs.e
    yield sum_of(xs)
Exemple #45
0
    def maintenance_cost(self,
            old_value           : Exp,
            new_value           : Exp,
            op                  : Op,
            storage_size,
            maintenance_cost,
            freebies            : [Exp] = []):
        assert type(e.type) in (TMinHeap, TMaxHeap)

        # added/removed elements
        t = TBag(e.type.elem_type)
        old_elems = EHeapElems(old_value).with_type(t)
        new_elems = EHeapElems(new_value).with_type(t)

        # Add these
        elems_added = storage_size(
            EBinOp(new_elems, "-", old_elems).with_type(t), freebies).with_type(INT)
        elems_rmved = storage_size(
            EBinOp(old_elems, "-", new_elems).with_type(t), freebies).with_type(INT)

        # modified elements
        f1 = heap_func(old_value)
        f2 = heap_func(new_value)
        v = fresh_var(t.elem_type)
        old_v_key = f1.apply_to(v)
        new_v_key = f2.apply_to(v)

        modified_elems = EFilter(old_elems, ELambda(v, EAll([EIn(v, new_elems), ENot(EEq(new_v_key, old_v_key))]))).with_type(new_elems.type)

        modified_cost = EUnaryOp(
            UOp.Sum,
            EMap(
                modified_elems,
                ELambda(
                    v,
                    maintenance_cost(
                        new_v_key, op, freebies)).with_type(INT)).with_type(INT)).with_type(INT_BAG)

        return ESum([elems_added, elems_rmved, modified_cost])
Exemple #46
0
 def visit_ELambda(self, e):
     if e.arg in fvs:
         v = fresh_var(e.arg.type, omit=fvs)
         e = syntax.ELambda(v, e.apply_to(v))
     return syntax.ELambda(e.arg, self.visit(e.body))
Exemple #47
0
 def visit_ELet(self, e):
     value_exp = self.visit(e.e)
     fv = fresh_var(value_exp.type, e.body_function.arg.id)
     self.stms.append(SDecl(fv, value_exp))
     return self.visit(subst(e.body_function.body, { e.body_function.arg.id : fv }))
Exemple #48
0
def _simple_filter(xs : Exp, p : ELambda, args : {EVar}):
    """Assumes the body of p is already in negation normal form"""
    if p.body == ETRUE:
        yield xs
        return
    if p.body == EFALSE:
        yield EEmptyList().with_type(xs.type)
        return
    if isinstance(xs, EEmptyList):
        yield xs
        return
    yielded = False
    if isinstance(xs, ESingleton):
        yielded = True
        yield optimized_cond(p.apply_to(xs.e), xs, EEmptyList().with_type(xs.type))
    if isinstance(p.body, EBinOp) and p.body.op == BOp.Or:
        for e1, e2 in itertools.permutations([p.body.e1, p.body.e2]):
            for r1 in _simple_filter(xs, ELambda(p.arg, e1), args):
                for r2 in _simple_filter(xs, ELambda(p.arg, EAll([e2, ENot(e1)])), args):
                    yielded = True
                    yield EBinOp(r1, "+", r2).with_type(xs.type)
    if isinstance(p.body, EBinOp) and p.body.op == BOp.And:
        for e1, e2 in itertools.permutations([p.body.e1, p.body.e2]):
            for r1 in _simple_filter(xs, ELambda(p.arg, e1), args):
                yielded = True
                yield from _simple_filter(r1, ELambda(p.arg, e2), args)
    if isinstance(xs, EStateVar) and not any(v in args for v in free_vars(p)):
        yielded = True
        yield EStateVar(EFilter(xs.e, strip_EStateVar(p)).with_type(xs.type)).with_type(xs.type)
    if isinstance(xs, EMapGet) and isinstance(xs.map, EStateVar) and not any(v in args for v in free_vars(p)):
        for m in map_values_multi(xs.map.e, lambda ys: _simple_filter(ys, p, args)):
            yielded = True
            yield EMapGet(EStateVar(m).with_type(m.type), xs.key).with_type(xs.type)
    if isinstance(xs, EBinOp) and xs.op in ("+", "-"):
        for e1 in _simple_filter(xs.e1, p, args):
            for e2 in _simple_filter(xs.e2, p, args):
                yielded = True
                yield EBinOp(e1, xs.op, e2).with_type(xs.type)
    if isinstance(p.body, EBinOp) and p.body.op == "==":
        e1 = p.body.e1
        e2 = p.body.e2
        fvs2 = free_vars(e2)
        fvs1 = free_vars(e1)
        for (e1, fvs1), (e2, fvs2) in itertools.permutations([(e1, fvs1), (e2, fvs2)]):
            if p.arg in fvs1 and not any(a in fvs1 for a in args) and p.arg not in fvs2 and isinstance(xs, EStateVar):
                if e1 == p.arg:
                    yield optimized_cond(
                        optimized_in(e2, xs),
                        ESingleton(e2).with_type(xs.type),
                        EEmptyList().with_type(xs.type))

                k = fresh_var(e1.type)
                e = EMapGet(
                    EStateVar(
                        EMakeMap2(
                            EMap(xs.e, ELambda(p.arg, e1)),
                            ELambda(k, EFilter(xs.e, ELambda(p.arg, EEq(e1, k)))))),
                    e2)
                res = retypecheck(e)
                assert res
                yielded = True
                yield e
    if not yielded:
        yield EFilter(xs, p).with_type(xs.type)
Exemple #49
0
 def visit_ECond(self, e):
     v = fresh_var(e.type, "conditional_result")
     self.stms.append(simplify_and_optimize(seq([
         SDecl(v, evaluation.construct_value(e.type)),
         SIf(e.cond, SAssign(v, e.then_branch), SAssign(v, e.else_branch))])))
     return v
Exemple #50
0
def stream(iterable : Exp, loop_var : EVar, body : Stm) -> Stm:
    """Convert an iterable expression to a streaming operation.

    Input:
      iterable - an expression with an iterable type (Bag, Set, or List), not
        yet optimized
      loop_var - a variable to use as the loop variable
      body - a statement to run on that variable, not yet optimized

    Output:
      A statement equivalent to
        for (loop_var in iterable) { body }
      that eliminates as many intermediate collections and objects as possible.

    NOTE: The output of function will not be correct if the body modifies any
    free variable in the iterable expression or writes to any pointers that
    are read by the iterable expression.

    Generating code for the expression

        Map {func} (Filter {predicate} big_collection)

    might create two new collections as large as `big_collection`: one to hold
    the result of the filter and one to hold the result of the map.  If all the
    code needs to do is to iterate over the result, then there is no reason to
    make the two new collections.

    This function is mutually recursive with `simplify_and_optimize`, so any
    transformations performed by that method are also applied to the output of
    this one.
    """

    if isinstance(iterable, EEmptyList):
        return SNoOp()
    elif isinstance(iterable, ESingleton):
        setup, value = simplify_and_optimize_expression(iterable.e)
        # SScoped because if the iterable is e.g. [x] + [y], then the body
        # might be appear in the same block twice.  If the body declares any
        # variables, that will cause problems in languages like Java or C++.
        return seq([setup, SScoped(re_use(value, loop_var, simplify_and_optimize(body)))])
    elif isinstance(iterable, ECond):
        cond_setup, cond = simplify_and_optimize_expression(iterable.cond)
        return seq([
            cond_setup,
            SIf(cond,
                stream(iterable.then_branch, loop_var, body),
                stream(iterable.else_branch, loop_var, body))])
    elif isinstance(iterable, EUnaryOp) and iterable.op == UOp.Distinct:
        tmp = fresh_var(TSet(iterable.type.elem_type), "distinct_elems")
        return seq([
            SDecl(tmp, EEmptyList().with_type(tmp.type)),
            stream(iterable.e, loop_var, SIf(
                ENot(EBinOp(loop_var, BOp.In, tmp).with_type(BOOL)),
                seq([body, SCall(tmp, "add", [loop_var])]),
                SNoOp()))])
    elif isinstance(iterable, EBinOp) and iterable.op == "+":
        return seq([
            stream(iterable.e1, loop_var, body),
            stream(iterable.e2, loop_var, body)])
    elif isinstance(iterable, EBinOp) and iterable.op == "-":
        if is_hashable(iterable.type.elem_type):
            h_setup, h = histogram(iterable.e2)
            val_ref = fresh_var(INT, "count")
            return seq([
                simplify_and_optimize(h_setup),
                stream(
                    iterable.e1,
                    loop_var,
                    SIf(EGt(EMapGet(h, loop_var).with_type(INT), ZERO),
                        SMapUpdate(h, loop_var, val_ref, SAssign(val_ref, EBinOp(val_ref, "-", ONE).with_type(INT))),
                        body))])
        else:
            rhs = fresh_var(iterable.e2.type, "bag_subtraction_right")
            return seq([
                simplify_and_optimize(SDecl(rhs, iterable.e2)),
                stream(
                    iterable.e1,
                    loop_var,
                    SIf(EIn(loop_var, rhs),
                        SCall(rhs, "remove", (loop_var,)),
                        body))])
    elif isinstance(iterable, EFilter):
        return stream(
            EFlatMap(iterable.e, ELambda(iterable.predicate.arg,
                ECond(iterable.predicate.body,
                    ESingleton(iterable.predicate.arg).with_type(iterable.type),
                    EEmptyList().with_type(iterable.type)).with_type(iterable.type))).with_type(iterable.type),
            loop_var,
            body)
    elif isinstance(iterable, EMap):
        return stream(
            EFlatMap(iterable.e, ELambda(iterable.transform_function.arg,
                ESingleton(iterable.transform_function.body).with_type(iterable.type))).with_type(iterable.type),
            loop_var,
            body)
    elif isinstance(iterable, EFlatMap):
        inner_loop_var = fresh_var(
            iterable.transform_function.arg.type,
            iterable.transform_function.arg.id)
        return stream(
            iterable.e,
            inner_loop_var,
            stream(iterable.transform_function.apply_to(inner_loop_var), loop_var, body))
    elif isinstance(iterable, EListSlice):
        elem_type = iterable.type.elem_type
        l = fresh_var(iterable.e.type, "list")
        s = fresh_var(INT, "start")
        e = fresh_var(INT, "end")
        return simplify_and_optimize(seq([
            SDecl(l, iterable.e),
            SDecl(s, max_of(iterable.start, ZERO)),
            SDecl(e, min_of(iterable.end, ELen(l))),
            SWhile(ELt(s, e), seq([
                SDecl(loop_var, EListGet(l, s).with_type(elem_type)),
                body,
                SAssign(s, EBinOp(s, "+", ONE).with_type(INT))]))]))
    elif isinstance(iterable, ELet):
        v = fresh_var(
            iterable.body_function.arg.type,
            iterable.body_function.arg.id)
        return seq([
            simplify_and_optimize(SDecl(v, iterable.e)),
            stream(iterable.body_function.apply_to(v), loop_var, body)])
    elif isinstance(iterable, EMove):
        return stream(iterable.e, loop_var, body)
    else:
        assert is_collection(iterable.type), repr(iterable)
        setup, e = simplify_and_optimize_expression(iterable)
        return seq([setup, SForEach(loop_var, e, simplify_and_optimize(body))])
Exemple #51
0
def EDeepIn(e1, e2):
    from cozy.syntax_tools import free_vars, fresh_var
    arg = fresh_var(e1.type, omit=free_vars(e1))
    return EUnaryOp(UOp.Any,
        EMap(e2, ELambda(arg,
            EDeepEq(arg, e1))).with_type(BOOL_BAG)).with_type(BOOL)
Exemple #52
0
def ECountIn(e, collection):
    """Count the number of times e occurs in the collection"""
    from cozy.syntax_tools import free_vars, fresh_var
    assert e.type == collection.type.elem_type
    arg = fresh_var(e.type, omit=free_vars(e))
    return EUnaryOp(UOp.Length, EFilter(collection, ELambda(arg, EEq(arg, e))).with_type(collection.type)).with_type(INT)
Exemple #53
0
 def visit_iterable(self, e):
     res = fresh_var(e.type)
     self.stms.append(SDecl(res, EEmptyList().with_type(e.type)))
     x = fresh_var(e.type.elem_type)
     self.stms.append(simplify_and_optimize(SForEach(x, e, SCall(res, "add", (x,)))))
     return EMove(res).with_type(res.type)