P4C
The P4 Compiler
Loading...
Searching...
No Matches
backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h
1
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 "bf-p4c/arch/fromv1.0/add_metadata_parser_states.h"
25#include "bf-p4c/arch/fromv1.0/checksum.h"
26#include "bf-p4c/arch/program_structure.h"
27#include "bf-p4c/bf-p4c-options.h"
28#include "bf-p4c/common/utils.h"
29#include "bf-p4c/lib/assoc.h"
30#include "bf-p4c/midend/parser_graph.h"
31#include "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 **
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
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
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_copy_to_cpu_ctrl"_cs, 1,
334 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_copy_to_cpu_ctrl"_cs, 1));
335 cvt(INGRESS, "ig_intr_md_for_mb.mirror_copy_to_cpu_ctrl"_cs, 1,
336 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_copy_to_cpu_ctrl"_cs, 1));
337 cvt(INGRESS, "ig_intr_md_for_mb.mirror_egress_port"_cs, portWidth,
338 mkMember("ig_intr_md_for_dprsr"_cs, "mirror_egress_port"_cs, portWidth));
339 cvt(INGRESS, "standard_metadata.ingress_port"_cs, 9,
340 mkMember("ig_intr_md"_cs, "ingress_port"_cs, portWidth));
341
342 // metadata to translate in egress
343 cvt(EGRESS, "eg_intr_md_from_parser_aux.egress_parser_err"_cs, 16,
344 mkMember("eg_intr_md_from_parser_aux"_cs, "parser_err"_cs, 16));
345 cvt(EGRESS, "eg_intr_md_from_parser_aux.clone_src"_cs, 4,
346 mkMember("meta"_cs, COMPILER_META, "clone_src"_cs, 4));
347 cvt(EGRESS, "eg_intr_md_from_parser_aux.egress_global_tstamp"_cs, 48,
348 mkMember("eg_intr_md_from_parser_aux"_cs, "global_tstamp"_cs, 48));
349 cvt(EGRESS, "eg_intr_md_for_oport.drop_ctl"_cs, 3,
350 mkMember("eg_intr_md_for_dprsr"_cs, "drop_ctl"_cs, 3));
351 cvt(EGRESS, "eg_intr_md_from_parser_aux.egress_global_ver"_cs, 32,
352 mkMember("eg_intr_md_from_parser_aux"_cs, "global_ver"_cs, 32));
353 cvt(EGRESS, "eg_intr_md.deq_timedelta"_cs, 32,
354 mkMember("eg_intr_md"_cs, "deq_timedelta"_cs, 18));
355 cvt(EGRESS, "eg_intr_md_for_mb.mirror_hash"_cs, 13,
356 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_hash"_cs, 13));
357 cvt(EGRESS, "eg_intr_md_for_mb.mirror_io_select"_cs, 1,
358 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_io_select"_cs, 1));
359 cvt(EGRESS, "eg_intr_md_for_mb.mirror_multicast_ctrl"_cs, 1,
360 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_multicast_ctrl"_cs, 1));
361 cvt(EGRESS, "eg_intr_md_for_mb.mirror_ingress_cos"_cs, 3,
362 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_ingress_cos"_cs, 3));
363 cvt(EGRESS, "eg_intr_md_for_mb.mirror_deflect_on_drop"_cs, 1,
364 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_deflect_on_drop"_cs, 1));
365 cvt(EGRESS, "eg_intr_md_for_mb.mirror_copy_to_cpu_ctrl"_cs, 1,
366 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_copy_to_cpu_ctrl"_cs, 1));
367 cvt(EGRESS, "eg_intr_md_for_mb.mirror_copy_to_cpu_ctrl"_cs, 1,
368 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_copy_to_cpu_ctrl"_cs, 1));
369 cvt(EGRESS, "eg_intr_md_for_mb.mirror_egress_port"_cs, portWidth,
370 mkMember("eg_intr_md_for_dprsr"_cs, "mirror_egress_port"_cs, portWidth));
371 cvt(EGRESS, "meta.standard_metadata.ingress_port"_cs, 9,
372 mkMember("meta"_cs, "ig_intr_md"_cs, "ingress_port"_cs, portWidth));
373 cvt(EGRESS, "standard_metadata.ingress_port"_cs, 9,
374 mkMember("meta"_cs, "ig_intr_md"_cs, "ingress_port"_cs, portWidth));
375 }
376
377 // convert P4-14 metadata to P4-16 tna metadata
378 const IR::Node *preorder(IR::Member *member) override {
379 prune();
380 if (!member->type->is<IR::Type_Bits>()) return member;
381
382 gress_t thread;
383 if (auto *parser = findOrigCtxt<IR::P4Parser>()) {
384 thread = (parser->name == "IngressParserImpl") ? INGRESS : EGRESS;
385 } else if (auto *control = findOrigCtxt<IR::P4Control>()) {
386 if (structure->mapControlToGress.count(control->name)) {
387 thread = structure->mapControlToGress.at(control->name);
388 } else if (control->name == "EgressDeparserImpl") {
389 thread = EGRESS;
390 } else if (control->name == "IngressDeparserImpl") {
391 thread = INGRESS;
392 } else {
393 // control might be dead-code, ignore.
394 return member;
395 }
396 } else {
397 return member;
398 }
399
400 auto &nameMap = (thread == INGRESS) ? ingressMetadataNameMap : egressMetadataNameMap;
401
402 auto linearizer = new BFN::PathLinearizer();
403 member->apply(*linearizer);
404 if (!linearizer->linearPath) return member;
405 auto path = *linearizer->linearPath;
406 auto fn = path.to_cstring("."_cs, false);
407 if (nameMap.count(fn)) {
408 LOG3("Translating " << member << " to " << nameMap.at(fn));
409 return nameMap.at(fn);
410 }
411 return member;
412 }
413};
414
416 TnaProgramStructure *structure;
417 const IR::Node *postorder(IR::MethodCallExpression *m) override;
418
419 public:
420 explicit FixApplyStatement(TnaProgramStructure *s) : structure(s) {}
421};
422
424 // the gress of the control block
425 gress_t gress;
426 ordered_set<cstring> &mayWrite;
427 ordered_set<cstring> &mayRead;
428 std::map<cstring, BFN::LinearPath> &fieldInfo;
429
430 public:
432 std::map<cstring, BFN::LinearPath> &p)
433 : gress(g), mayWrite(w), mayRead(r), fieldInfo(p) {}
434 bool preorder(const IR::Member *expr) override;
435 bool preorder(const IR::MethodCallExpression *m) override;
436};
437
438unsigned long computeHashOverFieldList(const IR::Expression *expr,
439 std::map<unsigned long, unsigned> &hm,
440 bool reserve_entry_zero = false);
441
443 TnaProgramStructure *structure;
444 gress_t gress;
445 DigestFieldList &digestFieldLists;
446
447 IR::Expression *flatten(const IR::ListExpression *);
448 void convertFieldList(const IR::Primitive *prim, size_t fieldListIndex,
449 std::map<unsigned long, unsigned> &indexHashes, bool);
450
451 public:
452 explicit CollectDigestFields(TnaProgramStructure *s, gress_t g, DigestFieldList &dfl)
453 : structure(s), gress(g), digestFieldLists(dfl) {}
454 bool preorder(const IR::Primitive *p) override;
455};
456
458 public:
459 cstring prefix;
460 std::set<cstring> paths;
461 RenameFieldPath() {}
462
463 IR::Node *preorder(IR::Member *member) {
464 BUG_CHECK(prefix != "", "must provide a prefix to the RenameFieldPath pass");
465 BFN::PathLinearizer linearizer;
466 member->apply(linearizer);
467 auto path = linearizer.linearPath;
468 if (!path) return member;
469 if (auto hdr = path->components.front()->to<IR::PathExpression>()) {
470 if (paths.count(hdr->path->name)) {
471 auto meta = new IR::Member(
472 new IR::Member(new IR::PathExpression(prefix), IR::ID(hdr->path->name)),
473 member->member);
474 LOG3("adding " << prefix << " to " << member);
475 return meta;
476 }
477 } else if (auto hdr = path->components.front()->to<IR::ConcreteHeaderRef>()) {
478 if (paths.count(hdr->ref->name)) {
479 auto meta = new IR::Member(
480 new IR::Member(new IR::PathExpression(prefix), IR::ID(hdr->ref->name)),
481 member->member);
482 LOG3("adding " << prefix << " to " << member);
483 return meta;
484 }
485 }
486 return member;
487 }
488
489 IR::Node *preorder(IR::PathExpression *path) {
490 BUG_CHECK(prefix != "", "must provide a prefix to the RenameFieldPath pass");
491 if (paths.count(path->path->name)) {
492 auto meta = new IR::Member(new IR::PathExpression(prefix), IR::ID(path->path->name));
493 LOG3("adding " << prefix << " to " << path);
494 return meta;
495 }
496 return path;
497 }
498};
499
505 TnaProgramStructure *structure;
506
507 public:
508 explicit FixBridgedIntrinsicMetadata(TnaProgramStructure *s) : structure(s) {
509 paths = {"ig_intr_md"_cs, "ig_intr_md_for_tm"_cs};
510 prefix = "meta"_cs;
511 }
512 IR::Node *preorder(IR::P4Parser *parser) override {
513 if (parser->name == "IngressParserImpl") {
514 prune();
515 }
516 return parser;
517 }
518 IR::Node *preorder(IR::P4Control *control) override {
519 LOG5("control " << control->name);
520 if (structure->mapControlToGress.count(control->name)) {
521 if (structure->mapControlToGress.at(control->name) == INGRESS) {
522 LOG5("\tskip " << control->name);
523 prune();
524 }
525 }
526 return control;
527 }
528};
529
530// rename 'pktgen_port_down' to 'hdr.pktgen_port_down'
532 public:
534 paths = {"pktgen_port_down"_cs, "pktgen_recirc"_cs, "pktgen_generic"_cs, "pktgen_timer"_cs};
535 prefix = "hdr"_cs;
536 }
537};
538
543 public:
545 IR::Node *preorder(IR::AssignmentStatement *assign) override;
546};
547
549 public:
550 bool isNegative = false;
551 bool needsCast = false;
552 int counterIdx = -1;
553
554 public:
556
557 void cannotFit(const IR::AssignmentStatement *stmt, const char *what) {
558 error("Parser counter %1% amount cannot fit into 8-bit. %2%", what, stmt);
559 }
560
561 bool isParserCounter(const IR::Member *member) {
562 auto linearizer = new BFN::PathLinearizer();
563 member->apply(*linearizer);
564 if (!linearizer->linearPath) return false;
565 auto path = linearizer->linearPath->to_cstring();
566 return (path == "ig_prsr_ctrl.parser_counter");
567 }
568
569 std::pair<unsigned, unsigned> getAlignLoHi(const IR::Member *member) {
570 auto header = member->expr->type->to<IR::Type_Header>();
571
572 CHECK_NULL(header);
573
574 unsigned bits = 0;
575
576 for (auto field : boost::adaptors::reverse(header->fields)) {
577 auto size = field->type->width_bits();
578
579 if (field->name == member->member) {
580 if (size > 8) {
581 ::fatal_error(
582 "Parser counter load field is of width %1% bits"
583 " which is greater than what HW supports (8 bits): %2%",
584 size, member);
585 }
586
587 return {bits % 8, (bits + size - 1) % 8};
588 }
589
590 bits += size;
591 }
592
593 BUG("%1% not found in header?", member->member);
594 }
595
596 // Simplified strength reduction to simplify
597 // the expressions that is possible to implement
598 // for parser counter:
599 // prsr_ctr = prsr_ctr - N;
600 // prsr_ctr = field * M - N;
601 struct StrengthReduction : public Transform {
602 bool isZero(const IR::Expression *expr) const {
603 auto cst = expr->to<IR::Constant>();
604 if (cst == nullptr) return false;
605 return cst->value == 0;
606 }
607
608 bool isOne(const IR::Expression *expr) const {
609 auto cst = expr->to<IR::Constant>();
610 if (cst == nullptr) return false;
611 return cst->value == 1;
612 }
613
614 int isPowerOf2(const IR::Expression *expr) const {
615 auto cst = expr->to<IR::Constant>();
616 if (cst == nullptr) return -1;
617 big_int value = cst->value;
618 if (value <= 0) return -1;
619 auto bitcnt = bitcount(value);
620 if (bitcnt != 1) return -1;
621 auto log = Util::scan1(value, 0);
622 // Assumes value does not have more than 2 billion bits
623 return log;
624 }
625
626 const IR::Node *postorder(IR::Sub *expr) {
627 if (isZero(expr->right)) return expr->left;
628 if (isZero(expr->left)) return new IR::Neg(expr->srcInfo, expr->right);
629 // Replace `a - constant` with `a + (-constant)`
630 if (expr->right->is<IR::Constant>()) {
631 auto cst = expr->right->to<IR::Constant>();
632 auto neg = new IR::Constant(cst->srcInfo, cst->type, -cst->value, cst->base, true);
633 auto result = new IR::Add(expr->srcInfo, expr->left, neg);
634 return result;
635 }
636 return expr;
637 }
638
639 const IR::Node *postorder(IR::Mul *expr) {
640 if (isZero(expr->left)) return expr->left;
641 if (isZero(expr->right)) return expr->right;
642 if (isOne(expr->left)) return expr->right;
643 if (isOne(expr->right)) return expr->left;
644 auto exp = isPowerOf2(expr->left);
645 if (exp >= 0) {
646 auto amt = new IR::Constant(exp);
647 auto sh = new IR::Shl(expr->srcInfo, expr->right, amt);
648 return sh;
649 }
650 exp = isPowerOf2(expr->right);
651 if (exp >= 0) {
652 auto amt = new IR::Constant(exp);
653 auto sh = new IR::Shl(expr->srcInfo, expr->left, amt);
654 return sh;
655 }
656 return expr;
657 }
658 };
659
660 const IR::Node *preorder(IR::AssignmentStatement *assign) {
661 prune();
662 auto stmt = getOriginal<IR::AssignmentStatement>();
663 auto parserCounter = new IR::PathExpression("ig_prsr_ctrl_parser_counter"_cs);
664 auto right = stmt->right;
665 auto left = stmt->left;
666
667 // Remove slice around the destination of the assignment.
668 while (auto slice = left->to<IR::Slice>()) left = slice->e0;
669 while (auto cast = left->to<IR::Cast>()) left = cast->expr;
670 auto linearizer = new BFN::PathLinearizer();
671 left->apply(*linearizer);
672 auto path = *linearizer->linearPath;
673 if (path.to_cstring() != "ig_prsr_ctrl.parser_counter") return assign;
674
675 // Remove any casts around the source of the assignment.
676 if (auto cast = right->to<IR::Cast>()) {
677 if (cast->destType->is<IR::Type_Bits>()) {
678 right = cast->expr;
679 }
680 }
681
682 // Strength reduce IR::Sub to IR::Add
683 if (right->is<IR::Sub>()) right = right->apply(StrengthReduction());
684
685 IR::MethodCallStatement *methodCall = nullptr;
686
687 if (right->to<IR::Constant>() || right->to<IR::Member>()) {
688 // Load operation (immediate or field)
689 methodCall = new IR::MethodCallStatement(
690 stmt->srcInfo, new IR::MethodCallExpression(
691 stmt->srcInfo, new IR::Member(parserCounter, "set"),
692 new IR::Vector<IR::Type>({stmt->right->type}),
693 new IR::Vector<IR::Argument>({new IR::Argument(stmt->right)})));
694 } else if (auto add = right->to<IR::Add>()) {
695 auto member = add->left->to<IR::Member>();
696
697 auto counterWidth = IR::Type::Bits::get(8);
698 auto maskWidth = IR::Type::Bits::get(Device::currentDevice() == Device::TOFINO ? 3 : 8);
699 // How does user specify the max in P4-14?
700 auto max = new IR::Constant(counterWidth, 255);
701
702 // Add operaton
703 if (member && isParserCounter(member)) {
704 methodCall = new IR::MethodCallStatement(stmt->srcInfo,
705 new IR::Member(parserCounter, "increment"),
706 {new IR::Argument(add->right)});
707 } else {
708 if (auto *amt = add->right->to<IR::Constant>()) {
709 // Load operation (expression of field)
710 if (member) {
711 auto shr = new IR::Constant(counterWidth, 0);
712 auto mask = new IR::Constant(
713 maskWidth, Device::currentDevice() == Device::TOFINO ? 7 : 255);
714 auto add = new IR::Constant(counterWidth, amt->asUnsigned());
715
716 methodCall = new IR::MethodCallStatement(
717 stmt->srcInfo, new IR::MethodCallExpression(
718 stmt->srcInfo, new IR::Member(parserCounter, "set"),
719 new IR::Vector<IR::Type>({member->type}),
721 {new IR::Argument(member), new IR::Argument(max),
722 new IR::Argument(shr), new IR::Argument(mask),
723 new IR::Argument(add)})));
724 } else if (auto *shl = add->left->to<IR::Shl>()) {
725 if (auto *rot = shl->right->to<IR::Constant>()) {
726 auto left = shl->left;
727 // Remove any casts around the parameter;
728 if (auto cast = left->to<IR::Cast>()) {
729 if (cast->destType->is<IR::Type_Bits>()) {
730 left = cast->expr;
731 }
732 }
733
734 if (auto *field = left->to<IR::Member>()) {
735 if (!rot->fitsUint() || rot->asUnsigned() >> 8)
736 cannotFit(stmt, "multiply");
737
738 if (!amt->fitsUint() || amt->asUnsigned() >> 8)
739 cannotFit(stmt, "immediate");
740
741 unsigned lo = 0, hi = 7;
742 std::tie(lo, hi) = getAlignLoHi(field);
743
744 auto shr =
745 new IR::Constant(counterWidth, 8 - rot->asUnsigned() - lo);
746
747 unsigned rot_hi = std::min(hi + rot->asUnsigned(), 7u);
748 auto mask = new IR::Constant(
749 maskWidth, Device::currentDevice() == Device::TOFINO
750 ? rot_hi
751 : (1 << (rot_hi + 1)) - 1);
752 auto add = new IR::Constant(counterWidth, amt->asUnsigned());
753
754 methodCall = new IR::MethodCallStatement(
755 stmt->srcInfo,
756 new IR::MethodCallExpression(
757 stmt->srcInfo, new IR::Member(parserCounter, "set"),
758 new IR::Vector<IR::Type>({field->type}),
760 {new IR::Argument(field), new IR::Argument(max),
761 new IR::Argument(shr), new IR::Argument(mask),
762 new IR::Argument(add)})));
763 }
764 }
765 }
766 }
767 }
768 }
769 if (!methodCall) error("Unsupported syntax for parser counter: %1%", stmt);
770 return methodCall;
771 }
772
773 const IR::Node *preorder(IR::SelectExpression *node) {
774 bool isPrsrCtr = false;
775 for (unsigned i = 0; i < node->select->components.size(); i++) {
776 auto select = node->select->components[i];
777 if (auto member = select->to<IR::Member>()) {
778 if (isParserCounter(member)) {
779 if (counterIdx >= 0) error("Multiple selects on parser counter in %1%", node);
780 counterIdx = i;
781 isPrsrCtr = true;
782 }
783 }
784 }
785 if (!isPrsrCtr) prune();
786 return node;
787 }
788
789 const IR::Node *postorder(IR::SelectExpression *node) {
790 counterIdx = -1;
791 return node;
792 }
793
795 bool isNegative = false;
796 bool needsCast = false;
797
798 const IR::Expression *convert(const IR::Constant *c, bool toBool = true,
799 bool check = true) {
800 auto val = c->asUnsigned();
801 if (check) {
802 if (val & 0x80) {
803 isNegative = true;
804 } else if (val) {
805 error(
806 "Parser counter only supports test "
807 "of value being zero or negative.");
808 }
809 }
810 if (toBool)
811 return new IR::BoolLiteral(~val);
812 else
813 return new IR::Constant(IR::Type::Bits::get(1), ~val & 1);
814 }
815
816 const IR::Node *preorder(IR::Mask *mask) override {
817 prune();
818 mask->left = convert(mask->left->to<IR::Constant>(), false);
819 mask->right = convert(mask->right->to<IR::Constant>(), false, false);
820 needsCast = true;
821 return mask;
822 }
823
824 const IR::Node *preorder(IR::Constant *c) override { return convert(c); }
825 };
826
827 const IR::Node *preorder(IR::SelectCase *node) {
828 RewriteSelectCase rewrite;
829
830 if (auto list = node->keyset->to<IR::ListExpression>()) {
831 auto newList = list->clone();
832 for (unsigned i = 0; i < newList->components.size(); i++) {
833 if (int(i) == counterIdx) {
834 newList->components[i] = newList->components[i]->apply(rewrite);
835 break;
836 }
837 }
838 node->keyset = newList;
839 } else {
840 node->keyset = node->keyset->apply(rewrite);
841 }
842 isNegative |= rewrite.isNegative;
843 needsCast |= rewrite.needsCast;
844 return node;
845 }
846};
847
849 const ParserCounterSelectCaseConverter &caseConverter;
850 std::set<cstring> need_parser_counter;
851
853 : caseConverter(cc) {}
854
855 bool isParserCounter(const IR::Member *member) {
856 auto linearizer = new BFN::PathLinearizer();
857 member->apply(*linearizer);
858 if (!linearizer->linearPath) return false;
859 auto path = linearizer->linearPath->to_cstring();
860 return (path == "ig_prsr_ctrl.parser_counter");
861 }
862
863 const IR::Node *postorder(IR::Member *node) {
864 if (isParserCounter(node)) {
865 auto prsr = findContext<IR::P4Parser>();
866 need_parser_counter.insert(prsr->name);
867 auto parserCounter = new IR::PathExpression("ig_prsr_ctrl_parser_counter");
868 auto testExpr =
869 new IR::Member(parserCounter, caseConverter.isNegative ? "is_negative" : "is_zero");
870 const IR::Expression *methodCall = new IR::MethodCallExpression(
871 node->srcInfo, testExpr, new IR::Vector<IR::Argument>());
872 if (caseConverter.needsCast)
873 methodCall = new IR::Cast(IR::Type::Bits::get(1), methodCall);
874 return methodCall;
875 }
876 return node;
877 }
878
879 const IR::Node *postorder(IR::P4Parser *parser) {
880 if (need_parser_counter.count(parser->name) != 0) {
881 auto type = new IR::Type_Name("ParserCounter");
882 auto decl = new IR::Declaration_Instance("ig_prsr_ctrl_parser_counter", type,
884 auto locals = parser->parserLocals.clone();
885 locals->push_back(decl);
886 return new IR::P4Parser(parser->srcInfo, parser->name, parser->type,
887 parser->constructorParams, *locals, parser->states);
888 }
889 return parser;
890 }
891};
892
894 public:
896 auto convertSelectCase = new ParserCounterSelectCaseConverter;
897 auto convertSelectExpr = new ParserCounterSelectExprConverter(*convertSelectCase);
898 addPasses({convertSelectCase, convertSelectExpr});
899 }
900};
901
902class FixIdleTimeout : public Transform {
903 public:
904 FixIdleTimeout() {}
905 const IR::Node *preorder(IR::Property *prop) override {
906 if (prop->name == "support_timeout") {
907 return new IR::Property("idle_timeout", prop->annotations, prop->value,
908 prop->isConstant);
909 }
910 return prop;
911 }
912};
913
915 public:
917 const IR::Node *preorder(IR::P4Program *program) override {
918 prune();
920 return program->apply(cloner);
921 }
922};
923
924// undo the changes to program for checksum translation
925class RemoveBuiltins : public Modifier {
926 public:
927 RemoveBuiltins() {}
928 void postorder(IR::P4Parser *parser) override {
929 parser->states.removeByName(IR::ParserState::accept);
930 parser->states.removeByName(IR::ParserState::reject);
931 }
932};
933
935 P4::ReferenceMap *refMap;
936 P4::TypeMap *typeMap;
937 TnaProgramStructure *structure;
938 P4ParserGraphs *graph;
940 std::map<cstring, std::set<cstring>> need_checksum;
941 std::map<cstring, ordered_set<const IR::Member *>> residualChecksumPayloadFields;
942
943 public:
944 template <typename Func>
945 void forAllExtracts(const IR::ParserState *state, Func function) {
947 for (auto expr : state->components) {
948 if (!expr->is<IR::MethodCallStatement>()) continue;
949 auto mce = expr->to<IR::MethodCallStatement>()->methodCall;
950 auto inst = P4::MethodInstance::resolve(mce, refMap, typeMap, nullptr /* ctxt */,
951 true /* incomplete */);
952 if (inst == nullptr) continue;
953 if (auto em = inst->to<P4::ExternMethod>()) {
954 if (em->actualExternType->name == "packet_in" && em->method->name == "extract") {
955 auto extracted = inst->substitution.lookupByName("hdr"_cs)->apply(cloner);
956 if (extracted == nullptr ||
957 !extracted->to<IR::Argument>()->expression->is<IR::Member>())
958 continue;
959 function(extracted->to<IR::Argument>()->expression->to<IR::Member>());
960 }
961 }
962 }
963 }
964
965 static bool equiv(const IR::Expression *a, const IR::Expression *b) {
966 if (a == b) return true;
967 if (typeid(*a) != typeid(*b)) return false;
968 if (auto ma = a->to<IR::Member>()) {
969 auto mb = b->to<IR::Member>();
970 return ma->member == mb->member && equiv(ma->expr, mb->expr);
971 }
972 if (auto pa = a->to<IR::PathExpression>()) {
973 auto pb = b->to<IR::PathExpression>();
974 return pa->path->name == pb->path->name;
975 }
976 return false;
977 }
978
979 static bool belongsTo(const IR::Member *a, const IR::Member *b) {
980 if (!a || !b) return false;
981
982 // case 1: a is field, b is field
983 if (equiv(a, b)) return true;
984
985 // case 2: a is field, b is header
986 if (a->type->is<IR::Type::Bits>()) {
987 if (equiv(a->expr, b)) return true;
988 }
989
990 return false;
991 }
992
994 TnaProgramStructure *structure, P4ParserGraphs *graph)
995 : refMap(refMap), typeMap(typeMap), structure(structure), graph(graph) {}
996
997 // this function must be invoked from IR::ParserState visitor.
998 ordered_set<const IR::Member *> collectResidualChecksumPayloadFields() {
999 auto orig = getOriginal<IR::ParserState>();
1000 BUG_CHECK(orig != nullptr, "function must be invoked within ParserState visitor");
1002 auto descendants = graph->get_all_descendants(orig);
1003 for (auto d : descendants) {
1004 forAllExtracts(d, [&](const IR::Member *member) { rv.insert(member); });
1005 }
1006 return rv;
1007 }
1008
1009 // iterate through residual checksum calculation,
1010 // create subtract call for the fields that belong to the provided member;
1011 void createSubtractCallsForResidualChecksum(IR::IndexedVector<IR::StatOrDecl> *statements,
1012 std::vector<ChecksumInfo> &checksum_fields,
1013 const IR::Member *member,
1015 gress_t gress) {
1016 if (checksum_fields.empty()) return;
1017 P4::CloneExpressions cloner;
1018 const IR::Expression *constant = nullptr;
1019 for (auto csum : checksum_fields) {
1020 if (!csum.parserUpdateLocations.count(gress)) continue;
1021 std::vector<const IR::Expression *> exprList;
1022 auto path = BFN::PathLinearizer::convert(csum.destField);
1023 if (!checksum.count(path))
1024 checksum.emplace(path, structure->makeUniqueName("checksum"_cs));
1025 auto fieldList = csum.fieldList;
1026 if (!fieldList->is<IR::ListExpression>()) continue;
1027 for (auto f : fieldList->to<IR::ListExpression>()->components) {
1028 if (f->is<IR::Constant>()) {
1029 constant = f;
1030 } else if (belongsTo(f->to<IR::Member>(), member)) {
1031 if (constant) {
1032 exprList.emplace_back(constant);
1033 constant = nullptr;
1034 // If immediate next field after the constant is extracted in this field
1035 // then the constant belongs to subtract field list of this state
1036 }
1037 exprList.emplace_back(f->apply(cloner));
1038
1039 } else {
1040 constant = nullptr;
1041 }
1042 }
1043 for (auto e : exprList) {
1044 auto mce = new IR::MethodCallExpression(
1045 new IR::Member(new IR::PathExpression(checksum.at(path)), "subtract"),
1046 new IR::Vector<IR::Type>({e->type}),
1047 new IR::Vector<IR::Argument>({new IR::Argument(e)}));
1048 auto subtractCall = new IR::MethodCallStatement(mce);
1049 statements->push_back(subtractCall);
1050 }
1051
1052 if (belongsTo(csum.destField->to<IR::Member>(), member)) {
1053 auto destField = csum.destField->apply(cloner);
1054 auto mce = new IR::MethodCallExpression(
1055 new IR::Member(new IR::PathExpression(checksum.at(path)), "subtract"),
1056 new IR::Vector<IR::Type>({destField->type}),
1057 new IR::Vector<IR::Argument>({new IR::Argument(destField)}));
1058 auto subtractCall = new IR::MethodCallStatement(mce);
1059 statements->push_back(subtractCall);
1060
1061 if (csum.with_payload) {
1062 BUG_CHECK(csum.residulChecksumName != std::nullopt,
1063 "residual checksum field name cannot be empty");
1064 auto *rmember = new IR::Member(
1065 IR::Type::Bits::get(16),
1066 new IR::Member(new IR::PathExpression("meta"), IR::ID("bridged_header")),
1067 IR::ID(*csum.residulChecksumName));
1068 auto *mce = new IR::MethodCallExpression(
1069 new IR::Member(new IR::PathExpression(checksum.at(path)),
1070 "subtract_all_and_deposit"),
1071 new IR::Vector<IR::Type>({rmember->type}),
1072 new IR::Vector<IR::Argument>({new IR::Argument(rmember)}));
1073 auto *deposit = new IR::MethodCallStatement(mce);
1074 std::cout << member->member << std::endl;
1075 structure->checksumDepositToHeader[gress][member->member] = deposit;
1076 }
1077 }
1078 }
1079 }
1080
1081 // iterate through verify checksum.
1082 // create add call for the fields that belongs to the provided member;
1083 void createAddCallsForVerifyChecksum(IR::IndexedVector<IR::StatOrDecl> *statements,
1084 std::vector<ChecksumInfo> &checksum_fields,
1085 const IR::Member *member,
1086 ordered_map<cstring, cstring> &checksum, gress_t gress,
1087 cstring state) {
1088 P4::CloneExpressions cloner;
1089 for (auto csum : checksum_fields) {
1090 if (!csum.parserUpdateLocations.count(gress)) continue;
1091 auto path = BFN::PathLinearizer::convert(csum.destField);
1092 if (!checksum.count(path))
1093 checksum.emplace(path, structure->makeUniqueName("checksum"_cs));
1094 auto csumInst = checksum.at(path);
1095 auto fieldList = csum.fieldList;
1096 if (!fieldList->is<IR::ListExpression>()) continue;
1097 for (auto f : fieldList->to<IR::ListExpression>()->components) {
1098 if (belongsTo(f->to<IR::Member>(), member)) {
1099 auto mce = new IR::MethodCallExpression(
1100 new IR::Member(new IR::PathExpression(checksum.at(path)), "add"),
1101 new IR::Vector<IR::Type>({f->type}),
1102 new IR::Vector<IR::Argument>({new IR::Argument(f->apply(cloner))}));
1103
1104 auto addCall = new IR::MethodCallStatement(mce);
1105 statements->push_back(addCall);
1106 structure->ingressVerifyChecksumToStates[csumInst].insert(state);
1107 }
1108 }
1109
1110 auto destField = csum.destField->apply(cloner);
1111 if (belongsTo(destField->to<IR::Member>(), member)) {
1112 auto mce = new IR::MethodCallExpression(
1113 new IR::Member(new IR::PathExpression(checksum.at(path)), "add"),
1114 new IR::Vector<IR::Type>({destField->type}),
1115 new IR::Vector<IR::Argument>({new IR::Argument(destField->apply(cloner))}));
1116 auto addCall = new IR::MethodCallStatement(mce);
1117 statements->push_back(addCall);
1118 structure->ingressVerifyChecksumToStates[csumInst].insert(state);
1119 }
1120 }
1121 }
1122
1123 void postorder(IR::ParserState *state) override {
1124 // IR::IndexedVector<IR::StatOrDecl>* subtractCalls = nullptr;
1125 // IR::IndexedVector<IR::StatOrDecl>* addCalls = nullptr;
1126 auto subtractCalls = new IR::IndexedVector<IR::StatOrDecl>();
1127 auto addCalls = new IR::IndexedVector<IR::StatOrDecl>();
1128 auto parser = findContext<IR::P4Parser>();
1129 auto gress = parser->name == "IngressParserImpl" ? INGRESS : EGRESS;
1130 forAllExtracts(state, [&](const IR::Member *member) {
1131 createAddCallsForVerifyChecksum(addCalls, structure->verifyChecksums, member, checksum,
1132 gress, state->name);
1133 });
1134 forAllExtracts(state, [&](const IR::Member *member) {
1135 auto rc = structure->residualChecksums;
1136 createSubtractCallsForResidualChecksum(subtractCalls, rc, member, checksum, gress);
1137 });
1138 if (subtractCalls->size() != 0) {
1139 state->components.append(*subtractCalls);
1140 }
1141 if (addCalls->size() != 0) state->components.append(*addCalls);
1142
1143 if (subtractCalls->size() != 0 || addCalls->size() != 0) {
1144 auto parser = findContext<IR::P4Parser>();
1145 for (auto it = checksum.begin(); it != checksum.end(); it++)
1146 need_checksum[parser->name].insert(it->second);
1147 }
1148 }
1149
1150 bool preorder(IR::P4Parser *) override {
1151 checksum.clear();
1152 return true;
1153 }
1154
1155 void postorder(IR::P4Parser *parser) override {
1156 if (need_checksum.count(parser->name)) {
1157 auto csums = need_checksum.at(parser->name);
1158 for (auto csum : csums) {
1159 auto inst = new IR::Type_Name("Checksum");
1160 auto args = new IR::Vector<IR::Argument>();
1161 auto decl = new IR::Declaration_Instance(csum, inst, args);
1162 parser->parserLocals.push_back(decl);
1163 }
1164 }
1165 }
1166
1167 void postorder(IR::MethodCallExpression *mce) override {
1168 auto inst = P4::MethodInstance::resolve(mce, refMap, typeMap, nullptr /* ctxt */,
1169 true /* incomplete */);
1170 if (!inst->is<P4::ExternMethod>()) return;
1171 auto em = inst->to<P4::ExternMethod>();
1172 if (em->actualExternType->name != "Checksum" || em->method->name != "update") return;
1173 auto assign = findOrigCtxt<IR::AssignmentStatement>();
1174 if (assign == nullptr) return;
1175 auto destField = assign->left;
1176 // auto parser = findContext<IR::P4Control>();
1177 // FIXME: should check parser for null!!
1178 // auto gress = parser->name == "IngressDeparserImpl" ? INGRESS : EGRESS;
1179 if (structure->residualChecksums.empty()) return;
1180 auto path = BFN::PathLinearizer::convert(destField);
1181 if (!residualChecksumPayloadFields.count(path)) return;
1182 auto data = inst->substitution.lookupByName("data"_cs);
1183 P4::CloneExpressions cloner;
1184 if (auto expr = data->expression->to<IR::ListExpression>()) {
1185 // add checksum fields
1186 IR::ListExpression *fl = expr->clone();
1187 // add payload
1188 auto payload = residualChecksumPayloadFields.at(path);
1189 for (auto p : payload) fl->push_back(p->apply(cloner));
1190 auto args = new IR::Vector<IR::Argument>();
1191 args->push_back(new IR::Argument(fl));
1192 mce->arguments = args;
1193 }
1194 }
1195};
1196
1197using EndStates = std::map<cstring, ordered_map<cstring, ordered_set<cstring>>>;
1198
1199static IR::AssignmentStatement *createChecksumError(cstring decl, gress_t gress) {
1200 auto methodCall = new IR::Member(new IR::PathExpression(decl), "verify");
1201 auto verifyCall = new IR::MethodCallExpression(methodCall, {});
1202 auto rhs = new IR::Cast(IR::Type::Bits::get(1), verifyCall);
1203
1204 cstring intr_md;
1205
1206 if (gress == INGRESS)
1207 intr_md = "ig_intr_md_from_prsr"_cs;
1208 else if (gress == EGRESS)
1209 intr_md = "eg_intr_md_from_prsr"_cs;
1210 else
1211 BUG("Unhandled gress: %1%.", gress);
1212
1213 auto parser_err = new IR::Member(new IR::PathExpression(intr_md), "parser_err");
1214
1215 auto lhs = new IR::Slice(parser_err, 12, 12);
1216 return new IR::AssignmentStatement(lhs, rhs);
1217}
1218
1220 public:
1221 TnaProgramStructure *structure;
1222 explicit InsertChecksumDeposit(TnaProgramStructure *structure) : structure(structure) {}
1223 const IR::Node *preorder(IR::ParserState *state) override {
1224 auto parser = findContext<IR::P4Parser>();
1225 auto gress = parser->name == "IngressParserImpl" ? INGRESS : EGRESS;
1226 if (!structure->checksumDepositToHeader.count(gress)) return state;
1227 auto components = new IR::IndexedVector<IR::StatOrDecl>();
1228 auto &checksumDeposit = structure->checksumDepositToHeader.at(gress);
1229 for (auto component : state->components) {
1230 components->push_back(component);
1231 if (auto methodCall = component->to<IR::MethodCallStatement>()) {
1232 if (auto call = methodCall->methodCall->to<IR::MethodCallExpression>()) {
1233 if (auto method = call->method->to<IR::Member>()) {
1234 if (method->member == "extract") {
1235 for (auto arg : *call->arguments) {
1236 auto member = arg->expression->to<IR::Member>();
1237 if (!member) continue;
1238 if (checksumDeposit.count(member->member)) {
1239 components->push_back(checksumDeposit.at(member->member));
1240 }
1241 }
1242 }
1243 }
1244 }
1245 }
1246 }
1247 state->components = *components;
1248 return state;
1249 }
1250};
1251
1253 public:
1254 EndStates endStates;
1255 struct ComputeEndStates : public Inspector {
1256 TnaProgramStructure *structure;
1257 const P4ParserGraphs *graph;
1258 EndStates &endStates;
1259 explicit ComputeEndStates(TnaProgramStructure *structure, const P4ParserGraphs *graph,
1260 EndStates &endStates)
1261 : structure(structure), graph(graph), endStates(endStates) {}
1262
1263 void printStates(const ordered_set<cstring> &states) {
1264 for (auto s : states) std::cout << " " << s << std::endl;
1265 }
1266
1267 ordered_set<cstring> computeChecksumEndStates(const ordered_set<cstring> &calcStates) {
1268 if (LOGGING(3)) {
1269 std::cout << "calc states are:" << std::endl;
1270 printStates(calcStates);
1271 }
1272
1273 ordered_set<cstring> endStates;
1274
1275 // A calculation state is a verification end state if no other state of the
1276 // same calculation is its descendant. Otherwise, include all of the state's
1277 // children states that are not a calculation state.
1278
1279 for (auto a : calcStates) {
1280 bool isEndState = true;
1281 for (auto b : calcStates) {
1282 if (graph->is_ancestor(a, b)) {
1283 isEndState = false;
1284 break;
1285 }
1286 }
1287 if (isEndState) {
1288 endStates.insert(a);
1289 } else {
1290 if (graph->succs.count(a)) {
1291 for (auto succ : graph->succs.at(a)) {
1292 if (calcStates.count(succ)) continue;
1293
1294 for (auto s : calcStates) {
1295 if (!graph->is_ancestor(succ, s)) {
1296 endStates.insert(succ);
1297 }
1298 }
1299 }
1300 }
1301 }
1302 }
1303
1304 BUG_CHECK(!endStates.empty(), "Unable to find verification end state?");
1305
1306 if (LOGGING(3)) {
1307 std::cout << "end states are:" << std::endl;
1308 printStates(endStates);
1309 }
1310
1311 return endStates;
1312 }
1313
1314 bool preorder(const IR::P4Parser *parser) override {
1315 if (parser->name != "IngressParserImpl") return false;
1316
1317 // compute checksum end states
1318 for (auto kv : structure->ingressVerifyChecksumToStates)
1319 endStates[parser->name][kv.first] = computeChecksumEndStates(kv.second);
1320
1321 return false;
1322 }
1323 };
1324
1325 // TODO we probably don't want to insert statement into the "accept" state
1326 // since this is a special state. Add a dummy state before "accept" if it is
1327 // a checksum verification end state.
1329 const IR::Node *preorder(IR::P4Parser *parser) override {
1330 for (auto &kv : endStates[parser->name]) {
1331 if (kv.second.count("accept"_cs)) {
1332 if (!dummy) {
1333 dummy =
1334 BFN::createGeneratedParserState("before_accept"_cs, {}, "accept"_cs);
1335 parser->states.push_back(dummy);
1336 }
1337 kv.second.erase("accept"_cs);
1338 kv.second.insert("__before_accept"_cs);
1339 LOG3("add dummy state before \"accept\"");
1340 }
1341 }
1342 return parser;
1343 }
1344
1345 const IR::Node *postorder(IR::PathExpression *path) override {
1346 auto parser = findContext<IR::P4Parser>();
1347 auto state = findContext<IR::ParserState>();
1348 auto select = findContext<IR::SelectCase>();
1349
1350 if (parser && state && select) {
1351 bool isCalcState = false;
1352
1353 for (auto kv : structure->ingressVerifyChecksumToStates) {
1354 for (auto s : kv.second) {
1355 if (s == state->name) {
1356 isCalcState = true;
1357 break;
1358 }
1359 }
1360 }
1361
1362 if (!isCalcState) return path;
1363
1364 for (auto &kv : endStates[parser->name]) {
1365 if (path->path->name == "accept" && kv.second.count("__before_accept"_cs)) {
1366 path = new IR::PathExpression("__before_accept");
1367 LOG3("modify transition to \"before_accept\"");
1368 }
1369 }
1370 }
1371
1372 return path;
1373 }
1374
1375 const IR::ParserState *dummy = nullptr;
1376 TnaProgramStructure *structure;
1377 EndStates &endStates;
1378
1379 explicit InsertBeforeAccept(TnaProgramStructure *structure, EndStates &endStates)
1380 : structure(structure), endStates(endStates) {
1381 CHECK_NULL(structure);
1382 }
1383 };
1384
1385 struct InsertEndStates : public Transform {
1386 const IR::Node *preorder(IR::ParserState *state) override {
1387 auto parser = findContext<IR::P4Parser>();
1388
1389 if (state->name == "reject") return state;
1390
1391 for (auto &kv : endStates[parser->name]) {
1392 auto decl = kv.first;
1393 for (auto endState : kv.second) {
1394 if (endState == state->name) {
1395 auto thread = (parser->name == "IngressParserImpl") ? INGRESS : EGRESS;
1396 auto *checksumError = createChecksumError(decl, thread);
1397 state->components.push_back(checksumError);
1398
1399 LOG3("verify " << toString(parser->name) << " " << decl << " in state "
1400 << endState);
1401 }
1402 }
1403 }
1404 return state;
1405 }
1406 EndStates &endStates;
1407 explicit InsertEndStates(EndStates &endStates) : endStates(endStates) {}
1408 };
1409
1410 explicit InsertChecksumError(TnaProgramStructure *structure, const P4ParserGraphs *graph) {
1411 addPasses({new ComputeEndStates(structure, graph, endStates),
1412 new InsertBeforeAccept(structure, endStates), new InsertEndStates(endStates)});
1413 }
1414};
1415
1416class FixChecksum : public PassManager {
1417 P4::ReferenceMap refMap;
1418 P4::TypeMap typeMap;
1419
1420 public:
1421 explicit FixChecksum(TnaProgramStructure *structure);
1422};
1423
1429 P4::CloneExpressions cloner;
1430
1431 public:
1433 const IR::Node *postorder(IR::ParserState *state) { return state->apply(cloner); }
1434};
1435
1436// Misc fixup passes to ensure a valid tna program, specifically, they deal with
1437// the following difference between a P4-14 program and a P4-16 tna program.
1438// - tna egress cannot refer to ingress intrinsic metadata
1439// - no support for standard metadata in tna.
1441 public:
1443 auto tnaStruct = dynamic_cast<TnaProgramStructure *>(s);
1444 if (!tnaStruct) error("Cannot convert to TnaProgramStructure");
1445 addPasses({
1446 new FixExtracts(tnaStruct),
1447 new FixBridgedIntrinsicMetadata(tnaStruct),
1448 new FixIdleTimeout(),
1449 new ConvertMetadata(tnaStruct),
1452 new FixParserPriority(), // FIXME
1453 new FixParserCounter(),
1455 new FixChecksum(tnaStruct),
1456 });
1457 }
1458};
1459
1460IR::BlockStatement *generate_hash_block_statement(P4V1::ProgramStructure *structure,
1461 const IR::Primitive *prim, const cstring temp,
1462 ExpressionConverter &conv, unsigned num_ops);
1463
1464void add_custom_enum_if_crc_poly(const IR::Expression *expr, IR::Vector<IR::Argument> &declArgs);
1465
1467 const IR::Primitive *prim, const cstring temp,
1468 ExpressionConverter &conv, unsigned num_ops,
1469 const IR::Expression *dest);
1470
1472 const IR::Primitive *prim,
1473 const cstring temp,
1474 ExpressionConverter &conv,
1475 unsigned num_ops);
1476
1477// used by P4-14 & v1model translation
1478bool use_v1model();
1479
1481// convert a P4-14 program to an equivalent P4-16 TNA program.
1483 ProgramStructure *structure;
1484
1485 public:
1486 TnaConverter();
1487 void loadModel() { structure->loadModel(); }
1488 Visitor::profile_t init_apply(const IR::Node *node) override;
1489};
1490
1491} // namespace P4::P4V1
1492
1493#endif /* BACKENDS_TOFINO_BF_P4C_ARCH_FROMV1_0_PROGRAMSTRUCTURE_H_ */
Definition cloner.h:26
Definition methodInstance.h:168
Definition node.h:52
Definition node.h:95
Definition vector.h:59
Definition visitor.h:400
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:27
Definition visitor.h:372
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:423
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:442
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:34
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:415
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:504
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1416
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:914
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1428
Definition converters.h:507
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:902
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:79
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:893
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:542
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:531
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1219
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1252
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:934
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:548
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1440
Information about the structure of a P4-14 program, used to convert it to a P4-16 program.
Definition frontends/p4-14/fromv1.0/programStructure.h:45
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:925
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:457
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:53
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1482
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:110
Definition visitor.h:788
Definition ir/pass_manager.h:40
Class used to encode maps from paths to declarations.
Definition referenceMap.h:66
Definition strengthReduction.h:119
Definition visitor.h:424
Definition typeMap.h:41
Definition source_file.h:125
Definition visitor.h:78
Definition cstring.h:85
Definition ordered_map.h:32
Definition ordered_set.h:32
Definition assoc.h:300
bool is_ancestor(cstring a, cstring b) const
Is "a" an ancestor of "b"?
Definition parser_graph.h:89
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:81
Definition converters.cpp:28
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
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition applyOptionsPragmas.cpp:24
const IR::Expression * convert(const IR::Expression *expression, const IR::Type *type)
Definition structInitializers.cpp:26
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition lib/error.h:51
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:148
Definition add_metadata_parser_states.h:32
Definition path_linearizer.h:53
Definition path_linearizer.h:75
Definition id.h:28
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1255
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1328
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:1385
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:794
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:601
Definition backends/tofino/bf-p4c/arch/fromv1.0/programStructure.h:848
T * to() noexcept
Definition rtti.h:226