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)
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.lt(x, y) return z3.ULT(x, y)