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