P4C
The P4 Compiler
Loading...
Searching...
No Matches
backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h
1
18
19#ifndef BACKENDS_TOFINO_BF_P4C_ARCH_FROMV1_0_PROGRAMSTRUCTURE_H_
20#define BACKENDS_TOFINO_BF_P4C_ARCH_FROMV1_0_PROGRAMSTRUCTURE_H_
21
22#include <boost/range/adaptor/reversed.hpp>
23
24#include "backends/tofino/bf-p4c/arch/fromv1.0/add_metadata_parser_states.h"
25#include "backends/tofino/bf-p4c/arch/fromv1.0/checksum.h"
26#include "backends/tofino/bf-p4c/arch/program_structure.h"
27#include "backends/tofino/bf-p4c/bf-p4c-options.h"
28#include "backends/tofino/bf-p4c/common/utils.h"
29#include "backends/tofino/bf-p4c/lib/assoc.h"
30#include "backends/tofino/bf-p4c/midend/parser_graph.h"
31#include "backends/tofino/bf-p4c/midend/path_linearizer.h"
32#include "frontends/common/constantFolding.h"
33#include "frontends/common/resolveReferences/resolveReferences.h"
34#include "frontends/p4-14/fromv1.0/converters.h"
35#include "frontends/p4-14/fromv1.0/programStructure.h"
36#include "frontends/p4/cloner.h"
37#include "frontends/p4/createBuiltins.h"
38#include "frontends/p4/directCalls.h"
39#include "frontends/p4/typeChecking/bindVariables.h"
40#include "frontends/p4/typeChecking/typeChecker.h"
41#include "frontends/p4/typeMap.h"
42#include "lib/big_int_util.h"
43
44namespace P4::P4V1 {
45
46using namespace P4;
47
48static const cstring COMPILER_META = "__bfp4c_compiler_generated_meta"_cs;
49
50// TODO older definition of ProgramStructure used by 14-to-v1model conversion path
51// to be removed
52// ** BEGIN **
53class TNA_ProgramStructure : public ProgramStructure {
54 const IR::Expression *convertHashAlgorithm(Util::SourceInfo srcInfo, IR::ID algorithm) override;
55
56 const IR::P4Table *convertTable(const IR::V1Table *table, cstring newName,
58 std::map<cstring, cstring> &) override;
59
60 const IR::Declaration_Instance *convertActionProfile(const IR::ActionProfile *action_profile,
61 cstring newName) override;
62
63 public:
64 static ProgramStructure *create() { return new TNA_ProgramStructure(); }
65};
66// ** END **
67
70using DigestFieldList =
71 std::map<cstring,
72 std::map<unsigned, std::pair<std::optional<cstring>, const IR::Expression *>>>;
79class FixParamType : public Transform {
80 const IR::Node *postorder(IR::Parameter *param) override {
81 auto tn = param->type->to<IR::Type_Name>();
82 if (!tn) return param;
83 if (tn->path->name == "ingress_intrinsic_metadata_from_parser_aux_t")
84 return new IR::Parameter(
85 param->name, param->direction,
86 new IR::Type_Name(new IR::Path("ingress_intrinsic_metadata_from_parser_t")),
87 param->defaultValue);
88 else if (tn->path->name == "egress_intrinsic_metadata_from_parser_aux_t")
89 return new IR::Parameter(
90 param->name, param->direction,
91 new IR::Type_Name(new IR::Path("egress_intrinsic_metadata_from_parser_t")),
92 param->defaultValue);
93 return param;
94 }
95};
96
98 const IR::Node *postorder(IR::ConcreteHeaderRef *href) override {
99 if (href->ref->name == "standard_metadata")
100 return new IR::Member(new IR::PathExpression("meta"), href->ref->name);
101 auto expr =
102 new IR::PathExpression(href->srcInfo, href->type, new IR::Path(href->ref->name));
103 return expr;
104 }
105};
106
110class TnaProgramStructure : public ProgramStructure {
111 // hash
112 const IR::Expression *convertHashAlgorithm(Util::SourceInfo srcInfo, IR::ID algorithm) override;
113 IR::ConstructorCallExpression *createHashExtern(const IR::Expression *, const IR::Type *);
114
115 // table
116 const IR::P4Table *convertTable(const IR::V1Table *table, cstring newName,
118 std::map<cstring, cstring> &) override;
119
120 // action profile
121 const IR::Declaration_Instance *convertActionProfile(const IR::ActionProfile *action_profile,
122 cstring newName) override;
123
124 // counter and meter
125 const IR::Declaration_Instance *convertDirectCounter(const IR::Counter *c,
126 cstring newName) override;
127 const IR::Declaration_Instance *convertDirectMeter(const IR::Meter *c,
128 cstring newName) override;
129 const IR::Statement *convertMeterCall(const IR::Meter *m) override;
130 const IR::Statement *convertCounterCall(cstring c) override;
131 const IR::Expression *counterType(const IR::CounterOrMeter *cm) override;
132 const IR::Declaration_Instance *convert(const IR::Register *reg, cstring newName,
133 const IR::Type *regElementType) override;
134
135 // control
136 IR::Vector<IR::Argument> *createApplyArguments(cstring n) override;
137 const IR::P4Control *convertControl(const IR::V1Control *control, cstring newName) override;
138 const IR::ParserState *convertParser(const IR::V1Parser *control,
140 const IR::Declaration_Instance *convert(const IR::CounterOrMeter *cm, cstring newName) override;
141 const IR::Type_Control *controlType(IR::ID name) override;
142
143 void addIngressParams(IR::ParameterList *param);
144 void addEgressParams(IR::ParameterList *param);
145 void setupControlArguments();
146
147 // metadata
148 const IR::Expression *stdMetaReference(const IR::Parameter *param);
149 void checkHeaderType(const IR::Type_StructLike *hdr, bool toStruct) override;
150
151 // action
152 std::vector<cstring> findActionInTables(const IR::V1Control *control);
153
154 // parsers
155 IR::IndexedVector<IR::ParserState> *createIngressParserStates();
156 IR::IndexedVector<IR::ParserState> *createEgressParserStates();
157 const IR::ParserState *createEmptyMirrorState(cstring nextState);
158 const IR::ParserState *createMirrorState(gress_t, unsigned, const IR::Expression *, cstring,
159 cstring);
160 const IR::ParserState *createResubmitState(gress_t, unsigned, const IR::Expression *, cstring,
161 cstring);
162 const IR::SelectCase *createSelectCase(gress_t gress, unsigned digestId,
163 const IR::ParserState *newState);
164 IR::IndexedVector<IR::ParserState> *createMirrorStates();
165 IR::IndexedVector<IR::ParserState> *createResubmitStates();
166
167 // types
168 void removeType(cstring typeName, cstring headerName);
169 void removeP14IntrinsicMetadataTypes();
170 void createType(cstring typeName, cstring headerName);
171 void createIntrinsicMetadataTypes();
172 void loadModel() override;
173
174 // helpers
175 void collectControlGressMapping();
176 void collectBridgedFieldsInControls();
177 void collectBridgedFieldsInParsers();
178 void collectBridgedFields();
179 void collectDigestFields();
180 void addBridgedFieldsFromChecksumUpdate(IR::IndexedVector<IR::StructField> *);
181 void createCompilerGeneratedTypes();
182 cstring convertLinearPathToStructFieldName(BFN::LinearPath *path);
183 void checkForReservedTnaTypes();
184 void collectHeaderReference(const IR::V1Control *control);
185 void parseUpdateLocationAnnotation(std::set<gress_t> &, const IR::Annotation *, cstring);
186 bool useResidualChecksum();
187 bool useBridgeMetadata();
188
189 // print program structure
190 void getStructFieldsFromFieldList(IR::IndexedVector<IR::StructField> *,
191 const IR::ListExpression *);
192 void createDigestHeaderTypeAndInstance(unsigned index, const IR::Expression *expr, cstring name,
194 const IR::StatOrDecl *createDigestEmit(
195 cstring, unsigned, std::pair<std::optional<cstring>, const IR::Expression *>, cstring,
197 const IR::StatOrDecl *createBridgeEmit();
198 using ChecksumUpdateInfo = std::pair<IR::StatOrDecl *, ChecksumInfo>;
199 void createChecksumUpdateStatements(gress_t gress, IR::IndexedVector<IR::Declaration> &,
200 std::vector<ChecksumUpdateInfo> &);
201 void createChecksumVerifyStatements(gress_t gress);
202 void createIngressParser();
203 void createEgressParser();
204 void createParser() override;
205 void createIngressDeparser();
206 void createEgressDeparser();
207 void createDeparser() override;
208 void createPipeline();
209 void createMain() override;
210 const IR::P4Program *create(Util::SourceInfo info) override;
211
212 public:
213 // used to indicate which gress the current control block being
214 // translated is invoked in.
215 gress_t currentGress;
216 std::map<cstring, bitvec> controlParamUse;
217 std::map<cstring, gress_t> mapControlToGress;
218 gress_t getGress(cstring name);
219 std::map<unsigned long, unsigned> cloneIndexHashes[2];
220 std::map<unsigned long, unsigned> resubmitIndexHashes;
221 std::map<unsigned long, unsigned> digestIndexHashes;
222 std::map<unsigned long, unsigned> recirculateIndexHashes;
223 std::map<cstring, std::vector<const IR::Parameter *>> controlParams;
224 // map { prim->name :
225 // map { field_list_index :
226 // pair(field_list_name, field_list_expr) } }
227 std::map<cstring, std::map<unsigned, std::pair<std::optional<cstring>, const IR::Expression *>>>
228 digestFieldLists;
229 ordered_set<cstring> bridgedFields;
230 std::map<cstring, BFN::LinearPath> bridgedFieldInfo;
231 std::map<cstring, const IR::Type_StructLike *> tna_intr_md_types;
232
233 IR::Vector<IR::StatOrDecl> checksumVerify;
234
235 std::vector<ChecksumInfo> residualChecksums;
236 std::vector<ChecksumInfo> verifyChecksums;
237
238 assoc::map<const IR::FieldList *, cstring> residualChecksumNames;
239
240 ordered_map<cstring, ordered_set<cstring>> ingressVerifyChecksumToStates;
241
242 std::map<gress_t, std::map<cstring, IR::Statement *>> checksumDepositToHeader;
243
244 static ProgramStructure *create() { return new TnaProgramStructure(); }
245
246 template <typename Func>
247 void forAllResidualChecksums(std::vector<const IR::CalculatedField *> calculated_fields,
248 Func function) {
249 for (auto cf : calculated_fields) {
250 for (auto uov : cf->specs) {
251 if (!uov.update) continue;
252 auto flc = field_list_calculations.get(uov.name.name);
253 auto fl = getFieldLists(flc);
254 if (fl == nullptr) continue;
255 if (fl->payload) {
256 function(fl);
257 }
258 }
259 }
260 }
261
262 template <typename Func>
263 void forAllChecksums(std::vector<const IR::CalculatedField *> calculated_fields,
264 Func function) {
265 for (auto cf : calculated_fields) {
266 for (auto uov : cf->specs) {
267 if (!uov.update) continue;
268 auto flc = field_list_calculations.get(uov.name.name);
269 auto fl = getFieldLists(flc);
270 if (fl == nullptr) continue;
271 function(cf, flc, uov, fl);
272 }
273 }
274 }
275};
276
277// convert P4-14 metadata to the corresponding P4-16 intrinsic metadata
278class ConvertMetadata : public Transform {
279 TnaProgramStructure *structure;
280 std::map<cstring, const IR::Expression *> ingressMetadataNameMap;
281 std::map<cstring, const IR::Expression *> egressMetadataNameMap;
282 std::map<cstring, int> widthMap;
283
284 void cvt(gress_t gress, cstring src, int src_width, const IR::Member *dst) {
285 auto &nameMap =
286 (gress == gress_t::INGRESS) ? ingressMetadataNameMap : egressMetadataNameMap;
287 if (src_width > dst->type->width_bits()) {
288 auto casted = new IR::Cast(IR::Type_Bits::get(src_width), dst);
289 nameMap.emplace(src, casted);
290 } else {
291 nameMap.emplace(src, dst);
292 }
293 widthMap.emplace(src, src_width);
294 }
295
296 const IR::Member *mkMember(cstring meta, cstring strct, cstring field, int width) {
297 auto mem =
298 new IR::Member(new IR::Member(new IR::PathExpression(IR::ID(meta)), strct), field);
299 mem->type = IR::Type::Bits::get(width);
300 return mem;
301 }
302
303 const IR::Member *mkMember(cstring strct, cstring field, int width) {
304 auto mem = new IR::Member(new IR::PathExpression(IR::ID(strct)), field);
305 mem->type = IR::Type::Bits::get(width);
306 return mem;
307 }
308
309 public:
310 explicit ConvertMetadata(TnaProgramStructure *s) : structure(s) {
311 int portWidth = Device::portBitWidth();
312 // metadata to translate in ingress
313 cvt(INGRESS, "ig_intr_md_from_parser_aux.ingress_parser_err"_cs, 16,
314 mkMember("ig_intr_md_from_parser_aux"_cs, "parser_err"_cs, 16));
315 cvt(INGRESS, "meta.standard_metadata.egress_spec"_cs, 9,
316 mkMember("ig_intr_md_for_tm"_cs, "ucast_egress_port"_cs, portWidth));
317 cvt(INGRESS, "meta.standard_metadata.ingress_port"_cs, 9,
318 mkMember("ig_intr_md"_cs, "ingress_port"_cs, portWidth));
319 cvt(INGRESS, "ig_intr_md_for_tm.drop_ctl"_cs, 1,
320 mkMember("ig_intr_md_for_dprsr"_cs, "drop_ctl"_cs, 1));
321 cvt(INGRESS, "ig_intr_md_from_parser_aux.ingress_global_tstamp"_cs, 48,
322 mkMember("ig_intr_md_from_parser_aux"_cs, "global_tstamp"_cs, 48));
323 cvt(INGRESS, "ig_intr_md_for_mb.mirror_hash"_cs, 13,
324 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_hash"_cs, 13));
325 cvt(INGRESS, "ig_intr_md_for_mb.mirror_io_select"_cs, 1,
326 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_io_select"_cs, 1));
327 cvt(INGRESS, "ig_intr_md_for_mb.mirror_multicast_ctrl"_cs, 1,
328 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_multicast_ctrl"_cs, 1));
329 cvt(INGRESS, "ig_intr_md_for_mb.mirror_ingress_cos"_cs, 3,
330 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_ingress_cos"_cs, 3));
331 cvt(INGRESS, "ig_intr_md_for_mb.mirror_deflect_on_drop"_cs, 1,
332 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_deflect_on_drop"_cs, 1));
333 cvt(INGRESS, "ig_intr_md_for_mb.mirror_egress_port"_cs, portWidth,
334 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_egress_port"_cs, portWidth));
335 cvt(INGRESS, "standard_metadata.ingress_port"_cs, 9,
336 mkMember("ig_intr_md"_cs, "ingress_port"_cs, portWidth));
337
338 // metadata to translate in egress
339 cvt(EGRESS, "eg_intr_md_from_parser_aux.egress_parser_err"_cs, 16,
340 mkMember("eg_intr_md_from_parser_aux"_cs, "parser_err"_cs, 16));
341 cvt(EGRESS, "eg_intr_md_from_parser_aux.clone_src"_cs, 4,
342 mkMember("meta"_cs, COMPILER_META, "clone_src"_cs, 4));
343 cvt(EGRESS, "eg_intr_md_from_parser_aux.egress_global_tstamp"_cs, 48,
344 mkMember("eg_intr_md_from_parser_aux"_cs, "global_tstamp"_cs, 48));
345 cvt(EGRESS, "eg_intr_md_for_oport.drop_ctl"_cs, 3,
346 mkMember("eg_intr_md_for_dprsr"_cs, "drop_ctl"_cs, 3));
347 cvt(EGRESS, "eg_intr_md_from_parser_aux.egress_global_ver"_cs, 32,
348 mkMember("eg_intr_md_from_parser_aux"_cs, "global_ver"_cs, 32));
349 cvt(EGRESS, "eg_intr_md.deq_timedelta"_cs, 32,
350 mkMember("eg_intr_md"_cs, "deq_timedelta"_cs, 18));
351 cvt(EGRESS, "eg_intr_md_for_mb.mirror_hash"_cs, 13,
352 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_hash"_cs, 13));
353 cvt(EGRESS, "eg_intr_md_for_mb.mirror_io_select"_cs, 1,
354 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_io_select"_cs, 1));
355 cvt(EGRESS, "eg_intr_md_for_mb.mirror_multicast_ctrl"_cs, 1,
356 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_multicast_ctrl"_cs, 1));
357 cvt(EGRESS, "eg_intr_md_for_mb.mirror_ingress_cos"_cs, 3,
358 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_ingress_cos"_cs, 3));
359 cvt(EGRESS, "eg_intr_md_for_mb.mirror_deflect_on_drop"_cs, 1,
360 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_deflect_on_drop"_cs, 1));
361 cvt(EGRESS, "eg_intr_md_for_mb.mirror_egress_port"_cs, portWidth,
362 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_egress_port"_cs, portWidth));
363 cvt(EGRESS, "meta.standard_metadata.ingress_port"_cs, 9,
364 mkMember("meta"_cs, "ig_intr_md"_cs, "ingress_port"_cs, portWidth));
365 cvt(EGRESS, "standard_metadata.ingress_port"_cs, 9,
366 mkMember("meta"_cs, "ig_intr_md"_cs, "ingress_port"_cs, portWidth));
367 }
368
369 // convert P4-14 metadata to P4-16 tna metadata
370 const IR::Node *preorder(IR::Member *member) override {
371 prune();
372 if (!member->type->is<IR::Type_Bits>()) return member;
373
374 gress_t thread;
375 if (auto *parser = findOrigCtxt<IR::P4Parser>()) {
376 thread = (parser->name == "IngressParserImpl") ? INGRESS : EGRESS;
377 } else if (auto *control = findOrigCtxt<IR::P4Control>()) {
378 if (structure->mapControlToGress.count(control->name)) {
379 thread = structure->mapControlToGress.at(control->name);
380 } else if (control->name == "EgressDeparserImpl") {
381 thread = EGRESS;
382 } else if (control->name == "IngressDeparserImpl") {
383 thread = INGRESS;
384 } else {
385 // control might be dead-code, ignore.
386 return member;
387 }
388 } else {
389 return member;
390 }
391
392 auto &nameMap = (thread == INGRESS) ? ingressMetadataNameMap : egressMetadataNameMap;
393
394 auto linearizer = new BFN::PathLinearizer();
395 member->apply(*linearizer);
396 if (!linearizer->linearPath) return member;
397 auto path = *linearizer->linearPath;
398 auto fn = path.to_cstring("."_cs, false);
399 if (nameMap.count(fn)) {
400 LOG3("Translating " << member << " to " << nameMap.at(fn));
401 return nameMap.at(fn);
402 }
403 return member;
404 }
405};
406
407class FixApplyStatement : public Transform {
408 TnaProgramStructure *structure;
409 const IR::Node *postorder(IR::MethodCallExpression *m) override;
410
411 public:
412 explicit FixApplyStatement(TnaProgramStructure *s) : structure(s) {}
413};
414
415class CollectBridgedFields : public Inspector, P4WriteContext {
416 // the gress of the control block
417 gress_t gress;
418 ordered_set<cstring> &mayWrite;
419 ordered_set<cstring> &mayRead;
420 std::map<cstring, BFN::LinearPath> &fieldInfo;
421
422 public:
423 CollectBridgedFields(gress_t g, ordered_set<cstring> &w, ordered_set<cstring> &r,
424 std::map<cstring, BFN::LinearPath> &p)
425 : gress(g), mayWrite(w), mayRead(r), fieldInfo(p) {}
426 bool preorder(const IR::Member *expr) override;
427 bool preorder(const IR::MethodCallExpression *m) override;
428};
429
430unsigned long computeHashOverFieldList(const IR::Expression *expr,
431 std::map<unsigned long, unsigned> &hm,
432 bool reserve_entry_zero = false);
433
434class CollectDigestFields : public Inspector {
435 TnaProgramStructure *structure;
436 gress_t gress;
437 DigestFieldList &digestFieldLists;
438
439 IR::Expression *flatten(const IR::ListExpression *);
440 void convertFieldList(const IR::Primitive *prim, size_t fieldListIndex,
441 std::map<unsigned long, unsigned> &indexHashes, bool);
442
443 public:
444 explicit CollectDigestFields(TnaProgramStructure *s, gress_t g, DigestFieldList &dfl)
445 : structure(s), gress(g), digestFieldLists(dfl) {}
446 bool preorder(const IR::Primitive *p) override;
447};
448
449class RenameFieldPath : public Transform {
450 public:
451 cstring prefix;
452 std::set<cstring> paths;
453 RenameFieldPath() {}
454
455 IR::Node *preorder(IR::Member *member) {
456 BUG_CHECK(prefix != "", "must provide a prefix to the RenameFieldPath pass");
457 BFN::PathLinearizer linearizer;
458 member->apply(linearizer);
459 auto path = linearizer.linearPath;
460 if (!path) return member;
461 if (auto hdr = path->components.front()->to<IR::PathExpression>()) {
462 if (paths.count(hdr->path->name)) {
463 auto meta = new IR::Member(
464 new IR::Member(new IR::PathExpression(prefix), IR::ID(hdr->path->name)),
465 member->member);
466 LOG3("adding " << prefix << " to " << member);
467 return meta;
468 }
469 } else if (auto hdr = path->components.front()->to<IR::ConcreteHeaderRef>()) {
470 if (paths.count(hdr->ref->name)) {
471 auto meta = new IR::Member(
472 new IR::Member(new IR::PathExpression(prefix), IR::ID(hdr->ref->name)),
473 member->member);
474 LOG3("adding " << prefix << " to " << member);
475 return meta;
476 }
477 }
478 return member;
479 }
480
481 IR::Node *preorder(IR::PathExpression *path) {
482 BUG_CHECK(prefix != "", "must provide a prefix to the RenameFieldPath pass");
483 if (paths.count(path->path->name)) {
484 auto meta = new IR::Member(new IR::PathExpression(prefix), IR::ID(path->path->name));
485 LOG3("adding " << prefix << " to " << path);
486 return meta;
487 }
488 return path;
489 }
490};
491
496class FixBridgedIntrinsicMetadata : public RenameFieldPath {
497 TnaProgramStructure *structure;
498
499 public:
500 explicit FixBridgedIntrinsicMetadata(TnaProgramStructure *s) : structure(s) {
501 paths = {"ig_intr_md"_cs, "ig_intr_md_for_tm"_cs};
502 prefix = "meta"_cs;
503 }
504 IR::Node *preorder(IR::P4Parser *parser) override {
505 if (parser->name == "IngressParserImpl") {
506 prune();
507 }
508 return parser;
509 }
510 IR::Node *preorder(IR::P4Control *control) override {
511 LOG5("control " << control->name);
512 if (structure->mapControlToGress.count(control->name)) {
513 if (structure->mapControlToGress.at(control->name) == INGRESS) {
514 LOG5("\tskip " << control->name);
515 prune();
516 }
517 }
518 return control;
519 }
520};
521
522// rename 'pktgen_port_down' to 'hdr.pktgen_port_down'
523class FixPktgenHeaderPath : public RenameFieldPath {
524 public:
525 FixPktgenHeaderPath() {
526 paths = {"pktgen_port_down"_cs, "pktgen_recirc"_cs, "pktgen_generic"_cs, "pktgen_timer"_cs};
527 prefix = "hdr"_cs;
528 }
529};
530
534class FixParserPriority : public Transform {
535 public:
536 FixParserPriority() {}
537 IR::Node *preorder(IR::BaseAssignmentStatement *assign) override;
538};
539
540class ParserCounterSelectCaseConverter : public Transform {
541 public:
542 bool isNegative = false;
543 bool needsCast = false;
544 int counterIdx = -1;
545
546 public:
547 ParserCounterSelectCaseConverter() {}
548
549 void cannotFit(const IR::AssignmentStatement *stmt, const char *what) {
550 error("Parser counter %1% amount cannot fit into 8-bit. %2%", what, stmt);
551 }
552
553 bool isParserCounter(const IR::Member *member) {
554 auto linearizer = new BFN::PathLinearizer();
555 member->apply(*linearizer);
556 if (!linearizer->linearPath) return false;
557 auto path = linearizer->linearPath->to_cstring();
558 return (path == "ig_prsr_ctrl.parser_counter");
559 }
560
561 std::pair<unsigned, unsigned> getAlignLoHi(const IR::Member *member) {
562 auto header = member->expr->type->to<IR::Type_Header>();
563
564 CHECK_NULL(header);
565
566 unsigned bits = 0;
567
568 for (auto field : boost::adaptors::reverse(header->fields)) {
569 auto size = field->type->width_bits();
570
571 if (field->name == member->member) {
572 if (size > 8) {
573 ::fatal_error(
574 "Parser counter load field is of width %1% bits"
575 " which is greater than what HW supports (8 bits): %2%",
576 size, member);
577 }
578
579 return {bits % 8, (bits + size - 1) % 8};
580 }
581
582 bits += size;
583 }
584
585 BUG("%1% not found in header?", member->member);
586 }
587
588 // Simplified strength reduction to simplify
589 // the expressions that is possible to implement
590 // for parser counter:
591 // prsr_ctr = prsr_ctr - N;
592 // prsr_ctr = field * M - N;
593 struct StrengthReduction : public Transform {
594 bool isZero(const IR::Expression *expr) const {
595 auto cst = expr->to<IR::Constant>();
596 if (cst == nullptr) return false;
597 return cst->value == 0;
598 }
599
600 bool isOne(const IR::Expression *expr) const {
601 auto cst = expr->to<IR::Constant>();
602 if (cst == nullptr) return false;
603 return cst->value == 1;
604 }
605
606 int isPowerOf2(const IR::Expression *expr) const {
607 auto cst = expr->to<IR::Constant>();
608 if (cst == nullptr) return -1;
609 big_int value = cst->value;
610 if (value <= 0) return -1;
611 auto bitcnt = bitcount(value);
612 if (bitcnt != 1) return -1;
613 auto log = Util::scan1(value, 0);
614 // Assumes value does not have more than 2 billion bits
615 return log;
616 }
617
618 const IR::Node *postorder(IR::Sub *expr) {
619 if (isZero(expr->right)) return expr->left;
620 if (isZero(expr->left)) return new IR::Neg(expr->srcInfo, expr->right);
621 // Replace `a - constant` with `a + (-constant)`
622 if (expr->right->is<IR::Constant>()) {
623 auto cst = expr->right->to<IR::Constant>();
624 auto neg = new IR::Constant(cst->srcInfo, cst->type, -cst->value, cst->base, true);
625 auto result = new IR::Add(expr->srcInfo, expr->left, neg);
626 return result;
627 }
628 return expr;
629 }
630
631 const IR::Node *postorder(IR::Mul *expr) {
632 if (isZero(expr->left)) return expr->left;
633 if (isZero(expr->right)) return expr->right;
634 if (isOne(expr->left)) return expr->right;
635 if (isOne(expr->right)) return expr->left;
636 auto exp = isPowerOf2(expr->left);
637 if (exp >= 0) {
638 auto amt = new IR::Constant(exp);
639 auto sh = new IR::Shl(expr->srcInfo, expr->right, amt);
640 return sh;
641 }
642 exp = isPowerOf2(expr->right);
643 if (exp >= 0) {
644 auto amt = new IR::Constant(exp);
645 auto sh = new IR::Shl(expr->srcInfo, expr->left, amt);
646 return sh;
647 }
648 return expr;
649 }
650 };
651
652 const IR::Node *preorder(IR::AssignmentStatement *assign) {
653 prune();
654 auto stmt = getOriginal<IR::AssignmentStatement>();
655 auto parserCounter = new IR::PathExpression("ig_prsr_ctrl_parser_counter"_cs);
656 auto right = stmt->right;
657 auto left = stmt->left;
658
659 // Remove slice around the destination of the assignment.
660 while (auto slice = left->to<IR::Slice>()) left = slice->e0;
661 while (auto cast = left->to<IR::Cast>()) left = cast->expr;
662 auto linearizer = new BFN::PathLinearizer();
663 left->apply(*linearizer);
664 auto path = *linearizer->linearPath;
665 if (path.to_cstring() != "ig_prsr_ctrl.parser_counter") return assign;
666
667 // Remove any casts around the source of the assignment.
668 if (auto cast = right->to<IR::Cast>()) {
669 if (cast->destType->is<IR::Type_Bits>()) {
670 right = cast->expr;
671 }
672 }
673
674 // Strength reduce IR::Sub to IR::Add
675 if (right->is<IR::Sub>()) right = right->apply(StrengthReduction());
676
677 IR::MethodCallStatement *methodCall = nullptr;
678
679 if (right->to<IR::Constant>() || right->to<IR::Member>()) {
680 // Load operation (immediate or field)
681 methodCall = new IR::MethodCallStatement(
682 stmt->srcInfo, new IR::MethodCallExpression(
683 stmt->srcInfo, new IR::Member(parserCounter, "set"),
684 new IR::Vector<IR::Type>({stmt->right->type}),
685 new IR::Vector<IR::Argument>({new IR::Argument(stmt->right)})));
686 } else if (auto add = right->to<IR::Add>()) {
687 auto member = add->left->to<IR::Member>();
688
689 auto counterWidth = IR::Type::Bits::get(8);
690 auto maskWidth = IR::Type::Bits::get(Device::currentDevice() == Device::TOFINO ? 3 : 8);
691 // How does user specify the max in P4-14?
692 auto max = new IR::Constant(counterWidth, 255);
693
694 // Add operaton
695 if (member && isParserCounter(member)) {
696 methodCall = new IR::MethodCallStatement(stmt->srcInfo,
697 new IR::Member(parserCounter, "increment"),
698 {new IR::Argument(add->right)});
699 } else {
700 if (auto *amt = add->right->to<IR::Constant>()) {
701 // Load operation (expression of field)
702 if (member) {
703 auto shr = new IR::Constant(counterWidth, 0);
704 auto mask = new IR::Constant(
705 maskWidth, Device::currentDevice() == Device::TOFINO ? 7 : 255);
706 auto add = new IR::Constant(counterWidth, amt->asUnsigned());
707
708 methodCall = new IR::MethodCallStatement(
709 stmt->srcInfo, new IR::MethodCallExpression(
710 stmt->srcInfo, new IR::Member(parserCounter, "set"),
711 new IR::Vector<IR::Type>({member->type}),
712 new IR::Vector<IR::Argument>(
713 {new IR::Argument(member), new IR::Argument(max),
714 new IR::Argument(shr), new IR::Argument(mask),
715 new IR::Argument(add)})));
716 } else if (auto *shl = add->left->to<IR::Shl>()) {
717 if (auto *rot = shl->right->to<IR::Constant>()) {
718 auto left = shl->left;
719 // Remove any casts around the parameter;
720 if (auto cast = left->to<IR::Cast>()) {
721 if (cast->destType->is<IR::Type_Bits>()) {
722 left = cast->expr;
723 }
724 }
725
726 if (auto *field = left->to<IR::Member>()) {
727 if (!rot->fitsUint() || rot->asUnsigned() >> 8)
728 cannotFit(stmt, "multiply");
729
730 if (!amt->fitsUint() || amt->asUnsigned() >> 8)
731 cannotFit(stmt, "immediate");
732
733 unsigned lo = 0, hi = 7;
734 std::tie(lo, hi) = getAlignLoHi(field);
735
736 auto shr =
737 new IR::Constant(counterWidth, 8 - rot->asUnsigned() - lo);
738
739 unsigned rot_hi = std::min(hi + rot->asUnsigned(), 7u);
740 auto mask = new IR::Constant(
741 maskWidth, Device::currentDevice() == Device::TOFINO
742 ? rot_hi
743 : (1 << (rot_hi + 1)) - 1);
744 auto add = new IR::Constant(counterWidth, amt->asUnsigned());
745
746 methodCall = new IR::MethodCallStatement(
747 stmt->srcInfo,
748 new IR::MethodCallExpression(
749 stmt->srcInfo, new IR::Member(parserCounter, "set"),
750 new IR::Vector<IR::Type>({field->type}),
751 new IR::Vector<IR::Argument>(
752 {new IR::Argument(field), new IR::Argument(max),
753 new IR::Argument(shr), new IR::Argument(mask),
754 new IR::Argument(add)})));
755 }
756 }
757 }
758 }
759 }
760 }
761 if (!methodCall) error("Unsupported syntax for parser counter: %1%", stmt);
762 return methodCall;
763 }
764
765 const IR::Node *preorder(IR::SelectExpression *node) {
766 bool isPrsrCtr = false;
767 for (unsigned i = 0; i < node->select->components.size(); i++) {
768 auto select = node->select->components[i];
769 if (auto member = select->to<IR::Member>()) {
770 if (isParserCounter(member)) {
771 if (counterIdx >= 0) error("Multiple selects on parser counter in %1%", node);
772 counterIdx = i;
773 isPrsrCtr = true;
774 }
775 }
776 }
777 if (!isPrsrCtr) prune();
778 return node;
779 }
780
781 const IR::Node *postorder(IR::SelectExpression *node) {
782 counterIdx = -1;
783 return node;
784 }
785
786 struct RewriteSelectCase : Transform {
787 bool isNegative = false;
788 bool needsCast = false;
789
790 const IR::Expression *convert(const IR::Constant *c, bool toBool = true,
791 bool check = true) {
792 auto val = c->asUnsigned();
793 if (check) {
794 if (val & 0x80) {
795 isNegative = true;
796 } else if (val) {
797 error(
798 "Parser counter only supports test "
799 "of value being zero or negative.");
800 }
801 }
802 if (toBool)
803 return new IR::BoolLiteral(~val);
804 else
805 return new IR::Constant(IR::Type::Bits::get(1), ~val & 1);
806 }
807
808 const IR::Node *preorder(IR::Mask *mask) override {
809 prune();
810 mask->left = convert(mask->left->to<IR::Constant>(), false);
811 mask->right = convert(mask->right->to<IR::Constant>(), false, false);
812 needsCast = true;
813 return mask;
814 }
815
816 const IR::Node *preorder(IR::Constant *c) override { return convert(c); }
817 };
818
819 const IR::Node *preorder(IR::SelectCase *node) {
820 RewriteSelectCase rewrite;
821
822 if (auto list = node->keyset->to<IR::ListExpression>()) {
823 auto newList = list->clone();
824 for (unsigned i = 0; i < newList->components.size(); i++) {
825 if (int(i) == counterIdx) {
826 newList->components[i] = newList->components[i]->apply(rewrite);
827 break;
828 }
829 }
830 node->keyset = newList;
831 } else {
832 node->keyset = node->keyset->apply(rewrite);
833 }
834 isNegative |= rewrite.isNegative;
835 needsCast |= rewrite.needsCast;
836 return node;
837 }
838};
839
840struct ParserCounterSelectExprConverter : Transform {
841 const ParserCounterSelectCaseConverter &caseConverter;
842 std::set<cstring> need_parser_counter;
843
844 explicit ParserCounterSelectExprConverter(const ParserCounterSelectCaseConverter &cc)
845 : caseConverter(cc) {}
846
847 bool isParserCounter(const IR::Member *member) {
848 auto linearizer = new BFN::PathLinearizer();
849 member->apply(*linearizer);
850 if (!linearizer->linearPath) return false;
851 auto path = linearizer->linearPath->to_cstring();
852 return (path == "ig_prsr_ctrl.parser_counter");
853 }
854
855 const IR::Node *postorder(IR::Member *node) {
856 if (isParserCounter(node)) {
857 auto prsr = findContext<IR::P4Parser>();
858 need_parser_counter.insert(prsr->name);
859 auto parserCounter = new IR::PathExpression("ig_prsr_ctrl_parser_counter");
860 auto testExpr =
861 new IR::Member(parserCounter, caseConverter.isNegative ? "is_negative" : "is_zero");
862 const IR::Expression *methodCall = new IR::MethodCallExpression(
863 node->srcInfo, testExpr, new IR::Vector<IR::Argument>());
864 if (caseConverter.needsCast)
865 methodCall = new IR::Cast(IR::Type::Bits::get(1), methodCall);
866 return methodCall;
867 }
868 return node;
869 }
870
871 const IR::Node *postorder(IR::P4Parser *parser) {
872 if (need_parser_counter.count(parser->name) != 0) {
873 auto type = new IR::Type_Name("ParserCounter");
874 auto decl = new IR::Declaration_Instance("ig_prsr_ctrl_parser_counter", type,
876 auto locals = parser->parserLocals.clone();
877 locals->push_back(decl);
878 return new IR::P4Parser(parser->srcInfo, parser->name, parser->type,
879 parser->constructorParams, *locals, parser->states);
880 }
881 return parser;
882 }
883};
884
885class FixParserCounter : public PassManager {
886 public:
887 FixParserCounter() {
888 auto convertSelectCase = new ParserCounterSelectCaseConverter;
889 auto convertSelectExpr = new ParserCounterSelectExprConverter(*convertSelectCase);
890 addPasses({convertSelectCase, convertSelectExpr});
891 }
892};
893
894class FixIdleTimeout : public Transform {
895 public:
896 FixIdleTimeout() {}
897 const IR::Node *preorder(IR::Property *prop) override {
898 if (prop->name == "support_timeout") {
899 return new IR::Property("idle_timeout", prop->annotations, prop->value,
900 prop->isConstant);
901 }
902 return prop;
903 }
904};
905
906class FixDuplicatePathExpression : public Transform {
907 public:
908 FixDuplicatePathExpression() {}
909 const IR::Node *preorder(IR::P4Program *program) override {
910 prune();
912 return program->apply(cloner);
913 }
914};
915
916// undo the changes to program for checksum translation
917class RemoveBuiltins : public Modifier {
918 public:
919 RemoveBuiltins() {}
920 void postorder(IR::P4Parser *parser) override {
921 parser->states.removeByName(IR::ParserState::accept);
922 parser->states.removeByName(IR::ParserState::reject);
923 }
924};
925
926class ModifyParserForChecksum : public Modifier {
927 P4::ReferenceMap *refMap;
928 P4::TypeMap *typeMap;
929 TnaProgramStructure *structure;
930 P4ParserGraphs *graph;
932 std::map<cstring, std::set<cstring>> need_checksum;
933 std::map<cstring, ordered_set<const IR::Member *>> residualChecksumPayloadFields;
934
935 public:
936 template <typename Func>
937 void forAllExtracts(const IR::ParserState *state, Func function) {
939 for (auto expr : state->components) {
940 if (!expr->is<IR::MethodCallStatement>()) continue;
941 auto mce = expr->to<IR::MethodCallStatement>()->methodCall;
942 auto inst = P4::MethodInstance::resolve(mce, refMap, typeMap, nullptr /* ctxt */,
943 true /* incomplete */);
944 if (inst == nullptr) continue;
945 if (auto em = inst->to<P4::ExternMethod>()) {
946 if (em->actualExternType->name == "packet_in" && em->method->name == "extract") {
947 auto extracted = inst->substitution.lookupByName("hdr"_cs)->apply(cloner);
948 if (extracted == nullptr ||
949 !extracted->to<IR::Argument>()->expression->is<IR::Member>())
950 continue;
951 function(extracted->to<IR::Argument>()->expression->to<IR::Member>());
952 }
953 }
954 }
955 }
956
957 static bool equiv(const IR::Expression *a, const IR::Expression *b) {
958 if (a == b) return true;
959 if (typeid(*a) != typeid(*b)) return false;
960 if (auto ma = a->to<IR::Member>()) {
961 auto mb = b->to<IR::Member>();
962 return ma->member == mb->member && equiv(ma->expr, mb->expr);
963 }
964 if (auto pa = a->to<IR::PathExpression>()) {
965 auto pb = b->to<IR::PathExpression>();
966 return pa->path->name == pb->path->name;
967 }
968 return false;
969 }
970
971 static bool belongsTo(const IR::Member *a, const IR::Member *b) {
972 if (!a || !b) return false;
973
974 // case 1: a is field, b is field
975 if (equiv(a, b)) return true;
976
977 // case 2: a is field, b is header
978 if (a->type->is<IR::Type::Bits>()) {
979 if (equiv(a->expr, b)) return true;
980 }
981
982 return false;
983 }
984
985 ModifyParserForChecksum(P4::ReferenceMap *refMap, P4::TypeMap *typeMap,
986 TnaProgramStructure *structure, P4ParserGraphs *graph)
987 : refMap(refMap), typeMap(typeMap), structure(structure), graph(graph) {}
988
989 // this function must be invoked from IR::ParserState visitor.
990 ordered_set<const IR::Member *> collectResidualChecksumPayloadFields() {
991 auto orig = getOriginal<IR::ParserState>();
992 BUG_CHECK(orig != nullptr, "function must be invoked within ParserState visitor");
994 auto descendants = graph->get_all_descendants(orig);
995 for (auto d : descendants) {
996 forAllExtracts(d, [&](const IR::Member *member) { rv.insert(member); });
997 }
998 return rv;
999 }
1000
1001 // iterate through residual checksum calculation,
1002 // create subtract call for the fields that belong to the provided member;
1003 void createSubtractCallsForResidualChecksum(IR::IndexedVector<IR::StatOrDecl> *statements,
1004 std::vector<ChecksumInfo> &checksum_fields,
1005 const IR::Member *member,
1007 gress_t gress) {
1008 if (checksum_fields.empty()) return;
1009 P4::CloneExpressions cloner;
1010 const IR::Expression *constant = nullptr;
1011 for (auto csum : checksum_fields) {
1012 if (!csum.parserUpdateLocations.count(gress)) continue;
1013 std::vector<const IR::Expression *> exprList;
1014 auto path = BFN::PathLinearizer::convert(csum.destField);
1015 if (!checksum.count(path))
1016 checksum.emplace(path, structure->makeUniqueName("checksum"_cs));
1017 auto fieldList = csum.fieldList;
1018 if (!fieldList->is<IR::ListExpression>()) continue;
1019 for (auto f : fieldList->to<IR::ListExpression>()->components) {
1020 if (f->is<IR::Constant>()) {
1021 constant = f;
1022 } else if (belongsTo(f->to<IR::Member>(), member)) {
1023 if (constant) {
1024 exprList.emplace_back(constant);
1025 constant = nullptr;
1026 // If immediate next field after the constant is extracted in this field
1027 // then the constant belongs to subtract field list of this state
1028 }
1029 exprList.emplace_back(f->apply(cloner));
1030
1031 } else {
1032 constant = nullptr;
1033 }
1034 }
1035 for (auto e : exprList) {
1036 auto mce = new IR::MethodCallExpression(
1037 new IR::Member(new IR::PathExpression(checksum.at(path)), "subtract"),
1038 new IR::Vector<IR::Type>({e->type}),
1039 new IR::Vector<IR::Argument>({new IR::Argument(e)}));
1040 auto subtractCall = new IR::MethodCallStatement(mce);
1041 statements->push_back(subtractCall);
1042 }
1043
1044 if (belongsTo(csum.destField->to<IR::Member>(), member)) {
1045 auto destField = csum.destField->apply(cloner);
1046 auto mce = new IR::MethodCallExpression(
1047 new IR::Member(new IR::PathExpression(checksum.at(path)), "subtract"),
1048 new IR::Vector<IR::Type>({destField->type}),
1049 new IR::Vector<IR::Argument>({new IR::Argument(destField)}));
1050 auto subtractCall = new IR::MethodCallStatement(mce);
1051 statements->push_back(subtractCall);
1052
1053 if (csum.with_payload) {
1054 BUG_CHECK(csum.residulChecksumName != std::nullopt,
1055 "residual checksum field name cannot be empty");
1056 auto *rmember = new IR::Member(
1057 IR::Type::Bits::get(16),
1058 new IR::Member(new IR::PathExpression("meta"), IR::ID("bridged_header")),
1059 IR::ID(*csum.residulChecksumName));
1060 auto *mce = new IR::MethodCallExpression(
1061 new IR::Member(new IR::PathExpression(checksum.at(path)),
1062 "subtract_all_and_deposit"),
1063 new IR::Vector<IR::Type>({rmember->type}),
1064 new IR::Vector<IR::Argument>({new IR::Argument(rmember)}));
1065 auto *deposit = new IR::MethodCallStatement(mce);
1066 std::cout << member->member << std::endl;
1067 structure->checksumDepositToHeader[gress][member->member] = deposit;
1068 }
1069 }
1070 }
1071 }
1072
1073 // iterate through verify checksum.
1074 // create add call for the fields that belongs to the provided member;
1075 void createAddCallsForVerifyChecksum(IR::IndexedVector<IR::StatOrDecl> *statements,
1076 std::vector<ChecksumInfo> &checksum_fields,
1077 const IR::Member *member,
1078 ordered_map<cstring, cstring> &checksum, gress_t gress,
1079 cstring state) {
1080 P4::CloneExpressions cloner;
1081 for (auto csum : checksum_fields) {
1082 if (!csum.parserUpdateLocations.count(gress)) continue;
1083 auto path = BFN::PathLinearizer::convert(csum.destField);
1084 if (!checksum.count(path))
1085 checksum.emplace(path, structure->makeUniqueName("checksum"_cs));
1086 auto csumInst = checksum.at(path);
1087 auto fieldList = csum.fieldList;
1088 if (!fieldList->is<IR::ListExpression>()) continue;
1089 for (auto f : fieldList->to<IR::ListExpression>()->components) {
1090 if (belongsTo(f->to<IR::Member>(), member)) {
1091 auto mce = new IR::MethodCallExpression(
1092 new IR::Member(new IR::PathExpression(checksum.at(path)), "add"),
1093 new IR::Vector<IR::Type>({f->type}),
1094 new IR::Vector<IR::Argument>({new IR::Argument(f->apply(cloner))}));
1095
1096 auto addCall = new IR::MethodCallStatement(mce);
1097 statements->push_back(addCall);
1098 structure->ingressVerifyChecksumToStates[csumInst].insert(state);
1099 }
1100 }
1101
1102 auto destField = csum.destField->apply(cloner);
1103 if (belongsTo(destField->to<IR::Member>(), member)) {
1104 auto mce = new IR::MethodCallExpression(
1105 new IR::Member(new IR::PathExpression(checksum.at(path)), "add"),
1106 new IR::Vector<IR::Type>({destField->type}),
1107 new IR::Vector<IR::Argument>({new IR::Argument(destField->apply(cloner))}));
1108 auto addCall = new IR::MethodCallStatement(mce);
1109 statements->push_back(addCall);
1110 structure->ingressVerifyChecksumToStates[csumInst].insert(state);
1111 }
1112 }
1113 }
1114
1115 void postorder(IR::ParserState *state) override {
1116 // IR::IndexedVector<IR::StatOrDecl>* subtractCalls = nullptr;
1117 // IR::IndexedVector<IR::StatOrDecl>* addCalls = nullptr;
1118 auto subtractCalls = new IR::IndexedVector<IR::StatOrDecl>();
1119 auto addCalls = new IR::IndexedVector<IR::StatOrDecl>();
1120 auto parser = findContext<IR::P4Parser>();
1121 auto gress = parser->name == "IngressParserImpl" ? INGRESS : EGRESS;
1122 forAllExtracts(state, [&](const IR::Member *member) {
1123 createAddCallsForVerifyChecksum(addCalls, structure->verifyChecksums, member, checksum,
1124 gress, state->name);
1125 });
1126 forAllExtracts(state, [&](const IR::Member *member) {
1127 auto rc = structure->residualChecksums;
1128 createSubtractCallsForResidualChecksum(subtractCalls, rc, member, checksum, gress);
1129 });
1130 if (subtractCalls->size() != 0) {
1131 state->components.append(*subtractCalls);
1132 }
1133 if (addCalls->size() != 0) state->components.append(*addCalls);
1134
1135 if (subtractCalls->size() != 0 || addCalls->size() != 0) {
1136 auto parser = findContext<IR::P4Parser>();
1137 for (auto it = checksum.begin(); it != checksum.end(); it++)
1138 need_checksum[parser->name].insert(it->second);
1139 }
1140 }
1141
1142 bool preorder(IR::P4Parser *) override {
1143 checksum.clear();
1144 return true;
1145 }
1146
1147 void postorder(IR::P4Parser *parser) override {
1148 if (need_checksum.count(parser->name)) {
1149 auto csums = need_checksum.at(parser->name);
1150 for (auto csum : csums) {
1151 auto inst = new IR::Type_Name("Checksum");
1152 auto args = new IR::Vector<IR::Argument>();
1153 auto decl = new IR::Declaration_Instance(csum, inst, args);
1154 parser->parserLocals.push_back(decl);
1155 }
1156 }
1157 }
1158
1159 void postorder(IR::MethodCallExpression *mce) override {
1160 auto inst = P4::MethodInstance::resolve(mce, refMap, typeMap, nullptr /* ctxt */,
1161 true /* incomplete */);
1162 if (!inst->is<P4::ExternMethod>()) return;
1163 auto em = inst->to<P4::ExternMethod>();
1164 if (em->actualExternType->name != "Checksum" || em->method->name != "update") return;
1165 auto assign = findOrigCtxt<IR::BaseAssignmentStatement>();
1166 if (assign == nullptr) return;
1167 auto destField = assign->left;
1168 // auto parser = findContext<IR::P4Control>();
1169 // FIXME: should check parser for null!!
1170 // auto gress = parser->name == "IngressDeparserImpl" ? INGRESS : EGRESS;
1171 if (structure->residualChecksums.empty()) return;
1172 auto path = BFN::PathLinearizer::convert(destField);
1173 if (!residualChecksumPayloadFields.count(path)) return;
1174 auto data = inst->substitution.lookupByName("data"_cs);
1175 P4::CloneExpressions cloner;
1176 if (auto expr = data->expression->to<IR::ListExpression>()) {
1177 // add checksum fields
1178 IR::ListExpression *fl = expr->clone();
1179 // add payload
1180 auto payload = residualChecksumPayloadFields.at(path);
1181 for (auto p : payload) fl->push_back(p->apply(cloner));
1182 auto args = new IR::Vector<IR::Argument>();
1183 args->push_back(new IR::Argument(fl));
1184 mce->arguments = args;
1185 }
1186 }
1187};
1188
1189using EndStates = std::map<cstring, ordered_map<cstring, ordered_set<cstring>>>;
1190
1191static IR::AssignmentStatement *createChecksumError(cstring decl, gress_t gress) {
1192 auto methodCall = new IR::Member(new IR::PathExpression(decl), "verify");
1193 auto verifyCall = new IR::MethodCallExpression(methodCall, {});
1194 auto rhs = new IR::Cast(IR::Type::Bits::get(1), verifyCall);
1195
1196 cstring intr_md;
1197
1198 if (gress == INGRESS)
1199 intr_md = "ig_intr_md_from_prsr"_cs;
1200 else if (gress == EGRESS)
1201 intr_md = "eg_intr_md_from_prsr"_cs;
1202 else
1203 BUG("Unhandled gress: %1%.", gress);
1204
1205 auto parser_err = new IR::Member(new IR::PathExpression(intr_md), "parser_err");
1206
1207 auto lhs = new IR::Slice(parser_err, 12, 12);
1208 return new IR::AssignmentStatement(lhs, rhs);
1209}
1210
1211class InsertChecksumDeposit : public Transform {
1212 public:
1213 TnaProgramStructure *structure;
1214 explicit InsertChecksumDeposit(TnaProgramStructure *structure) : structure(structure) {}
1215 const IR::Node *preorder(IR::ParserState *state) override {
1216 auto parser = findContext<IR::P4Parser>();
1217 auto gress = parser->name == "IngressParserImpl" ? INGRESS : EGRESS;
1218 if (!structure->checksumDepositToHeader.count(gress)) return state;
1219 auto components = new IR::IndexedVector<IR::StatOrDecl>();
1220 auto &checksumDeposit = structure->checksumDepositToHeader.at(gress);
1221 for (auto component : state->components) {
1222 components->push_back(component);
1223 if (auto methodCall = component->to<IR::MethodCallStatement>()) {
1224 if (auto call = methodCall->methodCall->to<IR::MethodCallExpression>()) {
1225 if (auto method = call->method->to<IR::Member>()) {
1226 if (method->member == "extract") {
1227 for (auto arg : *call->arguments) {
1228 auto member = arg->expression->to<IR::Member>();
1229 if (!member) continue;
1230 if (checksumDeposit.count(member->member)) {
1231 components->push_back(checksumDeposit.at(member->member));
1232 }
1233 }
1234 }
1235 }
1236 }
1237 }
1238 }
1239 state->components = *components;
1240 return state;
1241 }
1242};
1243
1244class InsertChecksumError : public PassManager {
1245 public:
1246 EndStates endStates;
1247 struct ComputeEndStates : public Inspector {
1248 TnaProgramStructure *structure;
1249 const P4ParserGraphs *graph;
1250 EndStates &endStates;
1251 explicit ComputeEndStates(TnaProgramStructure *structure, const P4ParserGraphs *graph,
1252 EndStates &endStates)
1253 : structure(structure), graph(graph), endStates(endStates) {}
1254
1255 void printStates(const ordered_set<cstring> &states) {
1256 for (auto s : states) std::cout << " " << s << std::endl;
1257 }
1258
1259 ordered_set<cstring> computeChecksumEndStates(const ordered_set<cstring> &calcStates) {
1260 if (LOGGING(3)) {
1261 std::cout << "calc states are:" << std::endl;
1262 printStates(calcStates);
1263 }
1264
1265 ordered_set<cstring> endStates;
1266
1267 // A calculation state is a verification end state if no other state of the
1268 // same calculation is its descendant. Otherwise, include all of the state's
1269 // children states that are not a calculation state.
1270
1271 for (auto a : calcStates) {
1272 bool isEndState = true;
1273 for (auto b : calcStates) {
1274 if (graph->is_ancestor(a, b)) {
1275 isEndState = false;
1276 break;
1277 }
1278 }
1279 if (isEndState) {
1280 endStates.insert(a);
1281 } else {
1282 if (graph->succs.count(a)) {
1283 for (auto succ : graph->succs.at(a)) {
1284 if (calcStates.count(succ)) continue;
1285
1286 for (auto s : calcStates) {
1287 if (!graph->is_ancestor(succ, s)) {
1288 endStates.insert(succ);
1289 }
1290 }
1291 }
1292 }
1293 }
1294 }
1295
1296 BUG_CHECK(!endStates.empty(), "Unable to find verification end state?");
1297
1298 if (LOGGING(3)) {
1299 std::cout << "end states are:" << std::endl;
1300 printStates(endStates);
1301 }
1302
1303 return endStates;
1304 }
1305
1306 bool preorder(const IR::P4Parser *parser) override {
1307 if (parser->name != "IngressParserImpl") return false;
1308
1309 // compute checksum end states
1310 for (auto kv : structure->ingressVerifyChecksumToStates)
1311 endStates[parser->name][kv.first] = computeChecksumEndStates(kv.second);
1312
1313 return false;
1314 }
1315 };
1316
1317 // TODO we probably don't want to insert statement into the "accept" state
1318 // since this is a special state. Add a dummy state before "accept" if it is
1319 // a checksum verification end state.
1320 struct InsertBeforeAccept : public Transform {
1321 const IR::Node *preorder(IR::P4Parser *parser) override {
1322 for (auto &kv : endStates[parser->name]) {
1323 if (kv.second.count("accept"_cs)) {
1324 if (!dummy) {
1325 dummy =
1326 BFN::createGeneratedParserState("before_accept"_cs, {}, "accept"_cs);
1327 parser->states.push_back(dummy);
1328 }
1329 kv.second.erase("accept"_cs);
1330 kv.second.insert("__before_accept"_cs);
1331 LOG3("add dummy state before \"accept\"");
1332 }
1333 }
1334 return parser;
1335 }
1336
1337 const IR::Node *postorder(IR::PathExpression *path) override {
1338 auto parser = findContext<IR::P4Parser>();
1339 auto state = findContext<IR::ParserState>();
1340 auto select = findContext<IR::SelectCase>();
1341
1342 if (parser && state && select) {
1343 bool isCalcState = false;
1344
1345 for (auto kv : structure->ingressVerifyChecksumToStates) {
1346 for (auto s : kv.second) {
1347 if (s == state->name) {
1348 isCalcState = true;
1349 break;
1350 }
1351 }
1352 }
1353
1354 if (!isCalcState) return path;
1355
1356 for (auto &kv : endStates[parser->name]) {
1357 if (path->path->name == "accept" && kv.second.count("__before_accept"_cs)) {
1358 path = new IR::PathExpression("__before_accept");
1359 LOG3("modify transition to \"before_accept\"");
1360 }
1361 }
1362 }
1363
1364 return path;
1365 }
1366
1367 const IR::ParserState *dummy = nullptr;
1368 TnaProgramStructure *structure;
1369 EndStates &endStates;
1370
1371 explicit InsertBeforeAccept(TnaProgramStructure *structure, EndStates &endStates)
1372 : structure(structure), endStates(endStates) {
1373 CHECK_NULL(structure);
1374 }
1375 };
1376
1377 struct InsertEndStates : public Transform {
1378 const IR::Node *preorder(IR::ParserState *state) override {
1379 auto parser = findContext<IR::P4Parser>();
1380
1381 if (state->name == "reject") return state;
1382
1383 for (auto &kv : endStates[parser->name]) {
1384 auto decl = kv.first;
1385 for (auto endState : kv.second) {
1386 if (endState == state->name) {
1387 auto thread = (parser->name == "IngressParserImpl") ? INGRESS : EGRESS;
1388 auto *checksumError = createChecksumError(decl, thread);
1389 state->components.push_back(checksumError);
1390
1391 LOG3("verify " << toString(parser->name) << " " << decl << " in state "
1392 << endState);
1393 }
1394 }
1395 }
1396 return state;
1397 }
1398 EndStates &endStates;
1399 explicit InsertEndStates(EndStates &endStates) : endStates(endStates) {}
1400 };
1401
1402 explicit InsertChecksumError(TnaProgramStructure *structure, const P4ParserGraphs *graph) {
1403 addPasses({new ComputeEndStates(structure, graph, endStates),
1404 new InsertBeforeAccept(structure, endStates), new InsertEndStates(endStates)});
1405 }
1406};
1407
1408class FixChecksum : public PassManager {
1409 P4::ReferenceMap refMap;
1410 P4::TypeMap typeMap;
1411
1412 public:
1413 explicit FixChecksum(TnaProgramStructure *structure);
1414};
1415
1420class FixEgressParserDuplicateReference : public Transform {
1421 P4::CloneExpressions cloner;
1422
1423 public:
1424 FixEgressParserDuplicateReference() {}
1425 const IR::Node *postorder(IR::ParserState *state) { return state->apply(cloner); }
1426};
1427
1428// Misc fixup passes to ensure a valid tna program, specifically, they deal with
1429// the following difference between a P4-14 program and a P4-16 tna program.
1430// - tna egress cannot refer to ingress intrinsic metadata
1431// - no support for standard metadata in tna.
1432class PostTranslationFix : public PassManager {
1433 public:
1434 explicit PostTranslationFix(ProgramStructure *s) {
1435 auto tnaStruct = dynamic_cast<TnaProgramStructure *>(s);
1436 if (!tnaStruct) error("Cannot convert to TnaProgramStructure");
1437 addPasses({
1438 new FixExtracts(tnaStruct),
1439 new FixBridgedIntrinsicMetadata(tnaStruct),
1440 new FixIdleTimeout(),
1441 new ConvertMetadata(tnaStruct),
1444 new FixParserPriority(), // FIXME
1445 new FixParserCounter(),
1447 new FixChecksum(tnaStruct),
1448 });
1449 }
1450};
1451
1452IR::BlockStatement *generate_hash_block_statement(P4V1::ProgramStructure *structure,
1453 const IR::Primitive *prim, const cstring temp,
1454 ExpressionConverter &conv, unsigned num_ops);
1455
1456void add_custom_enum_if_crc_poly(const IR::Expression *expr, IR::Vector<IR::Argument> &declArgs);
1457
1459 const IR::Primitive *prim, const cstring temp,
1460 ExpressionConverter &conv, unsigned num_ops,
1461 const IR::Expression *dest);
1462
1464 const IR::Primitive *prim,
1465 const cstring temp,
1466 ExpressionConverter &conv,
1467 unsigned num_ops);
1468
1469// used by P4-14 & v1model translation
1470bool use_v1model();
1471
1473// convert a P4-14 program to an equivalent P4-16 TNA program.
1474class TnaConverter : public PassManager {
1475 ProgramStructure *structure;
1476
1477 public:
1478 TnaConverter();
1479 void loadModel() { structure->loadModel(); }
1480 Visitor::profile_t init_apply(const IR::Node *node) override;
1481};
1482
1483} // namespace P4::P4V1
1484
1485#endif /* BACKENDS_TOFINO_BF_P4C_ARCH_FROMV1_0_PROGRAMSTRUCTURE_H_ */
Definition cloner.h:17
Definition methodInstance.h:159
Definition indexed_vector.h:31
Definition node.h:44
Definition ir/vector.h:50
Definition visitor.h:409
static MethodInstance * resolve(const IR::MethodCallExpression *mce, const DeclarationLookup *refMap, TypeMap *typeMap, bool useExpressionType=false, const Visitor::Context *ctxt=nullptr, bool incomplete=false)
Definition methodInstance.cpp:16
Definition visitor.h:376
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:97
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:278
Definition converters.h:25
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:496
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1408
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:906
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1420
Definition converters.h:500
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:894
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:79
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:885
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:534
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:540
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 backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:53
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:110
Definition visitor.h:802
Class used to encode maps from paths to declarations.
Definition referenceMap.h:58
Definition strengthReduction.h:139
Definition visitor.h:433
Definition typeMap.h:32
Definition source_file.h:123
Definition visitor.h:69
Definition cstring.h:76
Definition ordered_map.h:23
Definition ordered_set.h:23
Definition assoc.h:300
Extends p4c's parser graph with various algorithms.
Definition parser_graph.h:32
const IR::ParserState * createGeneratedParserState(cstring name, IR::IndexedVector< IR::StatOrDecl > &&statements, const IR::Expression *selectExpression)
Definition intrinsic_metadata.cpp:82
Definition converters.cpp:17
IR::BlockStatement * generate_arch_neutral_hash_block_statement(P4V1::ProgramStructure *structure, const IR::Primitive *prim, const cstring temp, ExpressionConverter &conv, unsigned num_ops)
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.cpp:299
IR::BlockStatement * generate_tna_hash_block_statement(P4V1::TnaProgramStructure *structure, const IR::Primitive *prim, const cstring temp, ExpressionConverter &conv, unsigned num_ops, const IR::Expression *dest=nullptr)
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.cpp:358
void add_custom_enum_if_crc_poly(const IR::Expression *expr, IR::Vector< IR::Argument > *declArgs)
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.cpp:344
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition lib/error.h:48
void info(const int kind, const char *format, const T *node, Args &&...args)
Report info messages of type kind. Requires that the node argument have source info.
Definition lib/error.h:157
Definition add_metadata_parser_states.h:32
Definition path_linearizer.h:53
Definition path_linearizer.h:75
Definition id.h:19
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1247
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1320
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1377
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:786
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:593
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:840
T * to() noexcept
Definition rtti.h:226