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