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/resolveReferences/referenceMap.h"
22#include "frontends/p4/evaluator/evaluator.h"
23#include "frontends/p4/evaluator/substituteParameters.h"
24#include "frontends/p4/typeChecking/typeChecker.h"
25#include "frontends/p4/unusedDeclarations.h"
26#include "ir/ir.h"
27#include "lib/ordered_map.h"
28
29// These are various data structures needed by the parser/parser and control/control inliners.
30// This only works correctly after local variable initializers have been removed,
31// and after the SideEffectOrdering pass has been executed.
32
33namespace P4 {
34
36struct CallInfo : public IHasDbPrint {
37 const IR::IContainer *caller;
38 const IR::IContainer *callee;
39 const IR::Declaration_Instance *instantiation; // callee instantiation
40 // Each instantiation may be invoked multiple times.
41 std::set<const IR::MethodCallStatement *> invocations; // all invocations within the caller
42
43 CallInfo(const IR::IContainer *caller, const IR::IContainer *callee,
44 const IR::Declaration_Instance *instantiation)
45 : caller(caller), callee(callee), instantiation(instantiation) {
46 CHECK_NULL(caller);
47 CHECK_NULL(callee);
48 CHECK_NULL(instantiation);
49 }
50 void addInvocation(const IR::MethodCallStatement *statement) { invocations.emplace(statement); }
51 void dbprint(std::ostream &out) const {
52 out << "Inline " << callee << " into " << caller << " with " << invocations.size()
53 << " invocations";
54 }
55};
56
58 std::map<const IR::IDeclaration *, cstring> internalName;
59 std::map<const IR::IDeclaration *, cstring> externalName;
60
61 public:
62 void setNewName(const IR::IDeclaration *decl, cstring name, cstring extName) {
63 CHECK_NULL(decl);
64 BUG_CHECK(!name.isNullOrEmpty() && !extName.isNullOrEmpty(), "Empty name");
65 LOG3("setNewName " << dbp(decl) << " to " << name);
66 if (internalName.find(decl) != internalName.end()) BUG("%1%: already renamed", decl);
67 internalName.emplace(decl, name);
68 externalName.emplace(decl, extName);
69 }
70 cstring getName(const IR::IDeclaration *decl) const {
71 CHECK_NULL(decl);
72 BUG_CHECK(internalName.find(decl) != internalName.end(), "%1%: no new name", decl);
73 auto result = ::P4::get(internalName, decl);
74 return result;
75 }
76 cstring getExtName(const IR::IDeclaration *decl) const {
77 CHECK_NULL(decl);
78 BUG_CHECK(externalName.find(decl) != externalName.end(), "%1%: no external name", decl);
79 auto result = ::P4::get(externalName, decl);
80 return result;
81 }
82 bool isRenamed(const IR::IDeclaration *decl) const {
83 CHECK_NULL(decl);
84 return internalName.find(decl) != internalName.end();
85 }
86};
87
89 ParameterSubstitution paramSubst;
91 SymRenameMap renameMap;
92 PerInstanceSubstitutions() = default;
94 : paramSubst(other.paramSubst), tvs(other.tvs), renameMap(other.renameMap) {}
95 template <class T>
96 const T *rename(ReferenceMap *refMap, const IR::Node *node);
97};
98
100struct InlineSummary : public IHasDbPrint {
102 struct PerCaller { // information for each caller
131 typedef std::pair<const IR::MethodCallStatement *, const IR::PathExpression *>
133
139 struct key_hash {
140 std::size_t operator()(const InlinedInvocationInfo &k) const {
141 std::ostringstream oss;
142 std::get<0>(k)->dbprint(oss);
143 std::get<1>(k)->dbprint(oss);
144 return std::hash<std::string>{}(oss.str());
145 }
146 };
147
153 struct key_equal {
154 bool operator()(const InlinedInvocationInfo &v0,
155 const InlinedInvocationInfo &v1) const {
156 return std::get<0>(v0)->equiv(*std::get<0>(v1)) &&
157 std::get<1>(v0)->equiv(*std::get<1>(v1));
158 }
159 };
160
162 std::map<const IR::Declaration_Instance *, const IR::IContainer *> declToCallee;
164 std::map<const IR::Declaration_Instance *, PerInstanceSubstitutions *> substitutions;
166 std::map<const IR::MethodCallStatement *, const IR::Declaration_Instance *> callToInstance;
167
276 std::unordered_map<const InlinedInvocationInfo, const IR::ID, key_hash, key_equal>
278
281 const IR::MethodCallStatement *uniqueCaller(
282 const IR::Declaration_Instance *instance) const {
283 const IR::MethodCallStatement *call = nullptr;
284 for (auto m : callToInstance) {
285 if (m.second == instance) {
286 if (call == nullptr)
287 call = m.first;
288 else
289 return nullptr;
290 }
291 }
292 return call;
293 }
294 };
295 std::map<const IR::IContainer *, PerCaller> callerToWork;
296
297 void add(const CallInfo *cci) {
298 callerToWork[cci->caller].declToCallee[cci->instantiation] = cci->callee;
299 for (auto mcs : cci->invocations)
300 callerToWork[cci->caller].callToInstance[mcs] = cci->instantiation;
301 }
302 void dbprint(std::ostream &out) const {
303 out << "Inline " << callerToWork.size() << " call sites";
304 }
305};
306
307// Inling information constructed here.
309 // We use an ordered map to make the iterator deterministic
311 std::vector<CallInfo *> toInline; // sorted in order of inlining
312 const bool allowMultipleCalls = true;
313
314 public:
315 void addInstantiation(const IR::IContainer *caller, const IR::IContainer *callee,
316 const IR::Declaration_Instance *instantiation) {
317 CHECK_NULL(caller);
318 CHECK_NULL(callee);
319 CHECK_NULL(instantiation);
320 LOG3("Inline instantiation " << dbp(instantiation));
321 auto inst = new CallInfo(caller, callee, instantiation);
322 inlineMap[instantiation] = inst;
323 }
324
325 size_t size() const { return inlineMap.size(); }
326
327 void addInvocation(const IR::Declaration_Instance *instance,
328 const IR::MethodCallStatement *statement) {
329 CHECK_NULL(instance);
330 CHECK_NULL(statement);
331 LOG3("Inline invocation " << dbp(instance) << " at " << dbp(statement));
332 auto info = inlineMap[instance];
333 BUG_CHECK(info, "Could not locate instance %1% invoked by %2%", instance, statement);
334 info->addInvocation(statement);
335 }
336
337 void replace(const IR::IContainer *container, const IR::IContainer *replacement) {
338 CHECK_NULL(container);
339 CHECK_NULL(replacement);
340 LOG3("Replacing " << dbp(container) << " with " << dbp(replacement));
341 for (auto e : toInline) {
342 if (e->callee == container) e->callee = replacement;
343 if (e->caller == container) e->caller = replacement;
344 }
345 }
346
347 void analyze();
348 InlineSummary *next();
349};
350
353 InlineList *inlineList; // output: result is here
354 ReferenceMap *refMap; // input
355 TypeMap *typeMap; // input
356 IHasBlock *evaluator; // used to obtain the toplevel block
357 IR::ToplevelBlock *toplevel;
358
359 public:
360 bool allowParsers = true;
361 bool allowControls = true;
362
363 DiscoverInlining(InlineList *inlineList, ReferenceMap *refMap, TypeMap *typeMap,
364 IHasBlock *evaluator)
365 : inlineList(inlineList),
366 refMap(refMap),
367 typeMap(typeMap),
368 evaluator(evaluator),
369 toplevel(nullptr) {
370 CHECK_NULL(inlineList);
371 CHECK_NULL(refMap);
372 CHECK_NULL(typeMap);
373 CHECK_NULL(evaluator);
374 setName("DiscoverInlining");
375 visitDagOnce = false;
376 }
377 Visitor::profile_t init_apply(const IR::Node *node) override {
378 toplevel = evaluator->getToplevelBlock();
379 CHECK_NULL(toplevel);
380 return Inspector::init_apply(node);
381 }
382 void visit_all(const IR::Block *block);
383 bool preorder(const IR::Block *block) override {
384 visit_all(block);
385 return false;
386 }
387 bool preorder(const IR::ControlBlock *block) override;
388 bool preorder(const IR::ParserBlock *block) override;
389 void postorder(const IR::MethodCallStatement *statement) override;
390 // We don't care to visit the program, we just visit the blocks.
391 bool preorder(const IR::P4Program *) override {
392 visit_all(toplevel);
393 return false;
394 }
395};
396
398class GeneralInliner : public AbstractInliner<InlineList, InlineSummary> {
399 ReferenceMap *refMap;
400 TypeMap *typeMap;
401 InlineSummary::PerCaller *workToDo;
402 bool optimizeParserInlining;
403
404 public:
405 explicit GeneralInliner(ReferenceMap *refMap, bool _optimizeParserInlining)
406 : refMap(refMap),
407 typeMap(new TypeMap()),
408 workToDo(nullptr),
409 optimizeParserInlining(_optimizeParserInlining) {
410 setName("GeneralInliner");
411 visitDagOnce = false;
412 }
413 // controlled visiting order
414 const IR::Node *preorder(IR::MethodCallStatement *statement) override;
419 template <class P4Block, class P4BlockType>
420 void inline_subst(P4Block *caller, IR::IndexedVector<IR::Declaration> P4Block::*blockLocals,
421 const P4BlockType *P4Block::*blockType);
422 const IR::Node *preorder(IR::P4Control *caller) override;
423 const IR::Node *preorder(IR::P4Parser *caller) override;
424 const IR::Node *preorder(IR::ParserState *state) override;
425 Visitor::profile_t init_apply(const IR::Node *node) override;
426};
427
429class InlinePass : public PassManager {
430 InlineList toInline;
431
432 public:
433 InlinePass(ReferenceMap *refMap, TypeMap *typeMap, EvaluatorPass *evaluator,
434 const RemoveUnusedPolicy &policy, bool optimizeParserInlining)
435 : PassManager({new TypeChecking(refMap, typeMap),
436 new DiscoverInlining(&toInline, refMap, typeMap, evaluator),
438 &toInline, new GeneralInliner(refMap, optimizeParserInlining)),
439 new RemoveAllUnusedDeclarations(refMap, policy)}) {
440 setName("InlinePass");
441 }
442};
443
449class Inline : public PassRepeated {
450 static std::set<cstring> noPropagateAnnotations;
451
452 public:
453 Inline(ReferenceMap *refMap, TypeMap *typeMap, EvaluatorPass *evaluator,
454 const RemoveUnusedPolicy &policy, bool optimizeParserInlining)
455 : PassManager({new InlinePass(refMap, typeMap, evaluator, policy, optimizeParserInlining),
456 // After inlining the output of the evaluator changes, so
457 // we have to run it again
458 evaluator}) {
459 setName("Inline");
460 }
461
463 static void setAnnotationNoPropagate(cstring name) { noPropagateAnnotations.emplace(name); }
464
466 static bool isAnnotationNoPropagate(cstring name) { return noPropagateAnnotations.count(name); }
467};
468
469} // namespace P4
470
471#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:352
Definition evaluator.h:114
Performs actual inlining work.
Definition inlining.h:398
void inline_subst(P4Block *caller, IR::IndexedVector< IR::Declaration > P4Block::*blockLocals, const P4BlockType *P4Block::*blockType)
Definition inlining.cpp:461
Definition evaluator.h:27
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:449
static bool isAnnotationNoPropagate(cstring name)
Is annotation name excluded from inline propagation?
Definition inlining.h:466
static void setAnnotationNoPropagate(cstring name)
Do not propagate annotation name during inlining.
Definition inlining.h:463
Definition inlining.h:308
Performs one round of inlining bottoms-up.
Definition inlining.h:429
Definition visitor.h:400
Definition parameterSubstitution.h:30
Definition pass_manager.h:40
Definition pass_manager.h:145
Class used to encode maps from paths to declarations.
Definition referenceMap.h:66
Iterates RemoveUnusedDeclarations until convergence.
Definition unusedDeclarations.h:146
Definition unusedDeclarations.h:28
Definition inlining.h:57
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 error.h:148
Describes information about a caller-callee pair.
Definition inlining.h:36
Various substitutions that must be applied for each instance.
Definition inlining.h:102
const IR::MethodCallStatement * uniqueCaller(const IR::Declaration_Instance *instance) const
Definition inlining.h:281
std::map< const IR::Declaration_Instance *, const IR::IContainer * > declToCallee
For each instance (key) the container that is intantiated.
Definition inlining.h:162
std::unordered_map< const InlinedInvocationInfo, const IR::ID, key_hash, key_equal > invocationToState
Definition inlining.h:277
std::map< const IR::MethodCallStatement *, const IR::Declaration_Instance * > callToInstance
For each invocation (key) call the instance that is invoked.
Definition inlining.h:166
std::map< const IR::Declaration_Instance *, PerInstanceSubstitutions * > substitutions
For each instance (key) we must apply a bunch of substitutions.
Definition inlining.h:164
std::pair< const IR::MethodCallStatement *, const IR::PathExpression * > InlinedInvocationInfo
Definition inlining.h:132
Summarizes all inline operations to be performed.
Definition inlining.h:100
Definition inlining.h:88