diff --git a/backends/bmv2/pna_nic/pnaNic.cpp b/backends/bmv2/pna_nic/pnaNic.cpp index 8402b3c626..33b5e70dbf 100644 --- a/backends/bmv2/pna_nic/pnaNic.cpp +++ b/backends/bmv2/pna_nic/pnaNic.cpp @@ -93,7 +93,7 @@ void PnaNicBackend::convert(const IR::ToplevelBlock *tlb) { new P4::RemoveComplexExpressions(typeMap, new ProcessControls(&structure.pipeline_controls)), new P4::SimplifyControlFlow(typeMap), - new P4::RemoveAllUnusedDeclarations(refMap, P4::RemoveUnusedPolicy()), + new P4::RemoveAllUnusedDeclarations(P4::RemoveUnusedPolicy()), // Converts the DAG into a TREE (at least for expressions) // This is important later for conversion to JSON. new P4::CloneExpressions(), diff --git a/backends/bmv2/psa_switch/psaSwitch.cpp b/backends/bmv2/psa_switch/psaSwitch.cpp index 1b36639512..4e8775c5fb 100644 --- a/backends/bmv2/psa_switch/psaSwitch.cpp +++ b/backends/bmv2/psa_switch/psaSwitch.cpp @@ -110,7 +110,7 @@ void PsaSwitchBackend::convert(const IR::ToplevelBlock *tlb) { new P4::RemoveComplexExpressions(typeMap, new ProcessControls(&structure.pipeline_controls)), new P4::SimplifyControlFlow(typeMap), - new P4::RemoveAllUnusedDeclarations(refMap, P4::RemoveUnusedPolicy()), + new P4::RemoveAllUnusedDeclarations(P4::RemoveUnusedPolicy()), // Converts the DAG into a TREE (at least for expressions) // This is important later for conversion to JSON. new P4::CloneExpressions(), diff --git a/backends/bmv2/simple_switch/simpleSwitch.cpp b/backends/bmv2/simple_switch/simpleSwitch.cpp index 5700306503..2a1b83ec7f 100644 --- a/backends/bmv2/simple_switch/simpleSwitch.cpp +++ b/backends/bmv2/simple_switch/simpleSwitch.cpp @@ -1175,20 +1175,20 @@ void SimpleSwitchBackend::convert(const IR::ToplevelBlock *tlb) { new P4::SynthesizeActions(refMap, typeMap, new SkipControls(&structure->non_pipeline_controls)), new P4::MoveActionsToTables(refMap, typeMap), - new P4::TypeChecking(refMap, typeMap), + new P4::TypeChecking(nullptr, typeMap), new P4::SimplifyControlFlow(typeMap), new LowerExpressions(typeMap), new P4::ConstantFolding(typeMap, false), - new P4::TypeChecking(refMap, typeMap), + new P4::TypeChecking(nullptr, typeMap), new RemoveComplexExpressions(typeMap, new ProcessControls(&structure->pipeline_controls)), new P4::SimplifyControlFlow(typeMap), - new P4::RemoveAllUnusedDeclarations(refMap, P4::RemoveUnusedPolicy()), + new P4::RemoveAllUnusedDeclarations(P4::RemoveUnusedPolicy()), new P4::FlattenLogMsg(typeMap), // Converts the DAG into a TREE (at least for expressions) // This is important later for conversion to JSON. new P4::CloneExpressions(), new P4::ClearTypeMap(typeMap), - new P4::TypeChecking(refMap, typeMap, true), + new P4::TypeChecking(nullptr, typeMap, true), evaluator, [this, evaluator]() { toplevel = evaluator->getToplevelBlock(); }, }); diff --git a/backends/dpdk/backend.cpp b/backends/dpdk/backend.cpp index fd4f35c7f4..1d98d7a39e 100644 --- a/backends/dpdk/backend.cpp +++ b/backends/dpdk/backend.cpp @@ -69,8 +69,7 @@ void DpdkBackend::convert(const IR::ToplevelBlock *tlb) { new DismantleMuxExpressions(typeMap, refMap), new P4::ConstantFolding(typeMap, false), new EliminateHeaderCopy(refMap, typeMap), - new P4::TypeChecking(refMap, typeMap), - new P4::RemoveAllUnusedDeclarations(refMap, P4::RemoveUnusedPolicy()), + new P4::RemoveAllUnusedDeclarations(P4::RemoveUnusedPolicy()), new ConvertActionSelectorAndProfile(refMap, typeMap, &structure), new CollectTableInfo(&structure), new CollectAddOnMissTable(refMap, typeMap, &structure), diff --git a/frontends/common/resolveReferences/resolveReferences.h b/frontends/common/resolveReferences/resolveReferences.h index ae477219ff..bb6501a82b 100644 --- a/frontends/common/resolveReferences/resolveReferences.h +++ b/frontends/common/resolveReferences/resolveReferences.h @@ -19,7 +19,9 @@ limitations under the License. #include "absl/container/flat_hash_map.h" #include "absl/container/inlined_vector.h" +#include "frontends/common/parser_options.h" #include "ir/ir.h" +#include "ir/pass_manager.h" #include "lib/cstring.h" #include "lib/iterator_range.h" #include "referenceMap.h" @@ -170,6 +172,18 @@ class ResolveReferences : public Inspector, private ResolutionContext { void checkShadowing(const IR::INamespace *ns) const; }; +class CheckShadowing : public PassManager { + ReferenceMap refMap; + + public: + CheckShadowing() { + refMap.setIsV1(P4CContext::get().options().isv1()); + + addPasses({new ResolveReferences(&refMap, /* checkShadow */ true)}); + setName("CheckShadowing"); + } +}; + } // namespace P4 #endif /* COMMON_RESOLVEREFERENCES_RESOLVEREFERENCES_H_ */ diff --git a/frontends/p4/actionsInlining.h b/frontends/p4/actionsInlining.h index e3a9961189..ea171fef23 100644 --- a/frontends/p4/actionsInlining.h +++ b/frontends/p4/actionsInlining.h @@ -66,12 +66,11 @@ class InlineActions : public PassManager { ActionsInlineList actionsToInline; public: - InlineActions(ReferenceMap *refMap, TypeMap *typeMap, const RemoveUnusedPolicy &policy) { + InlineActions(TypeMap *typeMap, const RemoveUnusedPolicy &policy) { passes.push_back(new TypeChecking(nullptr, typeMap)); passes.push_back(new DiscoverActionsInlining(&actionsToInline, typeMap)); passes.push_back(new InlineActionsDriver(&actionsToInline, new ActionsInliner())); - passes.push_back(new ResolveReferences(refMap)); - passes.push_back(new RemoveAllUnusedDeclarations(refMap, policy)); + passes.push_back(new RemoveAllUnusedDeclarations(policy)); setName("InlineActions"); } }; diff --git a/frontends/p4/evaluator/evaluator.cpp b/frontends/p4/evaluator/evaluator.cpp index ae4630b804..ee636bcd28 100644 --- a/frontends/p4/evaluator/evaluator.cpp +++ b/frontends/p4/evaluator/evaluator.cpp @@ -17,6 +17,8 @@ limitations under the License. #include "evaluator.h" #include "frontends/common/constantFolding.h" +#include "frontends/common/parser_options.h" +#include "frontends/common/resolveReferences/referenceMap.h" #include "frontends/p4/parameterSubstitution.h" #include "frontends/p4/typeChecking/typeChecker.h" @@ -352,6 +354,11 @@ bool Evaluator::preorder(const IR::StructExpression *se) { EvaluatorPass::EvaluatorPass(ReferenceMap *refMap, TypeMap *typeMap) { setName("EvaluatorPass"); + if (!refMap) { + selfRefMap.reset(new ReferenceMap); + selfRefMap->setIsV1(P4CContext::get().options().isv1()); + refMap = selfRefMap.get(); + } evaluator = new P4::Evaluator(refMap, typeMap); passes.emplace_back(new P4::TypeChecking(refMap, typeMap)); passes.emplace_back(evaluator); diff --git a/frontends/p4/evaluator/evaluator.h b/frontends/p4/evaluator/evaluator.h index 0e864d6694..78253d52c7 100644 --- a/frontends/p4/evaluator/evaluator.h +++ b/frontends/p4/evaluator/evaluator.h @@ -17,6 +17,7 @@ limitations under the License. #ifndef EVALUATOR_EVALUATOR_H_ #define EVALUATOR_EVALUATOR_H_ +#include "frontends/common/resolveReferences/referenceMap.h" #include "frontends/common/resolveReferences/resolveReferences.h" #include "frontends/p4/typeMap.h" #include "ir/ir.h" @@ -113,10 +114,12 @@ class Evaluator final : public Inspector, public IHasBlock { /// high-level constructs. class EvaluatorPass final : public PassManager, public IHasBlock { P4::Evaluator *evaluator; + std::unique_ptr selfRefMap; public: IR::ToplevelBlock *getToplevelBlock() const override { return evaluator->getToplevelBlock(); } EvaluatorPass(ReferenceMap *refMap, TypeMap *typeMap); + explicit EvaluatorPass(TypeMap *typeMap) : EvaluatorPass(nullptr, typeMap) {} }; } // namespace P4 diff --git a/frontends/p4/frontend.cpp b/frontends/p4/frontend.cpp index f85c2163ed..4c3275463f 100644 --- a/frontends/p4/frontend.cpp +++ b/frontends/p4/frontend.cpp @@ -149,17 +149,13 @@ const IR::P4Program *FrontEnd::run(const CompilerOptions &options, const IR::P4P std::ostream *outStream) { if (program == nullptr && options.listFrontendPasses == 0) return nullptr; - bool isv1 = options.isv1(); - ReferenceMap refMap; TypeMap typeMap; - refMap.setIsV1(isv1); ParseAnnotations *parseAnnotations = policy->getParseAnnotations(); if (!parseAnnotations) parseAnnotations = new ParseAnnotations(); ConstantFoldingPolicy *constantFoldingPolicy = policy->getConstantFoldingPolicy(); - auto evaluator = new P4::EvaluatorPass(&refMap, &typeMap); PassManager passes({ new P4V1::GetV1ModelVersion, // Parse annotations @@ -169,7 +165,7 @@ const IR::P4Program *FrontEnd::run(const CompilerOptions &options, const IR::P4P new ValidateParsedProgram(), // Synthesize some built-in constructs new CreateBuiltins(), - new ResolveReferences(&refMap, /* checkShadow */ true), + new CheckShadowing(), // First pass of constant folding, before types are known -- // may be needed to compute types. new ConstantFolding(constantFoldingPolicy), @@ -212,7 +208,7 @@ const IR::P4Program *FrontEnd::run(const CompilerOptions &options, const IR::P4P new SimplifyControlFlow(&typeMap), new SwitchAddDefault, new FrontEndDump(), // used for testing the program at this point - new RemoveAllUnusedDeclarations(&refMap, *policy, true), + new RemoveAllUnusedDeclarations(*policy, true), new SimplifyParsers(), new ResetHeaders(&typeMap), new UniqueNames(), // Give each local declaration a unique internal name @@ -222,31 +218,31 @@ const IR::P4Program *FrontEnd::run(const CompilerOptions &options, const IR::P4P new SimplifyControlFlow(&typeMap), new SimplifySwitch(&typeMap), new MoveDeclarations(), // Move all local declarations to the beginning - new SimplifyDefUse(&refMap, &typeMap), + new SimplifyDefUse(&typeMap), new UniqueParameters(&typeMap), new SimplifyControlFlow(&typeMap), - new SpecializeAll(&refMap, &typeMap, policy), + new SpecializeAll(&typeMap, policy), new RemoveParserControlFlow(&typeMap), new RemoveReturns(), new RemoveDontcareArgs(&typeMap), new MoveConstructors(), - new RemoveAllUnusedDeclarations(&refMap, *policy), - new RemoveRedundantParsers(&refMap, &typeMap, *policy), + new RemoveAllUnusedDeclarations(*policy), + new RemoveRedundantParsers(&typeMap, *policy), new ClearTypeMap(&typeMap), - evaluator, + new EvaluatorPass(&typeMap), }); if (policy->optimize(options)) { passes.addPasses({ - new Inline(&refMap, &typeMap, evaluator, *policy, options.optimizeParserInlining), - new InlineActions(&refMap, &typeMap, *policy), - new LocalizeAllActions(&refMap, *policy), + new Inline(&typeMap, *policy, options.optimizeParserInlining), + new InlineActions(&typeMap, *policy), + new LocalizeAllActions(*policy), new UniqueNames(), new UniqueParameters(&typeMap), // Must be done before inlining functions, to allow // function calls used as action arguments to be inlined // in the proper place. new RemoveActionParameters(&typeMap), - new InlineFunctions(&refMap, &typeMap, *policy), + new InlineFunctions(&typeMap, *policy), new SetHeaders(&typeMap), // Check for constants only after inlining new CheckConstants(&typeMap), @@ -256,15 +252,15 @@ const IR::P4Program *FrontEnd::run(const CompilerOptions &options, const IR::P4P new RemoveParserControlFlow(&typeMap), new UniqueNames(), // needed again after inlining new MoveDeclarations(), // needed again after inlining - new SimplifyDefUse(&refMap, &typeMap), - new RemoveAllUnusedDeclarations(&refMap, *policy), + new SimplifyDefUse(&typeMap), + new RemoveAllUnusedDeclarations(*policy), new SimplifyControlFlow(&typeMap), }); } passes.addPasses({ // Check for shadowing after all inlining passes. We disable this // check during inlining since it significantly slows compilation. - new ResolveReferences(&refMap, /* checkShadow */ true), + new CheckShadowing(), new HierarchicalNames(), new FrontEndLast(), }); diff --git a/frontends/p4/functionsInlining.h b/frontends/p4/functionsInlining.h index 269cf6b90d..e54a29c13e 100644 --- a/frontends/p4/functionsInlining.h +++ b/frontends/p4/functionsInlining.h @@ -112,12 +112,12 @@ class InlineFunctions : public PassManager { FunctionsInlineList functionsToInline; public: - InlineFunctions(ReferenceMap *refMap, TypeMap *typeMap, const RemoveUnusedPolicy &policy) { - passes.push_back(new PassRepeated( - {new TypeChecking(nullptr, typeMap), - new DiscoverFunctionsInlining(&functionsToInline, typeMap), - new InlineFunctionsDriver(&functionsToInline, new FunctionsInliner()), - new ResolveReferences(refMap), new RemoveAllUnusedDeclarations(refMap, policy)})); + InlineFunctions(TypeMap *typeMap, const RemoveUnusedPolicy &policy) { + passes.push_back( + new PassRepeated({new TypeChecking(nullptr, typeMap), + new DiscoverFunctionsInlining(&functionsToInline, typeMap), + new InlineFunctionsDriver(&functionsToInline, new FunctionsInliner()), + new RemoveAllUnusedDeclarations(policy)})); passes.push_back(new CloneVariableDeclarations()); setName("InlineFunctions"); } diff --git a/frontends/p4/inlining.h b/frontends/p4/inlining.h index 0fa3bed68b..b54d627417 100644 --- a/frontends/p4/inlining.h +++ b/frontends/p4/inlining.h @@ -18,6 +18,7 @@ limitations under the License. #define FRONTENDS_P4_INLINING_H_ #include "commonInlining.h" +#include "frontends/common/parser_options.h" #include "frontends/common/resolveReferences/referenceMap.h" #include "frontends/p4/evaluator/evaluator.h" #include "frontends/p4/evaluator/substituteParameters.h" @@ -436,7 +437,7 @@ class InlinePass : public PassManager { new DiscoverInlining(&toInline, refMap, typeMap, evaluator), new InlineDriver( &toInline, new GeneralInliner(refMap, optimizeParserInlining)), - new RemoveAllUnusedDeclarations(refMap, policy)}) { + new RemoveAllUnusedDeclarations(policy)}) { setName("InlinePass"); } }; @@ -446,16 +447,23 @@ Performs inlining as many times as necessary. Most frequently once will be enough. Multiple iterations are necessary only when instances are passed as arguments using constructor arguments. */ -class Inline : public PassRepeated { +class Inline : public PassManager { static std::set noPropagateAnnotations; + ReferenceMap refMap; public: - Inline(ReferenceMap *refMap, TypeMap *typeMap, EvaluatorPass *evaluator, - const RemoveUnusedPolicy &policy, bool optimizeParserInlining) - : PassManager({new InlinePass(refMap, typeMap, evaluator, policy, optimizeParserInlining), - // After inlining the output of the evaluator changes, so - // we have to run it again - evaluator}) { + Inline(TypeMap *typeMap, const RemoveUnusedPolicy &policy, bool optimizeParserInlining, + EvaluatorPass *evaluator = nullptr) { + refMap.setIsV1(P4CContext::get().options().isv1()); + auto *evInstance = evaluator ? evaluator : new EvaluatorPass(&refMap, typeMap); + addPasses({ + evInstance, + new PassRepeated( + {new InlinePass(&refMap, typeMap, evInstance, policy, optimizeParserInlining), + // After inlining the output of the evaluator changes, so we have to + // run it again + evInstance}), + }); setName("Inline"); } diff --git a/frontends/p4/localizeActions.h b/frontends/p4/localizeActions.h index 554f2575f6..77311ffa07 100644 --- a/frontends/p4/localizeActions.h +++ b/frontends/p4/localizeActions.h @@ -168,7 +168,7 @@ class LocalizeAllActions : public PassManager { ActionReplacement localReplacements; public: - explicit LocalizeAllActions(ReferenceMap *refMap, const RemoveUnusedPolicy &policy) { + explicit LocalizeAllActions(const RemoveUnusedPolicy &policy) { passes.emplace_back(new TagGlobalActions()); passes.emplace_back(new PassRepeated{ new FindGlobalActionUses(&globalReplacements), @@ -176,8 +176,7 @@ class LocalizeAllActions : public PassManager { }); passes.emplace_back(new FindRepeatedActionUses(&localReplacements)); passes.emplace_back(new DuplicateActions(&localReplacements)); - passes.emplace_back(new ResolveReferences(refMap)); - passes.emplace_back(new RemoveAllUnusedDeclarations(refMap, policy)); + passes.emplace_back(new RemoveAllUnusedDeclarations(policy)); setName("LocalizeAllActions"); } }; diff --git a/frontends/p4/redundantParsers.cpp b/frontends/p4/redundantParsers.cpp index ec3f2e8c89..575473e1a7 100644 --- a/frontends/p4/redundantParsers.cpp +++ b/frontends/p4/redundantParsers.cpp @@ -42,7 +42,7 @@ const IR::Node *EliminateSubparserCalls::postorder(IR::MethodCallStatement *mcs) // will be reported in later passes (i.e. DiscoverInlining). if (!findContext()) return mcs; - auto mi = MethodInstance::resolve(mcs->methodCall, refMap, typeMap, true); + auto mi = MethodInstance::resolve(mcs->methodCall, this, typeMap, true); if (!mi->isApply()) return mcs; auto apply = mi->to()->applyObject; @@ -52,7 +52,7 @@ const IR::Node *EliminateSubparserCalls::postorder(IR::MethodCallStatement *mcs) auto declInstance = mi->object->to(); if (!declInstance) return mcs; - auto decl = refMap->getDeclaration(declInstance->type->to()->path); + auto decl = getDeclaration(declInstance->type->to()->path); auto p4parser = decl->to(); if (!p4parser || !redundantParsers.count(p4parser)) return mcs; diff --git a/frontends/p4/redundantParsers.h b/frontends/p4/redundantParsers.h index 0c790e1bbb..370c430d39 100644 --- a/frontends/p4/redundantParsers.h +++ b/frontends/p4/redundantParsers.h @@ -17,6 +17,7 @@ limitations under the License. #ifndef FRONTENDS_P4_REDUNDANTPARSERS_H_ #define FRONTENDS_P4_REDUNDANTPARSERS_H_ +#include "frontends/common/resolveReferences/resolveReferences.h" #include "frontends/p4/typeChecking/typeChecker.h" #include "frontends/p4/unusedDeclarations.h" #include "ir/ir.h" @@ -38,27 +39,26 @@ class FindRedundantParsers : public Inspector { /** Find .apply() calls on parsers that are on redundantParsers, and * eliminate them. */ -class EliminateSubparserCalls : public Transform { +class EliminateSubparserCalls : public Transform, public ResolutionContext { const std::set &redundantParsers; - ReferenceMap *refMap; TypeMap *typeMap; const IR::Node *postorder(IR::MethodCallStatement *methodCallStmt) override; public: EliminateSubparserCalls(const std::set &redundantParsers, - ReferenceMap *refMap, TypeMap *typeMap) - : redundantParsers(redundantParsers), refMap(refMap), typeMap(typeMap) {} + TypeMap *typeMap) + : redundantParsers(redundantParsers), typeMap(typeMap) {} }; class RemoveRedundantParsers : public PassManager { std::set redundantParsers; public: - RemoveRedundantParsers(ReferenceMap *refMap, TypeMap *typeMap, const RemoveUnusedPolicy &policy) - : PassManager{new TypeChecking(refMap, typeMap, true), + RemoveRedundantParsers(TypeMap *typeMap, const RemoveUnusedPolicy &policy) + : PassManager{new TypeChecking(nullptr, typeMap, true), new FindRedundantParsers(redundantParsers), - new EliminateSubparserCalls(redundantParsers, refMap, typeMap), - new RemoveAllUnusedDeclarations(refMap, policy)} { + new EliminateSubparserCalls(redundantParsers, typeMap), + new RemoveAllUnusedDeclarations(policy)} { setName("RemoveRedundantParsers"); } }; diff --git a/frontends/p4/simplifyDefUse.h b/frontends/p4/simplifyDefUse.h index 3cebf9490d..ebee8e1d4c 100644 --- a/frontends/p4/simplifyDefUse.h +++ b/frontends/p4/simplifyDefUse.h @@ -17,6 +17,7 @@ limitations under the License. #ifndef FRONTENDS_P4_SIMPLIFYDEFUSE_H_ #define FRONTENDS_P4_SIMPLIFYDEFUSE_H_ +#include "frontends/common/parser_options.h" #include "frontends/p4/cloner.h" #include "frontends/p4/typeChecking/typeChecker.h" #include "ir/ir.h" @@ -61,6 +62,8 @@ class RemoveHidden : public Transform { }; class SimplifyDefUse : public PassManager { + ReferenceMap refMap; + class Cloner : public CloneExpressions { public: Cloner() { setName("Cloner"); } @@ -90,18 +93,18 @@ class SimplifyDefUse : public PassManager { }; public: - SimplifyDefUse(ReferenceMap *refMap, TypeMap *typeMap, TypeChecking *typeChecking = nullptr) { - CHECK_NULL(refMap); + explicit SimplifyDefUse(TypeMap *typeMap, TypeChecking *typeChecking = nullptr) { CHECK_NULL(typeMap); + refMap.setIsV1(P4CContext::get().options().isv1()); + // SimplifyDefUse needs the expression tree *not* to be a DAG, // because it keeps state in hash-maps indexed with PathExpressions. // This is achieved by Cloner. passes.push_back(new Cloner()); - if (!typeChecking) typeChecking = new TypeChecking(refMap, typeMap); + if (!typeChecking) typeChecking = new TypeChecking(&refMap, typeMap); - auto repeated = new PassRepeated({typeChecking, new DoSimplifyDefUse(refMap, typeMap)}); - passes.push_back(repeated); + passes.push_back(new PassRepeated({typeChecking, new DoSimplifyDefUse(&refMap, typeMap)})); passes.push_back(new RemoveHidden()); setName("SimplifyDefUse"); } diff --git a/frontends/p4/specialize.cpp b/frontends/p4/specialize.cpp index a6edf0fcab..16c6dbb2c9 100644 --- a/frontends/p4/specialize.cpp +++ b/frontends/p4/specialize.cpp @@ -287,15 +287,13 @@ const IR::Node *Specialize::postorder(IR::Declaration_Instance *decl) { return instantiate(replacement, getContext()); } -SpecializeAll::SpecializeAll(ReferenceMap *refMap, TypeMap *typeMap, FrontEndPolicy *policy) - : PassRepeated({}) { +SpecializeAll::SpecializeAll(TypeMap *typeMap, FrontEndPolicy *policy) : PassRepeated({}) { passes.emplace_back(new ConstantFolding(typeMap, policy->getConstantFoldingPolicy())); passes.emplace_back(new TypeChecking(nullptr, typeMap)); passes.emplace_back(new FindSpecializations(&specMap)); passes.emplace_back(new Specialize(&specMap)); passes.emplace_back(new TypeInference(typeMap, false)); // more casts may be needed - passes.emplace_back(new ResolveReferences(refMap)); - passes.emplace_back(new RemoveAllUnusedDeclarations(refMap, *policy)); + passes.emplace_back(new RemoveAllUnusedDeclarations(*policy)); specMap.typeMap = typeMap; setName("SpecializeAll"); } diff --git a/frontends/p4/specialize.h b/frontends/p4/specialize.h index 4722c5daef..44dda566ff 100644 --- a/frontends/p4/specialize.h +++ b/frontends/p4/specialize.h @@ -222,7 +222,7 @@ class SpecializeAll : public PassRepeated { SpecializationMap specMap; public: - SpecializeAll(ReferenceMap *refMap, TypeMap *typeMap, FrontEndPolicy *policy); + SpecializeAll(TypeMap *typeMap, FrontEndPolicy *policy); }; } // namespace P4 diff --git a/frontends/p4/unusedDeclarations.cpp b/frontends/p4/unusedDeclarations.cpp index 802801f4aa..dc20e7beab 100644 --- a/frontends/p4/unusedDeclarations.cpp +++ b/frontends/p4/unusedDeclarations.cpp @@ -16,21 +16,50 @@ limitations under the License. #include "unusedDeclarations.h" -#include "frontends/common/parser_options.h" #include "sideEffects.h" namespace P4 { RemoveUnusedDeclarations *RemoveUnusedPolicy::getRemoveUnusedDeclarationsPass( - const ReferenceMap *refMap, bool warn) const { - return new RemoveUnusedDeclarations(refMap, warn); + const UsedDeclSet &used, bool warn) const { + return new RemoveUnusedDeclarations(used, warn); } Visitor::profile_t RemoveUnusedDeclarations::init_apply(const IR::Node *node) { - LOG4("Reference map " << refMap); + LOG4("Used set " << used); return Transform::init_apply(node); } +bool CollectUsedDeclarations::preorder(const IR::KeyElement *ke) { + visit(ke->annotations, "annotations"); + visit(ke->expression, "expression"); + + auto decls = lookupMatchKind(ke->matchType->path->name); + BUG_CHECK(decls.size() == 1, ""); + used.setUsed(decls.front()); + + return false; +} + +bool CollectUsedDeclarations::preorder(const IR::PathExpression *path) { + auto decl = resolvePath(path->path, false); + if (decl) used.setUsed(decl); + + return true; +} + +bool CollectUsedDeclarations::preorder(const IR::Type_Name *type) { + auto decl = resolvePath(type->path, true); + if (decl) used.setUsed(decl); + + return true; +} + +void UsedDeclSet::dbprint(std::ostream &out) const { + if (usedDecls.empty()) out << "Empty" << '\n'; + for (const auto *decl : usedDecls) out << dbp(decl) << '\n'; +} + bool RemoveUnusedDeclarations::giveWarning(const IR::Node *node) { if (warned == nullptr) return false; auto p = warned->emplace(node); @@ -40,7 +69,7 @@ bool RemoveUnusedDeclarations::giveWarning(const IR::Node *node) { const IR::Node *RemoveUnusedDeclarations::preorder(IR::Type_Enum *type) { prune(); // never remove individual enum members - if (!refMap->isUsed(getOriginal())) { + if (!used.isUsed(getOriginal())) { LOG3("Removing " << type); return nullptr; } @@ -49,7 +78,7 @@ const IR::Node *RemoveUnusedDeclarations::preorder(IR::Type_Enum *type) { const IR::Node *RemoveUnusedDeclarations::preorder(IR::Type_SerEnum *type) { prune(); // never remove individual enum members - if (!refMap->isUsed(getOriginal())) { + if (!used.isUsed(getOriginal())) { LOG3("Removing " << type); return nullptr; } @@ -58,7 +87,7 @@ const IR::Node *RemoveUnusedDeclarations::preorder(IR::Type_SerEnum *type) { const IR::Node *RemoveUnusedDeclarations::preorder(IR::P4Control *cont) { auto orig = getOriginal(); - if (!refMap->isUsed(orig)) { + if (!used.isUsed(orig)) { if (giveWarning(orig)) warn(ErrorType::WARN_UNUSED, "Control %2% is not used; removing", cont, cont->externalName()); @@ -75,7 +104,7 @@ const IR::Node *RemoveUnusedDeclarations::preorder(IR::P4Control *cont) { const IR::Node *RemoveUnusedDeclarations::preorder(IR::P4Parser *parser) { auto orig = getOriginal(); - if (!refMap->isUsed(orig)) { + if (!used.isUsed(orig)) { if (giveWarning(orig)) warn(ErrorType::WARN_UNUSED, "Parser %2% is not used; removing", parser, parser->externalName()); @@ -91,7 +120,7 @@ const IR::Node *RemoveUnusedDeclarations::preorder(IR::P4Parser *parser) { } const IR::Node *RemoveUnusedDeclarations::preorder(IR::P4Table *table) { - if (!refMap->isUsed(getOriginal())) { + if (!used.isUsed(getOriginal())) { if (giveWarning(getOriginal())) warn(ErrorType::WARN_UNUSED, "Table %1% is not used; removing", table); LOG3("Removing " << table); @@ -115,7 +144,7 @@ const IR::Node *RemoveUnusedDeclarations::process(const IR::IDeclaration *decl) if (decl->externalName(decl->getName().name).startsWith("__")) // Internal identifiers, e.g., __v1model_version return decl->getNode(); - if (refMap->isUsed(getOriginal())) return decl->getNode(); + if (used.isUsed(getOriginal())) return decl->getNode(); LOG3("Removing " << getOriginal()); prune(); // no need to go deeper return nullptr; @@ -131,7 +160,7 @@ const IR::Node *RemoveUnusedDeclarations::preorder(IR::Parameter *param) { } const IR::Node *RemoveUnusedDeclarations::warnIfUnused(const IR::Node *node) { - if (!refMap->isUsed(getOriginal())) + if (!used.isUsed(getOriginal())) if (giveWarning(getOriginal())) warn(ErrorType::WARN_UNUSED, "'%1%' is unused", node); return node; } @@ -139,13 +168,13 @@ const IR::Node *RemoveUnusedDeclarations::warnIfUnused(const IR::Node *node) { const IR::Node *RemoveUnusedDeclarations::preorder(IR::Declaration_Instance *decl) { // Don't delete instances; they may have consequences on the control-plane API if (decl->getName().name == IR::P4Program::main && getParent()) return decl; - if (!refMap->isUsed(getOriginal())) { + if (!used.isUsed(getOriginal())) { if (giveWarning(getOriginal())) warn(ErrorType::WARN_UNUSED, "%1%: unused instance", decl); // We won't delete extern instances; these may be useful even if not references. auto type = decl->type; if (type->is()) type = type->to()->baseType; if (type->is()) - type = refMap->getDeclaration(type->to()->path, true)->to(); + type = getDeclaration(type->to()->path, true)->to(); if (!type->is()) return process(decl); prune(); return decl; @@ -160,7 +189,7 @@ const IR::Node *RemoveUnusedDeclarations::preorder(IR::ParserState *state) { state->name == IR::ParserState::start) return state; - if (refMap->isUsed(getOriginal())) return state; + if (used.isUsed(getOriginal())) return state; LOG3("Removing " << state); prune(); return nullptr; diff --git a/frontends/p4/unusedDeclarations.h b/frontends/p4/unusedDeclarations.h index 5bc9f838fc..cb877f531f 100644 --- a/frontends/p4/unusedDeclarations.h +++ b/frontends/p4/unusedDeclarations.h @@ -20,19 +20,60 @@ limitations under the License. #include "../common/resolveReferences/resolveReferences.h" #include "ir/ir.h" #include "ir/pass_manager.h" +#include "lib/stringify.h" namespace P4 { class RemoveUnusedDeclarations; +class UsedDeclSet : public IHasDbPrint { + /// Set containing all declarations in the program. + absl::flat_hash_set usedDecls; + + public: + bool setUsed(const IR::IDeclaration *decl) { return usedDecls.emplace(decl).second; } + + [[nodiscard]] auto begin() const { return usedDecls.begin(); } + + [[nodiscard]] auto end() const { return usedDecls.end(); } + + void clear() { usedDecls.clear(); } + + void dbprint(std::ostream &cout) const override; + + /// @returns @true if @p decl is used in the program. + bool isUsed(const IR::IDeclaration *decl) const { return usedDecls.contains(decl); } +}; + class RemoveUnusedPolicy { public: /// The policy for removing unused declarations is baked into the pass -- targets can specify /// their own subclass of the pass with a changed policy and return that here - virtual RemoveUnusedDeclarations *getRemoveUnusedDeclarationsPass(const ReferenceMap *refMap, + virtual RemoveUnusedDeclarations *getRemoveUnusedDeclarationsPass(const UsedDeclSet &used, bool warn = false) const; }; +/// @brief Collects all used declarations into @used set +class CollectUsedDeclarations : public Inspector, ResolutionContext { + UsedDeclSet &used; + + public: + explicit CollectUsedDeclarations(UsedDeclSet &used) : used(used) {} + + // We might be invoked in PassRepeated scenario, so the used set should be + // force cleared. + Visitor::profile_t init_apply(const IR::Node *node) override { + auto rv = Inspector::init_apply(node); + used.clear(); + + return rv; + } + + bool preorder(const IR::KeyElement *ke) override; + bool preorder(const IR::PathExpression *path) override; + bool preorder(const IR::Type_Name *type) override; +}; + /** @brief Removes unused declarations. * * The following kinds of nodes are not removed even if they are unreferenced: @@ -55,13 +96,17 @@ class RemoveUnusedPolicy { * * @pre Requires an up-to-date ReferenceMap. */ -class RemoveUnusedDeclarations : public Transform { +class RemoveUnusedDeclarations : public Transform, ResolutionContext { protected: - const ReferenceMap *refMap; + const UsedDeclSet &used; - /** If not null, logs the following unused elements in @warn: + /** If not null, logs the following unused elements in @warned: * - unused IR::P4Table nodes * - unused IR::Declaration_Instance nodes + * + * Unused extern instances are not removed but may still trigger + * warnings. The @warned set keeps track of warnings emitted in + * previous iterations to avoid emitting duplicate warnings. */ std::set *warned; @@ -78,9 +123,8 @@ class RemoveUnusedDeclarations : public Transform { // Prevent direct instantiations of this class. friend class RemoveUnusedPolicy; - RemoveUnusedDeclarations(const ReferenceMap *refMap, bool warn) - : refMap(refMap), warned(warn ? new std::set : nullptr) { - CHECK_NULL(refMap); + RemoveUnusedDeclarations(const UsedDeclSet &used, bool warn) + : used(used), warned(warn ? new std::set : nullptr) { setName("RemoveUnusedDeclarations"); } @@ -135,7 +179,6 @@ class RemoveUnusedDeclarations : public Transform { const IR::Node *preorder(IR::Declaration_Variable *decl) override; const IR::Node *preorder(IR::Declaration *decl) override { return process(decl); } const IR::Node *preorder(IR::Type_Declaration *decl) override { return process(decl); } - cstring ifSystemFile(const IR::Node *node); // return file containing node if system file }; /** @brief Iterates RemoveUnusedDeclarations until convergence. @@ -144,15 +187,12 @@ class RemoveUnusedDeclarations : public Transform { * IR::P4Table or IR::Declaration_Instance is removed. */ class RemoveAllUnusedDeclarations : public PassRepeated { + UsedDeclSet used; + public: - RemoveAllUnusedDeclarations(ReferenceMap *refMap, const RemoveUnusedPolicy &policy, - bool warn = false) - : PassManager({new ResolveReferences(refMap), - policy.getRemoveUnusedDeclarationsPass(refMap, warn)}) { - // Unused extern instances are not removed but may still trigger - // warnings. The @warned set keeps track of warnings emitted in - // previous iterations to avoid emitting duplicate warnings. - CHECK_NULL(refMap); + explicit RemoveAllUnusedDeclarations(const RemoveUnusedPolicy &policy, bool warn = false) + : PassManager({new CollectUsedDeclarations(used), + policy.getRemoveUnusedDeclarationsPass(used, warn)}) { setName("RemoveAllUnusedDeclarations"); setStopOnError(true); } diff --git a/midend/flattenUnions.h b/midend/flattenUnions.h index 637f2abb58..558017cc78 100644 --- a/midend/flattenUnions.h +++ b/midend/flattenUnions.h @@ -165,18 +165,31 @@ class HandleValidityHeaderUnion : public Transform { }; class RemoveUnusedHUDeclarations : public Transform { - P4::ReferenceMap *refMap; + const UsedDeclSet &used; public: - explicit RemoveUnusedHUDeclarations(P4::ReferenceMap *refMap) : refMap(refMap) {} - const IR::Node *preorder(IR::Type_HeaderUnion *type) { - if (!refMap->isUsed(getOriginal())) { - return nullptr; - } + explicit RemoveUnusedHUDeclarations(const UsedDeclSet &used) : used(used) {} + + const IR::Node *preorder(IR::Type_HeaderUnion *type) override { + if (!used.isUsed(getOriginal())) return nullptr; return type; } }; +class RemoveAllUnusedHUDDeclarations : public PassManager { + UsedDeclSet used; + + public: + RemoveAllUnusedHUDDeclarations() + : PassManager({ + new CollectUsedDeclarations(used), + new RemoveUnusedHUDeclarations(used), + }) { + setName("RemoveAllUnusedHUDDeclarations"); + setStopOnError(true); + } +}; + /** Passmanager to group necessary passes for flattening header unions * - RemoveAllUnusedDeclarations pass is used to remove the local standalone header union * variable declarations @@ -193,17 +206,16 @@ class FlattenHeaderUnion : public PassManager { // .next .last etc accessors for stack elements. if (loopsUnroll) { passes.push_back(new DoFlattenHeaderUnionStack(refMap, typeMap)); - passes.push_back(new P4::ClearTypeMap(typeMap)); - passes.push_back(new P4::ResolveReferences(refMap)); - passes.push_back(new P4::TypeInference(typeMap, false)); - passes.push_back(new P4::TypeChecking(refMap, typeMap)); - passes.push_back(new P4::RemoveAllUnusedDeclarations(refMap, RemoveUnusedPolicy())); + passes.push_back(new P4::RemoveAllUnusedDeclarations(RemoveUnusedPolicy())); } - passes.push_back(new DoFlattenHeaderUnion(refMap, typeMap)); passes.push_back(new P4::ClearTypeMap(typeMap)); + passes.push_back(new P4::TypeInference(typeMap, false)); passes.push_back(new P4::TypeChecking(refMap, typeMap)); - passes.push_back(new P4::RemoveAllUnusedDeclarations(refMap, RemoveUnusedPolicy())); - passes.push_back(new P4::RemoveUnusedHUDeclarations(refMap)); + passes.push_back(new DoFlattenHeaderUnion(refMap, typeMap)); + passes.push_back(new P4::RemoveAllUnusedDeclarations(RemoveUnusedPolicy())); + passes.push_back(new P4::RemoveAllUnusedHUDDeclarations()); + passes.push_back(new P4::ClearTypeMap(typeMap)); + passes.push_back(new P4::TypeChecking(nullptr, typeMap)); passes.push_back(new P4::RemoveParserIfs(typeMap)); } };