def eval_callable(self, ctx, merged_args, var_buffer): # initialize the local ctx 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 = ctx.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 = ctx.gen_instance(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
def initialize(self, ctx): # clear the flat names self.flat_names = [] flat_args = [] idx = 0 for z3_arg_name, z3_arg_type in self.members: z3_arg_type = ctx.resolve_type(z3_arg_type) if isinstance(z3_arg_type, P4ComplexType): member_cls = z3_arg_type.instantiate(f"{self.name}.{idx}") propagate_validity_bit(member_cls) for sub_member in z3_arg_type.flat_names: flat_args.append((str(idx), sub_member.p4_type)) self.flat_names.append( P4Member(z3_arg_name, sub_member.name)) idx += 1 # this is a complex datatype, create a P4ComplexType ctx.set_or_add_var(z3_arg_name, member_cls, True) else: flat_args.append((str(idx), z3_arg_type)) self.flat_names.append(z3_arg_name) idx += 1 z3_type = z3.Datatype(self.name) z3_type.declare(f"mk_{self.name}", *flat_args) self.z3_type = z3_type.create() self.const = z3.Const(self.name, self.z3_type) for type_idx, arg_name in enumerate(self.flat_names): member_constructor = self.z3_type.accessor(0, type_idx) ctx.set_or_add_var(arg_name, member_constructor(self.const), True)
def assign_values(self, ctx, method_args): for param_name, arg in method_args.items(): arg_mode, arg_ref, arg_expr, p4_src_type = arg # infer the type try: p4_type = ctx.resolve_type(p4_src_type) except KeyError: # This is dynamic type inference based on arguments # FIXME Check this hack. 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", ctx, arg_expr) else: p4_type = arg_expr.sort() ctx.add_type(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 = ctx.gen_instance(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 ctx.set_or_add_var(arg_ref, arg_expr)