P4C
The P4 Compiler
Loading...
Searching...
No Matches
dpdkArch.h
1/*
2 * Copyright 2020 Intel Corp.
3 * SPDX-FileCopyrightText: 2020 Intel Corp.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8#ifndef BACKENDS_DPDK_DPDKARCH_H_
9#define BACKENDS_DPDK_DPDKARCH_H_
10
11#include "constants.h"
12#include "dpdkProgramStructure.h"
13#include "dpdkUtils.h"
14#include "frontends/common/resolveReferences/resolveReferences.h"
15#include "frontends/p4/evaluator/evaluator.h"
16#include "frontends/p4/sideEffects.h"
17#include "frontends/p4/typeMap.h"
18#include "lib/error.h"
19#include "lib/ordered_map.h"
20#include "midend/flattenInterfaceStructs.h"
21#include "midend/removeLeftSlices.h"
22
23namespace P4::DPDK {
24
25using namespace P4::literals;
26
27cstring TypeStruct2Name(const cstring *s);
28bool isSimpleExpression(const IR::Expression *e);
29bool isNonConstantSimpleExpression(const IR::Expression *e);
30void expressionUnrollSanityCheck(const IR::Expression *e);
31
32using UserMeta = std::set<cstring>;
33
35
36/* According to the implementation of DPDK backend, for a control block, there
37 * are only two parameters: header and metadata. Therefore, first we need to
38 * rewrite the declaration of PSA architecture included in psa.p4 in order to
39 * pass the type checking. In addition, this pass changes the definition of
40 * P4Control and P4Parser(parameter list) in the P4 program provided by the
41 * user.
42 *
43 * This pass also modifies all metadata references and header reference. For
44 * metadata, struct_name.field_name -> m.struct_name_field_name. For header
45 * headers.header_name.field_name -> h.header_name.field_name The parameter
46 * named for header and metadata are also updated to "h" and "m" respectively.
47 */
48class ConvertToDpdkArch : public Transform {
49 P4::ReferenceMap *refMap;
50 DpdkProgramStructure *structure;
51
52 const IR::Type_Control *rewriteControlType(const IR::Type_Control *, cstring);
53 const IR::Type_Parser *rewriteParserType(const IR::Type_Parser *, cstring);
54 const IR::Type_Control *rewriteDeparserType(const IR::Type_Control *, cstring);
55 const IR::Node *postorder(IR::Type_Control *c) override;
56 const IR::Node *postorder(IR::Type_Parser *p) override;
57 const IR::Node *preorder(IR::Member *m) override;
58 const IR::Node *preorder(IR::PathExpression *pe) override;
59
60 public:
61 ConvertToDpdkArch(P4::ReferenceMap *refMap, DpdkProgramStructure *structure)
62 : refMap(refMap), structure(structure) {
63 CHECK_NULL(structure);
64 }
65};
66
99struct ConvertLookahead : public PassManager {
101 std::unordered_map<const IR::P4Program *, IR::IndexedVector<IR::Node>> newHeaderMap;
102 std::unordered_map<const IR::P4Parser *, IR::IndexedVector<IR::Declaration>> newLocalVarMap;
103 std::unordered_map<const IR::AssignmentStatement *, IR::IndexedVector<IR::StatOrDecl>>
104 newStatMap;
105
106 public:
107 void insertHeader(const IR::P4Program *p, const IR::Type_Header *h) {
108 if (newHeaderMap.count(p)) {
109 newHeaderMap.at(p).push_back(h);
110 } else {
111 newHeaderMap.emplace(p, IR::IndexedVector<IR::Node>(h));
112 }
113 LOG5("Program: " << dbp(p));
114 LOG2("Adding new header:" << std::endl << " " << h);
115 }
116 IR::IndexedVector<IR::Node> *getHeaders(const IR::P4Program *p) {
117 if (newHeaderMap.count(p)) {
118 return new IR::IndexedVector<IR::Node>(newHeaderMap.at(p));
119 }
120 return nullptr;
121 }
122 void insertVar(const IR::P4Parser *p, const IR::Declaration_Variable *v) {
123 if (newLocalVarMap.count(p)) {
124 newLocalVarMap.at(p).push_back(v);
125 } else {
126 newLocalVarMap.emplace(p, IR::IndexedVector<IR::Declaration>(v));
127 }
128 LOG5("Parser: " << dbp(p));
129 LOG2("Adding new local variable:" << std::endl << " " << v);
130 }
131 IR::IndexedVector<IR::Declaration> *getVars(const IR::P4Parser *p) {
132 if (newLocalVarMap.count(p)) {
133 return new IR::IndexedVector<IR::Declaration>(newLocalVarMap.at(p));
134 }
135 return nullptr;
136 }
137 void insertStatements(const IR::AssignmentStatement *as,
139 BUG_CHECK(newStatMap.count(as) == 0,
140 "Unexpectedly converting statement %1% multiple times!", as);
141 newStatMap.emplace(as, *vec);
142 LOG5("AssignmentStatement: " << dbp(as));
143 LOG2("Adding new statements:");
144 for (auto s : *vec) {
145 LOG2(" " << s);
146 }
147 }
148 IR::IndexedVector<IR::StatOrDecl> *getStatements(const IR::AssignmentStatement *as) {
149 if (newStatMap.count(as)) {
150 return new IR::IndexedVector<IR::StatOrDecl>(newStatMap.at(as));
151 }
152 return nullptr;
153 }
154 };
155
156 ReplacementMap repl;
157
158 class Collect : public Inspector {
159 P4::ReferenceMap *refMap;
160 P4::TypeMap *typeMap;
161 ReplacementMap *repl;
162
163 public:
164 Collect(P4::ReferenceMap *refMap, P4::TypeMap *typeMap, ReplacementMap *repl)
165 : refMap(refMap), typeMap(typeMap), repl(repl) {}
166 void postorder(const IR::AssignmentStatement *statement) override;
167 };
168
169 class Replace : public Transform {
170 DpdkProgramStructure *structure;
171 ReplacementMap *repl;
172
173 public:
174 Replace(DpdkProgramStructure *structure, ReplacementMap *repl)
175 : structure(structure), repl(repl) {}
176 const IR::Node *postorder(IR::AssignmentStatement *as) override;
177 const IR::Node *postorder(IR::Type_Struct *s) override;
178 const IR::Node *postorder(IR::P4Parser *parser) override;
179 };
180
181 ConvertLookahead(P4::ReferenceMap *refMap, P4::TypeMap *typeMap, DpdkProgramStructure *s) {
182 passes.push_back(new P4::TypeChecking(refMap, typeMap));
183 passes.push_back(new Collect(refMap, typeMap, &repl));
184 passes.push_back(new Replace(s, &repl));
185 passes.push_back(new P4::ClearTypeMap(typeMap));
186 }
187};
188
189// This Pass collects infomation about the name of all metadata and header
190// And it collects every field of metadata and renames all fields with a prefix
191// according to the metadata struct name. Eventually, the reference of a fields
192// will become m.$(struct_name)_$(field_name).
193class CollectMetadataHeaderInfo : public Inspector {
194 DpdkProgramStructure *structure;
195
196 void pushMetadata(const IR::Parameter *p);
197 void pushMetadata(const IR::ParameterList *, std::list<int> indices);
198
199 public:
200 explicit CollectMetadataHeaderInfo(DpdkProgramStructure *structure) : structure(structure) {}
201 bool preorder(const IR::P4Program *p) override;
202 bool preorder(const IR::Type_Struct *s) override;
203};
204
205// Previously, we have collected the information about how the single metadata
206// struct looks like in CollectMetadataHeaderInfo. This pass finds a suitable
207// place to inject this struct.
208class InjectJumboStruct : public Transform {
209 DpdkProgramStructure *structure;
210
211 public:
212 explicit InjectJumboStruct(DpdkProgramStructure *structure) : structure(structure) {}
213 const IR::Node *preorder(IR::Type_Struct *s) override;
214};
215
216// This pass injects metadata field which is used as port for 'tx' instruction
217// into the single metadata struct.
218// This pass has to be applied after CollectMetadataHeaderInfo fills
219// local_metadata_type field in DpdkProgramStructure which is passed to the constructor.
220class InjectFixedMetadataField : public Transform {
221 DpdkProgramStructure *structure;
222
223 public:
224 explicit InjectFixedMetadataField(DpdkProgramStructure *structure) : structure(structure) {}
225 const IR::Node *preorder(IR::Type_Struct *s) override;
226};
227
231class AlignHdrMetaField : public Transform {
232 DpdkProgramStructure *structure;
233
235
236 public:
237 explicit AlignHdrMetaField(DpdkProgramStructure *structure) : structure(structure) {
238 CHECK_NULL(structure);
239 }
240 const IR::Node *preorder(IR::Type_StructLike *st) override;
241 const IR::Node *preorder(IR::Member *m) override;
242};
243
244struct ByteAlignment : public PassManager {
245 P4::TypeMap *typeMap;
246 P4::ReferenceMap *refMap;
247 DpdkProgramStructure *structure;
248
249 public:
250 ByteAlignment(P4::TypeMap *typeMap, P4::ReferenceMap *refMap, DpdkProgramStructure *structure)
251 : typeMap(typeMap), refMap(refMap), structure(structure) {
252 CHECK_NULL(structure);
253 passes.push_back(new AlignHdrMetaField(structure));
254 passes.push_back(new P4::ClearTypeMap(typeMap));
255 passes.push_back(new P4::TypeChecking(refMap, typeMap, true));
256 /* DoRemoveLeftSlices pass converts the slice Member (LHS in assn stm)
257 resulting from above Pass into shift operation */
258 passes.push_back(new P4::DoRemoveLeftSlices(typeMap));
259 passes.push_back(new P4::ClearTypeMap(typeMap));
260 passes.push_back(new P4::TypeChecking(refMap, typeMap, true));
261 }
262};
263
264class ReplaceHdrMetaField : public Transform {
265 public:
266 const IR::Node *postorder(IR::Type_Struct *st) override;
267};
268
269struct fieldInfo {
270 unsigned fieldWidth;
271 fieldInfo() { fieldWidth = 0; }
272};
273
274// This class is helpful for StatementUnroll and IfStatementUnroll. Since dpdk
275// asm is not able to process complex expression, e.g., a = b + c * d. We need
276// break it down. Therefore, we need some temporary variables to hold the
277// intermediate values. And this class is helpful to inject the declarations of
278// temporary value into P4Control and P4Parser.
280 std::map<const IR::Node *, IR::IndexedVector<IR::Declaration> *> decl_map;
281
282 public:
283 // push the declaration to the right code block.
284 void collect(const IR::P4Control *control, const IR::P4Parser *parser,
285 const IR::Declaration *decl) {
286 IR::IndexedVector<IR::Declaration> *decls = nullptr;
287 if (parser) {
288 auto res = decl_map.find(parser);
289 if (res != decl_map.end()) {
290 decls = res->second;
291 } else {
293 decl_map.emplace(parser, decls);
294 }
295 } else if (control) {
296 auto res = decl_map.find(control);
297 if (res != decl_map.end()) {
298 decls = res->second;
299 } else {
301 decl_map.emplace(control, decls);
302 }
303 }
304 BUG_CHECK(decls != nullptr, "decls cannot be null");
305 decls->push_back(decl);
306 }
307 IR::Node *inject_control(const IR::Node *orig, IR::P4Control *control) {
308 auto res = decl_map.find(orig);
309 if (res == decl_map.end()) {
310 return control;
311 }
312 control->controlLocals.prepend(*res->second);
313 return control;
314 }
315 IR::Node *inject_parser(const IR::Node *orig, IR::P4Parser *parser) {
316 auto res = decl_map.find(orig);
317 if (res == decl_map.end()) {
318 return parser;
319 }
320 parser->parserLocals.prepend(*res->second);
321 return parser;
322 }
323};
324
325/* This pass breaks complex expressions down, since dpdk asm cannot describe
326 * complex expression. This pass is not complete. MethodCallStatement should be
327 * unrolled as well. Note that IfStatement should not be unrolled here, as we
328 * have a separate pass for it, because IfStatement does not want to unroll
329 * logical expression(dpdk asm has conditional jmp for these cases)
330 */
331class StatementUnroll : public Transform {
332 private:
333 P4::ReferenceMap *refMap;
334 DpdkProgramStructure *structure;
335 DeclarationInjector injector;
336
337 public:
338 StatementUnroll(P4::ReferenceMap *refMap, DpdkProgramStructure *structure)
339 : refMap(refMap), structure(structure) {}
340 const IR::Node *preorder(IR::AssignmentStatement *a) override;
341 const IR::Node *postorder(IR::P4Control *a) override;
342 const IR::Node *postorder(IR::P4Parser *a) override;
343};
344
345/* This pass helps StatementUnroll to unroll expressions inside statements.
346 * For example, if an AssignmentStatement looks like this: a = b + c * d
347 * StatementUnroll's AssignmentStatement preorder will call ExpressionUnroll
348 * twice for BinaryExpression's left(b) and right(c * d). For left, since it is
349 * a simple expression, ExpressionUnroll will set root to PathExpression(b) and
350 * the decl and stmt is empty. For right, ExpressionUnroll will set root to
351 * PathExpression(tmp), decl contains tmp's declaration and stmt contains:
352 * tmp = c * d, which will be injected in front of the AssignmentStatement.
353 */
354class ExpressionUnroll : public Inspector {
355 P4::ReferenceMap *refMap;
356
357 public:
360 IR::PathExpression *root;
361 ExpressionUnroll(P4::ReferenceMap *refMap, DpdkProgramStructure *) : refMap(refMap) {
362 setName("ExpressionUnroll");
363 }
364 bool preorder(const IR::Operation_Unary *a) override;
365 bool preorder(const IR::Operation_Binary *a) override;
366 bool preorder(const IR::MethodCallExpression *a) override;
367 bool preorder(const IR::Member *a) override;
368 bool preorder(const IR::PathExpression *a) override;
369 bool preorder(const IR::Constant *a) override;
370 bool preorder(const IR::BoolLiteral *a) override;
371};
372
373// This pass is similiar to StatementUnroll pass, the difference is that this
374// pass will call LogicalExpressionUnroll to unroll the expression, which treat
375// logical expression differently.
376class IfStatementUnroll : public Transform {
377 private:
378 P4::ReferenceMap *refMap;
379 DeclarationInjector injector;
380
381 public:
382 explicit IfStatementUnroll(P4::ReferenceMap *refMap) : refMap(refMap) {
383 setName("IfStatementUnroll");
384 }
385 const IR::Node *postorder(IR::SwitchStatement *a) override;
386 const IR::Node *postorder(IR::IfStatement *a) override;
387 const IR::Node *postorder(IR::P4Control *a) override;
388 const IR::Node *postorder(IR::P4Parser *a) override;
389};
390
391/* Assume one logical expression looks like this: a && (b + c > d), this pass
392 * will unroll the expression to {tmp = b + c; if(a && (tmp > d))}. Logical
393 * calculation will be unroll in a dedicated pass.
394 */
395class LogicalExpressionUnroll : public Inspector {
396 P4::ReferenceMap *refMap;
397
398 public:
401 IR::Expression *root;
402 static bool is_logical(const IR::Operation_Binary *bin) {
403 if (bin->is<IR::LAnd>() || bin->is<IR::LOr>() || bin->is<IR::Leq>() || bin->is<IR::Equ>() ||
404 bin->is<IR::Neq>() || bin->is<IR::Grt>() || bin->is<IR::Lss>() || bin->is<IR::Geq>() ||
405 bin->is<IR::Leq>())
406 return true;
407 else
408 return false;
409 }
410
411 explicit LogicalExpressionUnroll(P4::ReferenceMap *refMap) : refMap(refMap) {
412 visitDagOnce = false;
413 }
414 bool preorder(const IR::Operation_Unary *a) override;
415 bool preorder(const IR::Operation_Binary *a) override;
416 bool preorder(const IR::MethodCallExpression *a) override;
417 bool preorder(const IR::Member *a) override;
418 bool preorder(const IR::PathExpression *a) override;
419 bool preorder(const IR::Constant *a) override;
420 bool preorder(const IR::BoolLiteral *a) override;
421};
422
423// According to dpdk spec, Binary Operation will only have two parameters, which
424// looks like: a = a + b. Therefore, this pass transform all AssignStatement
425// that has Binary_Operation to become two-parameter form.
426class ConvertBinaryOperationTo2Params : public Transform {
427 DeclarationInjector injector;
428 P4::ReferenceMap *refMap;
429
430 public:
431 explicit ConvertBinaryOperationTo2Params(P4::ReferenceMap *refMap) : refMap(refMap) {}
432 const IR::Node *postorder(IR::AssignmentStatement *a) override;
433 const IR::Node *postorder(IR::P4Control *a) override;
434 const IR::Node *postorder(IR::P4Parser *a) override;
435};
436
437// Since in dpdk asm, there is no local variable declaration, we need to collect
438// all local variables and inject them into the metadata struct.
439// Local variables which are of header types are injected into headers struct
440// instead of metadata struct, so that they can be instantiated as headers in the
441// resulting dpdk asm file.
442class CollectLocalVariables : public Transform {
444 P4::ReferenceMap *refMap;
445 P4::TypeMap *typeMap;
446 DpdkProgramStructure *structure;
447
448 void insert(const cstring prefix, const IR::IndexedVector<IR::Declaration> *locals) {
449 for (auto d : *locals) {
450 if (auto dv = d->to<IR::Declaration_Variable>()) {
451 const cstring name = refMap->newName(prefix + "_" + dv->name.name);
452 localsMap.emplace(dv, name);
453 } else if (!d->is<IR::P4Action>() && !d->is<IR::P4Table>() &&
454 !d->is<IR::Declaration_Instance>()) {
455 BUG("%1%: Unhandled declaration type", d);
456 }
457 }
458 }
459
460 public:
461 CollectLocalVariables(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
462 DpdkProgramStructure *structure)
463 : refMap(refMap), typeMap(typeMap), structure(structure) {}
464 const IR::Node *preorder(IR::P4Program *p) override;
465 const IR::Node *postorder(IR::Type_Struct *s) override;
466 const IR::Node *postorder(IR::Member *m) override;
467 const IR::Node *postorder(IR::PathExpression *path) override;
468 const IR::Node *postorder(IR::P4Control *c) override;
469 const IR::Node *postorder(IR::P4Parser *p) override;
470};
471
472// According to dpdk spec, action parameters should prepend a p. In order to
473// respect this, we need at first make all action parameter lists into separate
474// structs and declare that struct in the P4 program. Then we modify the action
475// parameter list. Eventually, it will only contain one parameter `t`, which is a
476// struct containing all parameters previously defined. Next, we prepend t. in
477// front of action parameters. Please note that it is possible that the user
478// defines a struct paremeter himself or define multiple struct parameters in
479// action parameterlist. Current implementation does not support this.
480class PrependPDotToActionArgs : public Transform {
481 P4::TypeMap *typeMap;
482 P4::ReferenceMap *refMap;
483 DpdkProgramStructure *structure;
484
485 public:
486 PrependPDotToActionArgs(P4::TypeMap *typeMap, P4::ReferenceMap *refMap,
487 DpdkProgramStructure *structure)
488 : typeMap(typeMap), refMap(refMap), structure(structure) {}
489 const IR::Node *postorder(IR::P4Action *a) override;
490 const IR::Node *postorder(IR::P4Program *s) override;
491 const IR::Node *preorder(IR::PathExpression *path) override;
492 const IR::Node *preorder(IR::MethodCallExpression *) override;
493};
494
495/* This class is used to process the default action
496 and store the parameter list for each table.
497 Later, this infomation is passed and saved in table
498 properties and then used for generating instruction
499 for default action in each table.
500*/
501class DefActionValue : public Inspector {
502 P4::TypeMap *typeMap;
503 P4::ReferenceMap *refMap;
504 DpdkProgramStructure *structure;
505
506 public:
507 DefActionValue(P4::TypeMap *typeMap, P4::ReferenceMap *refMap, DpdkProgramStructure *structure)
508 : typeMap(typeMap), refMap(refMap), structure(structure) {}
509 void postorder(const IR::P4Table *t) override;
510};
511
512// dpdk does not support ternary operator so we need to translate ternary operator
513// to corresponding if else statement
514// Taken from frontend pass DoSimplifyExpressions in sideEffects.h
515class DismantleMuxExpressions : public Transform {
516 P4::TypeMap *typeMap;
517 P4::ReferenceMap *refMap;
518 IR::IndexedVector<IR::Declaration> toInsert; // temporaries
520
521 cstring createTemporary(const IR::Type *type);
522 const IR::Expression *addAssignment(Util::SourceInfo srcInfo, cstring varName,
523 const IR::Expression *expression);
524
525 public:
526 DismantleMuxExpressions(P4::TypeMap *typeMap, P4::ReferenceMap *refMap)
527 : typeMap(typeMap), refMap(refMap) {}
528 const IR::Node *preorder(IR::Mux *expression) override;
529 const IR::Node *postorder(IR::P4Parser *parser) override;
530 const IR::Node *postorder(IR::Function *function) override;
531 const IR::Node *postorder(IR::P4Control *control) override;
532 const IR::Node *postorder(IR::P4Action *action) override;
533 const IR::Node *postorder(IR::AssignmentStatement *statement) override;
534};
535
536// For dpdk asm, there is not object-oriented. Therefore, we cannot define a
537// checksum in dpdk asm. And dpdk asm only provides ckadd(checksum add) and
538// cksub(checksum sub). So we need to define a explicit state for each checksum
539// declaration. Essentially, this state will be declared in header struct and
540// initilized to 0. And for cksum.add(x), it will be translated to ckadd state
541// x. For dst = cksum.get(), it will be translated to mov dst state. This pass
542// collects checksum instances and index them.
543class CollectInternetChecksumInstance : public Inspector {
544 P4::TypeMap *typeMap;
545 DpdkProgramStructure *structure;
546 std::vector<cstring> *csum_vec;
547 int index = 0;
548
549 public:
550 CollectInternetChecksumInstance(P4::TypeMap *typeMap, DpdkProgramStructure *structure,
551 std::vector<cstring> *csum_vec)
552 : typeMap(typeMap), structure(structure), csum_vec(csum_vec) {}
553 bool preorder(const IR::Declaration_Instance *d) override {
554 auto type = typeMap->getType(d, true);
555 if (auto extn = type->to<IR::Type_Extern>()) {
556 if (extn->name == "InternetChecksum") {
557 std::ostringstream s;
558 s << "state_" << index++;
559 csum_vec->push_back(s.str());
560 structure->csum_map.emplace(d, s.str());
561 }
562 }
563 return false;
564 }
565};
566
567// This pass will inject checksum states into header. The reason why we inject
568// state into header instead of metadata is due to the implementation of dpdk
569// side(a question related to endianness)
570class InjectInternetChecksumIntermediateValue : public Transform {
571 DpdkProgramStructure *structure;
572 std::vector<cstring> *csum_vec;
573
574 public:
575 InjectInternetChecksumIntermediateValue(DpdkProgramStructure *structure,
576 std::vector<cstring> *csum_vec)
577 : structure(structure), csum_vec(csum_vec) {}
578
579 const IR::Node *postorder(IR::P4Program *p) override {
580 auto new_objs = new IR::Vector<IR::Node>;
581 bool inserted = false;
582 for (auto obj : p->objects) {
583 if (obj->to<IR::Type_Header>() && !inserted) {
584 inserted = true;
585 if (csum_vec->size() > 0) {
586 auto fields = new IR::IndexedVector<IR::StructField>;
587 for (auto fld : *csum_vec) {
588 fields->push_back(
589 new IR::StructField(IR::ID(fld), IR::Type::Bits::get(16, false)));
590 }
591 new_objs->push_back(new IR::Type_Header(IR::ID("cksum_state_t"), *fields));
592 }
593 }
594 new_objs->push_back(obj);
595 }
596 p->objects = *new_objs;
597 return p;
598 }
599
600 const IR::Node *postorder(IR::Type_Struct *s) override {
601 if (s->name.name == structure->header_type) {
602 if (structure->csum_map.size() > 0)
603 s->fields.push_back(new IR::StructField(
604 IR::ID("cksum_state"), new IR::Type_Name(IR::ID("cksum_state_t"))));
605 }
606 return s;
607 }
608};
609
610class ConvertInternetChecksum : public PassManager {
611 std::vector<cstring> csum_vec;
612
613 public:
614 ConvertInternetChecksum(P4::TypeMap *typeMap, DpdkProgramStructure *structure) {
615 passes.push_back(new CollectInternetChecksumInstance(typeMap, structure, &csum_vec));
616 passes.push_back(new InjectInternetChecksumIntermediateValue(structure, &csum_vec));
617 }
618};
619
620/* This pass collects PSA extern meter, counter and register declaration instances and
621 push them to a vector for emitting to the .spec file later */
622class CollectExternDeclaration : public Inspector {
623 DpdkProgramStructure *structure;
624
625 public:
626 explicit CollectExternDeclaration(DpdkProgramStructure *structure) : structure(structure) {}
627 bool preorder(const IR::Declaration_Instance *d) override {
628 if (auto type = d->type->to<IR::Type_Name>()) {
629 auto externTypeName = type->path->name.name;
630 if (externTypeName == "DirectMeter") {
631 if (d->arguments->size() != 1) {
632 ::P4::error(ErrorType::ERR_EXPECTED,
633 "%1%: expected type of meter as the only argument", d);
634 } else {
635 /* Check if the Direct meter is of PACKETS (0) type */
636 if (d->arguments->at(0)->expression->to<IR::Constant>()->asUnsigned() == 0)
637 warn(ErrorType::WARN_UNSUPPORTED,
638 "%1%: Packet metering is not supported."
639 " Falling back to byte metering.",
640 d);
641 }
642 } else {
643 // unsupported extern type
644 return false;
645 }
646 structure->externDecls.push_back(d);
647 } else if (auto type = d->type->to<IR::Type_Specialized>()) {
648 auto externTypeName = type->baseType->path->name.name;
649 if (externTypeName == "Meter") {
650 if (d->arguments->size() != 2) {
651 ::P4::error(ErrorType::ERR_EXPECTED,
652 "%1%: expected number of meters and type of meter as arguments", d);
653 } else {
654 /* Check if the meter is of PACKETS (0) type */
655 if (d->arguments->at(1)->expression->to<IR::Constant>()->asUnsigned() == 0)
656 warn(ErrorType::WARN_UNSUPPORTED,
657 "%1%: Packet metering is not supported."
658 " Falling back to byte metering.",
659 d);
660 }
661 } else if (externTypeName == "Counter") {
662 if (d->arguments->size() != 2) {
663 ::P4::error(ErrorType::ERR_EXPECTED,
664 "%1%: expected number of counters and type of counter as arguments",
665 d);
666 }
667 } else if (externTypeName == "DirectCounter") {
668 if (d->arguments->size() != 1) {
669 ::P4::error(ErrorType::ERR_EXPECTED,
670 "%1%: expected type of counter as the only argument", d);
671 }
672 } else if (externTypeName == "Register") {
673 if (d->arguments->size() != 1 && d->arguments->size() != 2) {
674 ::P4::error(ErrorType::ERR_EXPECTED,
675 "%1%: expected size and optionally init_val as arguments", d);
676 }
677 } else if (externTypeName == "Hash") {
678 if (d->arguments->size() != 1) {
679 ::P4::error(ErrorType::ERR_EXPECTED,
680 "%1%: expected hash algorithm as the only argument", d);
681 }
682 } else {
683 // unsupported extern type
684 return false;
685 }
686 structure->externDecls.push_back(d);
687 }
688 return false;
689 }
690};
691
692// This pass is preparing logical expression for following branching statement
693// optimization. This pass breaks parenthesis looks liks this: (a && b) && c.
694// After this pass, the expression looks like this: a && b && c. (The AST is
695// different).
696class BreakLogicalExpressionParenthesis : public Transform {
697 public:
698 const IR::Node *postorder(IR::LAnd *land) {
699 if (auto land2 = land->left->to<IR::LAnd>()) {
700 auto sub = new IR::LAnd(land2->right, land->right);
701 return new IR::LAnd(land2->left, sub);
702 } else if (!land->left->is<IR::LOr>() && !land->left->is<IR::Equ>() &&
703 !land->left->is<IR::Neq>() && !land->left->is<IR::Leq>() &&
704 !land->left->is<IR::Geq>() && !land->left->is<IR::Lss>() &&
705 !land->left->is<IR::Grt>() && !land->left->is<IR::MethodCallExpression>() &&
706 !land->left->is<IR::PathExpression>() && !land->left->is<IR::Member>()) {
707 BUG("Logical Expression Unroll pass failed");
708 }
709 return land;
710 }
711 const IR::Node *postorder(IR::LOr *lor) {
712 if (auto lor2 = lor->left->to<IR::LOr>()) {
713 auto sub = new IR::LOr(lor2->right, lor->right);
714 return new IR::LOr(lor2->left, sub);
715 } else if (!lor->left->is<IR::LAnd>() && !lor->left->is<IR::Equ>() &&
716 !lor->left->is<IR::Neq>() && !lor->left->is<IR::Lss>() &&
717 !lor->left->is<IR::Grt>() && !lor->left->is<IR::MethodCallExpression>() &&
718 !lor->left->is<IR::PathExpression>() && !lor->left->is<IR::Member>()) {
719 BUG("Logical Expression Unroll pass failed");
720 }
721 return lor;
722 }
723};
724
725// This pass will swap the simple expression to the front of an logical
726// expression. Note that even for a subexpression of a logical expression, we
727// will swap it as well. For example, a && ((b && c) || d), will become
728// a && (d || (b && c))
730 bool is_simple(const IR::Node *n) {
731 if (n->is<IR::Equ>() || n->is<IR::Neq>() || n->is<IR::Lss>() || n->is<IR::Grt>() ||
732 n->is<IR::Geq>() || n->is<IR::Leq>() || n->is<IR::MethodCallExpression>() ||
733 n->is<IR::PathExpression>() || n->is<IR::Member>()) {
734 return true;
735 } else if (!n->is<IR::LAnd>() && !n->is<IR::LOr>()) {
736 BUG("Logical Expression Unroll pass failed");
737 } else {
738 return false;
739 }
740 }
741
742 public:
743 const IR::Node *postorder(IR::LAnd *land) {
744 if (!is_simple(land->left) && is_simple(land->right)) {
745 return new IR::LAnd(land->right, land->left);
746 } else if (!is_simple(land->left)) {
747 if (auto land2 = land->right->to<IR::LAnd>()) {
748 if (is_simple(land2->left)) {
749 auto sub = new IR::LAnd(land->left, land2->right);
750 return new IR::LAnd(land2->left, sub);
751 }
752 }
753 }
754 return land;
755 }
756 const IR::Node *postorder(IR::LOr *lor) {
757 if (!is_simple(lor->left) && is_simple(lor->right)) {
758 return new IR::LOr(lor->right, lor->left);
759 } else if (!is_simple(lor->left)) {
760 if (auto lor2 = lor->right->to<IR::LOr>()) {
761 if (is_simple(lor2->left)) {
762 auto sub = new IR::LOr(lor->left, lor2->right);
763 return new IR::LOr(lor2->left, sub);
764 }
765 }
766 }
767 return lor;
768 }
769};
770
771// This passmanager togethor transform logical expression into a form that
772// the simple expression will go to the front of the expression. And for
773// expression at the same level(the same level is that expressions that are
774// connected directly by && or ||) should be traversed from left to right
775// (a && b) && c is not a valid expression here.
776class ConvertLogicalExpression : public PassManager {
777 public:
778 ConvertLogicalExpression() {
780 passes.push_back(r);
782 passes.push_back(r);
783 }
784};
785
786// This Pass collects infomation about the table keys for each table. This information
787// is later used for generating the context JSON output for use by the control plane
788// software.
789class CollectTableInfo : public Inspector {
790 DpdkProgramStructure *structure;
791
792 public:
793 explicit CollectTableInfo(DpdkProgramStructure *structure) : structure(structure) {
794 setName("CollectTableInfo");
795 }
796 bool preorder(const IR::Key *key) override;
797};
798
799// This pass transforms the tables such that all the Match keys are part of the same
800// header/metadata struct. If the match keys are from different headers, this pass creates
801// mirror copies of the struct field into the metadata struct and updates the table to use
802// the metadata copy.
803// This pass must be called right before CollectLocalVariables pass as the temporary
804// variables created for holding copy of the table keys are inserted to Metadata by
805// CollectLocalVariables pass.
807 int offsetInMetadata;
808 int size;
809};
810
811struct keyInfo {
812 int numElements;
813 int numExistingMetaFields;
814 bool isLearner;
815 bool isExact;
816 int size;
817 std::vector<struct keyElementInfo *> elements;
818};
819
820class CopyMatchKeysToSingleStruct : public P4::KeySideEffect {
822 DpdkProgramStructure *structure;
823 bool metaCopyNeeded = false;
824
825 public:
826 CopyMatchKeysToSingleStruct(P4::TypeMap *typeMap, std::set<const IR::P4Table *> *invokedInKey,
827 DpdkProgramStructure *structure)
828 : P4::KeySideEffect(typeMap, invokedInKey), structure(structure) {
829 setName("CopyMatchKeysToSingleStruct");
830 }
831
832 const IR::Node *preorder(IR::Key *key) override;
833 const IR::Node *postorder(IR::KeyElement *element) override;
834 const IR::Node *doStatement(const IR::Statement *statement, const IR::Expression *expression,
835 const Visitor::Context *ctxt) override;
836 struct keyInfo *getKeyInfo(IR::Key *keys);
837 cstring getTableKeyName(const IR::Expression *e);
838 int getFieldSizeBits(const IR::Type *field_type);
839 bool isLearnerTable(const IR::P4Table *t);
840};
841
843 // Map which holds the switch expression variable and constant tuple per switch statement for
844 // each action.
845 // action_name: {<switch_var, constant_value>, <switch_var1, constant_value1>, ...}
846 std::map<cstring, std::vector<std::tuple<cstring, IR::Constant *>>> actionCaseMap;
847
848 public:
849 // Fill Switch Action Map
850 void addToSwitchMap(cstring actionName, cstring switchExprTmp, IR::Constant *caseLabelValue) {
851 actionCaseMap[actionName].push_back(std::make_tuple(switchExprTmp, caseLabelValue));
852 }
853
854 const IR::Node *setSwitchVarInAction(IR::P4Action *action) {
855 if (actionCaseMap.count(action->name.name)) {
856 // Insert assignment statements for each switch statement which uses this action name as
857 // switch case.
858 auto acm = actionCaseMap[action->name.name];
859 auto body = new IR::BlockStatement(action->body->srcInfo);
860 for (auto pair : acm) {
861 auto assn = new IR::AssignmentStatement(
862 new IR::PathExpression(IR::ID(get<0>(pair))), get<1>(pair));
863 body->push_back(assn);
864 }
865 for (auto s : action->body->components) body->push_back(s);
866 action->body = body;
867 actionCaseMap.erase(action->name.name);
868 }
869 return action;
870 }
871};
872
876class SplitP4TableCommon : public Transform {
877 cstring switchExprTmp;
878 DeclarationInjector injector;
879
880 public:
881 enum class TableImplementation { DEFAULT, ACTION_PROFILE, ACTION_SELECTOR };
882 P4::ReferenceMap *refMap;
883 P4::TypeMap *typeMap;
884 DpdkProgramStructure *structure;
885 SwitchHandler &sw;
886 TableImplementation implementation;
887 std::set<cstring> match_tables;
888 std::map<cstring, cstring> group_tables;
889 std::map<cstring, cstring> member_tables;
890 std::map<cstring, cstring> member_ids;
891 std::map<cstring, cstring> group_ids;
892
893 SplitP4TableCommon(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
894 DpdkProgramStructure *structure, SwitchHandler &sw)
895 : refMap(refMap), typeMap(typeMap), structure(structure), sw(sw) {
896 implementation = TableImplementation::DEFAULT;
897 }
898
899 const IR::Node *postorder(IR::MethodCallStatement *) override;
900 const IR::Node *postorder(IR::IfStatement *) override;
901 const IR::Node *postorder(IR::SwitchStatement *) override;
902 const IR::Node *postorder(IR::P4Control *) override;
903 std::tuple<const IR::P4Table *, cstring, cstring> create_match_table(
904 const IR::P4Table * /* tbl */);
905 const IR::P4Action *create_action(cstring /* actionName */, cstring /* id */, cstring);
906 const IR::P4Table *create_member_table(const IR::P4Table *, cstring, cstring);
907 const IR::P4Table *create_group_table(const IR::P4Table *, cstring, cstring, cstring, unsigned,
908 unsigned);
909 IR::Expression *initializeMemberAndGroupId(cstring tableName,
911};
912
919class SplitActionSelectorTable : public SplitP4TableCommon {
920 public:
921 SplitActionSelectorTable(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
922 DpdkProgramStructure *structure, SwitchHandler &sw)
923 : SplitP4TableCommon(refMap, typeMap, structure, sw) {
924 implementation = TableImplementation::ACTION_SELECTOR;
925 }
926 const IR::Node *postorder(IR::P4Table *tbl) override;
927};
928
934class SplitActionProfileTable : public SplitP4TableCommon {
935 public:
936 SplitActionProfileTable(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
937 DpdkProgramStructure *structure, SwitchHandler &sw)
938 : SplitP4TableCommon(refMap, typeMap, structure, sw) {
939 implementation = TableImplementation::ACTION_PROFILE;
940 }
941 const IR::Node *postorder(IR::P4Table *tbl) override;
942};
943
963class UpdateActionForSwitch : public Transform {
964 SwitchHandler &sw;
965
966 public:
967 explicit UpdateActionForSwitch(SwitchHandler &sw) : sw(sw) { setName("UpdateActionForSwitch"); }
968 const IR::Node *postorder(IR::P4Action *action) { return sw.setSwitchVarInAction(action); }
969};
970
972class ConvertActionSelectorAndProfile : public PassManager {
973 SwitchHandler sw;
974
975 public:
976 ConvertActionSelectorAndProfile(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
977 DpdkProgramStructure *structure) {
978 passes.emplace_back(new P4::TypeChecking(refMap, typeMap));
979 passes.emplace_back(new SplitActionSelectorTable(refMap, typeMap, structure, sw));
980 passes.emplace_back(new UpdateActionForSwitch(sw));
981 passes.push_back(new P4::ClearTypeMap(typeMap));
982 passes.emplace_back(new P4::TypeChecking(refMap, typeMap, true));
983 passes.emplace_back(new SplitActionProfileTable(refMap, typeMap, structure, sw));
984 passes.emplace_back(new UpdateActionForSwitch(sw));
985 passes.push_back(new P4::ClearTypeMap(typeMap));
986 passes.emplace_back(new P4::TypeChecking(refMap, typeMap, true));
987 }
988};
989
990/* Collect size information from the owner table for direct counter and meter extern objects
991 * and validate some of the constraints on usage of Direct Meter and Direct Counter extern
992 * methods */
993class CollectDirectCounterMeter : public Inspector {
994 P4::ReferenceMap *refMap;
995 P4::TypeMap *typeMap;
996 DpdkProgramStructure *structure;
997 // To validate presence of specified method call for instancename within given action
998 cstring method;
999 cstring instancename;
1000 // To validate that same method call for different direct meter/counter instance does not exist
1001 // in any action
1002 cstring oneInstance;
1003 bool methodCallFound;
1004 int getTableSize(const IR::P4Table *tbl);
1005 bool ifMethodFound(const IR::P4Action *a, cstring method,
1006 cstring instancename = cstring::empty);
1007 void checkMethodCallInAction(const P4::ExternMethod *);
1008
1009 public:
1010 static ordered_map<cstring, int> directMeterCounterSizeMap;
1011 CollectDirectCounterMeter(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1012 DpdkProgramStructure *structure)
1013 : refMap(refMap), typeMap(typeMap), structure(structure) {
1014 setName("CollectDirectCounterMeter");
1015 visitDagOnce = false;
1016 method = cstring::empty;
1017 instancename = cstring::empty;
1018 oneInstance = cstring::empty;
1019 methodCallFound = false;
1020 }
1021
1022 bool preorder(const IR::MethodCallStatement *mcs) override;
1023 bool preorder(const IR::AssignmentStatement *assn) override;
1024 bool preorder(const IR::P4Action *a) override;
1025 bool preorder(const IR::P4Table *t) override;
1026};
1027
1028class ValidateDirectCounterMeter : public Inspector {
1029 P4::ReferenceMap *refMap;
1030 P4::TypeMap *typeMap;
1031 DpdkProgramStructure *structure;
1032 void validateMethodInvocation(P4::ExternMethod *);
1033
1034 public:
1035 ValidateDirectCounterMeter(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1036 DpdkProgramStructure *structure)
1037 : refMap(refMap), typeMap(typeMap), structure(structure) {}
1038
1039 void postorder(const IR::AssignmentStatement *) override;
1040 void postorder(const IR::MethodCallStatement *) override;
1041};
1042
1043class CollectAddOnMissTable : public Inspector {
1044 P4::ReferenceMap *refMap;
1045 P4::TypeMap *typeMap;
1046 DpdkProgramStructure *structure;
1047
1048 public:
1049 CollectAddOnMissTable(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1050 DpdkProgramStructure *structure)
1051 : refMap(refMap), typeMap(typeMap), structure(structure) {}
1052
1053 void postorder(const IR::P4Table *t) override;
1054 void postorder(const IR::MethodCallStatement *) override;
1055};
1056
1057class ValidateAddOnMissExterns : public Inspector {
1058 P4::ReferenceMap *refMap;
1059 P4::TypeMap *typeMap;
1060 DpdkProgramStructure *structure;
1061
1062 public:
1063 ValidateAddOnMissExterns(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1064 DpdkProgramStructure *structure)
1065 : refMap(refMap), typeMap(typeMap), structure(structure) {}
1066
1067 void postorder(const IR::MethodCallStatement *) override;
1068 cstring getDefActionName(const IR::P4Table *t) {
1069 auto act = t->getDefaultAction();
1070 BUG_CHECK(act != nullptr, "%1%: default action does not exist", t);
1071 if (auto mc = act->to<IR::MethodCallExpression>()) {
1072 auto method = mc->method->to<IR::PathExpression>();
1073 return method->path->name;
1074 }
1075 return NULL;
1076 }
1077};
1078
1079// Dpdk does not allow operations (arithmetic, logical, bitwise, relational etc) on operands
1080// greater than 64-bit.
1081class ValidateOperandSize : public Inspector {
1082 public:
1083 ValidateOperandSize() { setName("ValidateOperandSize"); }
1084 void isValidOperandSize(const IR::Expression *e) {
1085 if (auto t = e->type->to<IR::Type_Bits>()) {
1086 if (t->width_bits() > dpdk_max_operand_size) {
1087 ::P4::error(ErrorType::ERR_UNSUPPORTED_ON_TARGET, "Unsupported bitwidth %1% in %2%",
1088 t->width_bits(), e);
1089 return;
1090 }
1091 }
1092 }
1093
1094 void postorder(const IR::Operation_Binary *binop) override {
1095 if (binop->is<IR::BOr>() || binop->is<IR::BAnd>() || binop->is<IR::BXor>() ||
1096 binop->is<IR::Equ>() || binop->is<IR::Neq>()) {
1097 if (auto src1Type = binop->left->type->to<IR::Type_Bits>()) {
1098 if (src1Type->width_bits() == 128) return;
1099 }
1100 }
1101 isValidOperandSize(binop->left);
1102 isValidOperandSize(binop->right);
1103 }
1104
1105 // Reject all operations except typecast if the operand size is beyond the supported limit
1106 void postorder(const IR::Operation_Unary *unop) override {
1107 if (unop->is<IR::Cast>()) return;
1108 if (unop->is<IR::Cmpl>()) {
1109 if (auto src1Type = unop->expr->type->to<IR::Type_Bits>()) {
1110 if (src1Type->width_bits() == 128) return;
1111 }
1112 }
1113 isValidOperandSize(unop->expr);
1114 }
1115
1116 void postorder(const IR::Operation_Ternary *top) override {
1117 isValidOperandSize(top->e0);
1118 isValidOperandSize(top->e1);
1119 isValidOperandSize(top->e2);
1120 }
1121};
1122
1123class CollectErrors : public Inspector {
1124 DpdkProgramStructure *structure;
1125
1126 public:
1127 explicit CollectErrors(DpdkProgramStructure *structure) : structure(structure) {
1128 CHECK_NULL(structure);
1129 }
1130 void postorder(const IR::Type_Error *error) override {
1131 int id = 0;
1132 for (auto err : error->members) {
1133 if (structure->error_map.count(err->name.name) == 0) {
1134 structure->error_map.emplace(err->name.name, id++);
1135 }
1136 }
1137 }
1138};
1139
1164class ElimHeaderCopy : public Transform {
1165 P4::TypeMap *typeMap;
1175
1176 public:
1177 explicit ElimHeaderCopy(P4::TypeMap *typeMap) : typeMap{typeMap} {}
1178 bool isHeader(const IR::Expression *e);
1179 const IR::Node *preorder(IR::AssignmentStatement *as) override;
1180 const IR::Node *preorder(IR::MethodCallStatement *mcs) override;
1181 const IR::Node *postorder(IR::Member *m) override;
1182};
1183
1184class EliminateHeaderCopy : public PassManager {
1185 public:
1186 EliminateHeaderCopy(P4::ReferenceMap *refMap, P4::TypeMap *typeMap) {
1187 passes.push_back(new P4::ClearTypeMap(typeMap));
1188 passes.push_back(new P4::ResolveReferences(refMap));
1189 passes.push_back(new P4::TypeInference(typeMap, false));
1190 passes.push_back(new P4::TypeChecking(refMap, typeMap, true));
1191 passes.push_back(new ElimHeaderCopy(typeMap));
1192 }
1193};
1194
1196// If one operand is >64-bit and other is <= 64-bit, the smaller operand should be a header field
1197// to maintain the endianness for copy. This pass detects if these conditions are satisfied or not.
1198class HaveNonHeaderLargeOperandAssignment : public Inspector {
1199 bool &is_all_arg_header_fields;
1200
1201 public:
1202 explicit HaveNonHeaderLargeOperandAssignment(bool &is_all_arg_header_fields)
1203 : is_all_arg_header_fields(is_all_arg_header_fields) {}
1204 bool preorder(const IR::AssignmentStatement *assn) override {
1205 if (!is_all_arg_header_fields) return false;
1206 if ((isLargeFieldOperand(assn->left) && !isLargeFieldOperand(assn->right) &&
1207 !isInsideHeader(assn->right)) ||
1208 (isLargeFieldOperand(assn->left) && assn->right->is<IR::Constant>()) ||
1209 (!isLargeFieldOperand(assn->left) && isLargeFieldOperand(assn->right) &&
1210 !isInsideHeader(assn->left))) {
1211 is_all_arg_header_fields &= false;
1212 return false;
1213 }
1214 return false;
1215 }
1216};
1217
1220class HaveNonHeaderChecksumArgs : public Inspector {
1221 P4::TypeMap *typeMap;
1222 bool &is_all_arg_header_fields;
1223
1224 public:
1225 HaveNonHeaderChecksumArgs(P4::TypeMap *typeMap, bool &is_all_arg_header_fields)
1226 : typeMap(typeMap), is_all_arg_header_fields(is_all_arg_header_fields) {}
1227 bool preorder(const IR::MethodCallExpression *mce) override {
1228 if (!is_all_arg_header_fields) return false;
1229 if (auto *m = mce->method->to<IR::Member>()) {
1230 if (auto *type = typeMap->getType(m->expr)->to<IR::Type_Extern>()) {
1231 if (type->name == "InternetChecksum") {
1232 if (m->member == "add" || m->member == "subtract") {
1233 for (auto arg : *mce->arguments) {
1234 if (auto se = arg->expression->to<IR::StructExpression>()) {
1235 for (auto c : se->components) {
1236 if (auto m0 = c->expression->to<IR::Member>()) {
1237 if (!typeMap->getType(m0->expr, true)
1238 ->is<IR::Type_Header>()) {
1239 is_all_arg_header_fields = false;
1240 return false;
1241 }
1242 } else {
1243 is_all_arg_header_fields = false;
1244 return false;
1245 }
1246 }
1247 } else if (arg->expression->to<IR::Constant>()) {
1248 is_all_arg_header_fields = false;
1249 return false;
1250 } else if (auto m = arg->expression->to<IR::Member>()) {
1251 if (!(typeMap->getType(m->expr, true)->is<IR::Type_Header>() ||
1252 typeMap->getType(m, true)->is<IR::Type_Header>())) {
1253 is_all_arg_header_fields = false;
1254 return false;
1255 }
1256 }
1257 }
1258 }
1259 }
1260 }
1261 }
1262 return false;
1263 }
1264};
1265
1272class DpdkAddPseudoHeaderDecl : public Transform {
1273 P4::ReferenceMap *refMap;
1274 P4::TypeMap *typeMap;
1275 bool &is_all_args_header;
1276 IR::Vector<IR::Node> allTypeDecls;
1277
1278 public:
1279 static cstring pseudoHeaderInstanceName;
1280 static cstring pseudoHeaderTypeName;
1281 DpdkAddPseudoHeaderDecl(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1282 bool &is_all_args_header)
1283 : refMap(refMap), typeMap(typeMap), is_all_args_header(is_all_args_header) {
1284 pseudoHeaderInstanceName = refMap->newName("dpdk_pseudo_header");
1285 pseudoHeaderTypeName = refMap->newName("dpdk_pseudo_header_t");
1286 (void)this->typeMap;
1287 (void)this->refMap;
1288 }
1289
1290 const IR::Node *preorder(IR::P4Program *program) override;
1291 const IR::Node *preorder(IR::Type_Struct *st) override;
1292};
1293
1308
1309class MoveNonHeaderFieldsToPseudoHeader : public Transform {
1310 P4::ReferenceMap *refMap;
1311 P4::TypeMap *typeMap;
1312 bool &is_all_args_header;
1313 IR::Vector<IR::Node> newStructTypes;
1314
1315 public:
1316 static std::vector<std::pair<cstring, const IR::Type *>> pseudoFieldNameType;
1317 MoveNonHeaderFieldsToPseudoHeader(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1318 bool &is_all_args_header)
1319 : refMap(refMap), typeMap(typeMap), is_all_args_header(is_all_args_header) {}
1320 std::pair<IR::AssignmentStatement *, IR::Member *> addAssignmentStmt(const IR::Expression *ne);
1321
1322 const IR::Node *postorder(IR::P4Program *p) override {
1323 if (newStructTypes.size() > 0) {
1324 IR::Vector<IR::Node> allTypeDecls;
1325 allTypeDecls.append(newStructTypes);
1326 allTypeDecls.append(p->objects);
1327 p->objects = allTypeDecls;
1328 }
1329 return p;
1330 }
1331 const IR::Node *postorder(IR::MethodCallStatement *statement) override;
1332 const IR::Node *postorder(IR::AssignmentStatement *statement) override;
1333};
1334
1337class AddFieldsToPseudoHeader : public Transform {
1338 P4::ReferenceMap *refMap;
1339 P4::TypeMap *typeMap;
1340 bool &is_all_args_header;
1341
1342 public:
1343 AddFieldsToPseudoHeader(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1344 bool &is_all_args_header)
1345 : refMap(refMap), typeMap(typeMap), is_all_args_header(is_all_args_header) {
1346 (void)this->typeMap;
1347 (void)this->refMap;
1348 }
1349 const IR::Node *preorder(IR::Type_Header *h) override;
1350};
1351
1352struct DpdkAddPseudoHeader : public PassManager {
1353 P4::ReferenceMap *refMap;
1354 P4::TypeMap *typeMap;
1355 bool &is_all_args_header;
1356
1357 public:
1358 DpdkAddPseudoHeader(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1359 bool &is_all_args_header_fields)
1360 : refMap(refMap), typeMap(typeMap), is_all_args_header(is_all_args_header_fields) {
1361 passes.push_back(new HaveNonHeaderChecksumArgs(typeMap, is_all_args_header));
1362 passes.push_back(new HaveNonHeaderLargeOperandAssignment(is_all_args_header));
1363 passes.push_back(new DpdkAddPseudoHeaderDecl(refMap, typeMap, is_all_args_header));
1364 passes.push_back(new P4::ClearTypeMap(typeMap));
1365 passes.push_back(new P4::TypeChecking(refMap, typeMap));
1366 passes.push_back(
1367 new MoveNonHeaderFieldsToPseudoHeader(refMap, typeMap, is_all_args_header));
1368 passes.push_back(new AddFieldsToPseudoHeader(refMap, typeMap, is_all_args_header));
1369 passes.push_back(new P4::ClearTypeMap(typeMap));
1370 passes.push_back(new P4::TypeChecking(refMap, typeMap));
1371 }
1372};
1373
1374class DpdkArchFirst : public PassManager {
1375 public:
1376 DpdkArchFirst() { setName("DpdkArchFirst"); }
1377};
1378
1379class DpdkArchLast : public PassManager {
1380 public:
1381 DpdkArchLast() { setName("DpdkArchLast"); }
1382};
1383
1384class CollectProgramStructure : public PassManager {
1385 public:
1386 CollectProgramStructure(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
1387 DpdkProgramStructure *structure) {
1388 auto *evaluator = new P4::EvaluatorPass(refMap, typeMap);
1389 auto *parseDpdk = new ParseDpdkArchitecture(structure);
1390 passes.push_back(evaluator);
1391 passes.push_back(new VisitFunctor([evaluator, parseDpdk]() {
1392 auto toplevel = evaluator->getToplevelBlock();
1393 auto main = toplevel->getMain();
1394 if (main == nullptr) {
1395 ::P4::error(ErrorType::ERR_NOT_FOUND,
1396 "Could not locate top-level block; is there a %1% module?",
1397 IR::P4Program::main);
1398 return;
1399 }
1400 main->apply(*parseDpdk);
1401 }));
1402 }
1403};
1404
1405// Add collected local struct variable decls as a field in metadata
1406// struct
1407class MoveCollectedStructLocalVariableToMetadata : public Transform {
1408 P4::TypeMap *typeMap;
1409
1410 public:
1411 explicit MoveCollectedStructLocalVariableToMetadata(P4::TypeMap *typeMap) : typeMap(typeMap) {}
1412 const IR::Node *preorder(IR::Type_Struct *s) override;
1413 const IR::Node *postorder(IR::P4Control *c) override;
1414 const IR::Node *postorder(IR::P4Program *p) override;
1415 const IR::Node *postorder(IR::P4Parser *c) override;
1416};
1417
1418// Collect all local struct variable and metadata struct type
1419class CollectStructLocalVariables : public Transform {
1420 P4::ReferenceMap *refMap;
1421 P4::TypeMap *typeMap;
1422
1423 public:
1424 CollectStructLocalVariables(P4::ReferenceMap *refMap, P4::TypeMap *typeMap)
1425 : refMap(refMap), typeMap(typeMap) {}
1426 const IR::Node *postorder(IR::P4Parser *c) override;
1427 const IR::Node *preorder(IR::PathExpression *path) override;
1428 static const IR::Type_Struct *metadataStrct;
1429 static std::map<cstring, const IR::Type *> fieldNameType;
1430 static IR::Vector<IR::Node> type_tobe_moved_at_top;
1431};
1432
1433// Collect all local struct decls and move it to metadata struct and finally
1434// flatten the metadata struct.
1435class CollectLocalStructAndFlatten : public PassManager {
1436 public:
1437 CollectLocalStructAndFlatten(P4::ReferenceMap *refMap, P4::TypeMap *typeMap) {
1438 passes.push_back(new P4::ClearTypeMap(typeMap));
1439 passes.push_back(new P4::ResolveReferences(refMap));
1440 passes.push_back(new P4::TypeInference(typeMap, false));
1441 passes.push_back(new P4::TypeChecking(refMap, typeMap, true));
1442 passes.push_back(new CollectStructLocalVariables(refMap, typeMap));
1443 passes.push_back(new MoveCollectedStructLocalVariableToMetadata(typeMap));
1444 passes.push_back(new P4::ClearTypeMap(typeMap));
1445 passes.push_back(new P4::ResolveReferences(refMap));
1446 passes.push_back(new P4::TypeInference(typeMap, false));
1447 passes.push_back(new P4::TypeChecking(refMap, typeMap, true));
1448 passes.push_back(new P4::FlattenInterfaceStructs(typeMap));
1449 }
1450};
1451
1452/* Helper class to detect use of IPSec accelerator */
1453class CollectIPSecInfo : public Inspector {
1454 bool &is_ipsec_used;
1455 int &sa_id_width;
1456 P4::ReferenceMap *refMap;
1457 P4::TypeMap *typeMap;
1458 DpdkProgramStructure *structure;
1459
1460 public:
1461 CollectIPSecInfo(bool &is_ipsec_used, int &sa_id_width, P4::ReferenceMap *refMap,
1462 P4::TypeMap *typeMap, DpdkProgramStructure *structure)
1463 : is_ipsec_used(is_ipsec_used),
1464 sa_id_width(sa_id_width),
1465 refMap(refMap),
1466 typeMap(typeMap),
1467 structure(structure) {}
1468 bool preorder(const IR::MethodCallStatement *mcs) override {
1469 auto mi = P4::MethodInstance::resolve(mcs, refMap, typeMap);
1470 if (auto a = mi->to<P4::ExternMethod>()) {
1471 if (a->originalExternType->getName().name == "ipsec_accelerator") {
1472 if (structure->isPSA()) {
1473 ::P4::error(ErrorType::ERR_MODEL, "%1% is not available for PSA programs",
1474 a->originalExternType->getName().name);
1475 return false;
1476 }
1477 if (a->method->getName().name == "enable") {
1478 is_ipsec_used = true;
1479 } else if (a->method->getName().name == "set_sa_index") {
1480 auto typeArgs = a->expr->typeArguments;
1481 if (typeArgs->size() != 1) {
1482 ::P4::error(ErrorType::ERR_MODEL,
1483 "Unexpected number of type arguments for %1%", a->method->name);
1484 return false;
1485 }
1486 auto width = typeArgs->at(0);
1487 if (!width->is<IR::Type_Bits>()) {
1488 ::P4::error(ErrorType::ERR_MODEL, "Unexpected width type %1% for sa_index",
1489 width);
1490 return false;
1491 }
1492 sa_id_width = width->to<IR::Type_Bits>()->width_bits();
1493 }
1494 }
1495 }
1496 return false;
1497 }
1498};
1499
1500/* DPDK uses some fixed registers to hold the ipsec inbound/outbound input and output ports and a
1501 * pseudo compiler inserted header which shall be emitted in front of all headers. This class helps
1502 * insert required registers and a pseudo header for enabling IPSec encryption and decryption. It
1503 * also handles setting of output port in the deparser.
1504 */
1505class InsertReqDeclForIPSec : public Transform {
1506 P4::ReferenceMap *refMap;
1507 DpdkProgramStructure *structure;
1508 bool &is_ipsec_used;
1509 int &sa_id_width;
1510 cstring newHeaderName = "platform_hdr_t"_cs;
1511 IR::Type_Header *ipsecHeader = nullptr;
1512 std::vector<cstring> registerInstanceNames = {
1513 "ipsec_port_out_inbound"_cs, "ipsec_port_out_outbound"_cs, "ipsec_port_in_inbound"_cs,
1514 "ipsec_port_in_outbound"_cs};
1515
1516 public:
1517 InsertReqDeclForIPSec(P4::ReferenceMap *refMap, DpdkProgramStructure *structure,
1518 bool &is_ipsec_used, int &sa_id_width)
1519 : refMap(refMap),
1520 structure(structure),
1521 is_ipsec_used(is_ipsec_used),
1522 sa_id_width(sa_id_width) {
1523 setName("InsertReqDeclForIPSec");
1524 }
1525
1526 const IR::Node *preorder(IR::P4Program *program) override;
1527 const IR::Node *preorder(IR::Type_Struct *s) override;
1528 const IR::Node *preorder(IR::P4Control *c) override;
1529 IR::IndexedVector<IR::StatOrDecl> *addRegDeclInstance(std::vector<cstring> portRegs);
1530};
1531
1532struct DpdkHandleIPSec : public PassManager {
1533 P4::ReferenceMap *refMap;
1534 P4::TypeMap *typeMap;
1535 DpdkProgramStructure *structure;
1536 bool is_ipsec_used = false;
1537 int sa_id_width = 32;
1538
1539 public:
1540 DpdkHandleIPSec(P4::ReferenceMap *refMap, P4::TypeMap *typeMap, DpdkProgramStructure *structure)
1541 : refMap(refMap), typeMap(typeMap), structure(structure) {
1542 passes.push_back(
1543 new CollectIPSecInfo(is_ipsec_used, sa_id_width, refMap, typeMap, structure));
1544 passes.push_back(new InsertReqDeclForIPSec(refMap, structure, is_ipsec_used, sa_id_width));
1545 passes.push_back(new P4::ClearTypeMap(typeMap));
1546 passes.push_back(new P4::ResolveReferences(refMap));
1547 passes.push_back(new P4::TypeInference(typeMap, false));
1548 }
1549};
1550
1551} // namespace P4::DPDK
1552#endif /* BACKENDS_DPDK_DPDKARCH_H_ */
Definition typeChecker.h:32
This pass finally adds all the collected fields to pseudo header add collected pseudo header fields i...
Definition dpdkArch.h:1337
Definition dpdkArch.h:231
Definition dpdkArch.h:1453
Definition dpdkArch.h:193
Definition dpdkArch.h:1419
bool preorder(const IR::Key *key) override
Definition dpdkArch.cpp:636
Definition dpdkArch.h:158
Definition dpdkArch.h:169
Definition dpdkArch.h:279
void postorder(const IR::P4Table *t) override
Definition dpdkArch.cpp:1216
This pass adds a pseudo header declaration, it will be used as container of operands where dpdk instr...
Definition dpdkArch.h:1272
Definition dpdkArch.h:1164
Definition dpdkArch.h:1220
This pass checks whether an assignment statement has large operands (>64-bit).
Definition dpdkArch.h:1198
Definition dpdkArch.h:1505
IR::IndexedVector< IR::StatOrDecl > * addRegDeclInstance(std::vector< cstring > portRegs)
Create and add register declaration instance to program.
Definition dpdkArch.cpp:3215
bool preorder(const IR::Operation_Unary *a) override
Definition dpdkArch.cpp:898
This pass identifies and collects statement which requires it's operand to be in a header and also in...
Definition dpdkArch.h:1309
Definition dpdkArch.h:264
const IR::Node * postorder(IR::Type_Struct *st) override
Definition dpdkArch.cpp:612
Definition dpdkArch.h:934
Definition dpdkArch.h:919
IR::Expression * initializeMemberAndGroupId(cstring tableName, IR::IndexedVector< IR::StatOrDecl > *decls)
Definition dpdkArch.cpp:2118
std::tuple< const IR::P4Table *, cstring, cstring > create_match_table(const IR::P4Table *)
Definition dpdkArch.cpp:1773
Definition dpdkArch.h:842
Definition dpdkArch.h:963
Definition removeLeftSlices.h:35
Definition evaluator.h:115
Definition methodInstance.h:168
Definition flattenInterfaceStructs.h:238
Definition indexed_vector.h:31
Definition node.h:53
Definition ir/vector.h:59
Definition sideEffects.h:253
static MethodInstance * resolve(const IR::MethodCallExpression *mce, const DeclarationLookup *refMap, TypeMap *typeMap, bool useExpressionType=false, const Visitor::Context *ctxt=nullptr, bool incomplete=false)
Definition methodInstance.cpp:27
Definition dpdkProgramStructure.h:132
Definition ir/pass_manager.h:146
Class used to encode maps from paths to declarations.
Definition referenceMap.h:67
Definition resolveReferences.h:124
Definition typeChecker.h:55
Definition typeChecker.h:491
Definition typeMap.h:32
Definition source_file.h:132
Definition ir/pass_manager.h:185
Definition cstring.h:85
Definition ordered_map.h:32
Definition dpdk/backend.cpp:26
void expressionUnrollSanityCheck(const IR::Expression *e)
Definition dpdkArch.cpp:36
Definition dpdkArch.h:806
Definition dpdkArch.h:811
const int dpdk_max_operand_size
Maximum operand size for unary, binary and ternary operations.
Definition dpdk/constants.h:56
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition lib/error.h:58
Collect information related to P4 programs targeting dpdk.
Definition dpdkProgramStructure.h:22
Definition id.h:28
bool is() const noexcept
Definition rtti.h:216
Definition register_reference.h:28