P4C
The P4 Compiler
Loading...
Searching...
No Matches
checksum.h
1
19#ifndef BACKENDS_TOFINO_BF_P4C_ARCH_FROMV1_0_CHECKSUM_H_
20#define BACKENDS_TOFINO_BF_P4C_ARCH_FROMV1_0_CHECKSUM_H_
21
22#include "backends/tofino/bf-p4c/arch/intrinsic_metadata.h"
23#include "backends/tofino/bf-p4c/lib/assoc.h"
24#include "backends/tofino/bf-p4c/midend/parser_graph.h"
25#include "frontends/p4/methodInstance.h"
26#include "v1_program_structure.h"
27
29
31 bool with_payload;
32 const IR::Expression *cond;
33 const IR::Expression *fieldList;
34 const IR::Expression *destField;
35 std::set<gress_t> parserUpdateLocations;
36 std::set<gress_t> deparserUpdateLocations;
37 std::optional<cstring> residulChecksumName;
38};
39
40namespace BFN::V1 {
41
43 public:
47 residualChecksumPayloadFields;
49 destToGressToState;
50 std::map<gress_t, std::map<cstring, IR::Statement *>> checksumDepositToHeader;
51 DeclToStates ingressVerifyDeclToStates;
52 P4ParserGraphs parserGraphs;
53
55 P4::TypeMap *typeMap);
56};
57
58static bool analyzeChecksumCall(const IR::MethodCallStatement *statement, cstring which) {
59 auto methodCall = statement->methodCall->to<IR::MethodCallExpression>();
60 if (!methodCall) {
61 ::warning("Expected a non-empty method call expression: %1%", statement);
62 return false;
63 }
64 auto method = methodCall->method->to<IR::PathExpression>();
65 if (!method || (method->path->name != which)) {
66 ::warning("Expected an %1% statement in %2%", statement, which);
67 return false;
68 }
69 if (methodCall->arguments->size() != 4) {
70 ::warning("Expected 4 arguments for %1% statement: %2%", statement, which);
71 return false;
72 }
73
74 auto destField = (*methodCall->arguments)[2]->expression->to<IR::Member>();
75 CHECK_NULL(destField);
76
77 auto condition = (*methodCall->arguments)[0]->expression;
78 CHECK_NULL(condition);
79
80 bool nominalCondition = false;
81 if (auto mc = condition->to<IR::MethodCallExpression>()) {
82 if (auto m = mc->method->to<IR::Member>()) {
83 if (m->member == "isValid") {
84 if (m->expr->equiv(*(destField->expr))) {
85 nominalCondition = true;
86 }
87 }
88 }
89 }
90
91 auto bl = condition->to<IR::BoolLiteral>();
92 if (which == "verify_checksum" && !nominalCondition && (!bl || bl->value != true))
93 error("Tofino does not support conditional checksum verification: %1%", destField);
94
95 auto algorithm = (*methodCall->arguments)[3]->expression->to<IR::Member>();
96 if (!algorithm || (algorithm->member != "csum16"))
97 error("Tofino only supports \"csum16\" for checksum calculation: %1%", destField);
98
99 return true;
100}
101
102static IR::Declaration_Instance *createChecksumDeclaration(ProgramStructure *structure,
103 const IR::MethodCallStatement *) {
104 // auto mc = csum->methodCall->to<IR::MethodCallExpression>();
105
106 // auto typeArgs = new IR::Vector<IR::Type>();
107 auto inst = new IR::Type_Name("Checksum");
108
109 auto csum_name = cstring::make_unique(structure->unique_names, "checksum"_cs, '_');
110 structure->unique_names.insert(csum_name);
111 auto args = new IR::Vector<IR::Argument>();
112 auto decl = new IR::Declaration_Instance(csum_name, inst, args);
113
114 return decl;
115}
116
117static IR::AssignmentStatement *createChecksumError(const IR::Declaration *decl, gress_t gress) {
118 auto methodCall = new IR::Member(new IR::PathExpression(decl->name), "verify");
119 auto verifyCall = new IR::MethodCallExpression(methodCall, {});
120 auto rhs_val = new IR::Cast(IR::Type::Bits::get(1), verifyCall);
121
122 cstring intr_md;
123
124 if (gress == INGRESS)
125 intr_md = "ig_intr_md_from_prsr"_cs;
126 else if (gress == EGRESS)
127 intr_md = "eg_intr_md_from_prsr"_cs;
128 else
129 BUG("Unhandled gress: %1%.", gress);
130
131 auto parser_err = new IR::Member(new IR::PathExpression(intr_md), "parser_err");
132
133 auto lhs = new IR::Slice(parser_err, 12, 12);
134 auto rhs = new IR::BOr(lhs, rhs_val);
135 return new IR::AssignmentStatement(lhs, rhs);
136}
137
138static std::vector<gress_t> getChecksumUpdateLocations(const IR::MethodCallExpression *call,
139 const IR::BlockStatement *block,
140 cstring pragma) {
141 std::vector<gress_t> updateLocations;
142 if (pragma == "calculated_field_update_location")
143 updateLocations = {EGRESS};
144 else if (pragma == "residual_checksum_parser_update_location")
145 updateLocations = {INGRESS};
146 else
147 BUG("Invalid use of function getChecksumUpdateLocation");
148
149 for (auto annot : block->annotations->annotations) {
150 if (annot->name.name == pragma) {
151 auto &exprs = annot->expr;
152 auto gress = exprs[0]->to<IR::StringLiteral>();
153 auto pCall = exprs[1]->to<IR::MethodCallExpression>();
154 if (pCall && !pCall->equiv(*call)) continue;
155 if (gress->value == "ingress")
156 updateLocations = {INGRESS};
157 else if (gress->value == "egress")
158 updateLocations = {EGRESS};
159 else if (gress->value == "ingress_and_egress")
160 updateLocations = {INGRESS, EGRESS};
161 else
162 error(
163 "Invalid use of @pragma %1%, valid value "
164 " is ingress/egress/ingress_and_egress",
165 pragma);
166 }
167 }
168
169 return updateLocations;
170}
171
173 P4::ReferenceMap *refMap;
174 P4::TypeMap *typeMap;
175
176 public:
177 std::vector<const IR::MethodCallStatement *> verifyChecksums;
178 std::vector<const IR::MethodCallStatement *> residualChecksums;
181
183 : refMap(refMap), typeMap(typeMap) {
184 setName("CollectParserChecksums");
185 }
186
187 void postorder(const IR::MethodCallStatement *node) override {
188 auto mce = node->methodCall->to<IR::MethodCallExpression>();
189 CHECK_NULL(mce);
190 auto mi = P4::MethodInstance::resolve(node, refMap, typeMap);
191 if (auto ef = mi->to<P4::ExternFunction>()) {
192 if (ef->method->name == "verify_checksum" &&
193 analyzeChecksumCall(node, "verify_checksum"_cs)) {
194 verifyChecksums.push_back(node);
195 } else if (ef->method->name == "update_checksum_with_payload" &&
196 analyzeChecksumCall(node, "update_checksum_with_payload"_cs)) {
197 residualChecksums.push_back(node);
198
199 auto block = findContext<IR::BlockStatement>();
200 CHECK_NULL(block);
201 parserUpdateLocations[node] = getChecksumUpdateLocations(
202 mce, block, "residual_checksum_parser_update_location"_cs);
203 deparserUpdateLocations[node] =
204 getChecksumUpdateLocations(mce, block, "calculated_field_update_location"_cs);
205 }
206 }
207 }
208};
209
211 public:
213 const CollectParserChecksums *collect, const P4ParserGraphs *graph,
214 ProgramStructure *structure)
215 : translate(translate), collect(collect), graph(graph), structure(structure) {}
216
217 private:
218 TranslateParserChecksums *translate;
219 const CollectParserChecksums *collect;
220 const P4ParserGraphs *graph;
221 ProgramStructure *structure;
222
224
226 ingressParserResidualChecksumDecls, egressParserResidualChecksumDecls;
227
228 unsigned residualChecksumCnt = 0;
229
233
234 StateToExtracts stateToExtracts;
235 ExtractToState extractToState;
236
237 struct CollectExtractMembers : public Inspector {
238 CollectExtractMembers(StateToExtracts &stateToExtracts, ExtractToState &extractToState)
239 : stateToExtracts(stateToExtracts), extractToState(extractToState) {}
240
241 StateToExtracts &stateToExtracts;
242 ExtractToState &extractToState;
243
244 void postorder(const IR::MethodCallStatement *statement) override {
245 auto *call = statement->methodCall;
246 CHECK_NULL(call);
247 auto *method = call->method->to<IR::Member>();
248 auto *state = findContext<IR::ParserState>();
249
250 if (method && method->member == "extract") {
251 for (auto m : *(call->arguments)) {
252 stateToExtracts[state].push_back(m->expression);
253 extractToState[m->expression] = state;
254 }
255 }
256 }
257 };
258
259 profile_t init_apply(const IR::Node *root) override {
260 CollectExtractMembers cem(stateToExtracts, extractToState);
261 root->apply(cem);
262 return Inspector::init_apply(root);
263 }
264
265 // FIXME -- yet another 'deep' comparison for expressions
266 static bool equiv(const IR::Expression *a, const IR::Expression *b) {
267 if (a == b) return true;
268 if (typeid(*a) != typeid(*b)) return false;
269 if (auto ma = a->to<IR::Member>()) {
270 auto mb = b->to<IR::Member>();
271 return ma->member == mb->member && equiv(ma->expr, mb->expr);
272 }
273 if (auto pa = a->to<IR::PathExpression>()) {
274 auto pb = b->to<IR::PathExpression>();
275 return pa->path->name == pb->path->name;
276 }
277 return false;
278 }
279
280 static bool belongsTo(const IR::Member *a, const IR::Member *b) {
281 if (!a || !b) return false;
282
283 // case 1: a is field, b is field
284 if (equiv(a, b)) return true;
285
286 // case 2: a is field, b is header
287 if (a->type->is<IR::Type::Bits>()) {
288 if (equiv(a->expr, b)) return true;
289 }
290
291 return false;
292 }
293
294 // FIXME: Replacing the source info should not be necessary, but it currently required
295 // to work around an issue with ResolutionContext. ResolutionContext looks at the location
296 // of a name and a potential declaration to see if the declaration applies. The issue
297 // is that the source address of the substituted checksum add calls will use the source
298 // location of the checksum parser declaration.
299 const IR::Path *replaceSrcInfo(const IR::Path *path, const Util::SourceInfo &srcInfo) {
300 if (!srcInfo.isValid()) return path;
301
302 auto *newPath = path->clone();
303 newPath->srcInfo = srcInfo;
304 newPath->name = IR::ID(srcInfo, newPath->name.name, newPath->name.originalName);
305
306 return newPath;
307 }
308
309 const IR::PathExpression *replaceSrcInfo(const IR::PathExpression *pe,
310 const Util::SourceInfo &srcInfo) {
311 if (!srcInfo.isValid()) return pe;
312
313 auto *newPE = pe->clone();
314 newPE->srcInfo = srcInfo;
315 newPE->path = replaceSrcInfo(newPE->path, srcInfo);
316
317 return newPE;
318 }
319
320 const IR::Member *replaceSrcInfo(const IR::Member *member, const Util::SourceInfo &srcInfo) {
321 if (!srcInfo.isValid()) return member;
322
323 auto *newMember = member->clone();
324 newMember->srcInfo = srcInfo;
325
326 if (const auto *exprMember = newMember->expr->to<IR::Member>()) {
327 newMember->expr = replaceSrcInfo(exprMember, srcInfo);
328 } else if (const auto *exprPathExpression = newMember->expr->to<IR::PathExpression>()) {
329 newMember->expr = replaceSrcInfo(exprPathExpression, srcInfo);
330 }
331
332 return newMember;
333 }
334
335 void implementVerifyChecksum(const IR::MethodCallStatement *vc, const IR::ParserState *state) {
336 cstring stateName = state->name;
337 auto &extracts = stateToExtracts[state];
338
339 auto mc = vc->methodCall->to<IR::MethodCallExpression>();
340
341 auto fieldlist = mc->arguments->at(1)->expression;
342 auto destfield = mc->arguments->at(2)->expression;
343
344 // check if any of the fields or dest belong to extracts
345 // TODO verify on ingress only?
346
347 const IR::Declaration *decl = nullptr;
348
349 if (verifyDeclarationMap.count(vc)) {
350 decl = verifyDeclarationMap.at(vc);
351 } else {
352 decl = createChecksumDeclaration(structure, vc);
353 verifyDeclarationMap[vc] = decl;
354 structure->ingressParserDeclarations.push_back(decl);
355 }
356
357 if (fieldlist->is<IR::ListExpression>()) {
358 for (auto f : fieldlist->to<IR::ListExpression>()->components) {
359 for (auto extract : extracts) {
360 if (belongsTo(f->to<IR::Member>(), extract->to<IR::Member>())) {
361 if (const auto *member = f->to<IR::Member>()) {
362 f = replaceSrcInfo(member, state->srcInfo);
363 }
364 auto addCall = new IR::MethodCallStatement(
365 mc->srcInfo,
366 new IR::MethodCallExpression(
367 mc->srcInfo,
368 new IR::Member(new IR::PathExpression(decl->name), "add"),
369 new IR::Vector<IR::Type>({f->type}),
370 new IR::Vector<IR::Argument>({new IR::Argument(f)})));
371
372 structure->ingressParserStatements[stateName].push_back(addCall);
373 translate->ingressVerifyDeclToStates[decl].insert(state->name);
374 }
375 }
376 }
377 } else if (fieldlist->is<IR::StructExpression>()) {
378 for (auto fld : fieldlist->to<IR::StructExpression>()->components) {
379 auto f = fld->expression;
380 for (auto extract : extracts) {
381 if (belongsTo(f->to<IR::Member>(), extract->to<IR::Member>())) {
382 if (const auto *member = f->to<IR::Member>()) {
383 f = replaceSrcInfo(member, state->srcInfo);
384 }
385 auto addCall = new IR::MethodCallStatement(
386 mc->srcInfo,
387 new IR::MethodCallExpression(
388 mc->srcInfo,
389 new IR::Member(new IR::PathExpression(decl->name), "add"),
390 new IR::Vector<IR::Type>({f->type}),
391 new IR::Vector<IR::Argument>({new IR::Argument(f)})));
392
393 structure->ingressParserStatements[stateName].push_back(addCall);
394 translate->ingressVerifyDeclToStates[decl].insert(state->name);
395 LOG1("B: Adding add call: " << addCall);
396 }
397 }
398 }
399 }
400
401 auto *destfieldAdj = destfield;
402 if (const auto *member = destfield->to<IR::Member>()) {
403 destfieldAdj = replaceSrcInfo(member, state->srcInfo);
404 }
405 for (auto extract : extracts) {
406 if (belongsTo(destfieldAdj->to<IR::Member>(), extract->to<IR::Member>())) {
407 BUG_CHECK(decl, "No fields have been added before verify?");
408
409 auto addCall = new IR::MethodCallStatement(
410 mc->srcInfo,
411 new IR::MethodCallExpression(
412 mc->srcInfo, new IR::Member(new IR::PathExpression(decl->name), "add"),
413 new IR::Vector<IR::Type>({destfieldAdj->type}),
414 new IR::Vector<IR::Argument>({new IR::Argument(destfieldAdj)})));
415
416 structure->ingressParserStatements[stateName].push_back(addCall);
417 translate->ingressVerifyDeclToStates[decl].insert(state->name);
418 }
419 }
420 }
421
422 ordered_set<const IR::Expression *> collectResidualChecksumPayloadFields(
423 const IR::ParserState *state) {
425
426 auto descendants = graph->get_all_descendants(state);
427
428 for (auto d : descendants) {
429 auto &extracts = stateToExtracts[d];
430 for (auto m : extracts) rv.insert(m);
431 }
432
433 return rv;
434 }
435
436 void implementParserResidualChecksum(const IR::MethodCallStatement *rc,
437 const IR::ParserState *state,
438 const std::vector<gress_t> &parserUpdateLocations,
439 const std::vector<gress_t> &deparserUpdateLocations) {
440 cstring stateName = state->name;
441 auto &extracts = stateToExtracts[state];
442
443 auto mc = rc->methodCall->to<IR::MethodCallExpression>();
444
445 auto condition = mc->arguments->at(0)->expression;
446 auto fieldlist = mc->arguments->at(1)->expression;
447 auto destfield = mc->arguments->at(2)->expression;
448 if (parserUpdateLocations.size() == 1 && parserUpdateLocations[0] == INGRESS &&
449 deparserUpdateLocations.size() == 1 && deparserUpdateLocations[0] == EGRESS) {
450 translate->needBridging.insert(destfield);
451 }
452
453 if (!translate->residualChecksums.count(destfield)) {
454 auto *compilerMetadataPath = new IR::PathExpression(COMPILER_META);
455 auto *cgAnnotation =
456 new IR::Annotations({new IR::Annotation(IR::ID("__compiler_generated"), {})});
457
458 auto *compilerMetadataDecl = const_cast<IR::Type_Struct *>(
459 structure->type_declarations.at("compiler_generated_metadata_t"_cs)
460 ->to<IR::Type_Struct>());
461 compilerMetadataDecl->annotations = cgAnnotation;
462
463 std::stringstream residualFieldName;
464 residualFieldName << "residual_checksum_";
465 residualFieldName << residualChecksumCnt++;
466
467 auto *residualChecksum = new IR::Member(IR::Type::Bits::get(16), compilerMetadataPath,
468 residualFieldName.str().c_str());
469
470 compilerMetadataDecl->fields.push_back(
471 new IR::StructField(residualFieldName.str().c_str(), IR::Type::Bits::get(16)));
472
473 translate->residualChecksums[destfield] = residualChecksum;
474 translate->destToGressToState[destfield][deparserUpdateLocations[0]] = state;
475 }
476
477 for (auto location : parserUpdateLocations) {
478 std::vector<const IR::Declaration *> *parserDeclarations = nullptr;
479 std::vector<const IR::StatOrDecl *> *parserStatements = nullptr;
480
482 *parserResidualChecksumDecls = nullptr;
483
484 if (location == INGRESS) {
485 parserDeclarations = &structure->ingressParserDeclarations;
486 parserStatements = &structure->ingressParserStatements[stateName];
487 parserResidualChecksumDecls = &ingressParserResidualChecksumDecls;
488 } else if (location == EGRESS) {
489 parserDeclarations = &structure->egressParserDeclarations;
490 parserStatements = &structure->egressParserStatements[stateName];
491 parserResidualChecksumDecls = &egressParserResidualChecksumDecls;
492 }
493
494 IR::Declaration_Instance *decl = nullptr;
495 auto it = parserResidualChecksumDecls->find(rc);
496 if (it == parserResidualChecksumDecls->end()) {
497 decl = createChecksumDeclaration(structure, rc);
498 parserDeclarations->push_back(decl);
499 (*parserResidualChecksumDecls)[rc] = decl;
500 } else {
501 decl = parserResidualChecksumDecls->at(rc);
502 }
503 const IR::Expression *constant = nullptr;
504 for (auto extract : extracts) {
505 std::vector<const IR::Expression *> exprList;
506 if (fieldlist->is<IR::ListExpression>()) {
507 for (auto f : fieldlist->to<IR::ListExpression>()->components) {
508 if (f->is<IR::Constant>()) {
509 constant = f;
510 } else if (belongsTo(f->to<IR::Member>(), extract->to<IR::Member>())) {
511 if (constant) {
512 exprList.emplace_back(constant);
513 constant = nullptr;
514 // If immediate next field after the constant is extracted in this
515 // field then the constant belongs to subtract field list of this
516 // state
517 }
518 exprList.emplace_back(f);
519
520 } else {
521 constant = nullptr;
522 }
523 }
524 } else if (fieldlist->is<IR::StructExpression>()) {
525 for (auto fld : fieldlist->to<IR::StructExpression>()->components) {
526 auto f = fld->expression;
527 if (f->is<IR::Constant>()) {
528 constant = f;
529 } else if (belongsTo(f->to<IR::Member>(), extract->to<IR::Member>())) {
530 if (constant) {
531 exprList.emplace_back(constant);
532 constant = nullptr;
533 // If immediate next field after the constant is extracted in this
534 // field then the constant belongs to subtract field list of this
535 // state
536 }
537 exprList.emplace_back(f);
538
539 } else {
540 constant = nullptr;
541 }
542 }
543 }
544 for (auto e : exprList) {
545 auto subtractCall = new IR::MethodCallStatement(
546 mc->srcInfo,
547 new IR::MethodCallExpression(
548 mc->srcInfo,
549 new IR::Member(new IR::PathExpression(decl->name), "subtract"),
550 new IR::Vector<IR::Type>({e->type}),
551 new IR::Vector<IR::Argument>({new IR::Argument(e)})));
552
553 parserStatements->push_back(subtractCall);
554 }
555
556 if (belongsTo(destfield->to<IR::Member>(), extract->to<IR::Member>())) {
557 auto subtractCall = new IR::MethodCallStatement(
558 mc->srcInfo,
559 new IR::MethodCallExpression(
560 mc->srcInfo,
561 new IR::Member(new IR::PathExpression(decl->name), "subtract"),
562 new IR::Vector<IR::Type>({destfield->type}),
563 new IR::Vector<IR::Argument>({new IR::Argument(destfield)})));
564
565 parserStatements->push_back(subtractCall);
566 auto *residualChecksum = translate->residualChecksums.at(destfield);
567
568 auto *deposit = new IR::MethodCallStatement(
569 mc->srcInfo,
570 new IR::MethodCallExpression(
571 mc->srcInfo,
572 new IR::Member(new IR::PathExpression(decl->name),
573 "subtract_all_and_deposit"),
574 new IR::Vector<IR::Type>({residualChecksum->type}),
575 new IR::Vector<IR::Argument>({new IR::Argument(residualChecksum)})));
576
577 translate
578 ->checksumDepositToHeader[location][extract->to<IR::Member>()->member] =
579 deposit;
580 if (auto boolLiteral = condition->to<IR::BoolLiteral>()) {
581 if (!boolLiteral->value) {
582 // Do not add the if-statement if the condition is always true.
583 deposit = nullptr;
584 }
585 }
586
587 if (deposit) {
588 auto payloadFields = collectResidualChecksumPayloadFields(state);
589 translate->residualChecksumPayloadFields[destfield] = payloadFields;
590 }
591 }
592 }
593 }
594 }
595
596 void postorder(const IR::ParserState *state) override {
597 // see if any of the "verify_checksum" or "update_checksum_with_payload" statement
598 // is relavent to this state. If so, convert to TNA function calls.
599 for (auto *vc : collect->verifyChecksums) {
600 implementVerifyChecksum(vc, state);
601 }
602
603 for (auto *rc : collect->residualChecksums) {
604 implementParserResidualChecksum(rc, state, collect->parserUpdateLocations.at(rc),
605 collect->deparserUpdateLocations.at(rc));
606 }
607 }
608};
609
611 public:
612 const V1::TranslateParserChecksums *translate;
613 explicit InsertChecksumDeposit(const V1::TranslateParserChecksums *translate)
614 : translate(translate) {}
615 const IR::Node *preorder(IR::ParserState *state) override {
616 auto parser = findContext<IR::BFN::TnaParser>();
617 if (!translate->checksumDepositToHeader.count(parser->thread)) return state;
618 auto components = new IR::IndexedVector<IR::StatOrDecl>();
619 auto &checksumDeposit = translate->checksumDepositToHeader.at(parser->thread);
620 for (auto component : state->components) {
621 components->push_back(component);
622 auto methodCall = component->to<IR::MethodCallStatement>();
623 if (!methodCall) continue;
624 auto call = methodCall->methodCall->to<IR::MethodCallExpression>();
625 if (!call) continue;
626 if (auto method = call->method->to<IR::Member>()) {
627 if (method->member == "extract") {
628 for (auto arg : *call->arguments) {
629 const IR::Member *member = nullptr;
630 if (auto index = arg->expression->to<IR::ArrayIndex>()) {
631 member = index->left->to<IR::Member>();
632 } else if (arg->expression->is<IR::Member>()) {
633 member = arg->expression->to<IR::Member>();
634 }
635 if (member && checksumDeposit.count(member->member)) {
636 components->push_back(checksumDeposit.at(member->member));
637 }
638 }
639 }
640 }
641 }
642 state->components = *components;
643 return state;
644 }
645};
646
648 public:
649 std::map<cstring, ordered_map<const IR::Declaration *, ordered_set<cstring>>> endStates;
650
651 struct ComputeEndStates : public Inspector {
653
654 explicit ComputeEndStates(InsertChecksumError *self) : self(self) {}
655
656 void printStates(const ordered_set<cstring> &states) {
657 for (auto s : states) std::cout << " " << s << std::endl;
658 }
659
660 ordered_set<cstring> computeChecksumEndStates(const ordered_set<cstring> &calcStates) {
661 auto &parserGraphs = self->translate->parserGraphs;
662
663 if (LOGGING(3)) {
664 std::cout << "calc states are:" << std::endl;
665 printStates(calcStates);
666 }
667
668 ordered_set<cstring> endStates;
669
670 // A calculation state is a verification end state if no other state of the
671 // same calculation is its descendant. Otherwise, include all of the state's
672 // children states that are not a calculation state.
673
674 for (auto a : calcStates) {
675 bool isEndState = true;
676 for (auto b : calcStates) {
677 if (parserGraphs.is_ancestor(a, b)) {
678 isEndState = false;
679 break;
680 }
681 }
682 if (isEndState) {
683 endStates.insert(a);
684 } else {
685 if (parserGraphs.succs.count(a)) {
686 for (auto succ : parserGraphs.succs.at(a)) {
687 if (calcStates.count(succ)) continue;
688
689 for (auto s : calcStates) {
690 if (!parserGraphs.is_ancestor(succ, s)) {
691 endStates.insert(succ);
692 }
693 }
694 }
695 }
696 }
697 }
698
699 BUG_CHECK(!endStates.empty(), "Unable to find verification end state?");
700
701 if (LOGGING(3)) {
702 std::cout << "end states are:" << std::endl;
703 printStates(endStates);
704 }
705
706 return endStates;
707 }
708
709 bool preorder(const IR::BFN::TnaParser *parser) override {
710 // TODO verify on ingress only
711 if (parser->thread != INGRESS) return false;
712
713 // compute checksum end states
714 for (auto kv : self->translate->ingressVerifyDeclToStates)
715 self->endStates[parser->name][kv.first] = computeChecksumEndStates(kv.second);
716
717 return false;
718 }
719 };
720
721 // TODO we probably don't want to insert statement into the "accept" state
722 // since this is a special state. Add a dummy state before "accept" if it is
723 // a checksum verification end state.
725 const IR::Node *preorder(IR::BFN::TnaParser *parser) override {
726 for (auto &kv : self->endStates[parser->name]) {
727 if (kv.second.count("accept"_cs)) {
728 if (!dummy) {
729 dummy = createGeneratedParserState("before_accept"_cs, {}, "accept"_cs);
730 parser->states.push_back(dummy);
731 }
732 kv.second.erase("accept"_cs);
733 kv.second.insert("__before_accept"_cs);
734 LOG3("add dummy state before \"accept\"");
735 }
736 }
737
738 return parser;
739 }
740
741 const IR::Node *postorder(IR::PathExpression *path) override {
742 auto parser = findContext<IR::BFN::TnaParser>();
743 auto state = findContext<IR::ParserState>();
744 auto select = findContext<IR::SelectCase>();
745
746 if (parser && state && select) {
747 bool isCalcState = false;
748
749 for (auto kv : self->translate->ingressVerifyDeclToStates) {
750 for (auto s : kv.second) {
751 if (s == state->name) {
752 isCalcState = true;
753 break;
754 }
755 }
756 }
757
758 if (!isCalcState) return path;
759
760 for (auto &kv : self->endStates[parser->name]) {
761 if (path->path->name == "accept" && kv.second.count("__before_accept"_cs)) {
762 path = new IR::PathExpression("__before_accept");
763 LOG3("modify transition to \"before_accept\"");
764 }
765 }
766 }
767
768 return path;
769 }
770
771 const IR::ParserState *dummy = nullptr;
773
774 explicit InsertBeforeAccept(InsertChecksumError *self) : self(self) {}
775 };
776
777 struct InsertEndStates : public Transform {
778 const IR::Node *preorder(IR::ParserState *state) override {
779 auto parser = findContext<IR::BFN::TnaParser>();
780
781 if (state->name == "reject") return state;
782
783 for (auto &kv : self->endStates[parser->name]) {
784 auto *decl = kv.first;
785 for (auto endState : kv.second) {
786 if (endState == state->name) {
787 auto *checksumError = createChecksumError(decl, parser->thread);
788 state->components.push_back(checksumError);
789
790 LOG3("verify " << toString(parser->thread) << " " << decl->name.name
791 << " in state " << endState);
792 }
793 }
794 }
795
796 return state;
797 }
798
800
801 explicit InsertEndStates(InsertChecksumError *self) : self(self) {}
802 };
803
804 explicit InsertChecksumError(const V1::TranslateParserChecksums *translate)
805 : translate(translate) {
806 addPasses({
807 new ComputeEndStates(this),
808 new InsertBeforeAccept(this),
809 new InsertEndStates(this),
810 });
811 }
812
813 const V1::TranslateParserChecksums *translate;
814};
815
816} // namespace BFN::V1
817
818#endif /* BACKENDS_TOFINO_BF_P4C_ARCH_FROMV1_0_CHECKSUM_H_ */
Definition checksum.h:172
Definition checksum.h:610
Definition checksum.h:647
Definition checksum.h:210
Definition checksum.h:42
Definition methodInstance.h:194
Definition node.h:52
Definition node.h:94
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 ir/pass_manager.h:40
Definition backends/common/programStructure.h:32
Class used to encode maps from paths to declarations.
Definition referenceMap.h:66
Definition visitor.h:424
Definition typeMap.h:41
Definition source_file.h:131
Definition visitor.h:78
Definition cstring.h:85
Definition ordered_set.h:32
Definition assoc.h:300
Definition assoc.h:355
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
void warning(const char *format, Args &&...args)
Report a warning with the given message.
Definition lib/error.h:115
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition lib/error.h:51
Definition v1_program_structure.h:36
Definition checksum.h:30
Definition id.h:28