Exemple #1
0
    def __call__(self, p4_state, *args, **kwargs):
        merged_args = merge_parameters(self.params, *args, **kwargs)

        # resolve the inputs *before* we bind types
        method_args = {}
        for param_name, arg in merged_args.items():
            # we need to resolve "in" too because of side-effects
            if arg.p4_val is None:
                # sometimes we get optional parameters
                # FIXME: I do not actually know how to resolve them right now
                arg_expr = gen_instance(p4_state, "optional", arg.p4_type)
            else:
                arg_expr = p4_state.resolve_expr(arg.p4_val)
            method_args[param_name] = (arg.mode, arg.p4_val, arg_expr,
                                       arg.p4_type)

        # apply the local and parent extern type contexts
        local_context = {}
        for type_name, p4_type in self.extern_context.items():
            local_context[type_name] = resolve_type(p4_state, p4_type)
        for type_name, p4_type in self.type_context.items():
            local_context[type_name] = resolve_type(p4_state, p4_type)
        p4_state.type_contexts.append(local_context)

        # assign symbolic values to the inputs that are inout and out
        self.assign_values(p4_state, method_args)

        # execute the return expression within the new type environment
        expr = self.eval_callable(p4_state, merged_args, {})

        # cleanup and return the value
        p4_state.type_contexts.pop()
        self.call_counter += 1
        return expr
Exemple #2
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
Exemple #3
0
    def eval_callable(self, p4_state, merged_args, var_buffer):
        # initialize the local context of the function for execution
        if self.return_type is None:
            return None
        # methods can return values, we need to generate a new constant
        # we generate the name based on the input arguments
        # var_name = ""
        # for arg in merged_args.values():
        #     arg = p4_state.resolve_expr(arg.p4_val)
        #     # fold runtime-known values
        #     if isinstance(arg, z3.AstRef):
        #         arg = z3.simplify(arg)
        #     # elif isinstance(arg, list):
        #     #     for idx, member in enumerate(arg):
        #     #         arg[idx] = z3.simplify(member)

        #     # Because we do not know what the extern is doing
        #     # we initiate a new z3 const and
        #     # just overwrite all reference types
        #     # input arguments influence the output behavior
        #     # add the input value to the return constant
        #     var_name += str(arg)
        # If we return something, instantiate the type and return it
        # we merge the name
        # FIXME: We do not consider call order
        # and assume that externs are stateless
        return_instance = gen_instance(p4_state, self.name, self.return_type)
        # a returned header may or may not be valid
        if isinstance(return_instance, StructInstance):
            propagate_validity_bit(return_instance)
        return return_instance
Exemple #4
0
    def assign_values(self, p4_state, method_args):
        for param_name, arg in method_args.items():
            arg_mode, arg_ref, arg_expr, p4_src_type = arg
            # infer the type
            p4_type = resolve_type(p4_state, p4_src_type)
            # This is dynamic type inference based on arguments
            # FIXME Check this hack.
            if p4_type is None:
                if isinstance(arg_expr, list):
                    # synthesize a list type from the input list
                    # this mostly just a dummy
                    # FIXME: Need to get the actual sub-types
                    p4_type = ListType("tuple", p4_state, arg_expr)
                else:
                    p4_type = arg_expr.sort()
                p4_state.type_contexts[-1][p4_src_type] = p4_type

            if arg_mode not in ("out", "inout"):
                # this value is read-only so we do not care
                # however, we still used it to potentially infer a type
                continue

            arg_name = f"{self.name}_{param_name}"
            arg_expr = gen_instance(p4_state, arg_name, p4_type)

            if isinstance(arg_expr, StructInstance):
                # # In the case that the instance is a complex type make sure
                # # to propagate the variable through all its members
                # bind_const = z3.Const(arg_name, arg_expr.z3_type)
                # arg_expr.bind(bind_const)
                # we do not know whether the expression is valid afterwards
                propagate_validity_bit(arg_expr)
            # (in)outs are left-values so the arg_ref must be a string
            p4_state.set_or_add_var(arg_ref, arg_expr)
Exemple #5
0
    def eval_callable(self, p4_state, merged_args, var_buffer):
        # initialize the local context of the function for execution
        p4_context = P4Context(var_buffer)
        self.set_context(p4_state, merged_args, ("inout", "out"))
        p4_context.restore_context(p4_state)

        if self.return_type is not None:
            # methods can return values, we need to generate a new constant
            # we generate the name based on the input arguments
            var_name = ""
            for arg in merged_args.values():
                arg = p4_state.resolve_expr(arg.p4_val)
                # Because we do not know what the extern is doing
                # we initiate a new z3 const and
                # just overwrite all reference types
                # input arguments influence the output behavior
                # add the input value to the return constant
                var_name += str(arg)
            # If we return something, instantiate the type and return it
            # we merge the name
            # FIXME: We do not consider call order
            # and assume that externs are stateless
            return_instance = gen_instance(f"{self.name}_{var_name}",
                                           self.return_type)
            if isinstance(return_instance, P4ComplexInstance):
                return_instance.propagate_validity_bit()
            return return_instance
        p4z3_expr = p4_state.pop_next_expr()
        return p4z3_expr.eval(p4_state)
Exemple #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)
Exemple #7
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
Exemple #8
0
 def eval_action(self, p4_state, action_name, action_args):
     p4_action = p4_state.resolve_reference(action_name)
     if not isinstance(p4_action, P4Action):
         raise TypeError(f"Expected a P4Action got {type(p4_action)}!")
     merged_action_args = []
     action_args_len = len(action_args) - 1
     for idx, param in enumerate(p4_action.params):
         if idx > action_args_len:
             # this is a ctrl argument, generate an input
             ctrl_arg = gen_instance(p4_state, f"{self.name}{param.name}",
                                     param.p4_type)
             merged_action_args.append(ctrl_arg)
         else:
             merged_action_args.append(action_args[idx])
     return p4_action(p4_state, *merged_action_args)
Exemple #9
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)