From bfb4f08bf8ce13351be95bd2d3233675b2ef263d Mon Sep 17 00:00:00 2001 From: Chris Dodd Date: Mon, 10 Feb 2025 21:16:45 +0000 Subject: [PATCH 1/2] @command_line annotation - per-target selectable support, though most should want it Signed-off-by: Chris Dodd --- backends/bmv2/pna_nic/main.cpp | 2 +- backends/bmv2/psa_switch/main.cpp | 2 +- backends/bmv2/simple_switch/main.cpp | 2 +- backends/dpdk/main.cpp | 2 +- backends/ebpf/p4c-ebpf.cpp | 2 +- backends/graphs/p4c-graphs.cpp | 2 +- backends/p4test/p4test.cpp | 2 +- .../p4tools/common/compiler/compiler_target.cpp | 2 +- backends/tc/tc.cpp | 2 +- backends/tofino/bf-p4c/bf-p4c-options.h | 3 +++ frontends/common/applyOptionsPragmas.cpp | 16 ++++++++++++++++ frontends/common/applyOptionsPragmas.h | 4 ++++ frontends/p4/parseAnnotations.cpp | 3 +++ test/gtest/helpers.cpp | 2 +- 14 files changed, 36 insertions(+), 10 deletions(-) diff --git a/backends/bmv2/pna_nic/main.cpp b/backends/bmv2/pna_nic/main.cpp index 2f6872ec86e..b1fe62a00c0 100644 --- a/backends/bmv2/pna_nic/main.cpp +++ b/backends/bmv2/pna_nic/main.cpp @@ -65,7 +65,7 @@ int main(int argc, char *const argv[]) { if (program == nullptr || ::P4::errorCount() > 0) return 1; try { - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(true); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); P4::FrontEnd frontend; diff --git a/backends/bmv2/psa_switch/main.cpp b/backends/bmv2/psa_switch/main.cpp index 767fa312212..ecbead9033c 100644 --- a/backends/bmv2/psa_switch/main.cpp +++ b/backends/bmv2/psa_switch/main.cpp @@ -65,7 +65,7 @@ int main(int argc, char *const argv[]) { if (program == nullptr || ::P4::errorCount() > 0) return 1; try { - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(true); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); P4::FrontEnd frontend; diff --git a/backends/bmv2/simple_switch/main.cpp b/backends/bmv2/simple_switch/main.cpp index aaaa5aa0b44..bfa8c1b7c49 100644 --- a/backends/bmv2/simple_switch/main.cpp +++ b/backends/bmv2/simple_switch/main.cpp @@ -68,7 +68,7 @@ int main(int argc, char *const argv[]) { if (program == nullptr || ::P4::errorCount() > 0) return 1; try { - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(true); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); P4::FrontEnd frontend; diff --git a/backends/dpdk/main.cpp b/backends/dpdk/main.cpp index 6feba253855..91ea4ff9edf 100644 --- a/backends/dpdk/main.cpp +++ b/backends/dpdk/main.cpp @@ -86,7 +86,7 @@ int main(int argc, char *const argv[]) { if (program == nullptr || ::P4::errorCount() > 0) return 1; try { - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(true); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); P4::FrontEnd frontend; diff --git a/backends/ebpf/p4c-ebpf.cpp b/backends/ebpf/p4c-ebpf.cpp index 9c64fd411b1..563ff1c669a 100644 --- a/backends/ebpf/p4c-ebpf.cpp +++ b/backends/ebpf/p4c-ebpf.cpp @@ -66,7 +66,7 @@ void compile(EbpfOptions &options) { program = P4::parseP4File(options); if (::P4::errorCount() > 0) return; - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(true); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); P4::FrontEnd frontend; diff --git a/backends/graphs/p4c-graphs.cpp b/backends/graphs/p4c-graphs.cpp index d131ec0fa6e..a1d50612bf1 100644 --- a/backends/graphs/p4c-graphs.cpp +++ b/backends/graphs/p4c-graphs.cpp @@ -162,7 +162,7 @@ int main(int argc, char *const argv[]) { if (program == nullptr || ::P4::errorCount() > 0) return 1; try { - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(false); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); P4::FrontEnd fe; diff --git a/backends/p4test/p4test.cpp b/backends/p4test/p4test.cpp index 82d85a13a14..a94834a4894 100644 --- a/backends/p4test/p4test.cpp +++ b/backends/p4test/p4test.cpp @@ -135,7 +135,7 @@ int main(int argc, char *const argv[]) { info.emitInfo("PARSER"); if (program != nullptr && ::P4::errorCount() == 0) { - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(true); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); info.emitInfo("PASS P4COptionPragmaParser"); diff --git a/backends/p4tools/common/compiler/compiler_target.cpp b/backends/p4tools/common/compiler/compiler_target.cpp index 96a05c29fb7..54d29c2a81b 100644 --- a/backends/p4tools/common/compiler/compiler_target.cpp +++ b/backends/p4tools/common/compiler/compiler_target.cpp @@ -65,7 +65,7 @@ const IR::P4Program *CompilerTarget::runParser(const ParserOptions &options) { const IR::P4Program *CompilerTarget::runFrontend(const CompilerOptions &options, const IR::P4Program *program) const { - P4COptionPragmaParser optionsPragmaParser; + P4COptionPragmaParser optionsPragmaParser(false); program->apply(ApplyOptionsPragmas(optionsPragmaParser)); auto frontEnd = mkFrontEnd(); diff --git a/backends/tc/tc.cpp b/backends/tc/tc.cpp index ece7ec7a3f8..a3c938ef8f9 100644 --- a/backends/tc/tc.cpp +++ b/backends/tc/tc.cpp @@ -55,7 +55,7 @@ int main(int argc, char *const argv[]) { return 1; } try { - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(true); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); P4::FrontEnd frontend; frontend.addDebugHook(hook); diff --git a/backends/tofino/bf-p4c/bf-p4c-options.h b/backends/tofino/bf-p4c/bf-p4c-options.h index f4f0217c01e..48c8c33e045 100644 --- a/backends/tofino/bf-p4c/bf-p4c-options.h +++ b/backends/tofino/bf-p4c/bf-p4c-options.h @@ -223,6 +223,9 @@ class BFNOptionPragmaParser : public P4::P4COptionPragmaParser { public: std::optional tryToParse(const IR::Annotation *annotation) override; + BFNOptionPragmaParser() : P4::P4COptionPragmaParser(false) {} + // FIXME -- should remove the tofino @command_line stuff and use the base class + private: std::optional parseCompilerOption(const IR::Annotation *annotation); }; diff --git a/frontends/common/applyOptionsPragmas.cpp b/frontends/common/applyOptionsPragmas.cpp index fab308c2844..bcd6b76dead 100644 --- a/frontends/common/applyOptionsPragmas.cpp +++ b/frontends/common/applyOptionsPragmas.cpp @@ -51,6 +51,22 @@ std::optional P4COptionPragmaParser::tr const IR::Annotation *annotation) { auto pragmaName = annotation->name.name; if (pragmaName == "diagnostic") return parseDiagnostic(annotation); + if (supportCommandLinePragma && pragmaName == "command_line") { + IOptionPragmaParser::CommandLineOptions options; + auto *args = annotation->needsParsing() + ? P4ParserDriver::parseExpressionList(annotation->srcInfo, + annotation->getUnparsed()) + : &annotation->getExpr(); + for (auto *arg : *args) { + if (auto *a = arg->to()) { + options.push_back(a->value.c_str()); + } else { + // can this happen? annotation parser should require only strings + warning("ignoring non-string %1% in @command_line", a); + } + } + return options; + } return std::nullopt; } diff --git a/frontends/common/applyOptionsPragmas.h b/frontends/common/applyOptionsPragmas.h index e2c4c55f4db..d79ead6d5d3 100644 --- a/frontends/common/applyOptionsPragmas.h +++ b/frontends/common/applyOptionsPragmas.h @@ -76,9 +76,13 @@ class ApplyOptionsPragmas : public Inspector { * they're unable to parse a pragma. */ class P4COptionPragmaParser : public IOptionPragmaParser { + bool supportCommandLinePragma; + public: std::optional tryToParse(const IR::Annotation *annotation) override; + explicit P4COptionPragmaParser(bool sCLP) : supportCommandLinePragma(sCLP) {} + private: std::optional parseDiagnostic(const IR::Annotation *annotation); }; diff --git a/frontends/p4/parseAnnotations.cpp b/frontends/p4/parseAnnotations.cpp index b6d8ab12459..14500c2891b 100644 --- a/frontends/p4/parseAnnotations.cpp +++ b/frontends/p4/parseAnnotations.cpp @@ -51,6 +51,9 @@ ParseAnnotations::HandlerMap ParseAnnotations::standardHandlers() { // @match has an expression argument PARSE(IR::Annotation::matchAnnotation, Expression), + + // @command_line to add to the command line + PARSE_STRING_LITERAL_LIST("command_line"_cs), }; } diff --git a/test/gtest/helpers.cpp b/test/gtest/helpers.cpp index 2beb6c3125a..6ba789b269f 100644 --- a/test/gtest/helpers.cpp +++ b/test/gtest/helpers.cpp @@ -178,7 +178,7 @@ namespace P4::Test { return std::nullopt; } - P4::P4COptionPragmaParser optionsPragmaParser; + P4::P4COptionPragmaParser optionsPragmaParser(true); program->apply(P4::ApplyOptionsPragmas(optionsPragmaParser)); if (::P4::errorCount() > 0) { std::cerr << "Encountered " << ::P4::errorCount() From 5e02a506bb2cfbafcc9270002c7fb924fdd8a2f6 Mon Sep 17 00:00:00 2001 From: Chris Dodd Date: Fri, 20 Sep 2024 08:02:08 +0000 Subject: [PATCH 2/2] Pass to get rid of action_run() calls - introduce new metadata set in the actions of the table Signed-off-by: Chris Dodd --- backends/p4test/CMakeLists.txt | 1 + backends/p4test/midend.cpp | 6 +- backends/p4test/midend.h | 3 +- backends/p4test/p4test.cpp | 103 ++++++++------- backends/p4test/p4test.h | 33 +++++ midend/CMakeLists.txt | 2 + midend/eliminateActionRun.cpp | 125 ++++++++++++++++++ midend/eliminateActionRun.h | 87 ++++++++++++ testdata/p4_16_samples/elimActRun1.p4 | 51 +++++++ .../elimActRun1-first.p4 | 57 ++++++++ .../elimActRun1-frontend.p4 | 69 ++++++++++ .../elimActRun1-midend.p4 | 113 ++++++++++++++++ testdata/p4_16_samples_outputs/elimActRun1.p4 | 49 +++++++ .../elimActRun1.p4-stderr | 12 ++ .../elimActRun1.p4.entries.txtpb | 3 + .../elimActRun1.p4.p4info.txtpb | 73 ++++++++++ 16 files changed, 733 insertions(+), 54 deletions(-) create mode 100644 backends/p4test/p4test.h create mode 100644 midend/eliminateActionRun.cpp create mode 100644 midend/eliminateActionRun.h create mode 100644 testdata/p4_16_samples/elimActRun1.p4 create mode 100644 testdata/p4_16_samples_outputs/elimActRun1-first.p4 create mode 100644 testdata/p4_16_samples_outputs/elimActRun1-frontend.p4 create mode 100644 testdata/p4_16_samples_outputs/elimActRun1-midend.p4 create mode 100644 testdata/p4_16_samples_outputs/elimActRun1.p4 create mode 100644 testdata/p4_16_samples_outputs/elimActRun1.p4-stderr create mode 100644 testdata/p4_16_samples_outputs/elimActRun1.p4.entries.txtpb create mode 100644 testdata/p4_16_samples_outputs/elimActRun1.p4.p4info.txtpb diff --git a/backends/p4test/CMakeLists.txt b/backends/p4test/CMakeLists.txt index 87f10c7c778..8e6c8199ba8 100644 --- a/backends/p4test/CMakeLists.txt +++ b/backends/p4test/CMakeLists.txt @@ -22,6 +22,7 @@ set (P4TEST_SRCS midend.cpp ) set (P4TEST_HDRS + p4test.h midend.h ) diff --git a/backends/p4test/midend.cpp b/backends/p4test/midend.cpp index 4c1da8b857e..1c7dfffae1e 100644 --- a/backends/p4test/midend.cpp +++ b/backends/p4test/midend.cpp @@ -33,6 +33,7 @@ limitations under the License. #include "midend/complexComparison.h" #include "midend/copyStructures.h" #include "midend/def_use.h" +#include "midend/eliminateActionRun.h" #include "midend/eliminateInvalidHeaders.h" #include "midend/eliminateNewtype.h" #include "midend/eliminateSerEnums.h" @@ -76,7 +77,7 @@ class SkipControls : public P4::ActionSynthesisPolicy { } }; -MidEnd::MidEnd(CompilerOptions &options, std::ostream *outStream) { +MidEnd::MidEnd(P4TestOptions &options, std::ostream *outStream) { bool isv1 = options.langVersion == CompilerOptions::FrontendVersion::P4_14; refMap.setIsV1(isv1); auto evaluator = new P4::EvaluatorPass(&refMap, &typeMap); @@ -125,7 +126,8 @@ MidEnd::MidEnd(CompilerOptions &options, std::ostream *outStream) { new P4::SimplifyControlFlow(&typeMap), new P4::CompileTimeOperations(), new P4::TableHit(&typeMap), - new P4::EliminateSwitch(&typeMap), + !options.preferSwitch ? new P4::EliminateSwitch(&typeMap) : nullptr, + options.preferSwitch ? new P4::ElimActionRun() : nullptr, new P4::ResolveReferences(&refMap), new P4::TypeChecking(&refMap, &typeMap, true), // update types before ComputeDefUse new PassRepeated({ diff --git a/backends/p4test/midend.h b/backends/p4test/midend.h index 3b10075090d..14e6c101349 100644 --- a/backends/p4test/midend.h +++ b/backends/p4test/midend.h @@ -20,6 +20,7 @@ limitations under the License. #include "frontends/common/options.h" #include "frontends/p4/evaluator/evaluator.h" #include "ir/ir.h" +#include "p4test.h" namespace P4::P4Test { @@ -32,7 +33,7 @@ class MidEnd : public PassManager { IR::ToplevelBlock *toplevel = nullptr; void addDebugHook(DebugHook hook) { hooks.push_back(hook); } - explicit MidEnd(CompilerOptions &options, std::ostream *outStream = nullptr); + explicit MidEnd(P4TestOptions &options, std::ostream *outStream = nullptr); IR::ToplevelBlock *process(const IR::P4Program *&program) { addDebugHooks(hooks, true); program = program->apply(*this); diff --git a/backends/p4test/p4test.cpp b/backends/p4test/p4test.cpp index a94834a4894..2dfb245aa39 100644 --- a/backends/p4test/p4test.cpp +++ b/backends/p4test/p4test.cpp @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include "p4test.h" + #include // IWYU pragma: keep #include @@ -35,57 +37,56 @@ limitations under the License. #include "lib/nullstream.h" #include "midend.h" -using namespace P4; - -class P4TestOptions : public CompilerOptions { - public: - bool parseOnly = false; - bool validateOnly = false; - bool loadIRFromJson = false; - P4TestOptions() { - registerOption( - "--listMidendPasses", nullptr, - [this](const char *) { - listMidendPasses = true; - loadIRFromJson = false; - P4Test::MidEnd MidEnd(*this, outStream); - exit(0); - return false; - }, - "[p4test] Lists exact name of all midend passes.\n"); - registerOption( - "--parse-only", nullptr, - [this](const char *) { - parseOnly = true; - return true; - }, - "only parse the P4 input, without any further processing"); - registerOption( - "--validate", nullptr, - [this](const char *) { - validateOnly = true; - return true; - }, - "Validate the P4 input, running just the front-end"); - registerOption( - "--fromJSON", "file", - [this](const char *arg) { - loadIRFromJson = true; - file = arg; - return true; - }, - "read previously dumped json instead of P4 source code"); - registerOption( - "--turn-off-logn", nullptr, - [](const char *) { - ::P4::Log::Detail::enableLoggingGlobally = false; - return true; - }, - "Turn off LOGN() statements in the compiler.\n" - "Use '@__debug' annotation to enable LOGN on " - "the annotated P4 object within the source code.\n"); - } -}; +P4TestOptions::P4TestOptions() { + registerOption( + "--listMidendPasses", nullptr, + [this](const char *) { + listMidendPasses = true; + loadIRFromJson = false; + P4Test::MidEnd MidEnd(*this, outStream); + exit(0); + return false; + }, + "[p4test] Lists exact name of all midend passes.\n"); + registerOption( + "--parse-only", nullptr, + [this](const char *) { + parseOnly = true; + return true; + }, + "only parse the P4 input, without any further processing"); + registerOption( + "--validate", nullptr, + [this](const char *) { + validateOnly = true; + return true; + }, + "Validate the P4 input, running just the front-end"); + registerOption( + "--fromJSON", "file", + [this](const char *arg) { + loadIRFromJson = true; + file = arg; + return true; + }, + "read previously dumped json instead of P4 source code"); + registerOption( + "--turn-off-logn", nullptr, + [](const char *) { + ::P4::Log::Detail::enableLoggingGlobally = false; + return true; + }, + "Turn off LOGN() statements in the compiler.\n" + "Use '@__debug' annotation to enable LOGN on " + "the annotated P4 object within the source code.\n"); + registerOption( + "--preferSwitch", nullptr, + [this](const char *) { + preferSwitch = true; + return true; + }, + "use passes that convert to switch instead eliminating them"); +} using P4TestContext = P4CContextWithOptions; diff --git a/backends/p4test/p4test.h b/backends/p4test/p4test.h new file mode 100644 index 00000000000..2995406198c --- /dev/null +++ b/backends/p4test/p4test.h @@ -0,0 +1,33 @@ +/* +Copyright 2013-present Barefoot Networks, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef BACKENDS_P4TEST_P4TEST_H_ +#define BACKENDS_P4TEST_P4TEST_H_ + +#include "frontends/common/options.h" + +using namespace P4; + +class P4TestOptions : public CompilerOptions { + public: + bool parseOnly = false; + bool validateOnly = false; + bool loadIRFromJson = false; + bool preferSwitch = false; + P4TestOptions(); +}; + +#endif /* BACKENDS_P4TEST_P4TEST_H_ */ diff --git a/midend/CMakeLists.txt b/midend/CMakeLists.txt index f2716798fb4..d2dc03f86e3 100644 --- a/midend/CMakeLists.txt +++ b/midend/CMakeLists.txt @@ -21,6 +21,7 @@ set (MIDEND_SRCS copyStructures.cpp coverage.cpp def_use.cpp + eliminateActionRun.cpp eliminateInvalidHeaders.cpp eliminateNewtype.cpp eliminateSerEnums.cpp @@ -74,6 +75,7 @@ set (MIDEND_HDRS copyStructures.h coverage.h def_use.h + eliminateActionRun.h eliminateInvalidHeaders.h eliminateNewtype.h eliminateSerEnums.h diff --git a/midend/eliminateActionRun.cpp b/midend/eliminateActionRun.cpp new file mode 100644 index 00000000000..b8fe6bafb31 --- /dev/null +++ b/midend/eliminateActionRun.cpp @@ -0,0 +1,125 @@ +/* +Copyright 2024 Intel Corp. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "eliminateActionRun.h" + +#include "frontends/p4/methodInstance.h" + +namespace P4 { + +void ElimActionRun::ActionTableUse::postorder(const IR::Member *mem) { + if (mem->member != "action_run") return; + auto *mce = mem->expr->to(); + if (!mce) return; + auto *mi = MethodInstance::resolve(mce, this); + auto *tbl = mi->object->to(); + if (!tbl) return; + LOG3("found: " << mem); + auto [it, inserted] = self.actionRunTables.emplace(tbl->name, tbl); + if (!inserted) { + LOG3(" (additional apply() of table)"); + return; + } + auto *info = &it->second; + IR::IndexedVector tags; + for (auto *ale : tbl->getActionList()->actionList) { + cstring name = ale->getName(); + BUG_CHECK(!self.actionsToModify.count(name), + "action %s used in multiple tables (%s and %s) -- LocalizeActions must be run " + "before ElimActionRun", + name, self.actionsToModify.at(name)->tbl->name, tbl->name); + self.actionsToModify[name] = info; + cstring tag = self.nameGen.newName(tbl->name + "_" + name); + info->actions.emplace(name, tag); + tags.push_back(new IR::Declaration_ID(tag)); + LOG4(" action " << name << " [" << ale->expression << "] -> " << tag); + } + info->action_tags = + new IR::Type_Enum(self.nameGen.newName(tbl->name + "_action_run_t"), std::move(tags)); + info->action_run = new IR::Declaration_Variable(self.nameGen.newName(tbl->name + "_action_run"), + new IR::Type_Name(info->action_tags->name)); +} + +const IR::Expression *ElimActionRun::RewriteActionRun::postorder(IR::Member *mem) { + if (mem->member != "action_run") return mem; + auto *mce = mem->expr->to(); + if (!mce) return mem; + auto *mi = MethodInstance::resolve(mce, this); + auto *tbl = mi->object->to(); + if (!tbl) return mem; + auto &info = self.actionRunTables.at(tbl->name); + auto *parent = findContext(); + BUG_CHECK(parent && parent->is(), "action_run not in switch"); + auto &pps = self.prepend_statement[parent]; + if (!pps) pps = new IR::Vector; + pps->push_back(new IR::MethodCallStatement(mce)); + self.add_to_control.push_back(info.action_run); + return new IR::PathExpression(new IR::Type_Name(info.action_tags->name), info.action_run->name); +} + +const IR::P4Action *ElimActionRun::RewriteActionRun::preorder(IR::P4Action *act) { + auto *info = get(self.actionsToModify, act->name); + if (!info) return act; + auto *body = act->body->clone(); + auto *tag_type = new IR::Type_Name(info->action_tags->name); + + body->components.insert( + body->components.begin(), + new IR::AssignmentStatement( + new IR::PathExpression(tag_type->clone(), info->action_run->name), + new IR::Member(new IR::TypeNameExpression(tag_type), info->actions.at(act->name)))); + act->body = body; + return act; +} + +const IR::SwitchCase *ElimActionRun::RewriteActionRun::postorder(IR::SwitchCase *swCase) { + auto *swtch = getParent(); + BUG_CHECK(swtch, "case not in switch"); + if (!self.prepend_statement.count(swtch)) return swCase; + if (swCase->label->is()) return swCase; + auto *pe = swCase->label->to(); + BUG_CHECK(pe, "case label is not an action name"); + auto *info = self.actionsToModify.at(pe->path->name); + auto *tag_type = new IR::Type_Name(info->action_tags->name); + swCase->label = + new IR::Member(new IR::TypeNameExpression(tag_type), info->actions.at(pe->path->name)); + return swCase; +} + +const IR::Node *ElimActionRun::RewriteActionRun::postorder(IR::Statement *stmt) { + auto *pps = get(self.prepend_statement, stmt); + if (!pps) return stmt; + pps->push_back(stmt); + self.prepend_statement.erase(stmt); + return pps; +} + +const IR::P4Control *ElimActionRun::RewriteActionRun::postorder(IR::P4Control *ctrl) { + BUG_CHECK(self.prepend_statement.empty(), "orphan action_run in control %s", ctrl->name); + ctrl->controlLocals.prepend(self.add_to_control); + if (!self.add_to_control.empty()) LOG4(ctrl); + self.add_to_control.clear(); + return ctrl; +} + +const IR::P4Program *ElimActionRun::RewriteActionRun::postorder(IR::P4Program *prog) { + auto front = prog->objects.begin(); + for (auto &[_, info] : self.actionRunTables) + front = std::next(prog->objects.insert(front, info.action_tags)); + return prog; +} + +} // namespace P4 diff --git a/midend/eliminateActionRun.h b/midend/eliminateActionRun.h new file mode 100644 index 00000000000..645baf2ea72 --- /dev/null +++ b/midend/eliminateActionRun.h @@ -0,0 +1,87 @@ +/* +Copyright 2024 Intel Corp. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef MIDEND_ELIMINATEACTIONRUN_H_ +#define MIDEND_ELIMINATEACTIONRUN_H_ + +#include "frontends/common/resolveReferences/resolveReferences.h" +#include "ir/ir.h" + +namespace P4 { + +class ElimActionRun : public PassManager { + MinimalNameGenerator nameGen; + + struct atinfo_t { + const IR::P4Table *tbl; + const IR::Type_Enum *action_tags; + const IR::Declaration_Variable *action_run; + std::map actions; + explicit atinfo_t(const IR::P4Table *t) : tbl(t) {} + }; + + std::map actionRunTables; + std::map actionsToModify; + std::map *> prepend_statement; + IR::Vector add_to_control; + + class ActionTableUse : public Inspector, public ResolutionContext { + ElimActionRun &self; + + public: + explicit ActionTableUse(ElimActionRun &s) : self(s) {} + + bool preorder(const IR::P4Parser *) override { return false; } + bool preorder(const IR::P4Action *) override { return false; } + bool preorder(const IR::Function *) override { return false; } + + void postorder(const IR::Member *) override; + } actionTableUse; + + class RewriteActionRun : public Transform, public ResolutionContext { + ElimActionRun &self; + + public: + explicit RewriteActionRun(ElimActionRun &s) : self(s) {} + + const IR::P4Parser *preorder(IR::P4Parser *p) override { + prune(); + return p; + } + const IR::Function *preorder(IR::Function *f) override { + prune(); + return f; + } + + const IR::Expression *postorder(IR::Member *) override; + const IR::P4Action *preorder(IR::P4Action *) override; + const IR::SwitchCase *postorder(IR::SwitchCase *) override; + const IR::Node *postorder(IR::Statement *) override; + const IR::P4Control *postorder(IR::P4Control *) override; + const IR::P4Program *postorder(IR::P4Program *) override; + }; + + public: + ElimActionRun() : actionTableUse(*this) { + addPasses({&actionTableUse, new PassIf([this]() { return !actionRunTables.empty(); }, + {&nameGen, new RewriteActionRun(*this), + [this]() { actionRunTables.clear(); }})}); + } +}; + +} // namespace P4 + +#endif /* MIDEND_ELIMINATEACTIONRUN_H_ */ diff --git a/testdata/p4_16_samples/elimActRun1.p4 b/testdata/p4_16_samples/elimActRun1.p4 new file mode 100644 index 00000000000..744fc6b4829 --- /dev/null +++ b/testdata/p4_16_samples/elimActRun1.p4 @@ -0,0 +1,51 @@ +#include + +@command_line("--preferSwitch") + +header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +struct Headers { + ethernet_t eth_hdr; +} + +control ingress(inout Headers h) { + action dummy_action() { } + table simple_table_1 { + key = { + 48w1 : exact @name("key") ; + } + actions = { + dummy_action(); + } + } + table simple_table_2 { + key = { + 48w1 : exact @name("key") ; + } + actions = { + dummy_action(); + } + } + apply { + switch (simple_table_1.apply().action_run) { + dummy_action: { + switch (simple_table_2.apply().action_run) { + dummy_action: { + h.eth_hdr.src_addr = 4; + return; + } + } + } + } + exit; + + } +} + +control c(inout H h); +package top(c c); +top(ingress()) main; diff --git a/testdata/p4_16_samples_outputs/elimActRun1-first.p4 b/testdata/p4_16_samples_outputs/elimActRun1-first.p4 new file mode 100644 index 00000000000..2ccdf6f737d --- /dev/null +++ b/testdata/p4_16_samples_outputs/elimActRun1-first.p4 @@ -0,0 +1,57 @@ +#include + +@command_line("--preferSwitch") header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +struct Headers { + ethernet_t eth_hdr; +} + +control ingress(inout Headers h) { + action dummy_action() { + } + table simple_table_1 { + key = { + 48w1: exact @name("key"); + } + actions = { + dummy_action(); + @defaultonly NoAction(); + } + default_action = NoAction(); + } + table simple_table_2 { + key = { + 48w1: exact @name("key"); + } + actions = { + dummy_action(); + @defaultonly NoAction(); + } + default_action = NoAction(); + } + apply { + switch (simple_table_1.apply().action_run) { + dummy_action: { + switch (simple_table_2.apply().action_run) { + dummy_action: { + h.eth_hdr.src_addr = 48w4; + return; + } + default: { + } + } + } + default: { + } + } + exit; + } +} + +control c(inout H h); +package top(c c); +top(ingress()) main; diff --git a/testdata/p4_16_samples_outputs/elimActRun1-frontend.p4 b/testdata/p4_16_samples_outputs/elimActRun1-frontend.p4 new file mode 100644 index 00000000000..c8c8a9c3cfa --- /dev/null +++ b/testdata/p4_16_samples_outputs/elimActRun1-frontend.p4 @@ -0,0 +1,69 @@ +#include + +@command_line("--preferSwitch") header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +struct Headers { + ethernet_t eth_hdr; +} + +control ingress(inout Headers h) { + @name("ingress.hasReturned") bool hasReturned; + @noWarn("unused") @name(".NoAction") action NoAction_1() { + } + @noWarn("unused") @name(".NoAction") action NoAction_2() { + } + @name("ingress.dummy_action") action dummy_action() { + } + @name("ingress.dummy_action") action dummy_action_1() { + } + @name("ingress.simple_table_1") table simple_table { + key = { + 48w1: exact @name("key"); + } + actions = { + dummy_action(); + @defaultonly NoAction_1(); + } + default_action = NoAction_1(); + } + @name("ingress.simple_table_2") table simple_table_0 { + key = { + 48w1: exact @name("key"); + } + actions = { + dummy_action_1(); + @defaultonly NoAction_2(); + } + default_action = NoAction_2(); + } + apply { + hasReturned = false; + switch (simple_table.apply().action_run) { + dummy_action: { + switch (simple_table_0.apply().action_run) { + dummy_action_1: { + h.eth_hdr.src_addr = 48w4; + hasReturned = true; + } + default: { + } + } + } + default: { + } + } + if (hasReturned) { + ; + } else { + exit; + } + } +} + +control c(inout H h); +package top(c c); +top(ingress()) main; diff --git a/testdata/p4_16_samples_outputs/elimActRun1-midend.p4 b/testdata/p4_16_samples_outputs/elimActRun1-midend.p4 new file mode 100644 index 00000000000..79416c9e89b --- /dev/null +++ b/testdata/p4_16_samples_outputs/elimActRun1-midend.p4 @@ -0,0 +1,113 @@ +enum simple_table_action_run_t { + simple_table_dummy_action, + simple_table_NoAction +} + +enum simple_table_0_action_run_t { + simple_table_0_dummy_action, + simple_table_0_NoAction +} + +#include + +@command_line("--preferSwitch") header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +struct Headers { + ethernet_t eth_hdr; +} + +control ingress(inout Headers h) { + simple_table_action_run_t simple_table_action_run; + simple_table_0_action_run_t simple_table_0_action_run; + @name("ingress.hasReturned") bool hasReturned; + bit<48> key_0; + bit<48> key_1; + @noWarn("unused") @name(".NoAction") action NoAction_1() { + simple_table_action_run = simple_table_action_run_t.simple_table_NoAction; + } + @noWarn("unused") @name(".NoAction") action NoAction_2() { + simple_table_0_action_run = simple_table_0_action_run_t.simple_table_0_NoAction; + } + @name("ingress.dummy_action") action dummy_action() { + simple_table_action_run = simple_table_action_run_t.simple_table_dummy_action; + } + @name("ingress.dummy_action") action dummy_action_1() { + simple_table_0_action_run = simple_table_0_action_run_t.simple_table_0_dummy_action; + } + @name("ingress.simple_table_1") table simple_table { + key = { + key_0: exact @name("key"); + } + actions = { + dummy_action(); + @defaultonly NoAction_1(); + } + default_action = NoAction_1(); + } + @name("ingress.simple_table_2") table simple_table_0 { + key = { + key_1: exact @name("key"); + } + actions = { + dummy_action_1(); + @defaultonly NoAction_2(); + } + default_action = NoAction_2(); + } + @hidden action elimActRun1l19() { + hasReturned = false; + key_0 = 48w1; + } + @hidden action elimActRun1l27() { + key_1 = 48w1; + } + @hidden action elimActRun1l38() { + h.eth_hdr.src_addr = 48w4; + hasReturned = true; + } + @hidden table tbl_elimActRun1l19 { + actions = { + elimActRun1l19(); + } + const default_action = elimActRun1l19(); + } + @hidden table tbl_elimActRun1l27 { + actions = { + elimActRun1l27(); + } + const default_action = elimActRun1l27(); + } + @hidden table tbl_elimActRun1l38 { + actions = { + elimActRun1l38(); + } + const default_action = elimActRun1l38(); + } + apply { + tbl_elimActRun1l19.apply(); + simple_table.apply(); + switch (simple_table_action_run) { + simple_table_action_run_t.simple_table_dummy_action: { + tbl_elimActRun1l27.apply(); + simple_table_0.apply(); + switch (simple_table_0_action_run) { + simple_table_0_action_run_t.simple_table_0_dummy_action: { + tbl_elimActRun1l38.apply(); + } + default: { + } + } + } + default: { + } + } + } +} + +control c(inout H h); +package top(c c); +top(ingress()) main; diff --git a/testdata/p4_16_samples_outputs/elimActRun1.p4 b/testdata/p4_16_samples_outputs/elimActRun1.p4 new file mode 100644 index 00000000000..531e8c96400 --- /dev/null +++ b/testdata/p4_16_samples_outputs/elimActRun1.p4 @@ -0,0 +1,49 @@ +#include + +@command_line("--preferSwitch") header ethernet_t { + bit<48> dst_addr; + bit<48> src_addr; + bit<16> eth_type; +} + +struct Headers { + ethernet_t eth_hdr; +} + +control ingress(inout Headers h) { + action dummy_action() { + } + table simple_table_1 { + key = { + 48w1: exact @name("key"); + } + actions = { + dummy_action(); + } + } + table simple_table_2 { + key = { + 48w1: exact @name("key"); + } + actions = { + dummy_action(); + } + } + apply { + switch (simple_table_1.apply().action_run) { + dummy_action: { + switch (simple_table_2.apply().action_run) { + dummy_action: { + h.eth_hdr.src_addr = 4; + return; + } + } + } + } + exit; + } +} + +control c(inout H h); +package top(c c); +top(ingress()) main; diff --git a/testdata/p4_16_samples_outputs/elimActRun1.p4-stderr b/testdata/p4_16_samples_outputs/elimActRun1.p4-stderr new file mode 100644 index 00000000000..a016f3e461d --- /dev/null +++ b/testdata/p4_16_samples_outputs/elimActRun1.p4-stderr @@ -0,0 +1,12 @@ +elimActRun1.p4(19): [--Wwarn=ignore-prop] warning: KeyElement: constant key element + 48w1 : exact @name("key") ; + ^^^^ +elimActRun1.p4(27): [--Wwarn=ignore-prop] warning: KeyElement: constant key element + 48w1 : exact @name("key") ; + ^^^^ +elimActRun1.p4(19): [--Wwarn=mismatch] warning: 48w1: Constant key field + 48w1 : exact @name("key") ; + ^^^^ +elimActRun1.p4(27): [--Wwarn=mismatch] warning: 48w1: Constant key field + 48w1 : exact @name("key") ; + ^^^^ diff --git a/testdata/p4_16_samples_outputs/elimActRun1.p4.entries.txtpb b/testdata/p4_16_samples_outputs/elimActRun1.p4.entries.txtpb new file mode 100644 index 00000000000..5cb9652623a --- /dev/null +++ b/testdata/p4_16_samples_outputs/elimActRun1.p4.entries.txtpb @@ -0,0 +1,3 @@ +# proto-file: p4/v1/p4runtime.proto +# proto-message: p4.v1.WriteRequest + diff --git a/testdata/p4_16_samples_outputs/elimActRun1.p4.p4info.txtpb b/testdata/p4_16_samples_outputs/elimActRun1.p4.p4info.txtpb new file mode 100644 index 00000000000..b8a40a9449e --- /dev/null +++ b/testdata/p4_16_samples_outputs/elimActRun1.p4.p4info.txtpb @@ -0,0 +1,73 @@ +# proto-file: p4/config/v1/p4info.proto +# proto-message: p4.config.v1.P4Info + +pkg_info { + arch: "v1model" +} +tables { + preamble { + id: 36787877 + name: "ingress.simple_table_1" + alias: "simple_table_1" + } + match_fields { + id: 1 + name: "key" + bitwidth: 48 + match_type: EXACT + } + action_refs { + id: 27880877 + } + action_refs { + id: 21257015 + annotations: "@defaultonly" + scope: DEFAULT_ONLY + } + initial_default_action { + action_id: 21257015 + } + size: 1024 +} +tables { + preamble { + id: 48738834 + name: "ingress.simple_table_2" + alias: "simple_table_2" + } + match_fields { + id: 1 + name: "key" + bitwidth: 48 + match_type: EXACT + } + action_refs { + id: 27880877 + } + action_refs { + id: 21257015 + annotations: "@defaultonly" + scope: DEFAULT_ONLY + } + initial_default_action { + action_id: 21257015 + } + size: 1024 +} +actions { + preamble { + id: 21257015 + name: "NoAction" + alias: "NoAction" + annotations: "@noWarn(\"unused\")" + } +} +actions { + preamble { + id: 27880877 + name: "ingress.dummy_action" + alias: "dummy_action" + } +} +type_info { +}