P4C
The P4 Compiler
Loading...
Searching...
No Matches
commonInlining.h
1/*
2 * Copyright 2018 VMware, Inc.
3 * SPDX-FileCopyrightText: 2018 VMware, Inc.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8#ifndef FRONTENDS_P4_COMMONINLINING_H_
9#define FRONTENDS_P4_COMMONINLINING_H_
10
11#include "frontends/common/resolveReferences/resolveReferences.h"
12#define DEBUG_INLINER 0
13
14#if DEBUG_INLINER
15#include "frontends/p4/toP4/toP4.h"
16#endif
17#include "frontends/p4/callGraph.h"
18#include "ir/ir.h"
19
24
25namespace P4 {
26
27using namespace literals;
28
29template <class CallableT, class CallNodeT, class CallExpressionT = IR::MethodCallExpression>
30class SimpleCallInfo : public IHasDbPrint {
31 // Callable can be P4Action, Function, P4Control, P4Parser
32 public:
33 using Callable = CallableT;
34 using CallNode = CallNodeT;
35 using CallExpression = CallExpressionT;
36
37 const Callable *caller; // object that performs the call
38 const Callable *callee; // object that is called
39 const CallNode *call;
40 const CallExpression *callExpr;
41
42 SimpleCallInfo(const Callable *caller, const Callable *callee, const CallNode *call,
43 const CallExpression *expr)
44 : caller(caller), callee(callee), call(call), callExpr(expr) {
45 CHECK_NULL(caller);
46 CHECK_NULL(callee);
47 CHECK_NULL(call);
48 CHECK_NULL(callExpr);
49 }
50 void dbprint(std::ostream &out) const {
51 out << dbp(callee) << " into " << dbp(caller) << " at " << dbp(call) << " via "
52 << dbp(callExpr);
53 }
54};
55
56template <class CallInfo>
58 public:
59 using ReplacementMap = std::map<
60 const typename CallInfo::CallNode *,
61 std::pair<const typename CallInfo::Callable *, const typename CallInfo::CallExpression *>>;
62 // Map caller -> statement -> callee
63 std::map<const typename CallInfo::Callable *, ReplacementMap> sites;
64 void add(CallInfo *info) {
65 CHECK_NULL(info);
66 LOG3(info);
67 sites[info->caller][info->call] = {info->callee, info->callExpr};
68 }
69 void dbprint(std::ostream &out) const {
70 for (auto t : sites) {
71 out << dbp(t.first);
72 for (auto c : t.second) {
73 out << std::endl
74 << "\t" << dbp(c.first) << " => " << dbp(c.second.first) << " via "
75 << dbp(c.second.second);
76 }
77 }
78 }
79 bool empty() const { return sites.empty(); }
80};
81
82template <class Callable, class CallInfo, class InlineWorkList>
84 std::vector<CallInfo *> toInline; // initial data
85 std::vector<CallInfo *> inlineOrder; // sorted in inlining order
86
87 public:
88 // generate the inlining order
89 void analyze() {
90 // We only keep the call graph between objects of the same kind.
91 P4::CallGraph<const Callable *> cg("Call-graph");
92 for (auto c : toInline) cg.calls(c->caller, c->callee);
93
94 // must inline from leaves up
95 std::vector<const Callable *> order;
96 cg.sort(order);
97 for (auto c : order) {
98 // This is quadratic, but hopefully the call graph is not too large
99 for (auto ci : toInline) {
100 if (ci->caller == c) inlineOrder.push_back(ci);
101 }
102 }
103
104 std::reverse(inlineOrder.begin(), inlineOrder.end());
105 }
106
107 size_t size() const { return toInline.size(); }
108 void clear() {
109 toInline.clear();
110 inlineOrder.clear();
111 }
112
114 InlineWorkList *next() {
115 if (inlineOrder.size() == 0) return nullptr;
116
117 std::set<const Callable *> callers;
118 auto result = new InlineWorkList();
119
120 // Find callables that can be inlined simultaneously.
121 // This traversal is in topological order starting from leaf callees.
122 // We stop at the first callable which calls one of the callables
123 // we have already selected.
124 while (!inlineOrder.empty()) {
125 auto last = inlineOrder.back();
126 if (callers.find(last->callee) != callers.end()) break;
127 inlineOrder.pop_back();
128 result->add(last);
129 callers.emplace(last->caller);
130 }
131 BUG_CHECK(!result->empty(), "Empty list of methods to inline");
132 return result;
133 }
134
135 void add(CallInfo *aci) { toInline.push_back(aci); }
136
137 void replace(const Callable *container, const Callable *replacement) {
138 LOG2("Substituting " << container << " with " << replacement);
139 for (auto e : inlineOrder) {
140 if (e->callee == container) e->callee = replacement;
141 if (e->caller == container) e->caller = replacement;
142 }
143 }
144};
145
146// Base class for inliners
147template <class InlineList, class InlineWorkList>
148class AbstractInliner : public Transform, public ResolutionContext {
149 protected:
150 InlineList *list;
151 InlineWorkList *toInline;
152 AbstractInliner() : list(nullptr), toInline(nullptr) {}
153
154 public:
155 void prepare(InlineList *list, InlineWorkList *toInline) {
156 CHECK_NULL(list);
157 CHECK_NULL(toInline);
158 this->list = list;
159 this->toInline = toInline;
160 }
161 Visitor::profile_t init_apply(const IR::Node *node) {
162 LOG2("AbstractInliner " << toInline);
163 return Transform::init_apply(node);
164 }
165 virtual ~AbstractInliner() {}
166};
167
168template <class InlineList, class InlineWorkList>
169class InlineDriver : public Visitor {
170 InlineList *toInline;
172
173 public:
174 InlineDriver(InlineList *toInline, AbstractInliner<InlineList, InlineWorkList> *inliner)
175 : toInline(toInline), inliner(inliner) {
176 CHECK_NULL(toInline);
177 CHECK_NULL(inliner);
178 setName(("InlineDriver_"_cs + cstring(inliner->name())).c_str());
179 }
180 const IR::Node *apply_visitor(const IR::Node *program, const char * = 0) override {
181 LOG2("InlineDriver");
182 toInline->analyze();
183 LOG3("InlineList size " << toInline->size());
184 while (auto todo = toInline->next()) {
185 LOG2("Processing " << todo);
186 inliner->prepare(toInline, todo);
187 program = program->apply(*inliner);
188 if (::P4::errorCount() > 0) break;
189
190#if DEBUG_INLINER
191 // debugging code; we don't have an easy way to dump the program here,
192 // since we are not between passes
193 ToP4 top4(&std::cout, false, nullptr);
194 program->apply(top4);
195#endif
196 }
197 return program;
198 }
199};
200
201} // namespace P4
202
203#endif /* FRONTENDS_P4_COMMONINLINING_H_ */
Definition commonInlining.h:148
Definition callGraph.h:41
Definition stringify.h:33
Definition node.h:53
Definition inlining.h:300
Definition commonInlining.h:83
InlineWorkList * next()
Get next batch of objects to inline.
Definition commonInlining.h:114
Definition commonInlining.h:57
Definition toP4.h:36
Definition visitor.h:442
Definition visitor.h:78
Definition cstring.h:85
Definition cstring.h:80
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
unsigned errorCount()
Definition lib/error.h:34
Describes information about a caller-callee pair.
Definition inlining.h:28