P4C
The P4 Compiler
Loading...
Searching...
No Matches
parserUnroll.h
1/*
2 * Copyright 2016 VMware, Inc.
3 * SPDX-FileCopyrightText: 2016 VMware, Inc.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8#ifndef MIDEND_PARSERUNROLL_H_
9#define MIDEND_PARSERUNROLL_H_
10
11#include <unordered_map>
12
13#include "frontends/common/resolveReferences/referenceMap.h"
14#include "frontends/p4/callGraph.h"
15#include "frontends/p4/simplify.h"
16#include "frontends/p4/typeChecking/typeChecker.h"
17#include "frontends/p4/typeMap.h"
18#include "interpreter.h"
19#include "ir/ir.h"
20
21namespace P4 {
22
24const char outOfBoundsStateName[] = "stateOutOfBound";
25
27// The following are for a single parser
28
29// Represents a variable for storing indexes values for a header stack.
30//
31// This is a thin wrapper around a 'const IR::Member*' to (1) enforce invariants on which forms of
32// Members can represent state variables and (2) enable the use of StackVariable as map keys.
33//
34// A Member can represent a StackVariable exactly when its qualifying variable
35// (IR::Member::expr) either is a PathExpression or can represent a StackVariable.
37 friend class StackVariableHash;
38
39 public:
41 static bool repOk(const IR::Expression *expr);
42
43 // Implements comparisons so that StateVariables can be used as map keys.
44 bool operator==(const StackVariable &other) const;
45
46 private:
47 const IR::Expression *variable;
48
49 public:
51 StackVariable(const IR::Expression *expr); // NOLINT(runtime/explicit)
52};
53
56 public:
57 size_t operator()(const StackVariable &var) const;
58};
59
60typedef std::unordered_map<StackVariable, size_t, StackVariableHash> StackVariableMap;
61typedef std::unordered_map<StackVariable, const IR::Expression *, StackVariableHash>
62 StackVariableIndexMap;
63
65struct ParserStateInfo {
66 friend class ParserStateRewriter;
67 cstring name; // new state name
68 const IR::P4Parser *parser;
69 const IR::ParserState *state; // original state this is produced from
70 const ParserStateInfo *predecessor; // how we got here in the symbolic evaluation
71 ValueMap *before;
72 ValueMap *after;
73 IR::ParserState *newState; // pointer to a new state
74 size_t currentIndex;
75 StackVariableMap statesIndexes; // global map in state indexes
76 // set of parsers' states names with are in current path.
77 std::unordered_set<cstring> scenarioStates;
78 std::unordered_set<cstring> scenarioHS; // scenario header stack's operations
79 StackVariableIndexMap substitutedIndexes; // values of the evaluated indexes
80 ParserStateInfo(cstring name, const IR::P4Parser *parser, const IR::ParserState *state,
81 const ParserStateInfo *predecessor, ValueMap *before, size_t index)
82 : name(name),
83 parser(parser),
84 state(state),
85 predecessor(predecessor),
86 before(before),
87 after(nullptr),
88 newState(nullptr),
89 currentIndex(index) {
90 CHECK_NULL(parser);
91 CHECK_NULL(state);
92 CHECK_NULL(before);
93 if (predecessor) {
94 statesIndexes = predecessor->statesIndexes;
95 scenarioHS = predecessor->scenarioHS;
96 substitutedIndexes = predecessor->substitutedIndexes;
97 }
98 }
99};
100
103 friend class RewriteAllParsers;
104 // for each original state a vector of states produced by unrolling
105 std::vector<ParserStateInfo *> *ordered_states = new std::vector<ParserStateInfo *>();
106 std::map<cstring, std::vector<ParserStateInfo *> *> states;
107
108 public:
109 std::vector<ParserStateInfo *> *get(ParserStateInfo *si) {
110 cstring origState = si->state->name.name;
111 std::vector<ParserStateInfo *> *vec;
112 auto it = states.find(origState);
113 if (it == states.end()) {
114 vec = new std::vector<ParserStateInfo *>;
115 states.emplace(origState, vec);
116 ordered_states->push_back(si);
117 } else {
118 vec = it->second;
119 }
120 return vec;
121 }
122 void add(ParserStateInfo *si) {
123 auto vec = get(si);
124 vec->push_back(si);
125 }
126 std::map<cstring, std::vector<ParserStateInfo *> *> &getStates() { return states; }
127};
128
129typedef CallGraph<const IR::ParserState *> StateCallGraph;
130
133 friend class ParserStateRewriter;
134 friend class ParserSymbolicInterpreter;
135 friend class AnalyzeParser;
136 std::map<cstring, const IR::ParserState *> stateMap;
137
138 public:
139 const IR::P4Parser *parser;
140 const IR::ParserState *start;
141 const ParserInfo *result;
142 StateCallGraph *callGraph;
143 std::map<cstring, std::set<cstring>> statesWithHeaderStacks;
144 std::map<cstring, size_t> callsIndexes; // map for curent calls of state insite current one
145 void setParser(const IR::P4Parser *parser) {
146 CHECK_NULL(parser);
147 callGraph = new StateCallGraph(parser->name.name);
148 this->parser = parser;
149 start = nullptr;
150 }
151 void addState(const IR::ParserState *state) { stateMap.emplace(state->name, state); }
152 const IR::ParserState *get(cstring state) const { return ::P4::get(stateMap, state); }
153 void calls(const IR::ParserState *caller, const IR::ParserState *callee) {
154 callGraph->calls(caller, callee);
155 }
156
157 bool analyze(ReferenceMap *refMap, TypeMap *typeMap, bool unroll, bool &wasError);
159 bool reachableHSUsage(IR::ID id, const ParserStateInfo *state) const;
160
161 protected:
165 void addStateHSUsage(const IR::ParserState *state, const IR::Expression *expression);
166};
167
168class AnalyzeParser : public Inspector {
169 const ReferenceMap *refMap;
170 ParserStructure *current;
171 const IR::ParserState *currentState;
172
173 public:
174 AnalyzeParser(const ReferenceMap *refMap, ParserStructure *current)
175 : refMap(refMap), current(current), currentState(nullptr) {
176 CHECK_NULL(refMap);
177 CHECK_NULL(current);
178 setName("AnalyzeParser");
179 visitDagOnce = false;
180 }
181
182 bool preorder(const IR::P4Parser *parser) override {
183 LOG2("Scanning " << parser);
184 current->setParser(parser);
185 return true;
186 }
187 bool preorder(const IR::ParserState *state) override;
188 void postorder(const IR::ParserState *state) override;
189 void postorder(const IR::ArrayIndex *array) override;
190 void postorder(const IR::Member *member) override;
191 void postorder(const IR::PathExpression *expression) override;
192};
193
194// Applied to a P4Parser object.
195class ParserRewriter : public PassManager {
196 ParserStructure current;
197 friend class RewriteAllParsers;
198
199 public:
200 bool hasOutOfboundState;
201 bool wasError;
202 ParserRewriter(ReferenceMap *refMap, TypeMap *typeMap, bool unroll) {
203 CHECK_NULL(refMap);
204 CHECK_NULL(typeMap);
205 wasError = false;
206 setName("ParserRewriter");
207 addPasses({
208 new AnalyzeParser(refMap, &current),
209 [this, refMap, typeMap, unroll](void) {
210 hasOutOfboundState = current.analyze(refMap, typeMap, unroll, wasError);
211 },
212 });
213 }
214};
215
217// The following are applied to the entire program
218
220 bool unroll = true;
221 bool order_states = false;
222};
223
224class RewriteAllParsers : public Transform {
225 ReferenceMap *refMap;
226 ParserConfig config;
227 TypeMap *typeMap;
228
229 public:
230 RewriteAllParsers(ReferenceMap *refMap, TypeMap *typeMap, ParserConfig config)
231 : refMap(refMap), config(config), typeMap(typeMap) {
232 CHECK_NULL(refMap);
233 CHECK_NULL(typeMap);
234 setName("RewriteAllParsers");
235 }
236
237 // start generation of a code
238 const IR::Node *postorder(IR::P4Parser *parser) override {
239 // making rewriting
240 auto rewriter = new ParserRewriter(refMap, typeMap, config.unroll);
241 rewriter->setCalledBy(this);
242 parser->apply(*rewriter);
243 if (rewriter->wasError) {
244 return parser;
245 }
247 BUG_CHECK(rewriter->current.result,
248 "No result was found after unrolling of the parser loop");
249 IR::P4Parser *newParser = parser->clone();
250 IR::IndexedVector<IR::ParserState> states = newParser->states;
251 newParser->states.clear();
252 if (rewriter->hasOutOfboundState) {
253 // generating state with verify(false, error.StackOutOfBounds)
255 arguments->push_back(new IR::Argument(IR::BoolLiteral::get(false)));
256 arguments->push_back(new IR::Argument(
257 new IR::Member(new IR::TypeNameExpression(new IR::Type_Name(IR::ID("error"))),
258 IR::ID("StackOutOfBounds"))));
261 parameters.push_back(
262 new IR::Parameter(IR::ID("check"), IR::Direction::In, IR::Type::Boolean::get()));
263 parameters.push_back(new IR::Parameter(IR::ID("toSignal"), IR::Direction::In,
264 new IR::Type_Name(IR::ID("error"))));
265 components.push_back(new IR::MethodCallStatement(new IR::MethodCallExpression(
266 IR::Type::Void::get(),
267 new IR::PathExpression(
268 new IR::Type_Method(IR::Type::Void::get(), new IR::ParameterList(parameters),
269 "*method"_cs),
270 new IR::Path(IR::ID("verify"))),
271 arguments)));
272 auto *outOfBoundsState = new IR::ParserState(
273 IR::ID(outOfBoundsStateName), components,
274 new IR::PathExpression(IR::Type_State::get(),
275 new IR::Path(IR::ParserState::reject, false)));
276 newParser->states.push_back(outOfBoundsState);
277 }
278
279 if (config.order_states) {
280 if (rewriter->current.result->ordered_states) {
281 for (auto &i : *rewriter->current.result->ordered_states) {
282 if (rewriter->hasOutOfboundState && i->name == "stateOutOfBound") {
283 continue;
284 }
285 newParser->states.push_back(i->state);
286 }
287 }
288 } else {
289 for (auto &i : rewriter->current.result->states) {
290 for (auto &j : *i.second)
291 if (j->newState) {
292 if (rewriter->hasOutOfboundState &&
293 j->newState->name.name == "stateOutOfBound") {
294 continue;
295 }
296 newParser->states.push_back(j->newState);
297 }
298 }
299 }
300 // adding accept/reject
301 newParser->states.push_back(new IR::ParserState(IR::ParserState::accept, nullptr));
302 newParser->states.push_back(new IR::ParserState(IR::ParserState::reject, nullptr));
303
304 return newParser;
305 }
306};
307
308class ParsersUnroll : public PassManager {
309 public:
310 ParsersUnroll(ParserConfig config, ReferenceMap *refMap, TypeMap *typeMap) {
311 // remove block statements
312 passes.push_back(new SimplifyControlFlow(typeMap, false));
313 passes.push_back(new TypeChecking(refMap, typeMap));
314 passes.push_back(new RewriteAllParsers(refMap, typeMap, config));
315 setName("ParsersUnroll");
316 }
317};
318
319} // namespace P4
320
321#endif /* MIDEND_PARSERUNROLL_H_ */
Definition parserUnroll.h:168
Definition callGraph.h:41
Definition indexed_vector.h:31
Definition node.h:53
Definition ir/vector.h:59
Definition visitor.h:418
Information produced for a parser by the symbolic evaluator.
Definition parserUnroll.h:102
Definition parserUnroll.h:195
Information about a parser in the input program.
Definition parserUnroll.h:132
bool reachableHSUsage(IR::ID id, const ParserStateInfo *state) const
check reachability for usage of header stack
Definition parserUnroll.cpp:882
void evaluateReachability()
evaluates rechable states with HS operations for each path.
void addStateHSUsage(const IR::ParserState *state, const IR::Expression *expression)
add HS name which is used in a current state.
Definition parserUnroll.cpp:902
Class used to encode maps from paths to declarations.
Definition referenceMap.h:67
Definition parserUnroll.h:224
const IR::Node * postorder(IR::P4Parser *parser) override
Definition parserUnroll.h:238
Definition simplify.h:72
Class with hash function for StackVariable.
Definition parserUnroll.h:55
Definition parserUnroll.h:36
static bool repOk(const IR::Expression *expr)
Determines whether @expr can represent a StateVariable.
Definition parserUnroll.cpp:20
StackVariable(const IR::Expression *expr)
Implicitly converts IR::Expression* to a StackVariable.
Definition parserUnroll.cpp:14
Definition visitor.h:442
Definition typeChecker.h:55
Definition typeMap.h:32
Definition interpreter.h:63
Definition cstring.h:85
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:13
const char outOfBoundsStateName[]
Name of out of bound state.
Definition parserUnroll.h:24
Definition parserUnroll.h:219
Definition id.h:28
Information produced for a parser state by the symbolic evaluator.
Definition parserUnroll.h:65