Beispiel #1
0
 def eval(self, p4_state):
     # z3 does not like to shift operators of different size
     # but casting both values could lead to missing an overflow
     # so after the operation cast the lvalue down to its original size
     lval_expr = p4_state.resolve_expr(self.lval)
     rval_expr = p4_state.resolve_expr(self.rval)
     if isinstance(lval_expr, int):
         # if lval_expr is an int we might get a signed value
         # the only size adjustment is to make the rval expr large enough
         # for some reason a small rval leads to erroneous shifts...
         return op.lshift(lval_expr, rval_expr)
     if isinstance(rval_expr, int):
         # shift is larger than width, all zero
         if lval_expr.size() <= rval_expr:
             return z3.BitVecVal(0, lval_expr.size())
     # align the bitvectors to allow operations
     lval_is_bitvec = isinstance(lval_expr, z3.BitVecRef)
     rval_is_bitvec = isinstance(rval_expr, z3.BitVecRef)
     orig_lval_size = lval_expr.size()
     if lval_is_bitvec and rval_is_bitvec:
         # shift is larger than width, all zero
         if lval_expr.size() <= rval_expr.size():
             lval_expr = z3_cast(lval_expr, rval_expr.size())
         if lval_expr.size() > rval_expr.size():
             rval_expr = z3_cast(rval_expr, lval_expr.size())
     return z3_cast(op.lshift(lval_expr, rval_expr), orig_lval_size)
Beispiel #2
0
 def operator(x, y):
     # this extra check is necessary because of z3...
     if z3.is_int(x) and isinstance(y, z3.BitVecRef):
         x = z3_cast(x, y)
     if z3.is_int(y) and isinstance(x, z3.BitVecRef):
         y = z3_cast(y, x)
     return op.or_(x, y)
Beispiel #3
0
    def eval(self, p4_state):
        lval_expr = p4_state.resolve_expr(self.lval)
        rval_expr = p4_state.resolve_expr(self.rval)

        # align the bitvectors to allow operations
        lval_is_bitvec = isinstance(lval_expr, (z3.BitVecRef, z3.BitVecNumRef))
        rval_is_bitvec = isinstance(rval_expr, (z3.BitVecRef, z3.BitVecNumRef))
        if lval_is_bitvec and rval_is_bitvec:
            if lval_expr.size() < rval_expr.size():
                rval_expr = z3_cast(rval_expr, lval_expr.size())
            if lval_expr.size() > rval_expr.size():
                lval_expr = z3_cast(lval_expr, rval_expr.size())
        return self.operator(lval_expr, rval_expr)
Beispiel #4
0
    def eval(self, p4_state):

        # resolve the expr before restoring the state
        if self.expr is None:
            expr = None
        else:
            expr = p4_state.resolve_expr(self.expr)

        chain_copy = p4_state.copy_expr_chain()
        # remove all expressions until we hit the end (typically a context)
        for p4z3_expr in chain_copy:
            p4_state.expr_chain.popleft()
            # this is tricky, we need to restore the state before returning
            # so update the p4_state and then move on to return the expression
            # this technique preserves the return value
            if isinstance(p4z3_expr, P4Context):
                p4z3_expr.restore_context(p4_state)
                break
        # since we popped the P4Context object that would take care of this
        # return the z3 expressions of the state AFTER restoring it
        if expr is None:
            # FIXME: issue1386 requires us to keep running down the chain...
            # Need to run down to the remaining execution path after the return.
            p4z3_expr = p4_state.pop_next_expr()
            expr = p4z3_expr.eval(p4_state)
        # functions cast the returned value down to their actual return type
        # FIXME: We can only cast bitvecs right now
        if isinstance(self.z3_type, z3.BitVecSortRef):
            return z3_cast(expr, self.z3_type)
        # we return a complex typed expression list, instantiate
        if isinstance(expr, list):
            instance = self.z3_type.instantiate("undefined")
            instance.set_list(expr)
            return instance
        return expr
Beispiel #5
0
    def set_slice(self, ctx, lval, rval):
        slice_l = ctx.resolve_expr(lval.slice_l)
        slice_r = ctx.resolve_expr(lval.slice_r)
        lval = lval.val
        lval, slice_l, slice_r = self.find_nested_slice(lval, slice_l, slice_r)

        # need to resolve everything first, these can be members
        lval_expr = ctx.resolve_expr(lval)

        # z3 requires the extract value to be a bitvector, so we must cast ints
        # actually not sure where this could happen...
        if isinstance(lval_expr, int):
            lval_expr = lval_expr.as_bitvec

        rval_expr = ctx.resolve_expr(rval)

        lval_expr_max = lval_expr.size() - 1
        if slice_l == lval_expr_max and slice_r == 0:
            # slice is full lval, nothing to do
            ctx.set_or_add_var(lval, rval_expr)
            return
        assemble = []
        if slice_l < lval_expr_max:
            # left slice is smaller than the max, leave that chunk unchanged
            assemble.append(z3.Extract(lval_expr_max, slice_l + 1, lval_expr))
        # fill the rval_expr into the slice
        # this cast is necessary to match the margins and to handle integers
        rval_expr = z3_cast(rval_expr, slice_l + 1 - slice_r)
        assemble.append(rval_expr)
        if slice_r > 0:
            # right slice is larger than zero, leave that chunk unchanged
            assemble.append(z3.Extract(slice_r - 1, 0, lval_expr))
        rval_expr = z3.Concat(*assemble)
        ctx.set_or_add_var(lval, rval_expr)
        return
Beispiel #6
0
    def eval(self, p4_state):
        context = p4_state.current_context()

        if self.expr is None:
            expr = None
        else:
            # resolve the expr before restoring the state
            expr = p4_state.resolve_expr(self.expr)
            if isinstance(context.return_type, z3.BitVecSortRef):
                expr = z3_cast(expr, context.return_type)
            # we return a complex typed expression list, instantiate
            if isinstance(expr, list):
                instance = gen_instance(p4_state, "undefined",
                                        context.return_type)
                instance.set_list(expr)
                expr = instance

        cond = z3.simplify(
            z3.And(z3.Not(z3.Or(*context.forward_conds)),
                   context.tmp_forward_cond))
        if not cond == z3.BoolVal(False):
            context.return_states.append((cond, p4_state.copy_attrs()))
            if expr is not None:
                context.return_exprs.append((cond, expr))
            context.has_returned = True
        context.forward_conds.append(context.tmp_forward_cond)
Beispiel #7
0
    def eval(self, p4_state):
        val = p4_state.resolve_expr(self.val)
        if self.instance_type is None:
            # no type defined, return just the value
            return val
        else:
            instance = gen_instance("None", self.instance_type)

        if isinstance(val, P4ComplexInstance):
            # copy the reference if we initialize with another complex type
            return copy.copy(val)
        if isinstance(instance, P4ComplexInstance):
            if isinstance(val, dict):
                instance.setValid()
                for name, val in val.items():
                    val_expr = p4_state.resolve_expr(val)
                    instance.set_or_add_var(name, val_expr)
            elif isinstance(val, list):
                instance.set_list(val)
            else:
                raise RuntimeError(
                    f"P4StructInitializer members {val} not supported!")
            return instance
        else:
            # cast the value we assign to the instance we create
            # TODO: I do not like this, there must be a better way to do this
            if isinstance(val, int) and isinstance(
                    instance, (z3.BitVecSortRef, z3.BitVecRef)):
                val = z3_cast(val, instance.sort())
            return val
Beispiel #8
0
 def eval(self, p4_state):
     lval_expr = p4_state.resolve_expr(self.lval)
     # it can happen that we cast to a complex type...
     if isinstance(self.rval, P4ComplexType):
         instance = self.rval.instantiate(self.rval.name)
         initializer = P4Initializer(lval_expr, instance)
         return initializer.eval(p4_state)
     rval_expr = p4_state.resolve_expr(self.rval)
     # align the bitvectors to allow operations
     lval_is_bitvec = isinstance(lval_expr, z3.BitVecRef)
     rval_is_bitvec = isinstance(rval_expr, z3.BitVecRef)
     if lval_is_bitvec and rval_is_bitvec:
         if lval_expr.size() < rval_expr.size():
             rval_expr = z3_cast(rval_expr, lval_expr.size())
         if lval_expr.size() > rval_expr.size():
             lval_expr = z3_cast(lval_expr, rval_expr.size())
     return self.operator(lval_expr, rval_expr)
Beispiel #9
0
 def eval(self, p4_state):
     log.debug("Assigning %s to %s ", self.rval, self.lval)
     rval_expr = p4_state.resolve_expr(self.rval)
     # in assignments all complex types values are copied
     if isinstance(rval_expr, StructInstance):
         rval_expr = copy.copy(rval_expr)
     if isinstance(rval_expr, int):
         lval = p4_state.resolve_expr(self.lval)
         rval_expr = z3_cast(rval_expr, lval.sort())
     p4_state.set_or_add_var(self.lval, rval_expr)
Beispiel #10
0
 def eval(self, p4_state):
     # z3 does not like to shift operators of different size
     # but casting both values could lead to missing an overflow
     # so after the operation cast the lvalue down to its original size
     lval_expr = p4_state.resolve_expr(self.lval)
     rval_expr = p4_state.resolve_expr(self.rval)
     if isinstance(lval_expr, int):
         # if x is an int we might get a signed value
         # we need to use the arithmetic right shift in this case
         return op.rshift(lval_expr, rval_expr)
     # align the bitvectors to allow operations
     lval_is_bitvec = isinstance(lval_expr, z3.BitVecRef)
     rval_is_bitvec = isinstance(rval_expr, z3.BitVecRef)
     orig_lval_size = lval_expr.size()
     if lval_is_bitvec and rval_is_bitvec:
         if lval_expr.size() < rval_expr.size():
             lval_expr = z3_cast(lval_expr, rval_expr.size())
         if lval_expr.size() > rval_expr.size():
             rval_expr = z3_cast(rval_expr, lval_expr.size())
     return z3_cast(z3.LShR(lval_expr, rval_expr), orig_lval_size)
Beispiel #11
0
    def eval(self, p4_state):
        cond = p4_state.resolve_expr(self.cond)
        # handle side effects for function calls
        var_store, chain_copy = p4_state.checkpoint()
        then_val = p4_state.resolve_expr(self.then_val)
        then_vars = copy_attrs(p4_state.locals)
        p4_state.restore(var_store, chain_copy)
        else_val = p4_state.resolve_expr(self.else_val)
        p4_state.merge_attrs(cond, then_vars)

        then_expr = then_val
        else_expr = else_val
        # this is a really nasty hack, do not try this at home kids
        # because we have to be able to access the sub values again
        # we have to resolve the if condition in the case of complex types
        # we do this by splitting the if statement into a list
        # lists can easily be assigned to a target structure
        if isinstance(then_expr, P4ComplexInstance):
            then_expr = then_expr.flatten()
        if isinstance(else_expr, P4ComplexInstance):
            else_expr = else_expr.flatten()
        if isinstance(then_expr, list) and isinstance(else_expr, list):
            sub_cond = []
            # handle nested complex types
            then_expr = self.unravel_datatype(then_val, then_expr)
            else_expr = self.unravel_datatype(else_val, else_expr)
            for idx, member in enumerate(then_expr):
                if_expr = z3.If(cond, member, else_expr[idx])
                sub_cond.append(if_expr)
            return sub_cond
        then_is_const = isinstance(then_expr, (z3.BitVecRef, int))
        else_is_const = isinstance(else_expr, (z3.BitVecRef, int))
        if then_is_const and else_is_const:
            # align the bitvectors to allow operations, we cast ints downwards
            if else_expr.size() > then_expr.size():
                else_expr = z3_cast(else_expr, then_expr.size())
            if else_expr.size() < then_expr.size():
                then_expr = z3_cast(then_expr, else_expr.size())
        return z3.If(cond, then_expr, else_expr)
Beispiel #12
0
    def copy_in(self, ctx, merged_args):
        param_buffer = OrderedDict()
        var_buffer = OrderedDict()
        for param_name, arg in merged_args.items():
            # We need to resolve array indices appropriately
            arg_expr, _ = resolve_index(ctx, arg.p4_val)
            try:
                param_val = ctx.resolve_reference(param_name)
                var_buffer[param_name] = (arg.mode, arg_expr, param_val)
            except RuntimeError:
                # if the variable name does not exist, set the value to None
                var_buffer[param_name] = (arg.mode, arg_expr, None)
            # Sometimes expressions are passed, resolve those first
            arg_expr = ctx.resolve_expr(arg_expr)
            # it can happen that we receive a list
            # infer the type, generate, and set
            try:
                p4_type = ctx.resolve_type(arg.p4_type)
            except KeyError:
                # this is a generic type, we need to bind for this scope
                # FIXME: Clean this up
                p4_type = arg_expr.sort()
                ctx.add_type(arg.p4_type, p4_type)
            if isinstance(arg_expr, list):
                # if the type is undefined, do nothing
                if isinstance(p4_type, P4ComplexType):
                    arg_instance = ctx.gen_instance(UNDEF_LABEL, p4_type)
                    arg_instance.set_list(arg_expr)
                    arg_expr = arg_instance
            # it is possible to pass an int as value, we need to cast it
            elif isinstance(arg_expr, int):
                arg_expr = z3_cast(arg_expr, p4_type)
            # need to work with an independent copy
            # the purpose is to handle indirect assignments in an action
            if arg.mode in ("in", "inout") and isinstance(
                    arg_expr, StructInstance):
                arg_expr = copy.copy(arg_expr)
            if arg.mode == "out":
                # outs are left-values so the arg must be a string
                # infer the type value at runtime, param does not work yet
                # outs reset the input
                # In the case that the instance is a complex type make sure
                # to propagate the variable through all its members
                log.debug("Resetting %s to %s", arg_expr, param_name)
                arg_expr = ctx.gen_instance(UNDEF_LABEL, p4_type)

            log.debug("Copy-in: %s to %s", arg_expr, param_name)
            # buffer the value, do NOT set it yet
            param_buffer[param_name] = arg_expr
        return param_buffer, var_buffer
Beispiel #13
0
 def eval(self, ctx):
     # An assignment, written with the = sign, first evaluates its left
     # sub-expression to an l-value, then evaluates its right sub-expression
     # to a value, and finally copies the value into the l-value. Derived
     # types (e.g. structs) are copied recursively, and all components of
     # headers are copied, including “validity” bits. Assignment is not
     # defined for extern values.
     log.debug("Assigning %s to %s ", self.rval, self.lval)
     lval, _ = resolve_index(ctx, self.lval)
     rval_expr = ctx.resolve_expr(self.rval)
     # in assignments all complex types values are copied
     if isinstance(rval_expr, StructInstance):
         rval_expr = copy.copy(rval_expr)
     if isinstance(rval_expr, int):
         rval_expr = z3_cast(rval_expr, ctx.resolve_expr(self.lval).sort())
     ctx.set_or_add_var(lval, rval_expr)
Beispiel #14
0
 def set_context(self, p4_state, merged_args, ref_criteria):
     # we have to subclass because of slight different behavior
     # inout and out parameters are not undefined they are set
     # with a new free constant
     param_buffer = OrderedDict()
     for param_name, arg in merged_args.items():
         # Sometimes expressions are passed, resolve those first
         arg_expr = p4_state.resolve_expr(arg.p4_val)
         # for now use the param name, not the arg_name constructed here
         # FIXME: there are some passes that rename causing issues
         arg_name = f"{self.name}_{param_name}"
         # it can happen that we receive a list
         # infer the type, generate, and set
         if isinstance(arg_expr, list):
             # if the type is undefined, do nothing
             if isinstance(arg.p4_type, P4ComplexType):
                 arg_instance = gen_instance(arg_name, arg.p4_type)
                 arg_instance.set_list(arg_expr)
                 arg_expr = arg_instance
         if arg.is_ref in ref_criteria:
             # outs are left-values so the arg must be a string
             # infer the type value at runtime, param does not work yet
             # outs reset the input
             # In the case that the instance is a complex type make sure
             # to propagate the variable through all its members
             log.debug("Resetting %s to %s", arg_expr, param_name)
             if isinstance(arg_expr, P4ComplexInstance):
                 # assume that for inout header validity is not touched
                 if arg.is_ref == "inout":
                     arg_expr.bind_to_name(arg_name)
                 else:
                     arg_expr = arg_expr.p4z3_type.instantiate(arg_name)
                 # we do not know whether the expression is valid afterwards
                 arg_expr.propagate_validity_bit()
             else:
                 arg_expr = z3.Const(f"{param_name}", arg_expr.sort())
         log.debug("Copy-in: %s to %s", arg_expr, param_name)
         # it is possible to pass an int as value, we need to cast it
         if isinstance(arg_expr, int) and isinstance(
                 arg.p4_type, (z3.BitVecSortRef, z3.BitVecRef)):
             arg_expr = z3_cast(arg_expr, arg.p4_type)
         # buffer the value, do NOT set it yet
         param_buffer[param_name] = arg_expr
     # now we can set the arguments without influencing subsequent variables
     for param_name, param_val in param_buffer.items():
         p4_state.set_or_add_var(param_name, param_val)
Beispiel #15
0
    def eval(self, p4_state):
        log.debug("Assigning %s to %s ", self.rval, self.lval)
        rval_expr = p4_state.resolve_expr(self.rval)
        # in assignments all complex types values are copied
        if isinstance(rval_expr, P4ComplexInstance):
            rval_expr = copy.copy(rval_expr)
        # make sure the assignment is aligned appropriately
        # this can happen because we also evaluate before the
        # BindTypeVariables pass
        # we can only align if tmp_val is a bitvector
        # example test: instance_overwrite.p4
        lval = p4_state.resolve_expr(self.lval)
        if isinstance(rval_expr, int) and isinstance(
                lval, (z3.BitVecSortRef, z3.BitVecRef)):
            rval_expr = z3_cast(rval_expr, lval.sort())
        p4_state.set_or_add_var(self.lval, rval_expr)

        p4z3_expr = p4_state.pop_next_expr()
        return p4z3_expr.eval(p4_state)
Beispiel #16
0
    def copy_in(self, p4_state, merged_args):
        param_buffer = OrderedDict()
        for param_name, arg in merged_args.items():
            # Sometimes expressions are passed, resolve those first
            arg_expr = p4_state.resolve_expr(arg.p4_val)
            # for now use the param name, not the arg_name constructed here
            # FIXME: there are some passes that rename causing issues
            arg_name = "undefined"  # f"{self.name}_{param_name}"
            # it can happen that we receive a list
            # infer the type, generate, and set
            p4_type = resolve_type(p4_state, arg.p4_type)
            if isinstance(arg_expr, list):
                # if the type is undefined, do nothing
                if isinstance(p4_type, P4ComplexType):
                    arg_instance = gen_instance(p4_state, arg_name, p4_type)
                    arg_instance.set_list(arg_expr)
                    arg_expr = arg_instance
            # it is possible to pass an int as value, we need to cast it
            elif isinstance(arg_expr, int):
                arg_expr = z3_cast(arg_expr, p4_type)
            # need to work with an independent copy
            # the purpose is to handle indirect assignments in an action
            if arg.mode in ("in", "inout") and isinstance(
                    arg_expr, StructInstance):
                arg_expr = copy.copy(arg_expr)
            if arg.mode == "out":
                # outs are left-values so the arg must be a string
                # infer the type value at runtime, param does not work yet
                # outs reset the input
                # In the case that the instance is a complex type make sure
                # to propagate the variable through all its members
                log.debug("Resetting %s to %s", arg_expr, param_name)
                arg_expr = gen_instance(p4_state, arg_name, p4_type)

            log.debug("Copy-in: %s to %s", arg_expr, param_name)
            # buffer the value, do NOT set it yet
            param_buffer[param_name] = arg_expr
        return param_buffer
Beispiel #17
0
    def eval(self, ctx):

        if self.expr is None:
            expr = None
        else:
            # resolve the expr before restoring the state
            expr = ctx.resolve_expr(self.expr)
            if isinstance(ctx.return_type, z3.BitVecSortRef):
                expr = z3_cast(expr, ctx.return_type)
            # we return a complex typed expression list, instantiate
            if isinstance(expr, list):
                # name is meaningless here so keep it empty
                instance = ctx.gen_instance("", ctx.return_type)
                instance.set_list(expr)
                expr = instance

        cond = z3.simplify(z3.And(z3.Not(z3.Or(*ctx.forward_conds)),
                                  ctx.tmp_forward_cond))
        if not z3.is_false(cond):
            ctx.return_states.append((cond, ctx.copy_attrs()))
            if expr is not None:
                ctx.return_exprs.append((cond, expr))
            ctx.has_returned = True
        ctx.forward_conds.append(ctx.tmp_forward_cond)
Beispiel #18
0
 def operator(x, y):
     # for some reason, z3 borks if you use an int as x?
     if isinstance(x, int) and isinstance(y, z3.BitVecRef):
         x = z3_cast(x, y)
     return op.sub(x, y)