Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ConstraintInfo into a struct containing a map from table_id to table_info and a map from action_id to action_info. #113

Merged
merged 1 commit into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion p4_constraints/backend/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ cc_test(
":constraint_info",
":interpreter",
"//gutils:parse_text_proto",
"//gutils:status",
"//gutils:status_matchers",
"//p4_constraints:ast",
"//p4_constraints:ast_cc_proto",
Expand Down
20 changes: 15 additions & 5 deletions p4_constraints/backend/constraint_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -193,27 +193,37 @@ std::optional<AttributeInfo> GetAttributeInfo(

const TableInfo* GetTableInfoOrNull(const ConstraintInfo& constraint_info,
uint32_t table_id) {
auto it = constraint_info.find(table_id);
if (it == constraint_info.end()) return nullptr;
auto it = constraint_info.table_info_by_id.find(table_id);
if (it == constraint_info.table_info_by_id.end()) return nullptr;
return &it->second;
}

absl::StatusOr<ConstraintInfo> P4ToConstraintInfo(
const p4::config::v1::P4Info& p4info) {
// Allocate output.
absl::flat_hash_map<uint32_t, TableInfo> info;
// TODO: b/293655979 - Populate the action_info_by_id map.
absl::flat_hash_map<uint32_t, ActionInfo> action_info_by_id;
absl::flat_hash_map<uint32_t, TableInfo> table_info_by_id;

std::vector<absl::Status> errors;

for (const Table& table : p4info.tables()) {
absl::StatusOr<TableInfo> table_info = ParseTableInfo(table);
if (!table_info.ok()) {
errors.push_back(table_info.status());
} else if (!info.insert({table.preamble().id(), *table_info}).second) {
} else if (!table_info_by_id.insert({table.preamble().id(), *table_info})
.second) {
errors.push_back(gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "duplicate table: " << table.DebugString());
}
}
if (errors.empty()) return info;
if (errors.empty()) {
ConstraintInfo info{
.action_info_by_id = std::move(action_info_by_id),
.table_info_by_id = std::move(table_info_by_id),
};
return info;
}
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "P4Info to constraint info translation failed with the following "
"errors:\n- "
Expand Down
19 changes: 17 additions & 2 deletions p4_constraints/backend/constraint_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,24 @@ struct TableInfo {
absl::flat_hash_map<std::string, KeyInfo> keys_by_name;
};

struct ActionInfo {
uint32_t id; // Same as Action.preamble.id in p4info.proto.
std::string name; // Same as Action.preamble.name in p4info.proto.

// An optional constraint (aka action_restriction) on actions.
absl::optional<ast::Expression> constraint;
// If member `constraint` is present, this captures its source. Arbitrary
// otherwise.
ConstraintSource constraint_source;
};

// Contains all information required for constraint checking.
// Technically, a map from table IDs to TableInfo.
using ConstraintInfo = absl::flat_hash_map<uint32_t, TableInfo>;
struct ConstraintInfo {
// Maps from action IDs to ActionInfo.
absl::flat_hash_map<uint32_t, ActionInfo> action_info_by_id;
// Maps from table IDs to TableInfo.
absl::flat_hash_map<uint32_t, TableInfo> table_info_by_id;
};

// Translates P4Info to ConstraintInfo.
//
Expand Down
36 changes: 17 additions & 19 deletions p4_constraints/backend/interpreter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -761,30 +761,29 @@ absl::StatusOr<bool> EntryMeetsConstraint(const p4::v1::TableEntry& entry,
using ::p4_constraints::internal_interpreter::TableEntry;

// Find table associated with entry.
auto it = context.find(entry.table_id());
if (it == context.end()) {
auto* table_info = GetTableInfoOrNull(context, entry.table_id());
if (table_info == nullptr) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "table entry with unknown table ID "
<< P4IDToString(entry.table_id());
}
const TableInfo& table_info = it->second;

// Check if entry satisfies table constraint (if present).
if (!table_info.constraint.has_value()) return "";
const Expression& constraint = table_info.constraint.value();
if (!table_info->constraint.has_value()) return "";
const Expression& constraint = table_info->constraint.value();
if (constraint.type().type_case() != Type::kBoolean) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "table " << table_info.name
<< "table " << table_info->name
<< " has non-boolean constraint: " << constraint.DebugString();
}

// Parse entry and check constraint.
ASSIGN_OR_RETURN(TableEntry parsed_entry, ParseEntry(entry, table_info),
ASSIGN_OR_RETURN(TableEntry parsed_entry, ParseEntry(entry, *table_info),
_ << " while parsing P4RT table entry for table '"
<< table_info.name << "':");
<< table_info->name << "':");
EvaluationContext eval_context{
.entry = parsed_entry,
.source = table_info.constraint_source,
.source = table_info->constraint_source,
};
// No explanation is returned so no cache is provided.
return EvalToBool(constraint, eval_context, /*eval_cache=*/nullptr);
Expand All @@ -802,30 +801,29 @@ absl::StatusOr<std::string> ReasonEntryViolatesConstraint(
using ::p4_constraints::internal_interpreter::TableEntry;

// Find table associated with entry.
const auto it = context.find(entry.table_id());
if (it == context.end()) {
auto* table_info = GetTableInfoOrNull(context, entry.table_id());
if (table_info == nullptr) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "table entry with unknown table ID "
<< P4IDToString(entry.table_id());
}
const TableInfo& table_info = it->second;

// Check if entry satisfies table constraint (if present).
if (!table_info.constraint.has_value()) return "";
const Expression& constraint = table_info.constraint.value();
if (!table_info->constraint.has_value()) return "";
const Expression& constraint = table_info->constraint.value();
if (constraint.type().type_case() != Type::kBoolean) {
return gutils::InvalidArgumentErrorBuilder(GUTILS_LOC)
<< "table " << table_info.name
<< "table " << table_info->name
<< " has non-boolean constraint: " << constraint.DebugString();
}

// Parse entry and check constraint.
ASSIGN_OR_RETURN(const TableEntry parsed_entry, ParseEntry(entry, table_info),
ASSIGN_OR_RETURN(const TableEntry parsed_entry,
ParseEntry(entry, *table_info),
_ << " while parsing P4RT table entry for table '"
<< table_info.name << "':");
<< table_info->name << "':");
EvaluationContext eval_context{
.entry = parsed_entry,
.source = table_info.constraint_source,
.source = table_info->constraint_source,
};
EvaluationCache eval_cache;
ASSIGN_OR_RETURN(bool entry_satisfies_constraint,
Expand Down
21 changes: 16 additions & 5 deletions p4_constraints/backend/interpreter_golden_test_runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,18 @@ absl::StatusOr<ConstraintInfo> MakeConstraintInfo(TestCase test_case) {
ParseConstraint(ConstraintKind::kTableConstraint, source));
CHECK_OK(InferAndCheckTypes(&(expression), kTableInfo));
table_info.constraint = expression;
return ConstraintInfo{{table_info.id, table_info}};
return ConstraintInfo{
.action_info_by_id = {},
.table_info_by_id{{
table_info.id,
table_info,
}},
};
}

// NOTE: Test cases only define the "exact" field for brevity but kTableInfo is
// composed of several keys. These undeclared keys are implicitly set to hold
// "catchall" values (i.e. range=[min,max], ternary=*).
// NOTE: Test cases only define the "exact" field for brevity but kTableInfo
// is composed of several keys. These undeclared keys are implicitly set to
// hold "catchall" values (i.e. range=[min,max], ternary=*).
std::vector<TestCase> TestCases() {
std::vector<TestCase> test_cases;
test_cases.push_back(TestCase{
Expand Down Expand Up @@ -239,8 +245,12 @@ absl::Status main() {
for (const TestCase& test_case : TestCases()) {
ASSIGN_OR_RETURN(ConstraintInfo constraint_info,
MakeConstraintInfo(test_case));
auto* table_info = GetTableInfoOrNull(constraint_info, 1);
if (table_info == nullptr) {
return absl::InvalidArgumentError("No table info");
}
ASSIGN_OR_RETURN(TableEntry input,
ParseEntry(test_case.table_entry, constraint_info.at(1)));
ParseEntry(test_case.table_entry, *table_info));
std::cout << "### ReasonEntryViolatestConstraint Test ###################\n"
<< "=== INPUT ===\n"
<< "--- Constraint ---\n"
Expand All @@ -255,6 +265,7 @@ absl::Status main() {
<< (result->empty() ? "<empty>\n" : *result) << "\n";
} else {
std::cout << "=== ERROR ===\n" << result.status();
return absl::InvalidArgumentError(result.status().ToString());
}
}
return absl::OkStatus();
Expand Down
10 changes: 7 additions & 3 deletions p4_constraints/backend/interpreter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include <gtest/gtest.h>
#include <stdint.h>

#include <optional>
#include <string>
#include <utility>
#include <variant>
Expand All @@ -32,7 +31,6 @@
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "gutils/parse_text_proto.h"
#include "gutils/status_macros.h"
#include "gutils/status_matchers.h"
#include "p4/v1/p4runtime.pb.h"
#include "p4_constraints/ast.h"
Expand Down Expand Up @@ -129,7 +127,13 @@ class EntryMeetsConstraintTest : public ::testing::Test {
table_info.constraint = expr;
table_info.constraint_source.constraint_location.set_table_name(
table_info.name);
return {{table_info.id, table_info}};
return {
.action_info_by_id = {},
.table_info_by_id = {{
table_info.id,
table_info,
}},
};
}

static Expression ExpressionWithType(const Type& type,
Expand Down
32 changes: 28 additions & 4 deletions p4_constraints/backend/symbolic_interpreter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,13 @@ TEST_P(ConstraintTest, ConcretizeEntryGivesEntrySatisfyingConstraints) {
}
}

ConstraintInfo context{{table_info.id, table_info}};
ConstraintInfo context{
.action_info_by_id = {},
.table_info_by_id = {{
table_info.id,
table_info,
}},
};
// The empty string signifies that the entry doesn't violate the constraint.
EXPECT_THAT(ReasonEntryViolatesConstraint(concretized_entry, context),
IsOkAndHolds(""))
Expand Down Expand Up @@ -707,7 +713,13 @@ TEST_P(ConstraintTest, EncodeValidTableEntryInZ3AndConcretizeEntry) {
ConcretizeEntry(solver.get_model(), table_info, environment));

// The empty string signifies that the entry doesn't violate the constraint.
ConstraintInfo context{{table_info.id, table_info}};
ConstraintInfo context{
.action_info_by_id = {},
.table_info_by_id = {{
table_info.id,
table_info,
}},
};
EXPECT_THAT(ReasonEntryViolatesConstraint(concretized_entry, context),
IsOkAndHolds(""))
<< "\nFor entry:\n"
Expand Down Expand Up @@ -837,7 +849,13 @@ TEST_P(FullySpecifiedConstraintTest, ConcretizeEntryGivesExactEntry) {

TableInfo table_info =
GetTableInfoWithConstraint(GetParam().constraint_string);
ConstraintInfo context{{table_info.id, table_info}};
ConstraintInfo context{
.action_info_by_id = {},
.table_info_by_id = {{
table_info.id,
table_info,
}},
};
// TODO(b/297400616): p4-constraints currently does not correctly translate
// the bytestring representing 1280 to 1280 (instead turning it into 5).
if (!absl::StrContains(GetParam().constraint_string, "1280")) {
Expand Down Expand Up @@ -890,7 +908,13 @@ TEST_P(FullySpecifiedConstraintTest,

TableInfo table_info =
GetTableInfoWithConstraint(GetParam().constraint_string);
ConstraintInfo context{{table_info.id, table_info}};
ConstraintInfo context{
.action_info_by_id = {},
.table_info_by_id = {{
table_info.id,
table_info,
}},
};
// TODO(b/297400616): p4-constraints currently does not correctly translate
// the bytestring representing 1280 to 1280 (instead turning it into 5).
if (!absl::StrContains(GetParam().constraint_string, "1280")) {
Expand Down
Loading