Example #1
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))
Example #2
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
Example #3
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)
Example #4
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)
Example #5
0
def simplify_and_optimize(s : Stm) -> Stm:
    """Simplify and optimize a statement.

    Input:
      s - a statement to optimize

    Output:
      A statement that is functionally equivalent to the input.

    This function makes two big transformations:
      - "compile" many kinds of expressions (listed below) to simpler forms so
        that downstream code generation has less work to do
      - avoid creating short-lived intermediate objects (see `stream`)

    Expression types eliminated by this procedure:
      - EMap, EFilter, EFlatMap
      - EArg{Min,Max}
      - unary ops:
        Distinct,
        AreUnique,
        Length, Sum, All, Any,
        Exists, Empty,
        The
      - binary ops:
        In (where the collection is a Bag or List)
        "-" on collections
        "+" on collections
      - EMakeMap2
      - ELet
      - EListSlice
      - EStm
    """
    assert isinstance(s, Stm)

    if isinstance(s, SNoOp):
        return s
    if isinstance(s, SSeq):
        # TODO: while the first basic statement in s1 is an SDecl, we should
        # apply `re_use` to perhaps eliminate or inline the declaration.
        return seq([simplify_and_optimize(s.s1), simplify_and_optimize(s.s2)])
    if isinstance(s, SAssign):
        setup, e = simplify_and_optimize_expression(s.rhs)
        return seq([setup, SAssign(s.lhs, e)])
    if isinstance(s, SReturn):
        setup, e = simplify_and_optimize_expression(s.e)
        return seq([setup, SReturn(e)])
    if isinstance(s, SDecl):
        setup, e = simplify_and_optimize_expression(s.val)
        return seq([setup, SDecl(s.var, e)])
    if isinstance(s, SForEach):
        return stream(s.iter, s.loop_var, s.body)
    if isinstance(s, SEscape):
        return s
    if isinstance(s, SIf):
        setup, test = simplify_and_optimize_expression(s.cond)
        if test == ETRUE:
            return simplify_and_optimize(s.then_branch)
        if test == EFALSE:
            return simplify_and_optimize(s.else_branch)
        return seq([setup, SIf(test, simplify_and_optimize(s.then_branch), simplify_and_optimize(s.else_branch))])
    if isinstance(s, SWhile):
        setup, cond = simplify_and_optimize_expression(s.e)
        if setup != SNoOp():
            # This is a problem because we don't want to duplicate the setup
            # condition.
            # TODO: introduce an SEscapableBlock/SEscapeBlock to do it right
            raise ValueError("oops! setup for condition {} is very long:\n{}".format(pprint(s.e), pprint(setup)))
        return SWhile(cond, simplify_and_optimize(s.body))
    if isinstance(s, SScoped):
        return SScoped(simplify_and_optimize(s.s))
    if isinstance(s, SMapUpdate):
        # TODO: optimize s.map & s.key
        # TODO: s.map must be optimized as an lvalue
        mapsetup, map = simplify_and_optimize_lvalue(s.map)
        keysetup, key = simplify_and_optimize_expression(s.key)
        return seq([
            mapsetup,
            keysetup,
            SMapUpdate(map, key, s.val_var, simplify_and_optimize(s.change))])
    if isinstance(s, SMapDel):
        mapsetup, map = simplify_and_optimize_lvalue(s.map)
        keysetup, key = simplify_and_optimize_expression(s.key)
        return seq([
            mapsetup,
            keysetup,
            SMapDel(map, key)])
    if isinstance(s, SCall):
        setups, args = zip(*(simplify_and_optimize_expression(a) for a in s.args))
        return seq(list(setups) + [SCall(s.target, s.func, tuple(args))])
    if isinstance(s, SEscapableBlock):
        return SEscapableBlock(s.label, simplify_and_optimize(s.body))
    if isinstance(s, SEscapeBlock):
        return s
    if isinstance(s, SArrayAlloc):
        setup, cap = simplify_and_optimize_expression(s.capacity)
        return seq([setup, SArrayAlloc(s.a, cap)])
    if isinstance(s, SArrayReAlloc):
        setup, cap = simplify_and_optimize_expression(s.new_capacity)
        return seq([setup, SArrayReAlloc(s.a, cap)])
    if isinstance(s, SEnsureCapacity):
        setup, cap = simplify_and_optimize_expression(s.capacity)
        return seq([setup, SEnsureCapacity(s.a, cap)])
    if isinstance(s, SSwap):
        # TODO: if we want to optimize the operands we will need a special
        # procedure that optimizes lvalues while preserving meaning... same
        # goes for SAssign case above.
        return s
    if isinstance(s, SSwitch):
        setup, e = simplify_and_optimize_expression(s.e)
        new_cases = [(case, simplify_and_optimize(stm)) for (case, stm) in s.cases]
Example #6
0
    def implement_stmt(self, s: Stm, concretization_functions: {str:
                                                                Exp}) -> Stm:
        """Convert a call to a heap function into simpler statements.

        This function also requires the `concretization_functions` that
        describe the invariants for variables in `e`.
        """

        comparison_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))),
                                            comparison_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))),
                                                        comparison_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)),
                                                    comparison_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 ValueError("heaps do not support the function {}".format(
                    s.func))
        else:
            raise ValueError(
                "the statement {} is not an update to a heap variable".format(
                    pprint(s)))