Skip to content

Commit

Permalink
Make the key match type target specific.
Browse files Browse the repository at this point in the history
  • Loading branch information
fruffy committed Mar 13, 2023
1 parent 56c9e73 commit b0d6eb8
Show file tree
Hide file tree
Showing 18 changed files with 277 additions and 407 deletions.
27 changes: 12 additions & 15 deletions backends/p4tools/modules/testgen/core/small_step/table_stepper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@
#include "backends/p4tools/modules/testgen/lib/execution_state.h"
#include "backends/p4tools/modules/testgen/lib/test_spec.h"

namespace P4Tools {

namespace P4Testgen {
namespace P4Tools::P4Testgen {

const ExecutionState *TableStepper::getExecutionState() { return &stepper->state; }

Expand Down Expand Up @@ -106,17 +104,18 @@ bool TableStepper::compareLPMEntries(const IR::Entry *leftIn, const IR::Entry *r
left->node_type_name(), right, right->node_type_name());
}

const IR::Expression *TableStepper::computeTargetMatchType(
ExecutionState *nextState, const KeyProperties &keyProperties,
std::map<cstring, const FieldMatch> *matches, const IR::Expression *hitCondition) {
const IR::Expression *TableStepper::computeTargetMatchType(ExecutionState *nextState,
const KeyProperties &keyProperties,
TableMatchMap *matches,
const IR::Expression *hitCondition) {
const IR::Expression *keyExpr = keyProperties.key->expression;
// Create a new zombie constant that corresponds to the key expression.
cstring keyName = properties.tableName + "_key_" + keyProperties.name;
const auto ctrlPlaneKey = nextState->createZombieConst(keyExpr->type, keyName);

if (keyProperties.matchType == P4Constants::MATCH_KIND_EXACT) {
hitCondition = new IR::LAnd(hitCondition, new IR::Equ(keyExpr, ctrlPlaneKey));
matches->emplace(keyProperties.name, Exact(keyProperties.key, ctrlPlaneKey));
matches->emplace(keyProperties.name, new Exact(keyProperties.key, ctrlPlaneKey));
return hitCondition;
}
if (keyProperties.matchType == P4Constants::MATCH_KIND_TERNARY) {
Expand All @@ -129,7 +128,8 @@ const IR::Expression *TableStepper::computeTargetMatchType(
} else {
ternaryMask = nextState->createZombieConst(keyExpr->type, maskName);
}
matches->emplace(keyProperties.name, Ternary(keyProperties.key, ctrlPlaneKey, ternaryMask));
matches->emplace(keyProperties.name,
new Ternary(keyProperties.key, ctrlPlaneKey, ternaryMask));
return new IR::LAnd(hitCondition, new IR::Equ(new IR::BAnd(keyExpr, ternaryMask),
new IR::BAnd(ctrlPlaneKey, ternaryMask)));
}
Expand All @@ -151,7 +151,7 @@ const IR::Expression *TableStepper::computeTargetMatchType(
} else {
lpmMask = new IR::Shl(IR::getConstant(keyType, maxReturn), prefix);
}
matches->emplace(keyProperties.name, LPM(keyProperties.key, ctrlPlaneKey, maskVar));
matches->emplace(keyProperties.name, new LPM(keyProperties.key, ctrlPlaneKey, maskVar));
return new IR::LAnd(
hitCondition,
new IR::LAnd(
Expand All @@ -164,8 +164,7 @@ const IR::Expression *TableStepper::computeTargetMatchType(
TESTGEN_UNIMPLEMENTED("Match type %s not implemented for table keys.", keyProperties.matchType);
}

const IR::Expression *TableStepper::computeHit(ExecutionState *nextState,
std::map<cstring, const FieldMatch> *matches) {
const IR::Expression *TableStepper::computeHit(ExecutionState *nextState, TableMatchMap *matches) {
const IR::Expression *hitCondition = IR::getBoolLiteral(!properties.resolvedKeys.empty());
for (auto keyProperties : properties.resolvedKeys) {
hitCondition = computeTargetMatchType(nextState, keyProperties, matches, hitCondition);
Expand Down Expand Up @@ -391,7 +390,7 @@ void TableStepper::evalTableControlEntries(
auto *nextState = new ExecutionState(stepper->state);

// First, we compute the hit condition to trigger this particular action call.
std::map<cstring, const FieldMatch> matches;
TableMatchMap matches;
const auto *hitCondition = computeHit(nextState, &matches);

// We get the control plane name of the action we are calling.
Expand Down Expand Up @@ -683,6 +682,4 @@ TableStepper::TableStepper(ExprStepper *stepper, const IR::P4Table *table)
properties.tableName = table->controlPlaneName();
}

} // namespace P4Testgen

} // namespace P4Tools
} // namespace P4Tools::P4Testgen
10 changes: 5 additions & 5 deletions backends/p4tools/modules/testgen/core/small_step/table_stepper.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,16 @@ class TableStepper {
/// match or does not match at all. The table stepper first checks these custom match types. If
/// these do not match it steps through the default implementation. If it does not match either,
/// a P4C_UNIMPLEMENTED is thrown.
virtual const IR::Expression *computeTargetMatchType(
ExecutionState *nextState, const KeyProperties &keyProperties,
std::map<cstring, const FieldMatch> *matches, const IR::Expression *hitCondition);
virtual const IR::Expression *computeTargetMatchType(ExecutionState *nextState,
const KeyProperties &keyProperties,
TableMatchMap *matches,
const IR::Expression *hitCondition);

/// A helper function that computes whether a control-plane/table-key hits or not. This does not
/// handle constant entries, it is specialized for control plane entries.
/// The function also tracks the list of field matches created to achieve a hit. We later use
/// this to insert table entries using the STF/PTF framework.
const IR::Expression *computeHit(ExecutionState *nextState,
std::map<cstring, const FieldMatch> *matches);
const IR::Expression *computeHit(ExecutionState *nextState, TableMatchMap *matches);

/// Collects properties that may be set per table. Target back end may have different semantics
/// for table execution that need to be collect before evaluation the table.
Expand Down
43 changes: 8 additions & 35 deletions backends/p4tools/modules/testgen/lib/test_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
#include "ir/irutils.h"
#include "lib/exceptions.h"

namespace P4Tools {

namespace P4Testgen {
namespace P4Tools::P4Testgen {

/* =========================================================================================
* Test Specification Objects
Expand Down Expand Up @@ -214,32 +212,10 @@ const Exact *Exact::evaluate(const Model &model) const {

cstring Exact::getObjectName() const { return "Exact"; }

Optional::Optional(const IR::KeyElement *key, const IR::Expression *val, bool addMatch)
: TableMatch(key), value(val), addMatch(addMatch) {}

const IR::Constant *Optional::getEvaluatedValue() const {
const auto *constant = value->to<IR::Constant>();
BUG_CHECK(constant,
"Variable is not a constant. It has type %1% instead. Has the test object %2% "
"been evaluated?",
value->type->node_type_name(), getObjectName());
return constant;
}

const Optional *Optional::evaluate(const Model &model) const {
const auto *evaluatedValue = model.evaluate(value);
return new Optional(getKey(), evaluatedValue, addMatch);
}

cstring Optional::getObjectName() const { return "Optional"; }

bool Optional::addAsExactMatch() const { return addMatch; }

TableRule::TableRule(std::map<cstring, const FieldMatch> matches, int priority, ActionCall action,
int ttl)
TableRule::TableRule(TableMatchMap matches, int priority, ActionCall action, int ttl)
: matches(std::move(matches)), priority(priority), action(std::move(action)), ttl(ttl) {}

const std::map<cstring, const FieldMatch> *TableRule::getMatches() const { return &matches; }
const TableMatchMap *TableRule::getMatches() const { return &matches; }

int TableRule::getPriority() const { return priority; }

Expand All @@ -250,14 +226,13 @@ int TableRule::getTTL() const { return ttl; }
cstring TableRule::getObjectName() const { return "TableRule"; }

const TableRule *TableRule::evaluate(const Model &model) const {
std::map<cstring, const FieldMatch> evaluatedMatches;
TableMatchMap evaluatedMatches;
for (const auto &matchTuple : matches) {
auto name = matchTuple.first;
auto match = matchTuple.second;
const auto &match = matchTuple.second;
// This is a lambda function that applies the visitor to each variant.
const auto evaluatedMatch = boost::apply_visitor(
[model](auto const &obj) -> FieldMatch { return *obj.evaluate(model); }, match);
evaluatedMatches.insert({name, evaluatedMatch});
const auto *evaluatedMatch = match->evaluate(model)->checkedTo<TableMatch>();
evaluatedMatches[name] = evaluatedMatch;
}
const auto *evaluatedAction = action.evaluate(model);
return new TableRule(evaluatedMatches, priority, *evaluatedAction, ttl);
Expand Down Expand Up @@ -361,6 +336,4 @@ std::map<cstring, const TestObject *> TestSpec::getTestObjectCategory(cstring ca
return {};
}

} // namespace P4Testgen

} // namespace P4Tools
} // namespace P4Tools::P4Testgen
42 changes: 7 additions & 35 deletions backends/p4tools/modules/testgen/lib/test_spec.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
#include "lib/castable.h"
#include "lib/cstring.h"

namespace P4Tools {

namespace P4Testgen {
namespace P4Tools::P4Testgen {

/// This file defines a series of test objects which, in sum, produce an abstract test
/// specification.
Expand Down Expand Up @@ -148,6 +146,8 @@ class TableMatch : public TestObject {
const IR::KeyElement *getKey() const;
};

using TableMatchMap = std::map<cstring, const TableMatch *>;

class Ternary : public TableMatch {
private:
/// The actual match value.
Expand Down Expand Up @@ -244,35 +244,10 @@ class Exact : public TableMatch {
const IR::Constant *getEvaluatedValue() const;
};

class Optional : public TableMatch {
private:
/// The value the key is matched with.
const IR::Expression *value;

/// Whether to add this optional match as an exact match.
bool addMatch;

public:
explicit Optional(const IR::KeyElement *key, const IR::Expression *value, bool addMatch);

const Optional *evaluate(const Model &model) const override;

cstring getObjectName() const override;

/// @returns the match value. It is expected to be a constant at this point.
/// A BUG is thrown otherwise.
const IR::Constant *getEvaluatedValue() const;

/// @returns whether to add this optional match as an exact match.
bool addAsExactMatch() const;
};

using FieldMatch = boost::variant<Exact, Ternary, LPM, Range, Optional>;

class TableRule : public TestObject {
private:
/// Each element in the map is the control plane name of the key paired with its match rule.
const std::map<cstring, const FieldMatch> matches;
const TableMatchMap matches;

/// The priority of this entry. This is required for STF back ends when matching ternary.
int priority;
Expand All @@ -282,15 +257,14 @@ class TableRule : public TestObject {
int ttl;

public:
TableRule(std::map<cstring, const FieldMatch> matches, int priority, ActionCall action,
int ttl);
TableRule(TableMatchMap matches, int priority, ActionCall action, int ttl);

const TableRule *evaluate(const Model &model) const override;

cstring getObjectName() const override;

/// @returns the list of keys that need to match to execute the action.
const std::map<cstring, const FieldMatch> *getMatches() const;
const TableMatchMap *getMatches() const;

/// @returns the priority of this entry.
int getPriority() const;
Expand Down Expand Up @@ -404,8 +378,6 @@ class TestSpec {
static constexpr int TTL = 0;
};

} // namespace P4Testgen

} // namespace P4Tools
} // namespace P4Tools::P4Testgen

#endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_LIB_TEST_SPEC_H_ */
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "lib/null.h"
#include "nlohmann/json.hpp"

#include "backends/p4tools/modules/testgen/lib/exceptions.h"
#include "backends/p4tools/modules/testgen/lib/tf.h"
#include "backends/p4tools/modules/testgen/targets/bmv2/test_spec.h"

Expand Down Expand Up @@ -165,7 +166,7 @@ inja::json Protobuf::getControlPlane(const TestSpec *testSpec) {
return controlPlaneJson;
}

inja::json Protobuf::getControlPlaneForTable(const std::map<cstring, const FieldMatch> &matches,
inja::json Protobuf::getControlPlaneForTable(const TableMatchMap &matches,
const std::vector<ActionArg> &args) {
inja::json rulesJson;

Expand All @@ -182,69 +183,39 @@ inja::json Protobuf::getControlPlaneForTable(const std::map<cstring, const Field
auto const fieldName = match.first;
auto const &fieldMatch = match.second;

inja::json j;
j["field_name"] = fieldName;
auto p4RuntimeId = getIdAnnotation(fieldMatch->getKey());
BUG_CHECK(p4RuntimeId, "Id not present for key. Can not generate test.");
j["id"] = *p4RuntimeId;
// Iterate over the match fields and segregate them.
struct GetRange : public boost::static_visitor<void> {
cstring fieldName;
inja::json &rulesJson;

GetRange(inja::json &rulesJson, cstring fieldName)
: fieldName(fieldName), rulesJson(rulesJson) {}

void operator()(const Exact &elem) const {
inja::json j;
j["field_name"] = fieldName;
j["value"] = formatHexExprWithSep(elem.getEvaluatedValue());
auto p4RuntimeId = getIdAnnotation(elem.getKey());
BUG_CHECK(p4RuntimeId, "Id not present for key. Can not generate test.");
j["id"] = *p4RuntimeId;
rulesJson["single_exact_matches"].push_back(j);
}
void operator()(const Range &elem) const {
inja::json j;
j["field_name"] = fieldName;
j["lo"] = formatHexExprWithSep(elem.getEvaluatedLow());
j["hi"] = formatHexExprWithSep(elem.getEvaluatedHigh());
auto p4RuntimeId = getIdAnnotation(elem.getKey());
BUG_CHECK(p4RuntimeId, "Id not present for key. Can not generate test.");
j["id"] = *p4RuntimeId;
rulesJson["range_matches"].push_back(j);
// If the rule has a range match we need to add the priority.
rulesJson["needs_priority"] = true;
}
void operator()(const Ternary &elem) const {
inja::json j;
j["field_name"] = fieldName;
j["value"] = formatHexExprWithSep(elem.getEvaluatedValue());
j["mask"] = formatHexExprWithSep(elem.getEvaluatedMask());
auto p4RuntimeId = getIdAnnotation(elem.getKey());
BUG_CHECK(p4RuntimeId, "Id not present for key. Can not generate test.");
j["id"] = *p4RuntimeId;
rulesJson["ternary_matches"].push_back(j);
// If the rule has a range match we need to add the priority.
rulesJson["needs_priority"] = true;
}
void operator()(const LPM &elem) const {
inja::json j;
j["field_name"] = fieldName;
j["value"] = formatHexExprWithSep(elem.getEvaluatedValue());
j["prefix_len"] = elem.getEvaluatedPrefixLength()->value.str();
auto p4RuntimeId = getIdAnnotation(elem.getKey());
BUG_CHECK(p4RuntimeId, "Id not present for key. Can not generate test.");
j["id"] = *p4RuntimeId;
rulesJson["lpm_matches"].push_back(j);
}
void operator()(const Optional &elem) const {
inja::json j;
j["field_name"] = fieldName;
j["value"] = formatHexExpr(elem.getEvaluatedValue()).c_str();
auto p4RuntimeId = getIdAnnotation(elem.getKey());
BUG_CHECK(p4RuntimeId, "Id not present for key. Can not generate test.");
j["id"] = *p4RuntimeId;
rulesJson["needs_priority"] = true;
rulesJson["optional_matches"].push_back(j);
}
};
boost::apply_visitor(GetRange(rulesJson, fieldName), fieldMatch);
if (const auto *elem = fieldMatch->to<Exact>()) {
j["value"] = formatHexExprWithSep(elem->getEvaluatedValue());
rulesJson["single_exact_matches"].push_back(j);
} else if (const auto *elem = fieldMatch->to<Range>()) {
j["lo"] = formatHexExprWithSep(elem->getEvaluatedLow());
j["hi"] = formatHexExprWithSep(elem->getEvaluatedHigh());
rulesJson["range_matches"].push_back(j);
// If the rule has a range match we need to add the priority.
rulesJson["needs_priority"] = true;
} else if (const auto *elem = fieldMatch->to<Ternary>()) {
j["value"] = formatHexExprWithSep(elem->getEvaluatedValue());
j["mask"] = formatHexExprWithSep(elem->getEvaluatedMask());
rulesJson["ternary_matches"].push_back(j);
// If the rule has a range match we need to add the priority.
rulesJson["needs_priority"] = true;
} else if (const auto *elem = fieldMatch->to<LPM>()) {
j["value"] = formatHexExprWithSep(elem->getEvaluatedValue());
j["prefix_len"] = elem->getEvaluatedPrefixLength()->value.str();
rulesJson["lpm_matches"].push_back(j);
} else if (const auto *elem = fieldMatch->to<Optional>()) {
j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str();
rulesJson["needs_priority"] = true;
rulesJson["optional_matches"].push_back(j);
} else {
TESTGEN_UNIMPLEMENTED("Unsupported table match key type %1%",
fieldMatch->getObjectName());
}
}

for (const auto &actArg : args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class Protobuf : public TF {
static std::vector<std::pair<size_t, size_t>> getIgnoreMasks(const IR::Constant *mask);

/// Helper function for the control plane table inja objects.
static inja::json getControlPlaneForTable(const std::map<cstring, const FieldMatch> &matches,
static inja::json getControlPlaneForTable(const TableMatchMap &matches,
const std::vector<ActionArg> &args);

/// @return the id allocated to the object through the @id annotation if any, or
Expand Down
Loading

0 comments on commit b0d6eb8

Please sign in to comment.