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