def handle_select(self, p4_state): switches = [] select_conds = [] context = p4_state.current_context() forward_cond_copy = context.tmp_forward_cond match_list = p4_state.resolve_expr(self.match) for parser_cond, parser_node in reversed(self.child): case_expr = p4_state.resolve_expr(parser_cond) cond = build_select_cond(p4_state, case_expr, match_list) # state forks here var_store, contexts = p4_state.checkpoint() context.tmp_forward_cond = z3.And(forward_cond_copy, cond) parser_node.eval(p4_state) select_conds.append(cond) if not p4_state.has_exited: switches.append( (context.tmp_forward_cond, p4_state.get_attrs())) p4_state.has_exited = False context.has_returned = False p4_state.restore(var_store, contexts) # this hits when the table is either missed, or no action matches cond = z3.Not(z3.Or(*select_conds)) context.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.default.eval(p4_state) p4_state.has_exited = False context.has_returned = False context.tmp_forward_cond = forward_cond_copy for cond, then_vars in switches: merge_attrs(p4_state, cond, then_vars)
def handle_select(self, ctx): switches = [] select_conds = [] forward_cond_copy = ctx.tmp_forward_cond match_list = ctx.resolve_expr(self.match) for parser_cond, parser_node in self.child: case_expr = ctx.resolve_expr(parser_cond) cond = build_select_cond(ctx, case_expr, match_list) # state forks here var_store = ctx.checkpoint() ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) parser_node.eval(ctx) select_conds.append(cond) if not (ctx.get_exited() or z3.is_false(cond)): switches.append((ctx.tmp_forward_cond, ctx.get_attrs())) ctx.set_exited(False) ctx.has_returned = False ctx.restore(var_store) # this hits when no select matches cond = z3.Not(z3.Or(*select_conds)) ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.default.eval(ctx) ctx.set_exited(False) ctx.has_returned = False ctx.tmp_forward_cond = forward_cond_copy for cond, then_vars in reversed(switches): merge_attrs(ctx, cond, then_vars)
def eval(self, ctx): cond = z3.simplify(ctx.resolve_expr(self.cond)) forward_cond_copy = ctx.tmp_forward_cond then_vars = None if not z3.is_false(cond): var_store = ctx.checkpoint() ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) try: self.then_block.eval(ctx) except ParserException: RejectState().eval(ctx) if not(ctx.has_returned or ctx.get_exited()): then_vars = ctx.get_attrs() ctx.set_exited(False) ctx.has_returned = False ctx.restore(var_store) if not z3.is_true(cond): var_store = ctx.checkpoint() ctx.tmp_forward_cond = z3.And(forward_cond_copy, z3.Not(cond)) try: self.else_block.eval(ctx) except ParserException: RejectState().eval(ctx) if ctx.get_exited() or ctx.has_returned: ctx.restore(var_store) ctx.set_exited(False) ctx.has_returned = False ctx.tmp_forward_cond = forward_cond_copy if then_vars: merge_attrs(ctx, cond, then_vars)
def eval_table(self, p4_state): action_exprs = [] action_matches = [] context = p4_state.current_context() forward_cond_copy = context.tmp_forward_cond # only bother to evaluate if the table can actually hit if not self.locals["hit"] == z3.BoolVal(False): # note: the action lists are pass by reference here # first evaluate all the constant entries self.eval_const_entries(p4_state, action_exprs, action_matches) # then append dynamic table entries to the constant entries self.eval_table_entries(p4_state, action_exprs, action_matches) # finally start evaluating the default entry var_store, contexts = p4_state.checkpoint() # this hits when the table is either missed, or no action matches cond = z3.Or(self.locals["miss"], z3.Not(z3.Or(*action_matches))) context.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.eval_default(p4_state) if p4_state.has_exited: p4_state.restore(var_store, contexts) p4_state.has_exited = False context.tmp_forward_cond = forward_cond_copy # generate a nested set of if expressions per available action for cond, then_vars in action_exprs: merge_attrs(p4_state, cond, then_vars)
def eval_table(self, ctx): action_exprs = [] action_matches = [] forward_cond_copy = ctx.tmp_forward_cond # only bother to evaluate if the table can actually hit if not z3.is_false(self.locals["hit"]): # note: the action lists are pass by reference here # first evaluate all the constant entries self.eval_const_entries(ctx, action_exprs, action_matches) # then append dynamic table entries to the constant entries # only do this if the table can actually be manipulated if not self.immutable: self.eval_table_entries(ctx, action_exprs, action_matches) # finally start evaluating the default entry var_store = ctx.checkpoint() # this hits when the table is either missed, or no action matches cond = z3.Or(self.locals["miss"], z3.Not(z3.Or(*action_matches))) ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.eval_default(ctx) if ctx.get_exited(): ctx.restore(var_store) ctx.set_exited(False) ctx.tmp_forward_cond = forward_cond_copy # generate a nested set of if expressions per available action for cond, then_vars in action_exprs: merge_attrs(ctx, cond, then_vars)
def eval_cases(self, p4_state, cases): case_exprs = [] case_matches = [] context = p4_state.current_context() forward_cond_copy = context.tmp_forward_cond for case in reversed(cases.values()): var_store, contexts = p4_state.checkpoint() context.tmp_forward_cond = z3.And(forward_cond_copy, case["match"]) case["case_block"].eval(p4_state) if not (context.has_returned or p4_state.has_exited): then_vars = p4_state.get_attrs() case_exprs.append((case["match"], then_vars)) context.has_returned = False p4_state.has_exited = False p4_state.restore(var_store, contexts) case_matches.append(case["match"]) var_store, contexts = p4_state.checkpoint() cond = z3.Not(z3.Or(*case_matches)) context.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.default_case.eval(p4_state) if context.has_returned or p4_state.has_exited: p4_state.restore(var_store, contexts) context.has_returned = False p4_state.has_exited = False context.tmp_forward_cond = forward_cond_copy for cond, then_vars in case_exprs: merge_attrs(p4_state, cond, then_vars)
def eval(self, p4_state): context = p4_state.current_context() cond = z3.simplify(p4_state.resolve_expr(self.cond)) forward_cond_copy = context.tmp_forward_cond then_vars = None if not cond == z3.BoolVal(False): var_store, contexts = p4_state.checkpoint() context.tmp_forward_cond = z3.And(forward_cond_copy, cond) try: self.then_block.eval(p4_state) except ParserException: RejectState().eval(p4_state) if not (context.has_returned or p4_state.has_exited): then_vars = p4_state.get_attrs() p4_state.has_exited = False context.has_returned = False p4_state.restore(var_store, contexts) if not cond == z3.BoolVal(True): var_store, contexts = p4_state.checkpoint() context.tmp_forward_cond = z3.And(forward_cond_copy, z3.Not(cond)) try: self.else_block.eval(p4_state) except ParserException: RejectState().eval(p4_state) if p4_state.has_exited or context.has_returned: p4_state.restore(var_store, contexts) p4_state.has_exited = False context.has_returned = False context.tmp_forward_cond = forward_cond_copy if then_vars: merge_attrs(p4_state, cond, then_vars)
def eval(self, p4_state): # boolean expressions can short-circuit # so we save the result of the right-hand expression and merge lval_expr = p4_state.resolve_expr(self.lval) var_store, chain_copy = p4_state.checkpoint() context = p4_state.current_context() forward_cond_copy = context.tmp_forward_cond context.tmp_forward_cond = z3.And(forward_cond_copy, lval_expr) rval_expr = p4_state.resolve_expr(self.rval) else_vars = p4_state.get_attrs() p4_state.restore(var_store, chain_copy) context.tmp_forward_cond = forward_cond_copy merge_attrs(p4_state, lval_expr, else_vars) return self.operator(lval_expr, rval_expr)
def eval(self, ctx, *args, **kwargs): merged_args = merge_parameters(self.params, *args, **kwargs) param_buffer, var_buffer = self.copy_in(ctx, merged_args) # only add the ctx after all the arguments have been resolved sub_ctx = LocalContext(ctx, var_buffer) # now we can set the arguments without influencing subsequent variables for param_name, param_val in param_buffer.items(): sub_ctx.set_or_add_var(param_name, param_val) # execute the action expression with the new environment expr = self.eval_callable(sub_ctx, merged_args, var_buffer) self.call_counter += 1 while sub_ctx.return_states: cond, return_attrs = sub_ctx.return_states.pop() merge_attrs(sub_ctx, cond, return_attrs) sub_ctx.copy_out(ctx) return expr
def eval(self, ctx): self.start_node.eval(ctx) counter = 0 while counter < self.max_loop: parser_states = [] terminal_nodes = self.terminal_nodes self.terminal_nodes = {} for parser_state, (cond, state) in terminal_nodes.items(): sub_node = self.nodes[parser_state] # state forks here # FIXME dummy_ctx = LocalContext(ctx.master_ctx, {}) dummy_ctx.locals = copy.deepcopy(state) dummy_ctx.tmp_forward_cond = cond sub_node.eval(dummy_ctx) parser_states.append((cond, dummy_ctx.get_attrs())) for cond, then_vars in parser_states: merge_attrs(ctx, cond, then_vars) counter += 1
def eval(self, p4_state): self.start_node.eval(p4_state) counter = 0 while counter < self.max_loop: parser_states = [] terminal_nodes = self.terminal_nodes self.terminal_nodes = {} for parser_state, (cond, state) in terminal_nodes.items(): sub_node = self.nodes[parser_state] # state forks here dummy_context = P4Context({}) dummy_context.locals = state dummy_context.tmp_forward_cond = cond p4_state.contexts.append(dummy_context) sub_node.eval(p4_state) parser_states.append((cond, p4_state.get_attrs())) p4_state.contexts.pop() for cond, then_vars in parser_states: merge_attrs(p4_state, cond, then_vars) counter += 1
def eval(self, p4_state, *args, **kwargs): merged_args = merge_parameters(self.params, *args, **kwargs) var_buffer = save_variables(p4_state, merged_args) param_buffer = self.copy_in(p4_state, merged_args) # only add the context after all the arguments have been resolved context = P4Context(var_buffer) p4_state.contexts.append(context) # 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) # execute the action expression with the new environment expr = self.eval_callable(p4_state, merged_args, var_buffer) self.call_counter += 1 while context.return_states: cond, return_attrs = context.return_states.pop() merge_attrs(p4_state, cond, return_attrs) context.copy_out(p4_state) p4_state.contexts.pop() return expr
def eval_cases(self, ctx, cases): case_exprs = [] case_matches = [] forward_cond_copy = ctx.tmp_forward_cond fall_through_matches = [] for case_match, case_block in cases.values(): # there is no block for the switch # this expressions falls through to the next switch case if not case_block: fall_through_matches.append(case_match) continue # matches the condition OR all the other fall-through switches case_match = z3.Or(case_match, *fall_through_matches) fall_through_matches.clear() var_store = ctx.checkpoint() ctx.tmp_forward_cond = z3.And( forward_cond_copy, case_match) case_block.eval(ctx) if not (ctx.has_returned or ctx.get_exited()): then_vars = ctx.get_attrs() case_exprs.append((case_match, then_vars)) ctx.has_returned = False ctx.set_exited(False) ctx.restore(var_store) case_matches.append(case_match) var_store = ctx.checkpoint() # process the default expression cond = z3.Not(z3.Or(*case_matches)) if not z3.is_false(cond): ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond) self.default_case.eval(ctx) if ctx.has_returned or ctx.get_exited(): ctx.restore(var_store) ctx.has_returned = False ctx.set_exited(False) ctx.tmp_forward_cond = forward_cond_copy # merge all the expressions in reverse order for cond, then_vars in reversed(case_exprs): merge_attrs(ctx, cond, then_vars)
def eval(self, p4_state): cond = z3.simplify(p4_state.resolve_expr(self.cond)) # handle side effects for function and table calls if cond == z3.BoolVal(False): return p4_state.resolve_expr(self.else_val) if cond == z3.BoolVal(True): return p4_state.resolve_expr(self.then_val) var_store, chain_copy = p4_state.checkpoint() context = p4_state.current_context() forward_cond_copy = context.tmp_forward_cond context.tmp_forward_cond = z3.And(forward_cond_copy, cond) then_expr = p4_state.resolve_expr(self.then_val) then_vars = p4_state.get_attrs() p4_state.restore(var_store, chain_copy) context.tmp_forward_cond = forward_cond_copy else_expr = p4_state.resolve_expr(self.else_val) merge_attrs(p4_state, cond, then_vars) return handle_mux(cond, then_expr, else_expr)