Esempio n. 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
Esempio n. 2
0
    def initialize(self, context, *args, **kwargs):
        merged_args = merge_parameters(self.params, *args, **kwargs)
        for pipe_name, pipe_arg in merged_args.items():
            if pipe_arg.p4_val is None:
                # for some reason, the argument is uninitialized.
                # FIXME: This should not happen. Why?
                continue
            log.info("Loading %s pipe...", pipe_name)
            pipe_val = context.resolve_expr(pipe_arg.p4_val)
            if isinstance(pipe_val, P4Control):
                # This boilerplate is all necessary to initialize state...
                # FIXME: Ideally, this should be handled by the control...
                context.type_contexts.append(self.type_context)
                ctrl_type = resolve_type(context, pipe_arg.p4_type)
                pipe_val = pipe_val.bind_to_ctrl_type(context, ctrl_type)
                context.type_contexts.append(ctrl_type.type_context)
                args = []
                for idx, param in enumerate(pipe_val.params):
                    ctrl_type_param_type = ctrl_type.params[idx].p4_type
                    generic_type = resolve_type(context, ctrl_type_param_type)
                    if generic_type is None:
                        param_type = resolve_type(context, param.p4_type)
                        self.type_context[ctrl_type_param_type] = param_type
                    args.append(param.name)
                # create the z3 representation of this control state
                p4_state = self.z3_reg.set_p4_state(pipe_name, pipe_val.params)
                # dp not need the types for now
                context.type_contexts.pop()
                context.type_contexts.pop()

                # initialize the call with its own params we collected
                # this is essentially the input packet
                pipe_val.apply(p4_state, *args)
                # after executing the pipeline get its z3 representation
                state = p4_state.get_z3_repr()
                # and also merge back all the exit states we collected
                for exit_cond, exit_state in reversed(p4_state.exit_states):
                    state = z3.If(exit_cond, exit_state, state)
                # all done, that is our P4 representation!
                self.pipes[pipe_name] = (state, p4_state.members, pipe_val)
            elif isinstance(pipe_val, P4Extern):
                var = z3.Const(f"{pipe_name}{pipe_val.name}", pipe_val.z3_type)
                self.pipes[pipe_name] = (var, [], pipe_val)
            elif isinstance(pipe_val, P4Package):
                # execute the package by calling its initializer
                pipe_val.initialize(context)
                # resolve all the sub_pipes
                for sub_pipe_name, sub_pipe_val in pipe_val.pipes.items():
                    sub_pipe_name = f"{pipe_name}_{sub_pipe_name}"
                    self.pipes[sub_pipe_name] = sub_pipe_val
            elif isinstance(pipe_val, z3.ExprRef):
                # for some reason simple expressions are also possible.
                self.pipes[pipe_name] = (pipe_val, [], pipe_val)
            else:
                raise RuntimeError(
                    f"Unsupported value {pipe_val}, type {type(pipe_val)}."
                    " It does not make sense as a P4 pipeline.")
        return self
Esempio n. 3
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)
Esempio n. 4
0
 def apply(self, p4_state, *args, **kwargs):
     local_context = {}
     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(self.type_context)
     self.eval(p4_state, *args, **kwargs)
     p4_state.type_contexts.pop()
Esempio n. 5
0
 def init_type_params(self, context, *args, **kwargs):
     # TODO Figure out what to actually do here
     init_ctrl = copy.copy(self)
     # the type params sometimes include the return type also
     # it is typically the first value, but is bound somewhere else
     for idx, t_param in enumerate(init_ctrl.type_params):
         arg = resolve_type(context, args[idx])
         init_ctrl.type_context[t_param] = arg
     return init_ctrl
Esempio n. 6
0
 def apply(self, p4_state, *args, **kwargs):
     local_context = {}
     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(self.type_context)
     # disable unrolling for now, we do not really need it for validation
     # and with it, tests take unpleasantly long
     # self.statements.max_loop = self.compute_loop_bound(p4_state)
     self.eval(p4_state, *args, **kwargs)
     p4_state.type_contexts.pop()
Esempio n. 7
0
 def compute_loop_bound(self, p4_state):
     sizes = []
     for param in self.params:
         p4_type = resolve_type(p4_state, param.p4_type)
         self.collect_stack_sizes(p4_type, sizes)
     if sizes:
         max_size = max(sizes)
     else:
         max_size = 1
     return max_size
Esempio n. 8
0
    def eval(self, p4_state):
        lval_expr = p4_state.resolve_expr(self.lval)

        rval_expr = resolve_type(p4_state, self.rval)

        # it can happen that we cast to a complex type...
        if isinstance(rval_expr, P4ComplexType):
            # we produce an initializer that takes care of the details
            initializer = P4Initializer(lval_expr, rval_expr)
            return initializer.eval(p4_state)
        return self.operator(lval_expr, rval_expr)
Esempio n. 9
0
 def initialize(self, context, *args, **kwargs):
     ctrl_copy = copy.copy(self)
     ctrl_copy.merged_consts = merge_parameters(ctrl_copy.const_params,
                                                *args, **kwargs)
     # also bind types, because for reasons you can bind types everywhere...
     for idx, const_param in enumerate(ctrl_copy.const_params):
         # this means the type is generic
         p4_type = resolve_type(context, const_param.p4_type)
         if p4_type is None:
             # grab the type of the input arguments
             ctrl_copy.type_context[const_param.p4_type] = args[idx].sort()
     return ctrl_copy
Esempio n. 10
0
 def bind_to_ctrl_type(self, context, ctrl_type):
     # TODO Figure out what to actually do here
     # FIXME: A hack to deal with lack of input params
     if len(ctrl_type.params) < len(self.type_params):
         return self
     init_ctrl = copy.copy(self)
     # the type params sometimes include the return type also
     # it is typically the first value, but is bound somewhere else
     for idx, t_param in enumerate(init_ctrl.type_params):
         sub_type = resolve_type(context, ctrl_type.params[idx].p4_type)
         init_ctrl.type_context[t_param] = sub_type
         for param_idx, param in enumerate(init_ctrl.params):
             if isinstance(param.p4_type, str) and param.p4_type == t_param:
                 init_ctrl.params[param_idx] = ctrl_type.params[idx]
     return init_ctrl
Esempio n. 11
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
Esempio n. 12
0
 def init_type_params(self, context, *args, **kwargs):
     init_method = copy.copy(self)
     for idx, t_param in enumerate(init_method.type_params):
         arg = resolve_type(context, args[idx])
         init_method.type_context[t_param] = arg
     return init_method
Esempio n. 13
0
 def eval(self, context):
     p4_method = resolve_type(context, self.p4_method)
     return p4_method.initialize(context, *self.args, **self.kwargs)