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, "%1%: no length annotation on varbit field", f);
545 BUG_CHECK(anno->getExpr().size() == 1, "%1%: expected exactly 1 argument", anno);
546 headerLength = anno->getExpr(0);
547 // We keep going through the loop just to check whether there is another
548 // varbit field in the header.
549 } else if (fixedHeaderType == nullptr) {
550 // We only keep the fields prior to the varbit field
551 fields.push_back(f);
552 }
553 }
554 if (fixedHeaderType != nullptr) {
555 LOG3("Extracted fixed-size header type from " << type << " into " << fixedHeaderType);
556 fixed = new HeaderSplit;
557 fixed->fixedHeaderType = fixedHeaderType;
558 fixed->headerLength = headerLength;
559 fixedPart.emplace(type->name.name, fixed);
560 allTypeDecls.push_back(fixedHeaderType);
561 return fixed;
562 }
563 return nullptr;
564 }
565
573 class RewriteLength final : public Transform {
574 const IR::Type_Header *header;
575 const IR::Declaration *var;
576
577 public:
578 explicit RewriteLength(const IR::Type_Header *header, const IR::Declaration *var)
579 : header(header), var(var) {
580 setName("RewriteLength");
581 }
582
583 const IR::Node *postorder(IR::PathExpression *expression) override {
584 if (expression->path->absolute) return expression;
585 for (auto f : header->fields) {
586 if (f->name == expression->path->name)
587 return new IR::Member(expression->srcInfo, new IR::PathExpression(var->name),
588 f->name);
589 }
590 return expression;
591 }
592 };
593
594 public:
595 explicit FixExtracts(ProgramStructure *structure) : structure(structure) {
596 CHECK_NULL(structure);
597 setName("FixExtracts");
598 }
599
600 const IR::Node *postorder(IR::P4Program *program) override {
601 // P4-14 headers cannot refer to other types, so it is safe
602 // to prepend them to the list of declarations.
603 allTypeDecls.append(program->objects);
604 program->objects = allTypeDecls;
605 return program;
606 }
607
608 const IR::Node *postorder(IR::P4Parser *parser) override {
609 if (!varDecls.empty()) {
610 parser->parserLocals.append(varDecls);
611 varDecls.clear();
612 }
613 return parser;
614 }
615
616 const IR::Node *postorder(IR::MethodCallStatement *statement) override {
617 auto mce = getOriginal<IR::MethodCallStatement>()->methodCall;
618 LOG3("Looking up in extracts " << dbp(mce));
619 auto ht = ::P4::get(structure->extractsSynthesized, mce);
620 if (ht == nullptr)
621 // not an extract
622 return statement;
623
624 // This is an extract method invocation
625 BUG_CHECK(mce->arguments->size() == 1, "%1%: expected 1 argument", mce);
626 auto arg = mce->arguments->at(0);
627
628 auto fixed = splitHeaderType(ht);
629 if (fixed == nullptr) return statement;
630 CHECK_NULL(fixed->headerLength);
631 CHECK_NULL(fixed->fixedHeaderType);
632
633 auto result = new IR::IndexedVector<IR::StatOrDecl>();
634 cstring varName = structure->makeUniqueName("tmp_hdr"_cs);
635 auto var =
636 new IR::Declaration_Variable(IR::ID(varName), fixed->fixedHeaderType->to<IR::Type>());
637 varDecls.push_back(var);
638
639 // Create lookahead
640 auto member = mce->method->to<IR::Member>(); // should be packet_in.extract
641 CHECK_NULL(member);
642 auto typeArgs = new IR::Vector<IR::Type>();
643 typeArgs->push_back(fixed->fixedHeaderType->getP4Type());
644 auto lookaheadMethod =
645 new IR::Member(member->expr, P4::P4CoreLibrary::instance().packetIn.lookahead.name);
646 auto lookahead = new IR::MethodCallExpression(mce->srcInfo, lookaheadMethod, typeArgs,
648 auto assign =
649 new IR::AssignmentStatement(mce->srcInfo, new IR::PathExpression(varName), lookahead);
650 result->push_back(assign);
651 LOG3("Created lookahead " << assign);
652
653 // Create actual extract
654 RewriteLength rewrite(fixed->fixedHeaderType, var);
655 rewrite.setCalledBy(this);
656 auto length = fixed->headerLength->apply(rewrite);
657 auto args = new IR::Vector<IR::Argument>();
658 args->push_back(arg->clone());
659 auto type = IR::Type_Bits::get(P4::P4CoreLibrary::instance().packetIn.extractSecondArgSize);
660 auto cast = new IR::Cast(Util::SourceInfo(), type, length);
661 args->push_back(new IR::Argument(cast));
662 auto expression = new IR::MethodCallExpression(mce->srcInfo, mce->method->clone(), args);
663 result->push_back(new IR::MethodCallStatement(expression));
664 return result;
665 }
666};
667
668/*
669 This class is used to adjust the expressions in a @length
670 annotation on a varbit field. The P4-14 to P4-16 converter inserts
671 these annotations on the unique varbit field in a header; the
672 annotations are created from the header max_length and length
673 fields. The length annotation contains an expression which is used
674 to compute the length of the varbit field. The problem that we are
675 solving here is that expression semantics is different in P4-14 and
676 P4-16. Consider the canonical case of an IPv4 header:
677
678 header_type ipv4_t {
679 fields {
680 version : 4;
681 ihl : 4;
682 // lots of other fields...
683 options: *;
684 }
685 length : ihl*4;
686 max_length : 64;
687 }
688
689 This generates the following P4-16 structure:
690 struct ipv4_t {
691 bit<4> version;
692 bit<4> ihl;
693 @length((ihl*4) * 8 - 20) // 20 is the size of the fixed part of the header
694 varbit<(64 - 20) * 8> options;
695 }
696
697 When such a header is used in an extract statement, the @length
698 annotation is used to compute the second argument of the extract
699 method. The problem we are solving here is the fact that ihl is
700 only represented on 4 bits, so the evaluation ihl*4 will actually
701 overflow. This is not a problem in P4-14, but it is a problem in
702 P4-16. Unfortunately there is no easy way to guess how many bits
703 are required to evaluate this computation. So what we do is to cast
704 all PathExpressions to 32-bits. This is really just a heuristic,
705 but since the semantics of P4-14 expressions is unclear, we cannot
706 do much better than this.
707*/
708class AdjustLengths : public Transform {
709 public:
710 AdjustLengths() { setName("AdjustLengths"); }
711 const IR::Node *postorder(IR::PathExpression *expression) override {
712 auto anno = findContext<IR::Annotation>();
713 if (anno == nullptr) return expression;
714 if (anno->name != "length") return expression;
715
716 LOG3("Inserting cast in length annotation");
717 auto type = IR::Type_Bits::get(32);
718 auto cast = new IR::Cast(expression->srcInfo, type, expression);
719 return cast;
720 }
721};
722
725class DetectDuplicates : public Inspector {
726 public:
727 DetectDuplicates() { setName("DetectDuplicates"); }
728
729 bool preorder(const IR::V1Program *program) override {
730 auto &map = program->scope;
731 auto firstWithKey = map.begin();
732 while (firstWithKey != map.end()) {
733 auto key = firstWithKey->first;
734 auto range = map.equal_range(key);
735 for (auto s = range.first; s != range.second; s++) {
736 auto n = s;
737 for (n++; n != range.second; n++) {
738 auto e1 = s->second;
739 auto e2 = n->second;
740 if (e1->node_type_name() == e2->node_type_name()) {
741 if (e1->srcInfo.getStart().isValid())
742 ::P4::error(ErrorType::ERR_DUPLICATE, "%1%: same name as %2%", e1, e2);
743 else
744 // This name is probably standard_metadata_t, a built-in declaration
745 ::P4::error(ErrorType::ERR_INVALID,
746 "%1% is invalid; name %2% is reserved", e2, key);
747 }
748 }
749 }
750 firstWithKey = range.second;
751 }
752 // prune; we're done; everything is top-level
753 return false;
754 }
755};
756
757// If a parser state has a pragma @packet_entry, it is treated as a new entry
758// point to the parser.
759class CheckIfMultiEntryPoint : public Inspector {
760 ProgramStructure *structure;
761
762 public:
763 explicit CheckIfMultiEntryPoint(ProgramStructure *structure) : structure(structure) {
764 setName("CheckIfMultiEntryPoint");
765 }
766 bool preorder(const IR::ParserState *state) override {
767 for (const auto *anno : state->getAnnotations()) {
768 if (anno->name == "packet_entry") {
769 structure->parserEntryPoints.emplace(state->name, state);
770 }
771 }
772 return false;
773 }
774};
775
776// Generate a new start state that selects on the meta variable,
777// standard_metadata.instance_type and branches into one of the entry points.
778// The backend is responsible for removing the use of the meta variable and
779// eliminate the new start state. The new start state is not added if the user
780// does not use the @packet_entry pragma.
781class InsertCompilerGeneratedStartState : public Transform {
782 ProgramStructure *structure;
783 IR::Vector<IR::Node> allTypeDecls;
786 cstring newStartState;
787 cstring newInstanceType;
788
789 public:
790 explicit InsertCompilerGeneratedStartState(ProgramStructure *structure) : structure(structure) {
791 setName("InsertCompilerGeneratedStartState");
792 structure->allNames.insert({IR::ParserState::start, 0});
793 structure->allNames.insert({"InstanceType"_cs, 0});
794 newStartState = structure->makeUniqueName(IR::ParserState::start);
795 newInstanceType = structure->makeUniqueName("InstanceType"_cs);
796 }
797
798 const IR::Node *postorder(IR::P4Program *program) override {
799 allTypeDecls.append(program->objects);
800 program->objects = allTypeDecls;
801 return program;
802 }
803
804 // rename original start state
805 const IR::Node *postorder(IR::ParserState *state) override {
806 if (structure->parserEntryPoints.empty()) return state;
807 if (state->name == IR::ParserState::start) {
808 state->name = newStartState;
809 }
810 return state;
811 }
812
813 // Rename any path refering to original start state
814 const IR::Node *postorder(IR::Path *path) override {
815 if (structure->parserEntryPoints.empty()) return path;
816 // At this point any identifier called start should have been renamed
817 // to unique name (e.g. start_1) => we can safely assume that any
818 // "start" refers to the parser state
819 if (path->name.name != IR::ParserState::start) return path;
820 // Just to make sure we can also check it explicitly
821 auto pe = getContext()->node->to<IR::PathExpression>();
822 auto sc = findContext<IR::SelectCase>();
823 auto ps = findContext<IR::ParserState>();
824 // Either the path is within SelectCase->state<PathExpression>->path
825 if (pe && ((sc && pe->equiv(*sc->state->to<IR::PathExpression>())) ||
826 // Or just within ParserState->selectExpression<PathExpression>->path
827 (ps && pe->equiv(*ps->selectExpression->to<IR::PathExpression>()))))
828 path->name = newStartState;
829 return path;
830 }
831
832 const IR::Node *postorder(IR::P4Parser *parser) override {
833 if (structure->parserEntryPoints.empty()) return parser;
835 // transition to original start state
836 members.push_back(new IR::SerEnumMember("START", new IR::Constant(0)));
837 selCases.push_back(new IR::SelectCase(
838 new IR::Member(new IR::TypeNameExpression(new IR::Type_Name(newInstanceType)),
839 "START"_cs),
840 new IR::PathExpression(new IR::Path(newStartState))));
841
842 // transition to addtional entry points
843 unsigned idx = 1;
844 for (auto p : structure->parserEntryPoints) {
845 members.push_back(new IR::SerEnumMember(p.first, new IR::Constant(idx++)));
846 selCases.push_back(new IR::SelectCase(
847 new IR::Member(new IR::TypeNameExpression(new IR::Type_Name(newInstanceType)),
848 p.first),
849 new IR::PathExpression(new IR::Path(p.second->name))));
850 }
851 auto instEnum = new IR::Type_SerEnum(
852 newInstanceType,
853 {new IR::Annotation(IR::Annotation::nameAnnotation, ".$InstanceType"_cs)},
854 IR::Type_Bits::get(32), members);
855 allTypeDecls.push_back(instEnum);
856
858 selExpr.push_back(new IR::Cast(
859 new IR::Type_Name(newInstanceType),
860 new IR::Member(new IR::PathExpression(new IR::Path("standard_metadata"_cs)),
861 "instance_type"_cs)));
862 auto selects = new IR::SelectExpression(new IR::ListExpression(selExpr), selCases);
863 auto startState = new IR::ParserState(
864 IR::ParserState::start,
865 {new IR::Annotation(IR::Annotation::nameAnnotation, ".$start"_cs)}, selects);
866 parserStates.push_back(startState);
867
868 if (!parserStates.empty()) {
869 parser->states.append(parserStates);
870 parserStates.clear();
871 }
872 return parser;
873 }
874};
875
879class FixMultiEntryPoint : public PassManager {
880 public:
881 explicit FixMultiEntryPoint(ProgramStructure *structure) {
882 setName("FixMultiEntryPoint");
883 passes.emplace_back(new CheckIfMultiEntryPoint(structure));
884 passes.emplace_back(new InsertCompilerGeneratedStartState(structure));
885 }
886};
887
896class MoveIntrinsicMetadata : public Transform {
897 ProgramStructure *structure;
898 const IR::Type_Struct *stdType = nullptr;
899 const IR::Type_Struct *userType = nullptr;
900 const IR::Type_Struct *intrType = nullptr;
901 const IR::Type_Struct *queueType = nullptr;
902 const IR::StructField *intrField = nullptr;
903 const IR::StructField *queueField = nullptr;
904
905 public:
906 explicit MoveIntrinsicMetadata(ProgramStructure *structure) : structure(structure) {
907 CHECK_NULL(structure);
908 setName("MoveIntrinsicMetadata");
909 }
910 const IR::Node *preorder(IR::P4Program *program) override {
911 stdType = program->getDeclsByName(structure->v1model.standardMetadataType.name)
912 ->single()
913 ->to<IR::Type_Struct>();
914 userType = program->getDeclsByName(structure->v1model.metadataType.name)
915 ->single()
916 ->to<IR::Type_Struct>();
917 CHECK_NULL(stdType);
918 CHECK_NULL(userType);
919 intrField = userType->getField(structure->v1model.intrinsicMetadata.name);
920 if (intrField != nullptr) {
921 auto intrTypeName = intrField->type;
922 auto tn = intrTypeName->to<IR::Type_Name>();
923 BUG_CHECK(tn, "%1%: expected a Type_Name", intrTypeName);
924 auto nt = program->getDeclsByName(tn->path->name)->nextOrDefault();
925 if (nt == nullptr || !nt->is<IR::Type_Struct>()) {
926 ::P4::error(ErrorType::ERR_INVALID, "%1%: expected a structure", tn);
927 return program;
928 }
929 intrType = nt->to<IR::Type_Struct>();
930 LOG2("Intrinsic metadata type " << intrType);
931 }
932
933 queueField = userType->getField(structure->v1model.queueingMetadata.name);
934 if (queueField != nullptr) {
935 auto queueTypeName = queueField->type;
936 auto tn = queueTypeName->to<IR::Type_Name>();
937 BUG_CHECK(tn, "%1%: expected a Type_Name", queueTypeName);
938 auto nt = program->getDeclsByName(tn->path->name)->nextOrDefault();
939 if (nt == nullptr || !nt->is<IR::Type_Struct>()) {
940 ::P4::error(ErrorType::ERR_INVALID, "%1%: expected a structure", tn);
941 return program;
942 }
943 queueType = nt->to<IR::Type_Struct>();
944 LOG2("Queueing metadata type " << queueType);
945 }
946 return program;
947 }
948
949 const IR::Node *postorder(IR::Type_Struct *type) override {
950 if (getOriginal() == stdType) {
951 if (intrType != nullptr) {
952 for (auto f : intrType->fields) {
953 if (type->fields.getDeclaration(f->name) == nullptr) {
954 ::P4::error(ErrorType::ERR_NOT_FOUND,
955 "%1%: no such field in standard_metadata", f->name);
956 LOG2("standard_metadata: " << type);
957 }
958 }
959 }
960 if (queueType != nullptr) {
961 for (auto f : queueType->fields) {
962 if (type->fields.getDeclaration(f->name) == nullptr) {
963 ::P4::error(ErrorType::ERR_NOT_FOUND,
964 "%1%: no such field in standard_metadata", f->name);
965 LOG2("standard_metadata: " << type);
966 }
967 }
968 }
969 }
970 return type;
971 }
972
973 const IR::Node *postorder(IR::StructField *field) override {
974 if (getOriginal() == intrField || getOriginal() == queueField)
975 // delete it from its parent
976 return nullptr;
977 return field;
978 }
979
980 const IR::Node *postorder(IR::Member *member) override {
981 // We rewrite expressions like meta.intrinsic_metadata.x as
982 // standard_metadata.x. We know that these parameter names
983 // are always the same.
984 if (member->member != structure->v1model.intrinsicMetadata.name &&
985 member->member != structure->v1model.queueingMetadata.name)
986 return member;
987 auto pe = member->expr->to<IR::PathExpression>();
988 if (pe == nullptr || pe->path->absolute) return member;
989 if (pe->path->name == structure->v1model.parser.metadataParam.name) {
990 LOG2("Renaming reference " << member);
991 return new IR::PathExpression(new IR::Path(
992 member->expr->srcInfo,
993 IR::ID(pe->path->name.srcInfo, structure->v1model.standardMetadata.name)));
994 }
995 return member;
996 }
997};
998
1001class FindRecirculated : public Inspector {
1002 ProgramStructure *structure;
1003
1004 void add(const IR::Primitive *primitive, unsigned operand) {
1005 if (primitive->operands.size() <= operand) {
1006 // not enough arguments, do nothing.
1007 // resubmit and recirculate have optional arguments
1008 return;
1009 }
1010 auto expression = primitive->operands.at(operand);
1011 if (!expression->is<IR::PathExpression>()) {
1012 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a field list", expression);
1013 return;
1014 }
1015 auto nr = expression->to<IR::PathExpression>();
1016 auto fl = structure->field_lists.get(nr->path->name);
1017 if (fl == nullptr) {
1018 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: Expected a field list", expression);
1019 return;
1020 }
1021 LOG3("Recirculated " << nr->path->name);
1022 structure->allFieldLists.emplace(fl);
1023 }
1024
1025 public:
1026 explicit FindRecirculated(ProgramStructure *structure) : structure(structure) {
1027 CHECK_NULL(structure);
1028 setName("FindRecirculated");
1029 }
1030
1031 void postorder(const IR::Primitive *primitive) override {
1032 if (primitive->name == "recirculate" || primitive->name == "resubmit") {
1033 add(primitive, 0);
1034 } else if (primitive->name.startsWith("clone") && primitive->operands.size() == 2) {
1035 add(primitive, 1);
1036 }
1037 }
1038};
1039
1041
1042// Is fed a P4-14 program and outputs an equivalent P4-16 program in v1model
1043class Converter : public PassManager {
1044 public:
1045 ProgramStructure *structure;
1046 static ProgramStructure *(*createProgramStructure)();
1047 static ConversionContext *(*createConversionContext)();
1048 Converter();
1049 void loadModel() { structure->loadModel(); }
1050 Visitor::profile_t init_apply(const IR::Node *node) override;
1051};
1052
1053} // namespace P4::P4V1
1054
1055#endif /* FRONTENDS_P4_14_FROMV1_0_CONVERTERS_H_ */
Definition indexed_vector.h:31
Definition node.h:44
Definition ir/vector.h:50
Definition visitor.h:409
Definition coreLibrary.h:94
Definition converters.h:759
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:433
Definition source_file.h:123
Definition visitor.h:69
Definition cstring.h:76
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:49
Definition id.h:19
T * to() noexcept
Definition rtti.h:226