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