P4C
The P4 Compiler
Loading...
Searching...
No Matches
inlining.h
1/*
2Copyright 2013-present Barefoot Networks, Inc.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#ifndef FRONTENDS_P4_INLINING_H_
18#define FRONTENDS_P4_INLINING_H_
19
20#include "commonInlining.h"
21#include "frontends/common/parser_options.h"
22#include "frontends/common/resolveReferences/referenceMap.h"
23#include "frontends/p4/evaluator/evaluator.h"
24#include "frontends/p4/evaluator/substituteParameters.h"
25#include "frontends/p4/typeChecking/typeChecker.h"
26#include "frontends/p4/unusedDeclarations.h"
27#include "ir/ir.h"
28#include "lib/ordered_map.h"
29
30// These are various data structures needed by the parser/parser and control/control inliners.
31// This only works correctly after local variable initializers have been removed,
32// and after the SideEffectOrdering pass has been executed.
33
34namespace P4 {
35
37struct CallInfo : public IHasDbPrint {
38 const IR::IContainer *caller;
39 const IR::IContainer *callee;
40 const IR::Declaration_Instance *instantiation; // callee instantiation
41 // Each instantiation may be invoked multiple times.
42 std::set<const IR::MethodCallStatement *> invocations; // all invocations within the caller
43
44 CallInfo(const IR::IContainer *caller, const IR::IContainer *callee,
45 const IR::Declaration_Instance *instantiation)
46 : caller(caller), callee(callee), instantiation(instantiation) {
47 CHECK_NULL(caller);
48 CHECK_NULL(callee);
49 CHECK_NULL(instantiation);
50 }
51 void addInvocation(const IR::MethodCallStatement *statement) { invocations.emplace(statement); }
52 void dbprint(std::ostream &out) const {
53 out << "Inline " << callee << " into " << caller << " with " << invocations.size()
54 << " invocations";
55 }
56};
57
59 std::map<const IR::IDeclaration *, cstring> internalName;
60 std::map<const IR::IDeclaration *, cstring> externalName;
61
62 public:
63 void setNewName(const IR::IDeclaration *decl, cstring name, cstring extName) {
64 CHECK_NULL(decl);
65 BUG_CHECK(!name.isNullOrEmpty() && !extName.isNullOrEmpty(), "Empty name");
66 LOG3("setNewName " << dbp(decl) << " to " << name);
67 if (internalName.find(decl) != internalName.end()) BUG("%1%: already renamed", decl);
68 internalName.emplace(decl, name);
69 externalName.emplace(decl, extName);
70 }
71 cstring getName(const IR::IDeclaration *decl) const {
72 CHECK_NULL(decl);
73 BUG_CHECK(internalName.find(decl) != internalName.end(), "%1%: no new name", decl);
74 auto result = ::P4::get(internalName, decl);
75 return result;
76 }
77 cstring getExtName(const IR::IDeclaration *decl) const {
78 CHECK_NULL(decl);
79 BUG_CHECK(externalName.find(decl) != externalName.end(), "%1%: no external name", decl);
80 auto result = ::P4::get(externalName, decl);
81 return result;
82 }
83 bool isRenamed(const IR::IDeclaration *decl) const {
84 CHECK_NULL(decl);
85 return internalName.find(decl) != internalName.end();
86 }
87};
88
90 ParameterSubstitution paramSubst;
92 SymRenameMap renameMap;
93 PerInstanceSubstitutions() = default;
95 : paramSubst(other.paramSubst), tvs(other.tvs), renameMap(other.renameMap) {}
96 template <class T>
97 const T *rename(ReferenceMap *refMap, const IR::Node *node);
98};
99
101struct InlineSummary : public IHasDbPrint {
103 struct PerCaller { // information for each caller
132 typedef std::pair<const IR::MethodCallStatement *, const IR::PathExpression *>
134
140 struct key_hash {
141 std::size_t operator()(const InlinedInvocationInfo &k) const {
142 std::ostringstream oss;
143 std::get<0>(k)->dbprint(oss);
144 std::get<1>(k)->dbprint(oss);
145 return std::hash<std::string>{}(oss.str());
146 }
147 };
148
154 struct key_equal {
155 bool operator()(const InlinedInvocationInfo &v0,
156 const InlinedInvocationInfo &v1) const {
157 return std::get<0>(v0)->equiv(*std::get<0>(v1)) &&
158 std::get<1>(v0)->equiv(*std::get<1>(v1));
159 }
160 };
161
163 std::map<const IR::Declaration_Instance *, const IR::IContainer *> declToCallee;
165 std::map<const IR::Declaration_Instance *, PerInstanceSubstitutions *> substitutions;
167 std::map<const IR::MethodCallStatement *, const IR::Declaration_Instance *> callToInstance;
168
277 std::unordered_map<const InlinedInvocationInfo, const IR::ID, key_hash, key_equal>
279
282 const IR::MethodCallStatement *uniqueCaller(
283 const IR::Declaration_Instance *instance) const {
284 const IR::MethodCallStatement *call = nullptr;
285 for (auto m : callToInstance) {
286 if (m.second == instance) {
287 if (call == nullptr)
288 call = m.first;
289 else
290 return nullptr;
291 }
292 }
293 return call;
294 }
295 };
296 std::map<const IR::IContainer *, PerCaller> callerToWork;
297
298 void add(const CallInfo *cci) {
299 callerToWork[cci->caller].declToCallee[cci->instantiation] = cci->callee;
300 for (auto mcs : cci->invocations)
301 callerToWork[cci->caller].callToInstance[mcs] = cci->instantiation;
302 }
303 void dbprint(std::ostream &out) const {
304 out << "Inline " << callerToWork.size() << " call sites";
305 }
306};
307
308// Inling information constructed here.
310 // We use an ordered map to make the iterator deterministic
312 std::vector<CallInfo *> toInline; // sorted in order of inlining
313 const bool allowMultipleCalls = true;
314
315 public:
316 void addInstantiation(const IR::IContainer *caller, const IR::IContainer *callee,
317 const IR::Declaration_Instance *instantiation) {
318 CHECK_NULL(caller);
319 CHECK_NULL(callee);
320 CHECK_NULL(instantiation);
321 LOG3("Inline instantiation " << dbp(instantiation));
322 auto inst = new CallInfo(caller, callee, instantiation);
323 inlineMap[instantiation] = inst;
324 }
325
326 size_t size() const { return inlineMap.size(); }
327
328 void addInvocation(const IR::Declaration_Instance *instance,
329 const IR::MethodCallStatement *statement) {
330 CHECK_NULL(instance);
331 CHECK_NULL(statement);
332 LOG3("Inline invocation " << dbp(instance) << " at " << dbp(statement));
333 auto info = inlineMap[instance];
334 BUG_CHECK(info, "Could not locate instance %1% invoked by %2%", instance, statement);
335 info->addInvocation(statement);
336 }
337
338 void replace(const IR::IContainer *container, const IR::IContainer *replacement) {
339 CHECK_NULL(container);
340 CHECK_NULL(replacement);
341 LOG3("Replacing " << dbp(container) << " with " << dbp(replacement));
342 for (auto e : toInline) {
343 if (e->callee == container) e->callee = replacement;
344 if (e->caller == container) e->caller = replacement;
345 }
346 }
347
348 void analyze();
349 InlineSummary *next();
350};
351
354 InlineList *inlineList; // output: result is here
355 ReferenceMap *refMap; // input
356 TypeMap *typeMap; // input
357 IHasBlock *evaluator; // used to obtain the toplevel block
358 IR::ToplevelBlock *toplevel;
359
360 public:
361 bool allowParsers = true;
362 bool allowControls = true;
363
364 DiscoverInlining(InlineList *inlineList, ReferenceMap *refMap, TypeMap *typeMap,
365 IHasBlock *evaluator)
366 : inlineList(inlineList),
367 refMap(refMap),
368 typeMap(typeMap),
369 evaluator(evaluator),
370 toplevel(nullptr) {
371 CHECK_NULL(inlineList);
372 CHECK_NULL(refMap);
373 CHECK_NULL(typeMap);
374 CHECK_NULL(evaluator);
375 setName("DiscoverInlining");
376 visitDagOnce = false;
377 }
378 Visitor::profile_t init_apply(const IR::Node *node) override {
379 toplevel = evaluator->getToplevelBlock();
380 CHECK_NULL(toplevel);
381 return Inspector::init_apply(node);
382 }
383 void visit_all(const IR::Block *block);
384 bool preorder(const IR::Block *block) override {
385 visit_all(block);
386 return false;
387 }
388 bool preorder(const IR::ControlBlock *block) override;
389 bool preorder(const IR::ParserBlock *block) override;
390 void postorder(const IR::MethodCallStatement *statement) override;
391 // We don't care to visit the program, we just visit the blocks.
392 bool preorder(const IR::P4Program *) override {
393 visit_all(toplevel);
394 return false;
395 }
396};
397
399class GeneralInliner : public AbstractInliner<InlineList, InlineSummary> {
400 ReferenceMap *refMap;
401 TypeMap *typeMap;
402 InlineSummary::PerCaller *workToDo;
403 bool optimizeParserInlining;
404
405 public:
406 explicit GeneralInliner(ReferenceMap *refMap, bool _optimizeParserInlining)
407 : refMap(refMap),
408 typeMap(new TypeMap()),
409 workToDo(nullptr),
410 optimizeParserInlining(_optimizeParserInlining) {
411 setName("GeneralInliner");
412 visitDagOnce = false;
413 }
414 // controlled visiting order
415 const IR::Node *preorder(IR::MethodCallStatement *statement) override;
420 template <class P4Block, class P4BlockType>
421 void inline_subst(P4Block *caller, IR::IndexedVector<IR::Declaration> P4Block::*blockLocals,
422 const P4BlockType *P4Block::*blockType);
423 const IR::Node *preorder(IR::P4Control *caller) override;
424 const IR::Node *preorder(IR::P4Parser *caller) override;
425 const IR::Node *preorder(IR::ParserState *state) override;
426 Visitor::profile_t init_apply(const IR::Node *node) override;
427};
428
430class InlinePass : public PassManager {
431 InlineList toInline;
432
433 public:
434 InlinePass(ReferenceMap *refMap, TypeMap *typeMap, EvaluatorPass *evaluator,
435 const RemoveUnusedPolicy &policy, bool optimizeParserInlining)
436 : PassManager({new TypeChecking(refMap, typeMap),
437 new DiscoverInlining(&toInline, refMap, typeMap, evaluator),
439 &toInline, new GeneralInliner(refMap, optimizeParserInlining)),
440 new RemoveAllUnusedDeclarations(policy)}) {
441 setName("InlinePass");
442 }
443};
444
450class Inline : public PassManager {
451 static std::set<cstring> noPropagateAnnotations;
452 ReferenceMap refMap;
453
454 public:
455 Inline(TypeMap *typeMap, const RemoveUnusedPolicy &policy, bool optimizeParserInlining,
456 EvaluatorPass *evaluator = nullptr) {
457 refMap.setIsV1(P4CContext::get().options().isv1());
458 auto *evInstance = evaluator ? evaluator : new EvaluatorPass(&refMap, typeMap);
459 addPasses({
460 evInstance,
461 new PassRepeated(
462 {new InlinePass(&refMap, typeMap, evInstance, policy, optimizeParserInlining),
463 // After inlining the output of the evaluator changes, so we have to
464 // run it again
465 evInstance}),
466 });
467 setName("Inline");
468 }
469
471 static void setAnnotationNoPropagate(cstring name) { noPropagateAnnotations.emplace(name); }
472
474 static bool isAnnotationNoPropagate(cstring name) { return noPropagateAnnotations.count(name); }
475};
476
477} // namespace P4
478
479#endif /* FRONTENDS_P4_INLINING_H_ */
Definition commonInlining.h:157
Must be run after an evaluator; uses the blocks to discover caller/callee relationships.
Definition inlining.h:353
Definition evaluator.h:115
Performs actual inlining work.
Definition inlining.h:399
void inline_subst(P4Block *caller, IR::IndexedVector< IR::Declaration > P4Block::*blockLocals, const P4BlockType *P4Block::*blockType)
Definition inlining.cpp:461
Definition evaluator.h:28
Definition stringify.h:33
The Declaration interface, representing objects with names.
Definition declaration.h:26
Definition node.h:52
Definition node.h:95
Definition commonInlining.h:178
Definition inlining.h:450
static bool isAnnotationNoPropagate(cstring name)
Is annotation name excluded from inline propagation?
Definition inlining.h:474
static void setAnnotationNoPropagate(cstring name)
Do not propagate annotation name during inlining.
Definition inlining.h:471
Definition inlining.h:309
Performs one round of inlining bottoms-up.
Definition inlining.h:430
Definition visitor.h:400
static P4CContext & get()
Definition parser_options.cpp:539
Definition parameterSubstitution.h:30
Definition ir/pass_manager.h:40
Definition ir/pass_manager.h:145
Class used to encode maps from paths to declarations.
Definition referenceMap.h:66
void setIsV1(bool isv1)
Set boolean indicating whether map is for a P4_14 program to isV1.
Definition referenceMap.h:105
Iterates RemoveUnusedDeclarations until convergence.
Definition unusedDeclarations.h:189
Definition unusedDeclarations.h:48
Definition inlining.h:58
Definition typeChecker.h:55
Definition typeMap.h:41
Definition typeSubstitution.h:73
Definition visitor.h:78
Definition cstring.h:85
Definition ordered_map.h:32
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
void info(const int kind, const char *format, const T *node, Args &&...args)
Report info messages of type kind. Requires that the node argument have source info.
Definition lib/error.h:148
Describes information about a caller-callee pair.
Definition inlining.h:37
Various substitutions that must be applied for each instance.
Definition inlining.h:103
const IR::MethodCallStatement * uniqueCaller(const IR::Declaration_Instance *instance) const
Definition inlining.h:282
std::map< const IR::Declaration_Instance *, const IR::IContainer * > declToCallee
For each instance (key) the container that is intantiated.
Definition inlining.h:163
std::unordered_map< const InlinedInvocationInfo, const IR::ID, key_hash, key_equal > invocationToState
Definition inlining.h:278
std::map< const IR::MethodCallStatement *, const IR::Declaration_Instance * > callToInstance
For each invocation (key) call the instance that is invoked.
Definition inlining.h:167
std::map< const IR::Declaration_Instance *, PerInstanceSubstitutions * > substitutions
For each instance (key) we must apply a bunch of substitutions.
Definition inlining.h:165
std::pair< const IR::MethodCallStatement *, const IR::PathExpression * > InlinedInvocationInfo
Definition inlining.h:133
Summarizes all inline operations to be performed.
Definition inlining.h:101
Definition inlining.h:89