Skip to content

Commit f458a4a

Browse files
qobilidopShoooooon
authored andcommitted
[P4-Constraints] Added ConstraintSolver hook to export constraints to a specified z3 solver. (p4lang#174)
- Add ConstraintSolver::ExportConstraintsToTargetSolver that adds P4-Constraints constraints to a z3 solver, renaming variables according to a given SymbolicEnvironment if necessary. PUBLIC: Added ConstraintSolver::ExportConstraintsToTargetSolver to export constraints to a specified z3 solver. PiperOrigin-RevId: 780137491 Signed-off-by: Bili Dong <[email protected]> Co-authored-by: Shaan Nagy <[email protected]>
1 parent 5712d1e commit f458a4a

File tree

3 files changed

+380
-1
lines changed

3 files changed

+380
-1
lines changed

p4_constraints/backend/symbolic_interpreter.cc

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "p4_constraints/frontend/constraint_kind.h"
4848
#include "p4_constraints/frontend/parser.h"
4949
#include "z3++.h"
50+
#include "z3_api.h"
5051

5152
namespace p4_constraints {
5253
namespace {
@@ -833,4 +834,160 @@ absl::StatusOr<z3::expr> GetPrefixLength(const SymbolicKey& symbolic_key) {
833834
return GetFieldAccess(symbolic_key, "prefix_length");
834835
}
835836

837+
// Substitute variable names in `expr`, replacing variables in `src_env` with
838+
// ones in `dst_env`. Note that `dst_env` is a subset of `src_env` and may not
839+
// share the same z3 context.
840+
absl::StatusOr<z3::expr> TranslateBySymbolicEnvironment(
841+
z3::expr expr, const SymbolicEnvironment& src_env,
842+
const SymbolicEnvironment& dst_env) {
843+
for (const auto& entry : dst_env.symbolic_key_by_name) {
844+
const auto& key_name = entry.first;
845+
const auto& dst_key = entry.second;
846+
auto src_it = src_env.symbolic_key_by_name.find(key_name);
847+
if (src_it == src_env.symbolic_key_by_name.end()) {
848+
return gutil::InvalidArgumentErrorBuilder()
849+
<< "Cannot rename nonexistent key '" << key_name
850+
<< "' specified in destination but not in source.";
851+
}
852+
const SymbolicKey& src_key = src_it->second;
853+
854+
// Replace `src_env` variables appearing in `expr` with the
855+
// corresponding `dst_env` variables.
856+
z3::expr_vector from(expr.ctx());
857+
z3::expr_vector to(expr.ctx());
858+
ASSIGN_OR_RETURN(
859+
expr,
860+
std::visit(
861+
gutil::Overload{
862+
[&](const SymbolicExact& src_exact)
863+
-> absl::StatusOr<z3::expr> {
864+
const SymbolicExact& dst_exact =
865+
std::get<SymbolicExact>(dst_key);
866+
z3::expr translated_dst_value = z3::to_expr(
867+
expr.ctx(), Z3_translate(dst_exact.value.ctx(),
868+
dst_exact.value, expr.ctx()));
869+
if (!eq(src_exact.value.get_sort(),
870+
translated_dst_value.get_sort())) {
871+
return gutil::InternalErrorBuilder()
872+
<< "Mismatched Z3 sorts during symbolic translation "
873+
"for "
874+
"key '"
875+
<< key_name << "': src sort is "
876+
<< src_exact.value.get_sort() << ", dst sort is "
877+
<< translated_dst_value.get_sort();
878+
}
879+
from.push_back(src_exact.value);
880+
to.push_back(translated_dst_value);
881+
return expr.substitute(from, to);
882+
},
883+
[&](const SymbolicTernary& src_ternary)
884+
-> absl::StatusOr<z3::expr> {
885+
const SymbolicTernary& dst_ternary =
886+
std::get<SymbolicTernary>(dst_key);
887+
// Translate dst_ternary values to the context of expr.
888+
z3::expr translated_dst_value = z3::to_expr(
889+
expr.ctx(), Z3_translate(dst_ternary.value.ctx(),
890+
dst_ternary.value, expr.ctx()));
891+
z3::expr translated_dst_mask = z3::to_expr(
892+
expr.ctx(), Z3_translate(dst_ternary.mask.ctx(),
893+
dst_ternary.mask, expr.ctx()));
894+
if (!eq(src_ternary.value.get_sort(),
895+
translated_dst_value.get_sort())) {
896+
return gutil::InternalErrorBuilder()
897+
<< "Mismatched Z3 sorts during symbolic translation "
898+
"for "
899+
"key '"
900+
<< key_name << "': src sort is "
901+
<< src_ternary.value.get_sort() << ", dst sort is "
902+
<< translated_dst_value.get_sort();
903+
}
904+
if (!eq(src_ternary.mask.get_sort(),
905+
translated_dst_mask.get_sort())) {
906+
return gutil::InternalErrorBuilder()
907+
<< "Mismatched Z3 sorts during symbolic translation "
908+
"for "
909+
"mask of key '"
910+
<< key_name << "': src sort is "
911+
<< src_ternary.mask.get_sort() << ", dst sort is "
912+
<< translated_dst_mask.get_sort();
913+
}
914+
from.push_back(src_ternary.value);
915+
to.push_back(translated_dst_value);
916+
from.push_back(src_ternary.mask);
917+
to.push_back(translated_dst_mask);
918+
return expr.substitute(from, to);
919+
},
920+
[&](const SymbolicLpm& src_lpm) -> absl::StatusOr<z3::expr> {
921+
const SymbolicLpm& dst_lpm = std::get<SymbolicLpm>(dst_key);
922+
z3::expr translated_dst_value = z3::to_expr(
923+
expr.ctx(), Z3_translate(dst_lpm.value.ctx(),
924+
dst_lpm.value, expr.ctx()));
925+
z3::expr translated_dst_prefix_length = z3::to_expr(
926+
expr.ctx(),
927+
Z3_translate(dst_lpm.prefix_length.ctx(),
928+
dst_lpm.prefix_length, expr.ctx()));
929+
if (!eq(src_lpm.value.get_sort(),
930+
translated_dst_value.get_sort())) {
931+
return gutil::InternalErrorBuilder()
932+
<< "Mismatched Z3 sorts during symbolic translation "
933+
"for "
934+
"key '"
935+
<< key_name << "': src sort is "
936+
<< src_lpm.value.get_sort() << ", dst sort is "
937+
<< translated_dst_value.get_sort();
938+
}
939+
if (!eq(src_lpm.prefix_length.get_sort(),
940+
translated_dst_prefix_length.get_sort())) {
941+
return gutil::InternalErrorBuilder()
942+
<< "Mismatched Z3 sorts during symbolic translation "
943+
"for "
944+
"prefix_length of key '"
945+
<< key_name << "': src sort is "
946+
<< src_lpm.prefix_length.get_sort()
947+
<< ", dst sort is "
948+
<< translated_dst_prefix_length.get_sort();
949+
}
950+
from.push_back(src_lpm.value);
951+
to.push_back(translated_dst_value);
952+
from.push_back(src_lpm.prefix_length);
953+
to.push_back(translated_dst_prefix_length);
954+
return expr.substitute(from, to);
955+
},
956+
},
957+
src_key));
958+
}
959+
960+
// Substitute attribute names.
961+
for (const auto& [attribute_name, dst_attribute] :
962+
dst_env.symbolic_attribute_by_name) {
963+
auto src_it = src_env.symbolic_attribute_by_name.find(attribute_name);
964+
if (src_it == src_env.symbolic_attribute_by_name.end()) continue;
965+
const SymbolicAttribute& src_attribute = src_it->second;
966+
z3::expr translated_dst_value =
967+
z3::to_expr(expr.ctx(), Z3_translate(dst_attribute.value.ctx(),
968+
dst_attribute.value, expr.ctx()));
969+
z3::expr_vector from(expr.ctx());
970+
z3::expr_vector to(expr.ctx());
971+
from.push_back(src_attribute.value);
972+
to.push_back(translated_dst_value);
973+
expr = expr.substitute(from, to);
974+
}
975+
return expr;
976+
}
977+
978+
absl::Status ConstraintSolver::ExportConstraintsToTargetSolver(
979+
z3::solver& solver, const SymbolicEnvironment& environment) {
980+
z3::expr_vector constraints(solver.ctx());
981+
for (z3::expr assertion : solver_->assertions()) {
982+
ASSIGN_OR_RETURN(
983+
z3::expr translated_assertion,
984+
TranslateBySymbolicEnvironment(assertion, environment_, environment));
985+
constraints.push_back(z3::to_expr(
986+
solver.ctx(),
987+
Z3_translate(*context_, translated_assertion, solver.ctx())));
988+
}
989+
solver.add(constraints);
990+
return absl::OkStatus();
991+
}
992+
836993
} // namespace p4_constraints

p4_constraints/backend/symbolic_interpreter.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <variant>
3232

3333
#include "absl/container/flat_hash_map.h"
34+
#include "absl/status/status.h"
3435
#include "absl/status/statusor.h"
3536
#include "absl/strings/str_cat.h"
3637
#include "absl/strings/str_format.h"
@@ -121,12 +122,17 @@ class ConstraintSolver {
121122
// TODO(b/242201770): Extract actions once action constraints are supported.
122123
absl::StatusOr<p4::v1::TableEntry> ConcretizeEntry();
123124

125+
// Adds the ConstraintSolver's constraints to the target solver.
126+
// Renames variables according to the passed SymbolicEnvironment as needed.
127+
absl::Status ExportConstraintsToTargetSolver(
128+
z3::solver& solver, const SymbolicEnvironment& environment);
129+
124130
private:
125131
explicit ConstraintSolver()
126132
: context_(std::make_unique<z3::context>()),
127133
solver_(std::make_unique<z3::solver>(*context_)) {}
128134

129-
// Z3 context and solver. Solver requires a reference to `context` for
135+
// Z3 context and solver. 'solver' requires a reference to `context` for
130136
// construction so it is privately stored to avoid dangling reference.
131137
std::unique_ptr<z3::context> context_;
132138
std::unique_ptr<z3::solver> solver_;

0 commit comments

Comments
 (0)