diff --git a/backends/p4tools/modules/testgen/core/small_step/table_stepper.cpp b/backends/p4tools/modules/testgen/core/small_step/table_stepper.cpp index ee84bde69dc..81ad6627159 100644 --- a/backends/p4tools/modules/testgen/core/small_step/table_stepper.cpp +++ b/backends/p4tools/modules/testgen/core/small_step/table_stepper.cpp @@ -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; } @@ -106,9 +104,10 @@ 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 *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; @@ -116,7 +115,7 @@ const IR::Expression *TableStepper::computeTargetMatchType( 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) { @@ -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))); } @@ -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( @@ -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 *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); @@ -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 matches; + TableMatchMap matches; const auto *hitCondition = computeHit(nextState, &matches); // We get the control plane name of the action we are calling. @@ -683,6 +682,4 @@ TableStepper::TableStepper(ExprStepper *stepper, const IR::P4Table *table) properties.tableName = table->controlPlaneName(); } -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen diff --git a/backends/p4tools/modules/testgen/core/small_step/table_stepper.h b/backends/p4tools/modules/testgen/core/small_step/table_stepper.h index 57e20747ad9..1e18be4a671 100644 --- a/backends/p4tools/modules/testgen/core/small_step/table_stepper.h +++ b/backends/p4tools/modules/testgen/core/small_step/table_stepper.h @@ -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 *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 *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. diff --git a/backends/p4tools/modules/testgen/lib/test_spec.cpp b/backends/p4tools/modules/testgen/lib/test_spec.cpp index 3e5f677fca6..4c9cbf13f33 100644 --- a/backends/p4tools/modules/testgen/lib/test_spec.cpp +++ b/backends/p4tools/modules/testgen/lib/test_spec.cpp @@ -13,9 +13,7 @@ #include "ir/irutils.h" #include "lib/exceptions.h" -namespace P4Tools { - -namespace P4Testgen { +namespace P4Tools::P4Testgen { /* ========================================================================================= * Test Specification Objects @@ -167,35 +165,6 @@ const LPM *LPM::evaluate(const Model &model) const { cstring LPM::getObjectName() const { return "LPM"; } -Range::Range(const IR::KeyElement *key, const IR::Expression *low, const IR::Expression *high) - : TableMatch(key), low(low), high(high) {} - -const IR::Constant *Range::getEvaluatedLow() const { - const auto *constant = low->to(); - BUG_CHECK(constant, - "Variable is not a constant. It has type %1% instead. Has the test object %2% " - "been evaluated?", - low->type->node_type_name(), getObjectName()); - return constant; -} - -const IR::Constant *Range::getEvaluatedHigh() const { - const auto *constant = high->to(); - BUG_CHECK(constant, - "Variable is not a constant. It has type %1% instead. Has the test object %2% " - "been evaluated?", - high->type->node_type_name(), getObjectName()); - return constant; -} - -const Range *Range::evaluate(const Model &model) const { - const auto *evaluatedLow = model.evaluate(low); - const auto *evaluatedHigh = model.evaluate(high); - return new Range(getKey(), evaluatedLow, evaluatedHigh); -} - -cstring Range::getObjectName() const { return "Range"; } - Exact::Exact(const IR::KeyElement *key, const IR::Expression *val) : TableMatch(key), value(val) {} const IR::Constant *Exact::getEvaluatedValue() const { @@ -214,11 +183,10 @@ const Exact *Exact::evaluate(const Model &model) const { cstring Exact::getObjectName() const { return "Exact"; } -TableRule::TableRule(std::map 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 *TableRule::getMatches() const { return &matches; } +const TableMatchMap *TableRule::getMatches() const { return &matches; } int TableRule::getPriority() const { return priority; } @@ -229,14 +197,13 @@ int TableRule::getTTL() const { return ttl; } cstring TableRule::getObjectName() const { return "TableRule"; } const TableRule *TableRule::evaluate(const Model &model) const { - std::map 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(); + evaluatedMatches[name] = evaluatedMatch; } const auto *evaluatedAction = action.evaluate(model); return new TableRule(evaluatedMatches, priority, *evaluatedAction, ttl); @@ -340,6 +307,4 @@ std::map TestSpec::getTestObjectCategory(cstring ca return {}; } -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen diff --git a/backends/p4tools/modules/testgen/lib/test_spec.h b/backends/p4tools/modules/testgen/lib/test_spec.h index d06b88f5826..9f557c58135 100644 --- a/backends/p4tools/modules/testgen/lib/test_spec.h +++ b/backends/p4tools/modules/testgen/lib/test_spec.h @@ -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. @@ -148,6 +146,8 @@ class TableMatch : public TestObject { const IR::KeyElement *getKey() const; }; +using TableMatchMap = std::map; + class Ternary : public TableMatch { private: /// The actual match value. @@ -202,31 +202,6 @@ class LPM : public TableMatch { const IR::Constant *getEvaluatedPrefixLength() const; }; -class Range : public TableMatch { - private: - /// The inclusive start of the range. - const IR::Expression *low; - - /// The inclusive end of the range. - const IR::Expression *high; - - public: - explicit Range(const IR::KeyElement *key, const IR::Expression *low, - const IR::Expression *high); - - const Range *evaluate(const Model &model) const override; - - cstring getObjectName() const override; - - /// @returns the inclusive start of the range. It is expected to be a constant at this point. - /// A BUG is thrown otherwise. - const IR::Constant *getEvaluatedLow() const; - - /// @returns the inclusive end of the range. It is expected to be a constant at this point. - /// A BUG is thrown otherwise. - const IR::Constant *getEvaluatedHigh() const; -}; - class Exact : public TableMatch { private: /// The value the key is matched with. @@ -244,12 +219,10 @@ class Exact : public TableMatch { const IR::Constant *getEvaluatedValue() const; }; -using FieldMatch = boost::variant; - 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 matches; + const TableMatchMap matches; /// The priority of this entry. This is required for STF back ends when matching ternary. int priority; @@ -259,15 +232,14 @@ class TableRule : public TestObject { int ttl; public: - TableRule(std::map 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 *getMatches() const; + const TableMatchMap *getMatches() const; /// @returns the priority of this entry. int getPriority() const; @@ -381,8 +353,6 @@ class TestSpec { static constexpr int TTL = 0; }; -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen #endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_LIB_TEST_SPEC_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/bmv2/backend/protobuf/protobuf.cpp b/backends/p4tools/modules/testgen/targets/bmv2/backend/protobuf/protobuf.cpp index 3d55d38132e..3ecfee38e42 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/backend/protobuf/protobuf.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/backend/protobuf/protobuf.cpp @@ -31,14 +31,11 @@ #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" -namespace P4Tools { - -namespace P4Testgen { - -namespace Bmv2 { +namespace P4Tools::P4Testgen::Bmv2 { /// Wrapper helper function that automatically inserts separators for hex strings. std::string formatHexExprWithSep(const IR::Expression *expr) { @@ -169,7 +166,7 @@ inja::json Protobuf::getControlPlane(const TestSpec *testSpec) { return controlPlaneJson; } -inja::json Protobuf::getControlPlaneForTable(const std::map &matches, +inja::json Protobuf::getControlPlaneForTable(const TableMatchMap &matches, const std::vector &args) { inja::json rulesJson; @@ -178,6 +175,7 @@ inja::json Protobuf::getControlPlaneForTable(const std::mapgetKey()); + 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 { - 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); - } - }; - boost::apply_visitor(GetRange(rulesJson, fieldName), fieldMatch); + if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExprWithSep(elem->getEvaluatedValue()); + rulesJson["single_exact_matches"].push_back(j); + } else if (const auto *elem = fieldMatch->to()) { + 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()) { + 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()) { + 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()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + rulesJson["needs_priority"] = true; + rulesJson["optional_matches"].push_back(j); + } else { + TESTGEN_UNIMPLEMENTED("Unsupported table key match type \"%1%\"", + fieldMatch->getObjectName()); + } } for (const auto &actArg : args) { @@ -324,6 +302,15 @@ entities : [ } } ## endfor +## for r in rule.rules.optional_matches + # Match field {{r.field_name}} + match { + field_id: {{r.id}} + optional { + value: "{{r.value}}" + } + } +## endfor ## for r in rule.rules.range_matches # Match field {{r.field_name}} match { @@ -433,8 +420,4 @@ void Protobuf::outputTest(const TestSpec *testSpec, cstring selectedBranches, si emitTestcase(testSpec, selectedBranches, testIdx, testCase, currentCoverage); } -} // namespace Bmv2 - -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::Bmv2 diff --git a/backends/p4tools/modules/testgen/targets/bmv2/backend/protobuf/protobuf.h b/backends/p4tools/modules/testgen/targets/bmv2/backend/protobuf/protobuf.h index cd003c5123a..7aa467c492d 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/backend/protobuf/protobuf.h +++ b/backends/p4tools/modules/testgen/targets/bmv2/backend/protobuf/protobuf.h @@ -79,7 +79,7 @@ class Protobuf : public TF { static std::vector> getIgnoreMasks(const IR::Constant *mask); /// Helper function for the control plane table inja objects. - static inja::json getControlPlaneForTable(const std::map &matches, + static inja::json getControlPlaneForTable(const TableMatchMap &matches, const std::vector &args); /// @return the id allocated to the object through the @id annotation if any, or diff --git a/backends/p4tools/modules/testgen/targets/bmv2/backend/ptf/ptf.cpp b/backends/p4tools/modules/testgen/targets/bmv2/backend/ptf/ptf.cpp index c7beee7d8b7..c3065f975be 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/backend/ptf/ptf.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/backend/ptf/ptf.cpp @@ -23,6 +23,7 @@ #include "lib/log.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" @@ -115,7 +116,7 @@ inja::json PTF::getControlPlane(const TestSpec *testSpec) { return controlPlaneJson; } -inja::json PTF::getControlPlaneForTable(const std::map &matches, +inja::json PTF::getControlPlaneForTable(const TableMatchMap &matches, const std::vector &args) { inja::json rulesJson; @@ -124,54 +125,48 @@ inja::json PTF::getControlPlaneForTable(const std::map { - 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"] = formatHexExpr(elem.getEvaluatedValue()).c_str(); - rulesJson["single_exact_matches"].push_back(j); - } - void operator()(const Range &elem) const { - inja::json j; - j["field_name"] = fieldName; - j["lo"] = formatHexExpr(elem.getEvaluatedLow()).c_str(); - j["hi"] = formatHexExpr(elem.getEvaluatedHigh()).c_str(); - rulesJson["range_matches"].push_back(j); - } - void operator()(const Ternary &elem) const { - inja::json j; - j["field_name"] = fieldName; - j["value"] = formatHexExpr(elem.getEvaluatedValue()).c_str(); - j["mask"] = formatHexExpr(elem.getEvaluatedMask()).c_str(); - rulesJson["ternary_matches"].push_back(j); - // If the rule has a ternary 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"] = formatHexExpr(elem.getEvaluatedValue()).c_str(); - j["prefix_len"] = elem.getEvaluatedPrefixLength()->value.str(); - rulesJson["lpm_matches"].push_back(j); + inja::json j; + j["field_name"] = fieldName; + if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + rulesJson["single_exact_matches"].push_back(j); + } else if (const auto *elem = fieldMatch->to()) { + j["lo"] = formatHexExpr(elem->getEvaluatedLow()).c_str(); + j["hi"] = formatHexExpr(elem->getEvaluatedHigh()).c_str(); + rulesJson["range_matches"].push_back(j); + } else if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + j["mask"] = formatHexExpr(elem->getEvaluatedMask()).c_str(); + rulesJson["ternary_matches"].push_back(j); + // If the rule has a ternary match we need to add the priority. + rulesJson["needs_priority"] = true; + } else if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + j["prefix_len"] = elem->getEvaluatedPrefixLength()->value.str(); + rulesJson["lpm_matches"].push_back(j); + } else if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + if (elem->addAsExactMatch()) { + j["use_exact"] = "True"; + } else { + j["use_exact"] = "False"; } - }; - - boost::apply_visitor(GetRange(rulesJson, fieldName), fieldMatch); + rulesJson["needs_priority"] = true; + rulesJson["optional_matches"].push_back(j); + } else { + TESTGEN_UNIMPLEMENTED("Unsupported table key match type \"%1%\"", + fieldMatch->getObjectName()); + } } for (const auto &actArg : args) { @@ -299,8 +294,10 @@ class Test{{test_id}}(AbstractTest): ## for r in rule.rules.single_exact_matches self.Exact('{{r.field_name}}', {{r.value}}), ## endfor +## for r in rule.rules.optional_matches + self.Optional('{{r.field_name}}', {{r.value}}, {{r.use_exact}}), +## endfor ## for r in rule.rules.range_matches - # TODO: p4Runtime doesn't have Range match - this would fail. Need to fix. self.Range('{{r.field_name}}', {{r.lo}}, {{r.hi}}), ## endfor ## for r in rule.rules.ternary_matches diff --git a/backends/p4tools/modules/testgen/targets/bmv2/backend/ptf/ptf.h b/backends/p4tools/modules/testgen/targets/bmv2/backend/ptf/ptf.h index b29c265ebf0..2166aa4f5c3 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/backend/ptf/ptf.h +++ b/backends/p4tools/modules/testgen/targets/bmv2/backend/ptf/ptf.h @@ -78,7 +78,7 @@ class PTF : public TF { static std::vector> getIgnoreMasks(const IR::Constant *mask); /// Helper function for the control plane table inja objects. - static inja::json getControlPlaneForTable(const std::map &matches, + static inja::json getControlPlaneForTable(const TableMatchMap &matches, const std::vector &args); }; diff --git a/backends/p4tools/modules/testgen/targets/bmv2/backend/stf/stf.cpp b/backends/p4tools/modules/testgen/targets/bmv2/backend/stf/stf.cpp index e4dc3f5f9b2..bbb7cb5c8ca 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/backend/stf/stf.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/backend/stf/stf.cpp @@ -32,11 +32,7 @@ #include "backends/p4tools/modules/testgen/lib/tf.h" #include "backends/p4tools/modules/testgen/targets/bmv2/test_spec.h" -namespace P4Tools { - -namespace P4Testgen { - -namespace Bmv2 { +namespace P4Tools::P4Testgen::Bmv2 { STF::STF(cstring testName, boost::optional seed = boost::none) : TF(testName, seed) { boost::filesystem::path testFile(testName + ".stf"); @@ -88,7 +84,7 @@ inja::json STF::getControlPlane(const TestSpec *testSpec) { return controlPlaneJson; } -inja::json STF::getControlPlaneForTable(const std::map &matches, +inja::json STF::getControlPlaneForTable(const TableMatchMap &matches, const std::vector &args) { inja::json rulesJson; @@ -96,79 +92,65 @@ inja::json STF::getControlPlaneForTable(const std::map { - 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"] = formatHexExpr(elem.getEvaluatedValue()); - rulesJson["matches"].push_back(j); - } - void operator()(const Range & /*elem*/) const { - TESTGEN_UNIMPLEMENTED("Match type range not implemented."); - } - void operator()(const Ternary &elem) const { - inja::json j; - j["field_name"] = fieldName; - const auto *dataValue = elem.getEvaluatedValue(); - const auto *maskField = elem.getEvaluatedMask(); - BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), - "Data value and its mask should have the same bit width."); - // Using the width from mask - should be same as data - auto dataStr = formatBinExpr(dataValue, false, true, false); - auto maskStr = formatBinExpr(maskField, false, true, false); - std::string data = "0b"; - for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { - if (maskStr.at(dataPos) == '0') { - data += "*"; - } else { - data += dataStr.at(dataPos); - } + inja::json j; + j["field_name"] = fieldName; + if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()); + } else if (const auto *elem = fieldMatch->to()) { + const auto *dataValue = elem->getEvaluatedValue(); + const auto *maskField = elem->getEvaluatedMask(); + BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), + "Data value and its mask should have the same bit width."); + // Using the width from mask - should be same as data + auto dataStr = formatBinExpr(dataValue, false, true, false); + auto maskStr = formatBinExpr(maskField, false, true, false); + std::string data = "0b"; + for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { + if (maskStr.at(dataPos) == '0') { + data += "*"; + } else { + data += dataStr.at(dataPos); } - j["value"] = data; - rulesJson["matches"].push_back(j); - // If the rule has a ternary match we need to add the priority. - rulesJson["needs_priority"] = true; } - void operator()(const LPM &elem) const { - inja::json j; - j["field_name"] = fieldName; - const auto *dataValue = elem.getEvaluatedValue(); - auto prefixLen = elem.getEvaluatedPrefixLength()->asInt(); - auto fieldWidth = dataValue->type->width_bits(); - auto maxVal = IR::getMaxBvVal(prefixLen); - const auto *maskField = - IR::getConstant(dataValue->type, maxVal << (fieldWidth - prefixLen)); - BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), - "Data value and its mask should have the same bit width."); - // Using the width from mask - should be same as data - auto dataStr = formatBinExpr(dataValue, false, true, false); - auto maskStr = formatBinExpr(maskField, false, true, false); - std::string data = "0b"; - for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { - if (maskStr.at(dataPos) == '0') { - data += "*"; - } else { - data += dataStr.at(dataPos); - } + j["value"] = data; + // If the rule has a ternary match we need to add the priority. + rulesJson["needs_priority"] = true; + } else if (const auto *elem = fieldMatch->to()) { + const auto *dataValue = elem->getEvaluatedValue(); + auto prefixLen = elem->getEvaluatedPrefixLength()->asInt(); + auto fieldWidth = dataValue->type->width_bits(); + auto maxVal = IR::getMaxBvVal(prefixLen); + const auto *maskField = + IR::getConstant(dataValue->type, maxVal << (fieldWidth - prefixLen)); + BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), + "Data value and its mask should have the same bit width."); + // Using the width from mask - should be same as data + auto dataStr = formatBinExpr(dataValue, false, true, false); + auto maskStr = formatBinExpr(maskField, false, true, false); + std::string data = "0b"; + for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { + if (maskStr.at(dataPos) == '0') { + data += "*"; + } else { + data += dataStr.at(dataPos); } - j["value"] = data; - rulesJson["matches"].push_back(j); - // If the rule has a ternary match we need to add the priority. - rulesJson["needs_priority"] = true; } - }; - boost::apply_visitor(GetRange(rulesJson, fieldName), fieldMatch); + j["value"] = data; + // If the rule has a ternary match we need to add the priority. + rulesJson["needs_priority"] = true; + } else if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()).c_str(); + rulesJson["needs_priority"] = true; + } else { + TESTGEN_UNIMPLEMENTED("Unsupported table key match type \"%1%\"", + fieldMatch->getObjectName()); + } + rulesJson["matches"].push_back(j); } for (const auto &actArg : args) { @@ -332,8 +314,4 @@ void STF::outputTest(const TestSpec *testSpec, cstring selectedBranches, size_t emitTestcase(testSpec, selectedBranches, testIdx, testCase, currentCoverage); } -} // namespace Bmv2 - -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::Bmv2 diff --git a/backends/p4tools/modules/testgen/targets/bmv2/backend/stf/stf.h b/backends/p4tools/modules/testgen/targets/bmv2/backend/stf/stf.h index 81fb7ae72a7..114afc67b40 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/backend/stf/stf.h +++ b/backends/p4tools/modules/testgen/targets/bmv2/backend/stf/stf.h @@ -68,7 +68,7 @@ class STF : public TF { static inja::json::array_t getClone(const std::map &cloneInfos); /// Helper function for the control plane table inja objects. - static inja::json getControlPlaneForTable(const std::map &matches, + static inja::json getControlPlaneForTable(const TableMatchMap &matches, const std::vector &args); }; diff --git a/backends/p4tools/modules/testgen/targets/bmv2/table_stepper.cpp b/backends/p4tools/modules/testgen/targets/bmv2/table_stepper.cpp index 2f41e029ba2..c216204d065 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/table_stepper.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/table_stepper.cpp @@ -31,19 +31,28 @@ #include "backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.h" #include "backends/p4tools/modules/testgen/targets/bmv2/test_spec.h" -namespace P4Tools { - -namespace P4Testgen { - -namespace Bmv2 { +namespace P4Tools::P4Testgen::Bmv2 { const IR::Expression *BMv2_V1ModelTableStepper::computeTargetMatchType( - ExecutionState *nextState, const KeyProperties &keyProperties, - std::map *matches, const IR::Expression *hitCondition) { + ExecutionState *nextState, const KeyProperties &keyProperties, TableMatchMap *matches, + const IR::Expression *hitCondition) { const IR::Expression *keyExpr = keyProperties.key->expression; // TODO: We consider optional match types to be a no-op, but we could make them exact matches. if (keyProperties.matchType == BMv2Constants::MATCH_KIND_OPT) { + // We can recover from taint by simply not adding the optional match. + // 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.isTainted) { + matches->emplace(keyProperties.name, + new Optional(keyProperties.key, ctrlPlaneKey, false)); + } else { + const IR::Expression *keyExpr = keyProperties.key->expression; + matches->emplace(keyProperties.name, + new Optional(keyProperties.key, ctrlPlaneKey, true)); + hitCondition = new IR::LAnd(hitCondition, new IR::Equ(keyExpr, ctrlPlaneKey)); + } return hitCondition; } // Action selector entries are not part of the match. @@ -67,7 +76,7 @@ const IR::Expression *BMv2_V1ModelTableStepper::computeTargetMatchType( minKey = nextState->createZombieConst(keyExpr->type, minName); maxKey = nextState->createZombieConst(keyExpr->type, maxName); } - matches->emplace(keyProperties.name, Range(keyProperties.key, minKey, maxKey)); + matches->emplace(keyProperties.name, new Range(keyProperties.key, minKey, maxKey)); return new IR::LAnd(hitCondition, new IR::LAnd(new IR::LAnd(new IR::Lss(minKey, maxKey), new IR::Leq(minKey, keyExpr)), new IR::Leq(keyExpr, maxKey))); @@ -124,7 +133,7 @@ void BMv2_V1ModelTableStepper::evalTableActionProfile( synthesizedAction->arguments = arguments; // Now we compute the hit condition to trigger this particular action call. - std::map matches; + TableMatchMap matches; const auto *hitCondition = computeHit(nextState, &matches); // We need to set the table action in the state for eventual switch action_run hits. @@ -214,7 +223,7 @@ void BMv2_V1ModelTableStepper::evalTableActionSelector( synthesizedAction->arguments = arguments; // Now we compute the hit condition to trigger this particular action call. - std::map matches; + TableMatchMap matches; const auto *hitCondition = computeHit(nextState, &matches); // We need to set the table action in the state for eventual switch action_run hits. @@ -441,8 +450,4 @@ BMv2_V1ModelTableStepper::BMv2_V1ModelTableStepper(BMv2_V1ModelExprStepper *step const IR::P4Table *table) : TableStepper(stepper, table) {} -} // namespace Bmv2 - -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::Bmv2 diff --git a/backends/p4tools/modules/testgen/targets/bmv2/table_stepper.h b/backends/p4tools/modules/testgen/targets/bmv2/table_stepper.h index 9888f7ea819..bdc5fa9ca9b 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/table_stepper.h +++ b/backends/p4tools/modules/testgen/targets/bmv2/table_stepper.h @@ -64,7 +64,7 @@ class BMv2_V1ModelTableStepper : public TableStepper { protected: const IR::Expression *computeTargetMatchType(ExecutionState *nextState, const KeyProperties &keyProperties, - std::map *matches, + TableMatchMap *matches, const IR::Expression *hitCondition) override; void checkTargetProperties( diff --git a/backends/p4tools/modules/testgen/targets/bmv2/test/BMV2PTFXfail.cmake b/backends/p4tools/modules/testgen/targets/bmv2/test/BMV2PTFXfail.cmake index 829821ce3bc..6b4f94ca318 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/test/BMV2PTFXfail.cmake +++ b/backends/p4tools/modules/testgen/targets/bmv2/test/BMV2PTFXfail.cmake @@ -265,8 +265,6 @@ p4tools_add_xfail_reason( # At index 0: INVALID_ARGUMENT, 'Bytestring provided does not fit within 0 bits' pins_middleblock.p4 issue2283_1-bmv2.p4 - # INVALID_ARGUMENT, 'Zero priority for ternary match' - dash-pipeline.p4 ) p4tools_add_xfail_reason( diff --git a/backends/p4tools/modules/testgen/targets/bmv2/test/p4-programs/bmv2_table_opt.p4 b/backends/p4tools/modules/testgen/targets/bmv2/test/p4-programs/bmv2_table_opt.p4 new file mode 100644 index 00000000000..6409f10df63 --- /dev/null +++ b/backends/p4tools/modules/testgen/targets/bmv2/test/p4-programs/bmv2_table_opt.p4 @@ -0,0 +1,94 @@ +#include + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> ether_type; +} + +header ipv4_t { + bit<4> version; + bit<4> ihl; + bit<6> dscp; + bit<2> ecn; + bit<16> total_len; + bit<16> identification; + bit<1> reserved; + bit<1> do_not_fragment; + bit<1> more_fragments; + bit<13> frag_offset; + bit<8> ttl; + bit<8> protocol; + bit<16> header_checksum; + bit<32> src_addr; + bit<32> dst_addr; +} + +struct local_metadata_t { +} + +struct Headers { + ethernet_t ethernet; + ipv4_t ipv4; +} + + +parser p(packet_in pkt, out Headers h, inout local_metadata_t local_metadata, inout standard_metadata_t stdmeta) { + state start { + pkt.extract(h.ethernet); + pkt.extract(h.ipv4); + transition accept; + } + +} + +control vrfy(inout Headers h, inout local_metadata_t local_metadata) { + apply { } +} + + +control ingress(inout Headers h, inout local_metadata_t local_metadata, inout standard_metadata_t s) { + action acl_drop(inout standard_metadata_t standard_metadata) { + mark_to_drop(standard_metadata); + } + + table acl_ingress_table { + key = { + h.ethernet.dst_addr : exact @name("dst_mac"); + h.ipv4.src_addr : exact @name("src_ip"); + h.ipv4.dst_addr : exact @name("dst_ip"); + h.ipv4.ttl : optional @name("ttl"); + h.ipv4.dscp : optional @name("dscp"); + h.ipv4.ecn : optional @name("ecn"); + h.ipv4.protocol : optional @name("ip_protocol"); + } + actions = { + acl_drop(s); + @defaultonly NoAction; + } + const default_action = NoAction; + } + apply { + if (h.ipv4.isValid()) { + acl_ingress_table.apply(); + } + } + +} + +control egress(inout Headers h, inout local_metadata_t local_metadata, inout standard_metadata_t s) { + apply { } +} + +control update(inout Headers h, inout local_metadata_t local_metadata) { + apply { } +} + +control deparser(packet_out pkt, in Headers h) { + apply { + pkt.emit(h); + } +} + + +V1Switch(p(), vrfy(), ingress(), egress(), update(), deparser()) main; diff --git a/backends/p4tools/modules/testgen/targets/bmv2/test_spec.cpp b/backends/p4tools/modules/testgen/targets/bmv2/test_spec.cpp index 741978de0df..e47cd5fe4db 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/test_spec.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/test_spec.cpp @@ -5,11 +5,7 @@ #include "backends/p4tools/modules/testgen/lib/test_spec.h" -namespace P4Tools { - -namespace P4Testgen { - -namespace Bmv2 { +namespace P4Tools::P4Testgen::Bmv2 { /* ========================================================================================= * Bmv2Register @@ -173,8 +169,58 @@ const Bmv2_CloneInfo *Bmv2_CloneInfo::evaluate(const Model &model) const { bool Bmv2_CloneInfo::isClonedPacket() const { return isClone; } -} // namespace Bmv2 +/* ========================================================================================= + * Table Key Match Types + * ========================================================================================= */ + +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(); + 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; } + +Range::Range(const IR::KeyElement *key, const IR::Expression *low, const IR::Expression *high) + : TableMatch(key), low(low), high(high) {} + +const IR::Constant *Range::getEvaluatedLow() const { + const auto *constant = low->to(); + BUG_CHECK(constant, + "Variable is not a constant. It has type %1% instead. Has the test object %2% " + "been evaluated?", + low->type->node_type_name(), getObjectName()); + return constant; +} + +const IR::Constant *Range::getEvaluatedHigh() const { + const auto *constant = high->to(); + BUG_CHECK(constant, + "Variable is not a constant. It has type %1% instead. Has the test object %2% " + "been evaluated?", + high->type->node_type_name(), getObjectName()); + return constant; +} + +const Range *Range::evaluate(const Model &model) const { + const auto *evaluatedLow = model.evaluate(low); + const auto *evaluatedHigh = model.evaluate(high); + return new Range(getKey(), evaluatedLow, evaluatedHigh); +} -} // namespace P4Testgen +cstring Range::getObjectName() const { return "Range"; } -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::Bmv2 diff --git a/backends/p4tools/modules/testgen/targets/bmv2/test_spec.h b/backends/p4tools/modules/testgen/targets/bmv2/test_spec.h index 680e78647e0..9181411e580 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/test_spec.h +++ b/backends/p4tools/modules/testgen/targets/bmv2/test_spec.h @@ -12,11 +12,7 @@ #include "backends/p4tools/modules/testgen/lib/test_spec.h" -namespace P4Tools { - -namespace P4Testgen { - -namespace Bmv2 { +namespace P4Tools::P4Testgen::Bmv2 { /* ========================================================================================= * Bmv2Register @@ -181,10 +177,58 @@ class Bmv2_CloneInfo : public TestObject { const IR::Constant *getEvaluatedSessionId() const; }; -} // namespace Bmv2 +/* ========================================================================================= + * Table Key Match Types + * ========================================================================================= */ + +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; -} // namespace P4Testgen + 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; +}; + +class Range : public TableMatch { + private: + /// The inclusive start of the range. + const IR::Expression *low; + + /// The inclusive end of the range. + const IR::Expression *high; + + public: + explicit Range(const IR::KeyElement *key, const IR::Expression *low, + const IR::Expression *high); + + const Range *evaluate(const Model &model) const override; + + cstring getObjectName() const override; + + /// @returns the inclusive start of the range. It is expected to be a constant at this point. + /// A BUG is thrown otherwise. + const IR::Constant *getEvaluatedLow() const; + + /// @returns the inclusive end of the range. It is expected to be a constant at this point. + /// A BUG is thrown otherwise. + const IR::Constant *getEvaluatedHigh() const; +}; -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::Bmv2 #endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_BMV2_TEST_SPEC_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/ebpf/backend/stf/stf.cpp b/backends/p4tools/modules/testgen/targets/ebpf/backend/stf/stf.cpp index 56b0ec6e7e3..bd4ed82ffa9 100644 --- a/backends/p4tools/modules/testgen/targets/ebpf/backend/stf/stf.cpp +++ b/backends/p4tools/modules/testgen/targets/ebpf/backend/stf/stf.cpp @@ -32,11 +32,7 @@ #include "backends/p4tools/modules/testgen/lib/exceptions.h" #include "backends/p4tools/modules/testgen/lib/tf.h" -namespace P4Tools { - -namespace P4Testgen { - -namespace EBPF { +namespace P4Tools::P4Testgen::EBPF { STF::STF(cstring testName, boost::optional seed = boost::none) : TF(testName, seed) { boost::filesystem::path testFile(testName + ".stf"); @@ -77,7 +73,7 @@ inja::json STF::getControlPlane(const TestSpec *testSpec) { return controlPlaneJson; } -inja::json STF::getControlPlaneForTable(const std::map &matches, +inja::json STF::getControlPlaneForTable(const TableMatchMap &matches, const std::vector &args) { inja::json rulesJson; @@ -97,75 +93,57 @@ inja::json STF::getControlPlaneForTable(const std::map { - 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"] = formatHexExpr(elem.getEvaluatedValue()); - rulesJson["matches"].push_back(j); - } - void operator()(const Range & /*elem*/) const { - TESTGEN_UNIMPLEMENTED("Match type range not implemented."); - } - void operator()(const Ternary &elem) const { - inja::json j; - j["field_name"] = fieldName; - const auto *dataValue = elem.getEvaluatedValue(); - const auto *maskField = elem.getEvaluatedMask(); - BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), - "Data value and its mask should have the same bit width."); - // Using the width from mask - should be same as data - auto dataStr = formatBinExpr(dataValue, false, true, false); - auto maskStr = formatBinExpr(maskField, false, true, false); - std::string data = "0b"; - for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { - if (maskStr.at(dataPos) == '0') { - data += "*"; - } else { - data += dataStr.at(dataPos); - } + inja::json j; + j["field_name"] = fieldName; + if (const auto *elem = fieldMatch->to()) { + j["value"] = formatHexExpr(elem->getEvaluatedValue()); + } else if (const auto *elem = fieldMatch->to()) { + const auto *dataValue = elem->getEvaluatedValue(); + const auto *maskField = elem->getEvaluatedMask(); + BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), + "Data value and its mask should have the same bit width."); + // Using the width from mask - should be same as data + auto dataStr = formatBinExpr(dataValue, false, true, false); + auto maskStr = formatBinExpr(maskField, false, true, false); + std::string data = "0b"; + for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { + if (maskStr.at(dataPos) == '0') { + data += "*"; + } else { + data += dataStr.at(dataPos); } - j["value"] = data; - rulesJson["matches"].push_back(j); - // If the rule has a ternary match we need to add the priority. - rulesJson["needs_priority"] = true; } - void operator()(const LPM &elem) const { - inja::json j; - j["field_name"] = fieldName; - const auto *dataValue = elem.getEvaluatedValue(); - auto prefixLen = elem.getEvaluatedPrefixLength()->asInt(); - auto fieldWidth = dataValue->type->width_bits(); - auto maxVal = IR::getMaxBvVal(prefixLen); - const auto *maskField = - IR::getConstant(dataValue->type, maxVal << (fieldWidth - prefixLen)); - BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), - "Data value and its mask should have the same bit width."); - // Using the width from mask - should be same as data - auto dataStr = formatBinExpr(dataValue, false, true, false); - auto maskStr = formatBinExpr(maskField, false, true, false); - std::string data = "0b"; - for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { - if (maskStr.at(dataPos) == '0') { - data += "*"; - } else { - data += dataStr.at(dataPos); - } + j["value"] = data; + // If the rule has a ternary match we need to add the priority. + rulesJson["needs_priority"] = true; + } else if (const auto *elem = fieldMatch->to()) { + const auto *dataValue = elem->getEvaluatedValue(); + auto prefixLen = elem->getEvaluatedPrefixLength()->asInt(); + auto fieldWidth = dataValue->type->width_bits(); + auto maxVal = IR::getMaxBvVal(prefixLen); + const auto *maskField = + IR::getConstant(dataValue->type, maxVal << (fieldWidth - prefixLen)); + BUG_CHECK(dataValue->type->width_bits() == maskField->type->width_bits(), + "Data value and its mask should have the same bit width."); + // Using the width from mask - should be same as data + auto dataStr = formatBinExpr(dataValue, false, true, false); + auto maskStr = formatBinExpr(maskField, false, true, false); + std::string data = "0b"; + for (size_t dataPos = 0; dataPos < dataStr.size(); ++dataPos) { + if (maskStr.at(dataPos) == '0') { + data += "*"; + } else { + data += dataStr.at(dataPos); } - j["value"] = data; - rulesJson["matches"].push_back(j); - // If the rule has a ternary match we need to add the priority. - rulesJson["needs_priority"] = true; } - }; - boost::apply_visitor(GetRange(rulesJson, fieldName), fieldMatch); + j["value"] = data; + // If the rule has a ternary match we need to add the priority. + rulesJson["needs_priority"] = true; + } else { + TESTGEN_UNIMPLEMENTED("Unsupported table key match type \"%1%\"", + fieldMatch->getObjectName()); + } + rulesJson["matches"].push_back(j); } for (const auto &actArg : args) { @@ -284,8 +262,4 @@ void STF::outputTest(const TestSpec *testSpec, cstring selectedBranches, size_t emitTestcase(testSpec, selectedBranches, testIdx, testCase, currentCoverage); } -} // namespace EBPF - -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::EBPF diff --git a/backends/p4tools/modules/testgen/targets/ebpf/backend/stf/stf.h b/backends/p4tools/modules/testgen/targets/ebpf/backend/stf/stf.h index aaec86f38c8..a400253a2ff 100644 --- a/backends/p4tools/modules/testgen/targets/ebpf/backend/stf/stf.h +++ b/backends/p4tools/modules/testgen/targets/ebpf/backend/stf/stf.h @@ -15,11 +15,7 @@ #include "backends/p4tools/modules/testgen/lib/test_spec.h" #include "backends/p4tools/modules/testgen/lib/tf.h" -namespace P4Tools { - -namespace P4Testgen { - -namespace EBPF { +namespace P4Tools::P4Testgen::EBPF { /// Extracts information from the @testSpec to emit a STF test case. class STF : public TF { @@ -65,14 +61,10 @@ class STF : public TF { static inja::json getVerify(const TestSpec *testSpec); /// Helper function for the control plane table inja objects. - static inja::json getControlPlaneForTable(const std::map &matches, + static inja::json getControlPlaneForTable(const TableMatchMap &matches, const std::vector &args); }; -} // namespace EBPF - -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::EBPF #endif /* BACKENDS_P4TOOLS_MODULES_TESTGEN_TARGETS_EBPF_BACKEND_STF_STF_H_ */ diff --git a/backends/p4tools/modules/testgen/targets/ebpf/table_stepper.cpp b/backends/p4tools/modules/testgen/targets/ebpf/table_stepper.cpp index 07e0ecd8527..756a6ff2f75 100644 --- a/backends/p4tools/modules/testgen/targets/ebpf/table_stepper.cpp +++ b/backends/p4tools/modules/testgen/targets/ebpf/table_stepper.cpp @@ -14,15 +14,12 @@ #include "backends/p4tools/modules/testgen/lib/test_spec.h" #include "backends/p4tools/modules/testgen/targets/ebpf/expr_stepper.h" -namespace P4Tools { +namespace P4Tools::P4Testgen::EBPF { -namespace P4Testgen { - -namespace EBPF { - -const IR::Expression *EBPFTableStepper::computeTargetMatchType( - ExecutionState *nextState, const KeyProperties &keyProperties, - std::map *matches, const IR::Expression *hitCondition) { +const IR::Expression *EBPFTableStepper::computeTargetMatchType(ExecutionState *nextState, + const KeyProperties &keyProperties, + TableMatchMap *matches, + const IR::Expression *hitCondition) { // If the custom match type does not match, delete to the core match types. return TableStepper::computeTargetMatchType(nextState, keyProperties, matches, hitCondition); } @@ -84,8 +81,4 @@ void EBPFTableStepper::evalTargetTable( EBPFTableStepper::EBPFTableStepper(EBPFExprStepper *stepper, const IR::P4Table *table) : TableStepper(stepper, table) {} -} // namespace EBPF - -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::EBPF diff --git a/backends/p4tools/modules/testgen/targets/ebpf/table_stepper.h b/backends/p4tools/modules/testgen/targets/ebpf/table_stepper.h index b87dcc6b823..51d0f3a0c85 100644 --- a/backends/p4tools/modules/testgen/targets/ebpf/table_stepper.h +++ b/backends/p4tools/modules/testgen/targets/ebpf/table_stepper.h @@ -12,11 +12,7 @@ #include "backends/p4tools/modules/testgen/lib/test_spec.h" #include "backends/p4tools/modules/testgen/targets/ebpf/expr_stepper.h" -namespace P4Tools { - -namespace P4Testgen { - -namespace EBPF { +namespace P4Tools::P4Testgen::EBPF { class EBPFTableStepper : public TableStepper { private: @@ -36,7 +32,7 @@ class EBPFTableStepper : public TableStepper { protected: const IR::Expression *computeTargetMatchType(ExecutionState *nextState, const KeyProperties &keyProperties, - std::map *matches, + TableMatchMap *matches, const IR::Expression *hitCondition) override; void checkTargetProperties( @@ -49,10 +45,6 @@ class EBPFTableStepper : public TableStepper { explicit EBPFTableStepper(EBPFExprStepper *stepper, const IR::P4Table *table); }; -} // namespace EBPF - -} // namespace P4Testgen - -} // namespace P4Tools +} // namespace P4Tools::P4Testgen::EBPF #endif /* TESTGEN_TARGETS_EBPF_TABLE_STEPPER_H_ */