P4C
The P4 Compiler
Loading...
Searching...
No Matches
converters.h
1/*
2Copyright 2013-present Barefoot Networks, Inc.
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 FRONTENDS_P4_14_FROMV1_0_CONVERTERS_H_
18#define FRONTENDS_P4_14_FROMV1_0_CONVERTERS_H_
19
20#include <typeindex>
21#include <typeinfo>
22
23#include "frontends/p4/coreLibrary.h"
24#include "ir/dump.h"
25#include "ir/ir.h"
26#include "ir/pass_manager.h"
27#include "lib/safe_vector.h"
28#include "programStructure.h"
29
30namespace P4::P4V1 {
31
32// Converts expressions from P4-14 to P4-16
33// However, the type in each expression is still a P4-14 type.
35 protected:
36 ProgramStructure *structure;
37 P4::P4CoreLibrary &p4lib;
38 using funcType = std::function<const IR::Node *(const IR::Node *)>;
39 static std::map<cstring, funcType> *cvtForType;
40
41 public:
42 bool replaceNextWithLast; // if true p[next] becomes p.last
43 explicit ExpressionConverter(ProgramStructure *structure)
44 : structure(structure), p4lib(P4::P4CoreLibrary::instance()), replaceNextWithLast(false) {
45 setName("ExpressionConverter");
46 }
47 const IR::Type *getFieldType(const IR::Type_StructLike *ht, cstring fieldName);
48 const IR::Node *postorder(IR::Constant *expression) override;
49 const IR::Node *postorder(IR::Member *field) override;
50 const IR::Node *postorder(IR::FieldList *fl) override;
51 const IR::Node *postorder(IR::Mask *expression) override;
52 const IR::Node *postorder(IR::ActionArg *arg) override;
53 const IR::Node *postorder(IR::Primitive *primitive) override;
54 const IR::Node *postorder(IR::PathExpression *ref) override;
55 const IR::Node *postorder(IR::ConcreteHeaderRef *nhr) override;
56 const IR::Node *postorder(IR::HeaderStackItemRef *ref) override;
57 const IR::Node *postorder(IR::GlobalRef *gr) override;
58 const IR::Node *postorder(IR::Equ *equ) override;
59 const IR::Node *postorder(IR::Neq *neq) override;
60 const IR::Expression *convert(const IR::Node *node) {
61 auto result = node->apply(*this);
62 return result->to<IR::Expression>();
63 }
64 static void addConverter(cstring type, funcType);
65 static funcType get(cstring type);
66};
67
69 std::map<cstring, cstring> *renameMap;
70
71 public:
72 StatementConverter(ProgramStructure *structure, std::map<cstring, cstring> *renameMap)
73 : ExpressionConverter(structure), renameMap(renameMap) {}
74
75 const IR::Node *preorder(IR::Apply *apply) override;
76 const IR::Node *preorder(IR::Primitive *primitive) override;
77 const IR::Node *preorder(IR::If *cond) override;
78 const IR::Statement *convert(const IR::Vector<IR::Expression> *toConvert);
79
80 const IR::Statement *convert(const IR::Node *node) {
81 auto conv = node->apply(*this);
82 auto result = conv->to<IR::Statement>();
83 BUG_CHECK(result != nullptr, "Conversion of %1% did not produce a statement", node);
84 return result;
85 }
86};
87
89 const IR::Type_Varbits *postorder(IR::Type_Varbits *) override;
90 const IR::Type_StructLike *postorder(IR::Type_StructLike *) override;
91 const IR::StructField *postorder(IR::StructField *) override;
92
93 public:
94 explicit TypeConverter(ProgramStructure *structure) : ExpressionConverter(structure) {}
95};
96
98 static std::map<cstring, ExternConverter *> *cvtForType;
99
100 public:
101 virtual const IR::Type_Extern *convertExternType(ProgramStructure *, const IR::Type_Extern *,
102 cstring);
103 virtual const IR::Declaration_Instance *convertExternInstance(
104 ProgramStructure *, const IR::Declaration_Instance *, cstring,
106 virtual const IR::Statement *convertExternCall(ProgramStructure *,
107 const IR::Declaration_Instance *,
108 const IR::Primitive *);
109 virtual bool convertAsGlobal(ProgramStructure *, const IR::Declaration_Instance *) {
110 return false;
111 }
112 ExternConverter() {}
115 static void addConverter(cstring type, ExternConverter *);
116 static ExternConverter *get(cstring type);
117 static ExternConverter *get(const IR::Type_Extern *type) { return get(type->name); }
118 static ExternConverter *get(const IR::Declaration_Instance *ext) {
119 return get(ext->type->to<IR::Type_Extern>());
120 }
121 static const IR::Type_Extern *cvtExternType(ProgramStructure *s, const IR::Type_Extern *e,
122 cstring name) {
123 return get(e)->convertExternType(s, e, name);
124 }
125 static const IR::Declaration_Instance *cvtExternInstance(
126 ProgramStructure *s, const IR::Declaration_Instance *di, cstring name,
128 return get(di)->convertExternInstance(s, di, name, scope);
129 }
130 static const IR::Statement *cvtExternCall(ProgramStructure *s,
131 const IR::Declaration_Instance *di,
132 const IR::Primitive *p) {
133 return get(di)->convertExternCall(s, di, p);
134 }
135 static bool cvtAsGlobal(ProgramStructure *s, const IR::Declaration_Instance *di) {
136 return get(di)->convertAsGlobal(s, di);
137 }
138};
139
141 static std::map<cstring, std::vector<PrimitiveConverter *>> *all_converters;
142 cstring prim_name;
143 int priority;
144
145 protected:
146 PrimitiveConverter(std::string_view name, int prio);
147 virtual ~PrimitiveConverter();
148
149 // helper functions
150 safe_vector<const IR::Expression *> convertArgs(ProgramStructure *, const IR::Primitive *);
151
152 public:
153 virtual const IR::Statement *convert(ProgramStructure *, const IR::Primitive *) = 0;
154 static const IR::Statement *cvtPrimitive(ProgramStructure *, const IR::Primitive *);
155};
156
165#define CONVERT_PRIMITIVE(NAME, ...) \
166 class PrimitiveConverter_##NAME##_##__VA_ARGS__ : public PrimitiveConverter { \
167 const IR::Statement *convert(ProgramStructure *, const IR::Primitive *) override; \
168 PrimitiveConverter_##NAME##_##__VA_ARGS__() \
169 : PrimitiveConverter(#NAME, __VA_ARGS__ + 0) {} \
170 static PrimitiveConverter_##NAME##_##__VA_ARGS__ singleton; \
171 } PrimitiveConverter_##NAME##_##__VA_ARGS__::singleton; \
172 const IR::Statement *PrimitiveConverter_##NAME##_##__VA_ARGS__::convert( \
173 ProgramStructure *structure, const IR::Primitive *primitive)
174
176
178 ProgramStructure *structure;
179
180 // These names can only be used for very specific purposes
181 std::map<cstring, cstring> reserved_names = {{"standard_metadata_t"_cs, "type"_cs},
182 {"standard_metadata"_cs, "metadata"_cs},
183 {"egress"_cs, "control"_cs}};
184
185 void checkReserved(const IR::Node *node, cstring nodeName, cstring kind) const {
186 auto it = reserved_names.find(nodeName);
187 if (it == reserved_names.end()) return;
188 if (it->second != kind)
189 ::P4::error(ErrorType::ERR_INVALID, "%1%: invalid name; it can only be used for %2%",
190 node, it->second);
191 }
192 void checkReserved(const IR::Node *node, cstring nodeName) const {
193 checkReserved(node, nodeName, nullptr);
194 }
195
196 public:
197 explicit DiscoverStructure(ProgramStructure *structure) : structure(structure) {
198 CHECK_NULL(structure);
199 setName("DiscoverStructure");
200 }
201
202 void postorder(const IR::ParserException *ex) override {
203 warn(ErrorType::WARN_UNSUPPORTED, "%1%: parser exception is not translated to P4-16", ex);
204 }
205 void postorder(const IR::Metadata *md) override {
206 structure->metadata.emplace(md);
207 checkReserved(md, md->name, "metadata"_cs);
208 }
209 void postorder(const IR::Header *hd) override {
210 structure->headers.emplace(hd);
211 checkReserved(hd, hd->name);
212 }
213 void postorder(const IR::Type_StructLike *t) override {
214 structure->types.emplace(t);
215 checkReserved(t, t->name, "type"_cs);
216 }
217 void postorder(const IR::V1Control *control) override {
218 structure->controls.emplace(control);
219 checkReserved(control, control->name, "control"_cs);
220 }
221 void postorder(const IR::V1Parser *parser) override {
222 structure->parserStates.emplace(parser);
223 checkReserved(parser, parser->name);
224 }
225 void postorder(const IR::V1Table *table) override {
226 structure->tables.emplace(table);
227 checkReserved(table, table->name);
228 }
229 void postorder(const IR::ActionFunction *action) override {
230 structure->actions.emplace(action);
231 checkReserved(action, action->name);
232 }
233 void postorder(const IR::HeaderStack *stack) override {
234 structure->stacks.emplace(stack);
235 checkReserved(stack, stack->name);
236 }
237 void postorder(const IR::Counter *count) override {
238 structure->counters.emplace(count);
239 checkReserved(count, count->name);
240 }
241 void postorder(const IR::Register *reg) override {
242 structure->registers.emplace(reg);
243 checkReserved(reg, reg->name);
244 }
245 void postorder(const IR::ActionProfile *ap) override {
246 structure->action_profiles.emplace(ap);
247 checkReserved(ap, ap->name);
248 }
249 void postorder(const IR::FieldList *fl) override {
250 structure->field_lists.emplace(fl);
251 checkReserved(fl, fl->name);
252 }
253 void postorder(const IR::FieldListCalculation *flc) override {
254 structure->field_list_calculations.emplace(flc);
255 checkReserved(flc, flc->name);
256 }
257 void postorder(const IR::CalculatedField *cf) override {
258 structure->calculated_fields.push_back(cf);
259 }
260 void postorder(const IR::Meter *m) override {
261 structure->meters.emplace(m);
262 checkReserved(m, m->name);
263 }
264 void postorder(const IR::ActionSelector *as) override {
265 structure->action_selectors.emplace(as);
266 checkReserved(as, as->name);
267 }
268 void postorder(const IR::Type_Extern *ext) override {
269 structure->extern_types.emplace(ext);
270 checkReserved(ext, ext->name);
271 }
272 void postorder(const IR::Declaration_Instance *ext) override {
273 structure->externs.emplace(ext);
274 checkReserved(ext, ext->name);
275 }
276 void postorder(const IR::ParserValueSet *pvs) override {
277 structure->value_sets.emplace(pvs);
278 checkReserved(pvs, pvs->name);
279 }
280};
281
283 ProgramStructure *structure;
284
285 public:
286 explicit ComputeCallGraph(ProgramStructure *structure) : structure(structure) {
287 CHECK_NULL(structure);
288 setName("ComputeCallGraph");
289 }
290
291 void postorder(const IR::V1Parser *parser) override {
292 LOG3("Scanning parser " << parser->name);
293 structure->parsers.add(parser->name);
294 if (!parser->default_return.name.isNullOrEmpty())
295 structure->parsers.calls(parser->name, parser->default_return);
296 if (parser->cases != nullptr)
297 for (auto ce : *parser->cases) structure->parsers.calls(parser->name, ce->action.name);
298 for (auto expr : parser->stmts) {
299 if (expr->is<IR::Primitive>()) {
300 auto primitive = expr->to<IR::Primitive>();
301 if (primitive->name == "extract") {
302 BUG_CHECK(primitive->operands.size() == 1, "Expected 1 operand for %1%",
303 primitive);
304 auto dest = primitive->operands.at(0);
305 LOG3("Parser " << parser->name << " extracts into " << dest);
306 structure->extracts[parser->name].push_back(dest);
307 }
308 }
309 }
310 }
311 void postorder(const IR::Primitive *primitive) override {
312 auto name = primitive->name;
313 const IR::GlobalRef *glob = nullptr;
314 const IR::Declaration_Instance *extrn = nullptr;
315 if (!primitive->operands.empty()) glob = primitive->operands[0]->to<IR::GlobalRef>();
316 if (glob) extrn = glob->obj->to<IR::Declaration_Instance>();
317
318 if (extrn) {
319 auto parent = findContext<IR::ActionFunction>();
320 BUG_CHECK(parent != nullptr, "%1%: Extern call not within action", primitive);
321 structure->calledExterns.calls(parent->name, extrn->name.name);
322 return;
323 } else if (primitive->name == "count") {
324 // counter invocation
325 auto ctrref = primitive->operands.at(0);
326 const IR::Counter *ctr = nullptr;
327 if (auto gr = ctrref->to<IR::GlobalRef>())
328 ctr = gr->obj->to<IR::Counter>();
329 else if (auto nr = ctrref->to<IR::PathExpression>())
330 ctr = structure->counters.get(nr->path->name);
331 if (ctr == nullptr) {
332 ::P4::error(ErrorType::ERR_NOT_FOUND, "%1%: Cannot find counter", ctrref);
333 return;
334 }
335 auto parent = findContext<IR::ActionFunction>();
336 BUG_CHECK(parent != nullptr, "%1%: Counter call not within action", primitive);
337 structure->calledCounters.calls(parent->name, ctr->name.name);
338 return;
339 } else if (primitive->name == "execute_meter") {
340 auto mtrref = primitive->operands.at(0);
341 const IR::Meter *mtr = nullptr;
342 if (auto gr = mtrref->to<IR::GlobalRef>())
343 mtr = gr->obj->to<IR::Meter>();
344 else if (auto nr = mtrref->to<IR::PathExpression>())
345 mtr = structure->meters.get(nr->path->name);
346 if (mtr == nullptr) {
347 ::P4::error(ErrorType::ERR_NOT_FOUND, "%1%: Cannot find meter", mtrref);
348 return;
349 }
350 auto parent = findContext<IR::ActionFunction>();
351 BUG_CHECK(parent != nullptr, "%1%: not within action", primitive);
352 structure->calledMeters.calls(parent->name, mtr->name.name);
353 return;
354 } else if (primitive->name == "register_read" || primitive->name == "register_write") {
355 const IR::Expression *regref;
356 if (primitive->name == "register_read")
357 regref = primitive->operands.at(1);
358 else
359 regref = primitive->operands.at(0);
360 const IR::Register *reg = nullptr;
361 if (auto gr = regref->to<IR::GlobalRef>())
362 reg = gr->obj->to<IR::Register>();
363 else if (auto nr = regref->to<IR::PathExpression>())
364 reg = structure->registers.get(nr->path->name);
365 if (reg == nullptr) {
366 ::P4::error(ErrorType::ERR_NOT_FOUND, "%1%: Cannot find register", regref);
367 return;
368 }
369 auto parent = findContext<IR::ActionFunction>();
370 BUG_CHECK(parent != nullptr, "%1%: not within action", primitive);
371 structure->calledRegisters.calls(parent->name, reg->name.name);
372 return;
373 } else if (structure->actions.contains(name)) {
374 auto parent = findContext<IR::ActionFunction>();
375 BUG_CHECK(parent != nullptr, "%1%: Action call not within action", primitive);
376 structure->calledActions.calls(parent->name, name);
377 } else if (structure->controls.contains(name)) {
378 auto parent = findContext<IR::V1Control>();
379 BUG_CHECK(parent != nullptr, "%1%: Control call not within control", primitive);
380 structure->calledControls.calls(parent->name, name);
381 }
382 }
383 void postorder(const IR::GlobalRef *gref) override {
384 cstring caller;
385 if (auto af = findContext<IR::ActionFunction>()) {
386 caller = af->name;
387 } else if (auto di = findContext<IR::Declaration_Instance>()) {
388 caller = di->name;
389 } else {
390 BUG("%1%: GlobalRef not within action or extern", gref);
391 }
392 if (auto ctr = gref->obj->to<IR::Counter>())
393 structure->calledCounters.calls(caller, ctr->name.name);
394 else if (auto mtr = gref->obj->to<IR::Meter>())
395 structure->calledMeters.calls(caller, mtr->name.name);
396 else if (auto reg = gref->obj->to<IR::Register>())
397 structure->calledRegisters.calls(caller, reg->name.name);
398 else if (auto ext = gref->obj->to<IR::Declaration_Instance>())
399 structure->calledExterns.calls(caller, ext->name.name);
400 }
401};
402
407 ProgramStructure *structure;
408
409 public:
410 explicit ComputeTableCallGraph(ProgramStructure *structure) : structure(structure) {
411 CHECK_NULL(structure);
412 setName("ComputeTableCallGraph");
413 }
414
415 void postorder(const IR::Apply *apply) override {
416 LOG3("Scanning " << apply->name);
417 auto tbl = structure->tables.get(apply->name.name);
418 if (tbl == nullptr) {
419 ::P4::error(ErrorType::ERR_NOT_FOUND, "%1%: Could not find table", apply->name);
420 return;
421 }
422 auto parent = findContext<IR::V1Control>();
423 if (!parent) {
424 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: Apply not within a control block?", apply);
425 return;
426 }
427
428 auto ctrl = get(structure->tableMapping, tbl);
429
430 // skip control block that is unused.
431 if (!structure->calledControls.isCallee(parent->name) &&
432 parent->name != P4V1::V1Model::instance.ingress.name &&
433 parent->name != P4V1::V1Model::instance.egress.name)
434 return;
435
436 if (ctrl != nullptr && ctrl != parent) {
437 auto previous = get(structure->tableInvocation, tbl);
438 ::P4::error(ErrorType::ERR_INVALID,
439 "%1%: Table invoked from two different controls: %2% and %3%", tbl, apply,
440 previous);
441 }
442 LOG3("Invoking " << tbl << " in " << parent->name);
443 structure->tableMapping.emplace(tbl, parent);
444 structure->tableInvocation.emplace(tbl, apply);
445 }
446};
447
448class Rewriter : public Transform {
449 ProgramStructure *structure;
450
451 public:
452 explicit Rewriter(ProgramStructure *structure) : structure(structure) {
453 CHECK_NULL(structure);
454 setName("Rewriter");
455 }
456
457 const IR::Node *preorder(IR::V1Program *global) override {
458 if (LOGGING(4)) {
459 LOG4("#### Initial P4_14 program");
460 dump(global);
461 }
462 prune();
463 auto *rv = structure->create(global->srcInfo);
464 if (LOGGING(4)) {
465 LOG4("#### Generated P4_16 program");
466 dump(rv);
467 }
468 return rv;
469 }
470};
471
507class FixExtracts final : public Transform {
508 ProgramStructure *structure;
509
510 struct HeaderSplit {
512 const IR::Type_Header *fixedHeaderType;
514 const IR::Expression *headerLength;
515 };
516
518 // The following vector contains only IR::Type_Header, but it is easier
519 // to append if the elements are Node.
520 IR::Vector<IR::Node> allTypeDecls;
524 std::map<cstring, HeaderSplit *> fixedPart;
525
529 HeaderSplit *splitHeaderType(const IR::Type_Header *type) {
530 // Maybe we have seen this type already
531 auto fixed = ::P4::get(fixedPart, type->name.name);
532 if (fixed != nullptr) return fixed;
533
534 const IR::Expression *headerLength = nullptr;
535 // We allocate the following when we find the first varbit field.
536 const IR::Type_Header *fixedHeaderType = nullptr;
538
539 for (auto f : type->fields) {
540 if (f->type->is<IR::Type_Varbits>()) {
541 cstring hname = structure->makeUniqueName(type->name.name);
542 if (fixedHeaderType != nullptr) {
543 ::P4::error(ErrorType::ERR_INVALID,
544 "%1%: header types with multiple varbit fields are not supported",
545 type);
546 return nullptr;
547 }
548 fixedHeaderType = new IR::Type_Header(IR::ID(hname), fields);
549 // extract length from annotation
550 auto anno = f->getAnnotation(IR::Annotation::lengthAnnotation);
551 BUG_CHECK(anno != nullptr, "No length annotation on varbit field", f);
552 BUG_CHECK(anno->expr.size() == 1, "Expected exactly 1 argument", anno->expr);
553 headerLength = anno->expr.at(0);
554 // We keep going through the loop just to check whether there is another
555 // varbit field in the header.
556 } else if (fixedHeaderType == nullptr) {
557 // We only keep the fields prior to the varbit field
558 fields.push_back(f);
559 }
560 }
561 if (fixedHeaderType != nullptr) {
562 LOG3("Extracted fixed-size header type from " << type << " into " << fixedHeaderType);
563 fixed = new HeaderSplit;
564 fixed->fixedHeaderType = fixedHeaderType;
565 fixed->headerLength = headerLength;
566 fixedPart.emplace(type->name.name, fixed);
567 allTypeDecls.push_back(fixedHeaderType);
568 return fixed;
569 }
570 return nullptr;
571 }
572
580 class RewriteLength final : public Transform {
581 const IR::Type_Header *header;
582 const IR::Declaration *var;
583
584 public:
585 explicit RewriteLength(const IR::Type_Header *header, const IR::Declaration *var)
586 : header(header), var(var) {
587 setName("RewriteLength");
588 }
589
590 const IR::Node *postorder(IR::PathExpression *expression) override {
591 if (expression->path->absolute) return expression;
592 for (auto f : header->fields) {
593 if (f->name == expression->path->name)
594 return new IR::Member(expression->srcInfo, new IR::PathExpression(var->name),
595 f->name);
596 }
597 return expression;
598 }
599 };
600
601 public:
602 explicit FixExtracts(ProgramStructure *structure) : structure(structure) {
603 CHECK_NULL(structure);
604 setName("FixExtracts");
605 }
606
607 const IR::Node *postorder(IR::P4Program *program) override {
608 // P4-14 headers cannot refer to other types, so it is safe
609 // to prepend them to the list of declarations.
610 allTypeDecls.append(program->objects);
611 program->objects = allTypeDecls;
612 return program;
613 }
614
615 const IR::Node *postorder(IR::P4Parser *parser) override {
616 if (!varDecls.empty()) {
617 parser->parserLocals.append(varDecls);
618 varDecls.clear();
619 }
620 return parser;
621 }
622
623 const IR::Node *postorder(IR::MethodCallStatement *statement) override {
624 auto mce = getOriginal<IR::MethodCallStatement>()->methodCall;
625 LOG3("Looking up in extracts " << dbp(mce));
626 auto ht = ::P4::get(structure->extractsSynthesized, mce);
627 if (ht == nullptr)
628 // not an extract
629 return statement;
630
631 // This is an extract method invocation
632 BUG_CHECK(mce->arguments->size() == 1, "%1%: expected 1 argument", mce);
633 auto arg = mce->arguments->at(0);
634
635 auto fixed = splitHeaderType(ht);
636 if (fixed == nullptr) return statement;
637 CHECK_NULL(fixed->headerLength);
638 CHECK_NULL(fixed->fixedHeaderType);
639
640 auto result = new IR::IndexedVector<IR::StatOrDecl>();
641 cstring varName = structure->makeUniqueName("tmp_hdr"_cs);
642 auto var =
643 new IR::Declaration_Variable(IR::ID(varName), fixed->fixedHeaderType->to<IR::Type>());
644 varDecls.push_back(var);
645
646 // Create lookahead
647 auto member = mce->method->to<IR::Member>(); // should be packet_in.extract
648 CHECK_NULL(member);
649 auto typeArgs = new IR::Vector<IR::Type>();
650 typeArgs->push_back(fixed->fixedHeaderType->getP4Type());
651 auto lookaheadMethod =
652 new IR::Member(member->expr, P4::P4CoreLibrary::instance().packetIn.lookahead.name);
653 auto lookahead = new IR::MethodCallExpression(mce->srcInfo, lookaheadMethod, typeArgs,
655 auto assign =
656 new IR::AssignmentStatement(mce->srcInfo, new IR::PathExpression(varName), lookahead);
657 result->push_back(assign);
658 LOG3("Created lookahead " << assign);
659
660 // Create actual extract
661 RewriteLength rewrite(fixed->fixedHeaderType, var);
662 rewrite.setCalledBy(this);
663 auto length = fixed->headerLength->apply(rewrite);
664 auto args = new IR::Vector<IR::Argument>();
665 args->push_back(arg->clone());
666 auto type = IR::Type_Bits::get(P4::P4CoreLibrary::instance().packetIn.extractSecondArgSize);
667 auto cast = new IR::Cast(Util::SourceInfo(), type, length);
668 args->push_back(new IR::Argument(cast));
669 auto expression = new IR::MethodCallExpression(mce->srcInfo, mce->method->clone(), args);
670 result->push_back(new IR::MethodCallStatement(expression));
671 return result;
672 }
673};
674
675/*
676 This class is used to adjust the expressions in a @length
677 annotation on a varbit field. The P4-14 to P4-16 converter inserts
678 these annotations on the unique varbit field in a header; the
679 annotations are created from the header max_length and length
680 fields. The length annotation contains an expression which is used
681 to compute the length of the varbit field. The problem that we are
682 solving here is that expression semantics is different in P4-14 and
683 P4-16. Consider the canonical case of an IPv4 header:
684
685 header_type ipv4_t {
686 fields {
687 version : 4;
688 ihl : 4;
689 // lots of other fields...
690 options: *;
691 }
692 length : ihl*4;
693 max_length : 64;
694 }
695
696 This generates the following P4-16 structure:
697 struct ipv4_t {
698 bit<4> version;
699 bit<4> ihl;
700 @length((ihl*4) * 8 - 20) // 20 is the size of the fixed part of the header
701 varbit<(64 - 20) * 8> options;
702 }
703
704 When such a header is used in an extract statement, the @length
705 annotation is used to compute the second argument of the extract
706 method. The problem we are solving here is the fact that ihl is
707 only represented on 4 bits, so the evaluation ihl*4 will actually
708 overflow. This is not a problem in P4-14, but it is a problem in
709 P4-16. Unfortunately there is no easy way to guess how many bits
710 are required to evaluate this computation. So what we do is to cast
711 all PathExpressions to 32-bits. This is really just a heuristic,
712 but since the semantics of P4-14 expressions is unclear, we cannot
713 do much better than this.
714*/
715class AdjustLengths : public Transform {
716 public:
717 AdjustLengths() { setName("AdjustLengths"); }
718 const IR::Node *postorder(IR::PathExpression *expression) override {
719 auto anno = findContext<IR::Annotation>();
720 if (anno == nullptr) return expression;
721 if (anno->name != "length") return expression;
722
723 LOG3("Inserting cast in length annotation");
724 auto type = IR::Type_Bits::get(32);
725 auto cast = new IR::Cast(expression->srcInfo, type, expression);
726 return cast;
727 }
728};
729
733 public:
734 DetectDuplicates() { setName("DetectDuplicates"); }
735
736 bool preorder(const IR::V1Program *program) override {
737 auto &map = program->scope;
738 auto firstWithKey = map.begin();
739 while (firstWithKey != map.end()) {
740 auto key = firstWithKey->first;
741 auto range = map.equal_range(key);
742 for (auto s = range.first; s != range.second; s++) {
743 auto n = s;
744 for (n++; n != range.second; n++) {
745 auto e1 = s->second;
746 auto e2 = n->second;
747 if (e1->node_type_name() == e2->node_type_name()) {
748 if (e1->srcInfo.getStart().isValid())
749 ::P4::error(ErrorType::ERR_DUPLICATE, "%1%: same name as %2%", e1, e2);
750 else
751 // This name is probably standard_metadata_t, a built-in declaration
752 ::P4::error(ErrorType::ERR_INVALID,
753 "%1% is invalid; name %2% is reserved", e2, key);
754 }
755 }
756 }
757 firstWithKey = range.second;
758 }
759 // prune; we're done; everything is top-level
760 return false;
761 }
762};
763
764// If a parser state has a pragma @packet_entry, it is treated as a new entry
765// point to the parser.
767 ProgramStructure *structure;
768
769 public:
770 explicit CheckIfMultiEntryPoint(ProgramStructure *structure) : structure(structure) {
771 setName("CheckIfMultiEntryPoint");
772 }
773 bool preorder(const IR::ParserState *state) {
774 for (const auto *anno : state->getAnnotations()) {
775 if (anno->name == "packet_entry") {
776 structure->parserEntryPoints.emplace(state->name, state);
777 }
778 }
779 return false;
780 }
781};
782
783// Generate a new start state that selects on the meta variable,
784// standard_metadata.instance_type and branches into one of the entry points.
785// The backend is responsible for removing the use of the meta variable and
786// eliminate the new start state. The new start state is not added if the user
787// does not use the @packet_entry pragma.
789 ProgramStructure *structure;
790 IR::Vector<IR::Node> allTypeDecls;
793 cstring newStartState;
794 cstring newInstanceType;
795
796 public:
797 explicit InsertCompilerGeneratedStartState(ProgramStructure *structure) : structure(structure) {
798 setName("InsertCompilerGeneratedStartState");
799 structure->allNames.insert({IR::ParserState::start, 0});
800 structure->allNames.insert({"InstanceType"_cs, 0});
801 newStartState = structure->makeUniqueName(IR::ParserState::start);
802 newInstanceType = structure->makeUniqueName("InstanceType"_cs);
803 }
804
805 const IR::Node *postorder(IR::P4Program *program) override {
806 allTypeDecls.append(program->objects);
807 program->objects = allTypeDecls;
808 return program;
809 }
810
811 // rename original start state
812 const IR::Node *postorder(IR::ParserState *state) override {
813 if (structure->parserEntryPoints.empty()) return state;
814 if (state->name == IR::ParserState::start) {
815 state->name = newStartState;
816 }
817 return state;
818 }
819
820 // Rename any path refering to original start state
821 const IR::Node *postorder(IR::Path *path) override {
822 if (structure->parserEntryPoints.empty()) return path;
823 // At this point any identifier called start should have been renamed
824 // to unique name (e.g. start_1) => we can safely assume that any
825 // "start" refers to the parser state
826 if (path->name.name != IR::ParserState::start) return path;
827 // Just to make sure we can also check it explicitly
828 auto pe = getContext()->node->to<IR::PathExpression>();
829 auto sc = findContext<IR::SelectCase>();
830 auto ps = findContext<IR::ParserState>();
831 // Either the path is within SelectCase->state<PathExpression>->path
832 if (pe && ((sc && pe->equiv(*sc->state->to<IR::PathExpression>())) ||
833 // Or just within ParserState->selectExpression<PathExpression>->path
834 (ps && pe->equiv(*ps->selectExpression->to<IR::PathExpression>()))))
835 path->name = newStartState;
836 return path;
837 }
838
839 const IR::Node *postorder(IR::P4Parser *parser) override {
840 if (structure->parserEntryPoints.empty()) return parser;
842 // transition to original start state
843 members.push_back(new IR::SerEnumMember("START", new IR::Constant(0)));
844 selCases.push_back(new IR::SelectCase(
845 new IR::Member(new IR::TypeNameExpression(new IR::Type_Name(newInstanceType)),
846 "START"_cs),
847 new IR::PathExpression(new IR::Path(newStartState))));
848
849 // transition to addtional entry points
850 unsigned idx = 1;
851 for (auto p : structure->parserEntryPoints) {
852 members.push_back(new IR::SerEnumMember(p.first, new IR::Constant(idx++)));
853 selCases.push_back(new IR::SelectCase(
854 new IR::Member(new IR::TypeNameExpression(new IR::Type_Name(newInstanceType)),
855 p.first),
856 new IR::PathExpression(new IR::Path(p.second->name))));
857 }
858 auto instEnum = new IR::Type_SerEnum(
859 newInstanceType,
860 {new IR::Annotation(IR::Annotation::nameAnnotation, ".$InstanceType"_cs)},
861 IR::Type_Bits::get(32), members);
862 allTypeDecls.push_back(instEnum);
863
865 selExpr.push_back(new IR::Cast(
866 new IR::Type_Name(newInstanceType),
867 new IR::Member(new IR::PathExpression(new IR::Path("standard_metadata"_cs)),
868 "instance_type"_cs)));
869 auto selects = new IR::SelectExpression(new IR::ListExpression(selExpr), selCases);
870 auto startState = new IR::ParserState(
871 IR::ParserState::start,
872 {new IR::Annotation(IR::Annotation::nameAnnotation, ".$start"_cs)}, selects);
873 parserStates.push_back(startState);
874
875 if (!parserStates.empty()) {
876 parser->states.append(parserStates);
877 parserStates.clear();
878 }
879 return parser;
880 }
881};
882
887 public:
888 explicit FixMultiEntryPoint(ProgramStructure *structure) {
889 setName("FixMultiEntryPoint");
890 passes.emplace_back(new CheckIfMultiEntryPoint(structure));
891 passes.emplace_back(new InsertCompilerGeneratedStartState(structure));
892 }
893};
894
904 ProgramStructure *structure;
905 const IR::Type_Struct *stdType = nullptr;
906 const IR::Type_Struct *userType = nullptr;
907 const IR::Type_Struct *intrType = nullptr;
908 const IR::Type_Struct *queueType = nullptr;
909 const IR::StructField *intrField = nullptr;
910 const IR::StructField *queueField = nullptr;
911
912 public:
913 explicit MoveIntrinsicMetadata(ProgramStructure *structure) : structure(structure) {
914 CHECK_NULL(structure);
915 setName("MoveIntrinsicMetadata");
916 }
917 const IR::Node *preorder(IR::P4Program *program) override {
918 stdType = program->getDeclsByName(structure->v1model.standardMetadataType.name)
919 ->single()
920 ->to<IR::Type_Struct>();
921 userType = program->getDeclsByName(structure->v1model.metadataType.name)
922 ->single()
923 ->to<IR::Type_Struct>();
924 CHECK_NULL(stdType);
925 CHECK_NULL(userType);
926 intrField = userType->getField(structure->v1model.intrinsicMetadata.name);
927 if (intrField != nullptr) {
928 auto intrTypeName = intrField->type;
929 auto tn = intrTypeName->to<IR::Type_Name>();
930 BUG_CHECK(tn, "%1%: expected a Type_Name", intrTypeName);
931 auto nt = program->getDeclsByName(tn->path->name)->nextOrDefault();
932 if (nt == nullptr || !nt->is<IR::Type_Struct>()) {
933 ::P4::error(ErrorType::ERR_INVALID, "%1%: expected a structure", tn);
934 return program;
935 }
936 intrType = nt->to<IR::Type_Struct>();
937 LOG2("Intrinsic metadata type " << intrType);
938 }
939
940 queueField = userType->getField(structure->v1model.queueingMetadata.name);
941 if (queueField != nullptr) {
942 auto queueTypeName = queueField->type;
943 auto tn = queueTypeName->to<IR::Type_Name>();
944 BUG_CHECK(tn, "%1%: expected a Type_Name", queueTypeName);
945 auto nt = program->getDeclsByName(tn->path->name)->nextOrDefault();
946 if (nt == nullptr || !nt->is<IR::Type_Struct>()) {
947 ::P4::error(ErrorType::ERR_INVALID, "%1%: expected a structure", tn);
948 return program;
949 }
950 queueType = nt->to<IR::Type_Struct>();
951 LOG2("Queueing metadata type " << queueType);
952 }
953 return program;
954 }
955
956 const IR::Node *postorder(IR::Type_Struct *type) override {
957 if (getOriginal() == stdType) {
958 if (intrType != nullptr) {
959 for (auto f : intrType->fields) {
960 if (type->fields.getDeclaration(f->name) == nullptr) {
961 ::P4::error(ErrorType::ERR_NOT_FOUND,
962 "%1%: no such field in standard_metadata", f->name);
963 LOG2("standard_metadata: " << type);
964 }
965 }
966 }
967 if (queueType != nullptr) {
968 for (auto f : queueType->fields) {
969 if (type->fields.getDeclaration(f->name) == nullptr) {
970 ::P4::error(ErrorType::ERR_NOT_FOUND,
971 "%1%: no such field in standard_metadata", f->name);
972 LOG2("standard_metadata: " << type);
973 }
974 }
975 }
976 }
977 return type;
978 }
979
980 const IR::Node *postorder(IR::StructField *field) override {
981 if (getOriginal() == intrField || getOriginal() == queueField)
982 // delete it from its parent
983 return nullptr;
984 return field;
985 }
986
987 const IR::Node *postorder(IR::Member *member) override {
988 // We rewrite expressions like meta.intrinsic_metadata.x as
989 // standard_metadata.x. We know that these parameter names
990 // are always the same.
991 if (member->member != structure->v1model.intrinsicMetadata.name &&
992 member->member != structure->v1model.queueingMetadata.name)
993 return member;
994 auto pe = member->expr->to<IR::PathExpression>();
995 if (pe == nullptr || pe->path->absolute) return member;
996 if (pe->path->name == structure->v1model.parser.metadataParam.name) {
997 LOG2("Renaming reference " << member);
998 return new IR::PathExpression(new IR::Path(
999 member->expr->srcInfo,
1000 IR::ID(pe->path->name.srcInfo, structure->v1model.standardMetadata.name)));
1001 }
1002 return member;
1003 }
1004};
1005
1009 ProgramStructure *structure;
1010
1011 void add(const IR::Primitive *primitive, unsigned operand) {
1012 if (primitive->operands.size() <= operand) {
1013 // not enough arguments, do nothing.
1014 // resubmit and recirculate have optional arguments
1015 return;
1016 }
1017 auto expression = primitive->operands.at(operand);
1018 if (!expression->is<IR::PathExpression>()) {
1019 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a field list", expression);
1020 return;
1021 }
1022 auto nr = expression->to<IR::PathExpression>();
1023 auto fl = structure->field_lists.get(nr->path->name);
1024 if (fl == nullptr) {
1025 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: Expected a field list", expression);
1026 return;
1027 }
1028 LOG3("Recirculated " << nr->path->name);
1029 structure->allFieldLists.emplace(fl);
1030 }
1031
1032 public:
1033 explicit FindRecirculated(ProgramStructure *structure) : structure(structure) {
1034 CHECK_NULL(structure);
1035 setName("FindRecirculated");
1036 }
1037
1038 void postorder(const IR::Primitive *primitive) override {
1039 if (primitive->name == "recirculate" || primitive->name == "resubmit") {
1040 add(primitive, 0);
1041 } else if (primitive->name.startsWith("clone") && primitive->operands.size() == 2) {
1042 add(primitive, 1);
1043 }
1044 }
1045};
1046
1048
1049// Is fed a P4-14 program and outputs an equivalent P4-16 program in v1model
1050class Converter : public PassManager {
1051 public:
1052 ProgramStructure *structure;
1053 static ProgramStructure *(*createProgramStructure)();
1054 static ConversionContext *(*createConversionContext)();
1055 Converter();
1056 void loadModel() { structure->loadModel(); }
1057 Visitor::profile_t init_apply(const IR::Node *node) override;
1058};
1059
1060} // namespace P4::P4V1
1061
1062#endif /* FRONTENDS_P4_14_FROMV1_0_CONVERTERS_H_ */
Definition node.h:52
Definition node.h:94
Definition vector.h:59
Definition visitor.h:400
Definition coreLibrary.h:103
Definition converters.h:715
Definition converters.h:766
Definition converters.h:282
Definition converters.h:406
Definition frontends/p4-14/fromv1.0/programStructure.h:32
Definition converters.h:1050
Definition converters.h:732
Definition converters.h:177
Definition converters.h:34
Definition converters.h:97
static void addConverter(cstring type, ExternConverter *)
Definition converters.cpp:582
Definition converters.h:1008
Definition converters.h:507
Definition converters.h:886
Definition converters.h:903
Definition converters.h:140
Information about the structure of a P4-14 program, used to convert it to a P4-16 program.
Definition frontends/p4-14/fromv1.0/programStructure.h:45
ordered_set< const IR::FieldList * > allFieldLists
Field lists that appear in the program.
Definition frontends/p4-14/fromv1.0/programStructure.h:165
Definition converters.h:448
Definition converters.h:68
Definition converters.h:88
Definition ir/pass_manager.h:40
Definition visitor.h:424
Definition source_file.h:131
Definition visitor.h:78
Definition cstring.h:85
Definition safe_vector.h:27
Definition converters.cpp:28
const IR::Expression * convert(const IR::Expression *expression, const IR::Type *type)
Definition structInitializers.cpp:26
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition lib/error.h:51
Definition id.h:28
T * to() noexcept
Definition rtti.h:226