Exemple #1
0
def build_select_cond(p4_state, case_expr, match_list):
    select_cond = []
    # these casts are kind of silly but simplify the code a lot
    if isinstance(case_expr, StructInstance):
        case_expr = case_expr.flatten()
    elif not isinstance(case_expr, list):
        case_expr = [case_expr]

    for idx, case_match in enumerate(case_expr):
        # default implies don't care, do not add
        # TODO: Verify that this assumption is right...
        if isinstance(case_match, DefaultExpression):
            select_cond.append(z3.BoolVal(True))
        elif isinstance(case_match, P4Range):
            x = case_match.min
            y = case_match.max
            match_key = z3.And(z3.ULE(x, match_list[idx]),
                               z3.UGE(y, match_list[idx]))
            select_cond.append(match_key)
        elif isinstance(case_match, P4Mask):
            val = p4_state.resolve_expr(case_match.value)
            mask = case_match.mask
            match_key = (val | ~mask) == (match_list[idx] | ~mask)
            select_cond.append(match_key)
        else:
            select_cond.append(case_match == match_list[idx])
    if not select_cond:
        return z3.BoolVal(False)
    return z3.And(*select_cond)
Exemple #2
0
 def get_const_matches(self, ctx, c_keys):
     matches = []
     # match the constant keys with the normal table keys
     # this generates the match expression for a specific constant entry
     # this is a little inefficient, FIXME.
     for index, (key_expr, _) in enumerate(self.keys):
         c_key_expr = c_keys[index]
         # default implies don't care, do not add
         # TODO: Verify that this assumption is right...
         if isinstance(c_key_expr, DefaultExpression):
             continue
         # TODO: Unclear about the role of side-effects here
         key_eval = ctx.resolve_expr(key_expr)
         if isinstance(c_key_expr, P4Range):
             x = ctx.resolve_expr(c_key_expr.min)
             y = ctx.resolve_expr(c_key_expr.max)
             c_key_eval = z3.And(z3.ULE(x, key_eval), z3.UGE(y, key_eval))
             matches.append(c_key_eval)
         elif isinstance(c_key_expr, P4Mask):
             # TODO: Unclear about the role of side-effects here
             val = ctx.resolve_expr(c_key_expr.value)
             mask = ctx.resolve_expr(c_key_expr.mask)
             c_key_eval = (val & mask) == (key_eval & mask)
             matches.append(c_key_eval)
         else:
             c_key_eval = ctx.resolve_expr(c_key_expr)
             matches.append(key_eval == c_key_eval)
     return z3.And(*matches)
Exemple #3
0
 def eval_keys(self, ctx):
     key_pairs = []
     if not self.keys:
         # there is nothing to match with...
         return z3.BoolVal(False)
     for index, (key_expr, key_type) in enumerate(self.keys):
         key_eval = ctx.resolve_expr(key_expr)
         key_sort = key_eval.sort()
         key_match = z3.Const(f"{self.name}_table_key_{index}", key_sort)
         if key_type == "exact":
             # Just a simple comparison, nothing special
             key_pairs.append(key_eval == key_match)
         elif key_type == "lpm":
             # I think this can be arbitrarily large...
             # If the shift exceeds the bit width, everything will be zero
             # but that does not matter
             # TODO: Test this?
             mask_var = z3.BitVec(f"{self.name}_table_mask_{index}",
                                  key_sort)
             lpm_mask = z3.BitVecVal(2**key_sort.size() - 1,
                                     key_sort) << mask_var
             match = (key_eval & lpm_mask) == (key_match & lpm_mask)
             key_pairs.append(match)
         elif key_type == "ternary":
             # Just apply a symbolic mask, any zero bit is a wildcard
             # TODO: Test this?
             mask = z3.Const(f"{self.name}_table_mask_{index}", key_sort)
             # this is dumb...
             if isinstance(key_sort, z3.BoolSortRef):
                 match = z3.And(key_eval, mask) == z3.And(key_match, mask)
             else:
                 match = (key_eval & mask) == (key_match & mask)
             key_pairs.append(match)
         elif key_type == "range":
             # Pick an arbitrary minimum and maximum within the bit range
             # the minimum must be strictly lesser than the max
             # I do not think a match is needed?
             # TODO: Test this?
             min_key = z3.Const(f"{self.name}_table_min_{index}", key_sort)
             max_key = z3.Const(f"{self.name}_table_max_{index}", key_sort)
             match = z3.And(z3.ULE(min_key, key_eval),
                            z3.UGE(max_key, key_eval))
             key_pairs.append(z3.And(match, z3.ULT(min_key, max_key)))
         elif key_type == "optional":
             # As far as I understand this is just a wildcard for control
             # plane purposes. Semantically, there is no point?
             # TODO: Test this?
             key_pairs.append(z3.BoolVal(True))
         elif key_type == "selector":
             # Selectors are a deep rabbit hole
             # This rabbit hole does not yet make sense to me
             # FIXME: Implement
             # will intentionally fail if no implementation is present
             # impl = self.properties["implementation"]
             # impl_extern = self.prog_state.resolve_reference(impl)
             key_pairs.append(z3.BoolVal(True))
         else:
             # weird key, might be some specific specification
             raise RuntimeError(f"Key type {key_type} not supported!")
     return z3.And(key_pairs)
Exemple #4
0
 def eval_const_entries(self, p4_state, action_exprs, action_matches):
     context = p4_state.current_context()
     forward_cond_copy = context.tmp_forward_cond
     for c_keys, (action_name, action_args) in reversed(self.const_entries):
         matches = []
         # match the constant keys with the normal table keys
         # this generates the match expression for a specific constant entry
         # this is a little inefficient, fix.
         # TODO: Figure out if key type matters here?
         for index, (key_expr, key_type) in enumerate(self.keys):
             c_key_expr = c_keys[index]
             # default implies don't care, do not add
             # TODO: Verify that this assumption is right...
             if isinstance(c_key_expr, DefaultExpression):
                 continue
             key_eval = p4_state.resolve_expr(key_expr)
             if isinstance(c_key_expr, P4Range):
                 x = c_key_expr.min
                 y = c_key_expr.max
                 c_key_eval = z3.And(z3.ULE(x, key_eval),
                                     z3.UGE(y, key_eval))
                 matches.append(c_key_eval)
             elif isinstance(c_key_expr, P4Mask):
                 val = p4_state.resolve_expr(c_key_expr.mask)
                 mask = c_key_expr.mask
                 c_key_eval = (val & mask) == (key_eval & mask)
                 matches.append(c_key_eval)
             else:
                 c_key_eval = p4_state.resolve_expr(c_key_expr)
                 matches.append(key_eval == c_key_eval)
         action_match = z3.And(*matches)
         log.debug("Evaluating constant action %s...", action_name)
         # state forks here
         var_store, contexts = p4_state.checkpoint()
         cond = z3.And(self.locals["hit"], action_match)
         context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
         self.eval_action(p4_state, action_name, action_args)
         if not p4_state.has_exited:
             action_exprs.append((cond, p4_state.get_attrs()))
         p4_state.has_exited = False
         action_matches.append(action_match)
         p4_state.restore(var_store, contexts)
Exemple #5
0
 def operator(x, y):
     # if x and y are ints we might deal with a signed value
     # we need to use the normal operator in this case
     if isinstance(x, int) and isinstance(y, int):
         return op.le(x, y)
     return z3.ULE(x, y)