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