@@ -136,6 +136,248 @@ const IR::Node* LowerExpressions::postorder(IR::Concat* expression) {
136
136
137
137
namespace {
138
138
139
+ /* *
140
+ A list of assignments that all write to a "variable".
141
+ This really only handles scalar variables.
142
+ It is customized for the needs of FixupChecksum.
143
+ */
144
+ struct VariableWriters {
145
+ std::set<const IR::AssignmentStatement*> writers;
146
+ VariableWriters () = default ;
147
+ explicit VariableWriters (const IR::AssignmentStatement* writer)
148
+ { writers.emplace (writer); }
149
+ VariableWriters* join (const VariableWriters* other) const {
150
+ auto result = new VariableWriters ();
151
+ result->writers = writers;
152
+ for (auto e : other->writers )
153
+ result->writers .emplace (e);
154
+ return result;
155
+ }
156
+ /* *
157
+ This function returns a non-null value only if there is exaclty one writer statement.
158
+ In that case it returns the RHS of the assignment
159
+ */
160
+ const IR::Expression* substitution () const {
161
+ if (writers.size () != 1 )
162
+ return nullptr ;
163
+ auto first = *writers.begin ();
164
+ return first->right ;
165
+ }
166
+ };
167
+
168
+ /* *
169
+ Maintains a map from variable names to VariableWriters
170
+ It is customized for the needs of FixupChecksum.
171
+ */
172
+ struct VariableDefinitions {
173
+ std::map<cstring, const VariableWriters*> writers;
174
+ VariableDefinitions (const VariableDefinitions& other) = default ;
175
+ VariableDefinitions () = default ;
176
+ VariableDefinitions* clone () const {
177
+ return new VariableDefinitions (*this );
178
+ }
179
+ VariableDefinitions* join (const VariableDefinitions* other) const {
180
+ auto result = clone ();
181
+ for (auto e : other->writers ) {
182
+ auto &prev = result->writers [e.first ];
183
+ prev = prev ? prev->join (e.second ) : e.second ;
184
+ }
185
+ return result;
186
+ }
187
+ void declare (const IR::Declaration_Variable* decl) {
188
+ writers.emplace (decl->getName ().name , new VariableWriters ());
189
+ }
190
+ const VariableWriters* getWriters (const IR::Path* path) {
191
+ if (path->absolute )
192
+ return nullptr ;
193
+ return ::get (writers, path->name .name );
194
+ }
195
+ VariableDefinitions* setDefinition (const IR::Path* path,
196
+ const IR::AssignmentStatement* statement) {
197
+ auto w = getWriters (path);
198
+ if (w == nullptr )
199
+ // Path does not represent a variable
200
+ return this ;
201
+ auto result = clone ();
202
+ result->writers [path->name .name ] = new VariableWriters (statement);
203
+ return result;
204
+ }
205
+ };
206
+
207
+ /* *
208
+ Maintain def-use information.
209
+ It is customized for the needs of FixupChecksum.
210
+ */
211
+ struct PathSubstitutions {
212
+ std::map<const IR::PathExpression*, const IR::Expression*> definitions;
213
+ std::set<const IR::AssignmentStatement*> haveUses;
214
+ PathSubstitutions () = default ;
215
+ void add (const IR::PathExpression* path, const IR::Expression* expression) {
216
+ definitions.emplace (path, expression);
217
+ LOG3 (" Will substitute " << dbp (path) << " with " << expression);
218
+ }
219
+ const IR::Expression* get (const IR::PathExpression* path) const {
220
+ return ::get (definitions, path);
221
+ }
222
+ void foundUses (const VariableWriters* writers) {
223
+ for (auto w : writers->writers )
224
+ haveUses.emplace (w);
225
+ }
226
+ void foundUses (const IR::AssignmentStatement* statement) {
227
+ haveUses.emplace (statement);
228
+ }
229
+ bool hasUses (const IR::AssignmentStatement* statement) const {
230
+ return haveUses.find (statement) != haveUses.end ();
231
+ }
232
+ };
233
+
234
+ /* *
235
+ See the SimpleCopyProp pass below for the context in which this
236
+ analysis is run. We take advantage that some more complex code
237
+ patterns have already been eliminated.
238
+ */
239
+ class Accesses : public Inspector {
240
+ PathSubstitutions* substitutions;
241
+ VariableDefinitions* currentDefinitions;
242
+
243
+ bool notSupported (const IR::Node* node) {
244
+ ::error (" %1%: not supported in checksum update control" , node);
245
+ return false ;
246
+ }
247
+
248
+ public:
249
+ explicit Accesses (PathSubstitutions* substitutions): substitutions(substitutions) {
250
+ CHECK_NULL (substitutions); setName (" Accesses" );
251
+ currentDefinitions = new VariableDefinitions ();
252
+ }
253
+
254
+ bool preorder (const IR::Declaration_Variable* decl) override {
255
+ // we assume all variable declarations are at the beginning
256
+ currentDefinitions->declare (decl);
257
+ return false ;
258
+ }
259
+
260
+ // This is only invoked for read expressions
261
+ bool preorder (const IR::PathExpression* expression) override {
262
+ auto writers = currentDefinitions->getWriters (expression->path );
263
+ if (writers != nullptr ) {
264
+ if (auto s = writers->substitution ())
265
+ substitutions->add (expression, s);
266
+ else
267
+ substitutions->foundUses (writers);
268
+ }
269
+ return false ;
270
+ }
271
+
272
+ bool preorder (const IR::AssignmentStatement* statement) override {
273
+ visit (statement->right );
274
+ if (statement->left ->is <IR::PathExpression>()) {
275
+ auto pe = statement->left ->to <IR::PathExpression>();
276
+ currentDefinitions = currentDefinitions->setDefinition (pe->path , statement);
277
+ } else {
278
+ substitutions->foundUses (statement);
279
+ }
280
+ return false ;
281
+ }
282
+
283
+ bool preorder (const IR::IfStatement* statement) override {
284
+ visit (statement->condition );
285
+ auto defs = currentDefinitions->clone ();
286
+ visit (statement->ifTrue );
287
+ auto afterTrue = currentDefinitions;
288
+ if (statement->ifFalse != nullptr ) {
289
+ currentDefinitions = defs;
290
+ visit (statement->ifFalse );
291
+ currentDefinitions = afterTrue->join (currentDefinitions);
292
+ } else {
293
+ currentDefinitions = defs->join (afterTrue);
294
+ }
295
+ return false ;
296
+ }
297
+
298
+ bool preorder (const IR::SwitchStatement* statement) override
299
+ { return notSupported (statement); }
300
+
301
+ bool preorder (const IR::P4Action* action) override
302
+ { return notSupported (action); }
303
+
304
+ bool preorder (const IR::P4Table* table) override
305
+ { return notSupported (table); }
306
+
307
+ bool preorder (const IR::ReturnStatement* statement) override
308
+ { return notSupported (statement); }
309
+
310
+ bool preorder (const IR::ExitStatement* statement) override
311
+ { return notSupported (statement); }
312
+ };
313
+
314
+ class Replace : public Transform {
315
+ const PathSubstitutions* substitutions;
316
+ public:
317
+ explicit Replace (const PathSubstitutions* substitutions): substitutions(substitutions) {
318
+ CHECK_NULL (substitutions); setName (" Replace" ); }
319
+
320
+ const IR::Node* postorder (IR::AssignmentStatement* statement) override {
321
+ if (!substitutions->hasUses (getOriginal<IR::AssignmentStatement>()))
322
+ return new IR::EmptyStatement ();
323
+ return statement;
324
+ }
325
+
326
+ const IR::Node* postorder (IR::PathExpression* expression) override {
327
+ auto repl = substitutions->get (getOriginal<IR::PathExpression>());
328
+ if (repl != nullptr ) {
329
+ Replace rpl (substitutions);
330
+ auto recurse = repl->apply (rpl);
331
+ return recurse;
332
+ }
333
+ return expression;
334
+ }
335
+ };
336
+
337
+ /* *
338
+ This analysis is only executed on the control which performs
339
+ checksum update computations.
340
+
341
+ This is a simpler variant of copy propagation; it just finds
342
+ patterns of the form:
343
+ tmp = X;
344
+ ...
345
+ out = tmp;
346
+
347
+ then it substitutes the definition into the use.
348
+ The LocalCopyPropagation pass does not do this, because
349
+ it won't consider replacing definitions where the RHS has side-effects.
350
+ Since the only method call we accept in the checksum update block
351
+ is a checksum unit "get" method (this is not checked here, but
352
+ in the json code generator), we know that this method has no side-effects,
353
+ so we can safely reorder calls to methods.
354
+ Also, this is run after eliminating struct and tuple operations,
355
+ so we know that all assignments operate on scalar values.
356
+ */
357
+ class SimpleCopyProp : public PassManager {
358
+ PathSubstitutions substitutions;
359
+ public:
360
+ SimpleCopyProp () {
361
+ setName (" SimpleCopyProp" );
362
+ passes.push_back (new Accesses (&substitutions));
363
+ passes.push_back (new Replace (&substitutions));
364
+ }
365
+ };
366
+
367
+ } // namespace
368
+
369
+ const IR::Node* FixupChecksum::preorder (IR::P4Control* control) {
370
+ if (updateChecksumBlocks->find (control->name ) != updateChecksumBlocks->end ()) {
371
+ SimpleCopyProp scp;
372
+ return control->apply (scp);
373
+ }
374
+ return control;
375
+ }
376
+
377
+ // //////////////////////////////////////////////////////////////////////////////////
378
+
379
+ namespace {
380
+
139
381
/* *
140
382
Detect whether a Select expression is too complicated for BMv2.
141
383
Also used to detect complex expressions that are arguments
0 commit comments