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