P4C
The P4 Compiler
Loading...
Searching...
No Matches
control.h
1/*
2 * SPDX-FileCopyrightText: 2013 Barefoot Networks, Inc.
3 * Copyright 2013-present Barefoot Networks, Inc.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8#ifndef BACKENDS_BMV2_COMMON_CONTROL_H_
9#define BACKENDS_BMV2_COMMON_CONTROL_H_
10
11#include "controlFlowGraph.h"
12#include "expression.h"
13#include "extern.h"
14#include "frontends/common/resolveReferences/referenceMap.h"
15#include "frontends/p4/coreLibrary.h"
16#include "frontends/p4/typeChecking/typeChecker.h"
17#include "frontends/p4/typeMap.h"
18#include "helpers.h"
19#include "ir/ir.h"
20#include "lib/algorithm.h"
21#include "lib/json.h"
22#include "midend/convertEnums.h"
23#include "sharedActionSelectorCheck.h"
24
25namespace P4::BMV2 {
26
27static constexpr unsigned INVALID_ACTION_ID = 0xffffffff;
28
29template <Standard::Arch arch>
30class ControlConverter : public Inspector {
32 cstring name;
33 P4::P4CoreLibrary &corelib;
34
35 protected:
36 Util::IJson *convertTable(const CFG::TableNode *node, Util::JsonArray *action_profiles,
38 auto table = node->table;
39 LOG3("Processing " << dbp(table));
40 auto result = new Util::JsonObject();
41 cstring name = table->controlPlaneName();
42 result->emplace("name", name);
43 result->emplace("id", nextId("tables"_cs));
44 result->emplace_non_null("source_info"_cs, table->sourceInfoJsonObj());
45 cstring table_match_type = corelib.exactMatch.name;
46 auto key = table->getKey();
47 auto tkey = mkArrayField(result, "key"_cs);
48 ctxt->conv->simpleExpressionsOnly = true;
49
50 if (key != nullptr) {
51 int count_lpm = 0;
52 for (auto ke : key->keyElements) {
53 auto expr = ke->expression;
54 auto ket = ctxt->typeMap->getType(expr, true);
55 if (!ket->is<IR::Type_Bits>() && !ket->is<IR::Type_Boolean>() &&
56 !ket->is<IR::Type_Error>())
57 ::P4::error(ErrorType::ERR_UNSUPPORTED,
58 "%1%: unsupporded key type %2%. "
59 "Supported key types are be bit<> or boolean, or error.",
60 expr, ket);
61
62 auto match_type = getKeyMatchType(ke);
63 if (match_type == BMV2::MatchImplementation::selectorMatchTypeName) continue;
64 // Decreasing order of precedence (bmv2 specification):
65 // 0) more than one LPM field is an error
66 // 1) if there is at least one RANGE field, then the table is RANGE
67 // 2) if there is at least one TERNARY or OPTIONAL field, then the table is TERNARY
68 // 3) if there is a LPM field, then the table is LPM
69 // 4) otherwise the table is EXACT
70 if (match_type == corelib.lpmMatch.name) count_lpm++;
71 if (count_lpm > 1)
72 ::P4::error(ErrorType::ERR_UNSUPPORTED,
73 "multiple LPM keys in table %1% not supported", table);
74 if (match_type != table_match_type) {
75 if (match_type == BMV2::MatchImplementation::rangeMatchTypeName)
76 table_match_type = BMV2::MatchImplementation::rangeMatchTypeName;
77 if ((match_type == corelib.ternaryMatch.name ||
78 match_type == BMV2::MatchImplementation::optionalMatchTypeName) &&
79 table_match_type != BMV2::MatchImplementation::rangeMatchTypeName)
80 table_match_type = corelib.ternaryMatch.name;
81 if (match_type == corelib.lpmMatch.name &&
82 table_match_type == corelib.exactMatch.name)
83 table_match_type = corelib.lpmMatch.name;
84 }
85
86 big_int mask;
87 if (auto mexp = expr->to<IR::BAnd>()) {
88 if (mexp->right->is<IR::Constant>()) {
89 mask = mexp->right->to<IR::Constant>()->value;
90 expr = mexp->left;
91 } else if (mexp->left->is<IR::Constant>()) {
92 mask = mexp->left->to<IR::Constant>()->value;
93 expr = mexp->right;
94 } else {
95 ::P4::error(ErrorType::ERR_EXPECTED, "%1% must be a constant", expr);
96 }
97 } else if (auto slice = expr->to<IR::Slice>()) {
98 expr = slice->e0;
99 int h = slice->getH();
100 int l = slice->getL();
101 mask = Util::maskFromSlice(h, l);
102 }
103
104 auto keyelement = new Util::JsonObject();
105 // Table key fields with match_kind optional will be
106 // represented in the BMv2 JSON file the same as a ternary
107 // field would be.
108 if (match_type == BMV2::MatchImplementation::optionalMatchTypeName) {
109 keyelement->emplace("match_type", corelib.ternaryMatch.name);
110 } else {
111 keyelement->emplace("match_type", match_type);
112 }
113 if (auto na = ke->getAnnotation(IR::Annotation::nameAnnotation)) {
114 BUG_CHECK(na->getExpr().size() == 1, "%1%: expected 1 name", na);
115 auto name = na->getExpr(0)->to<IR::StringLiteral>();
116 BUG_CHECK(name != nullptr, "%1%: expected a string", na);
117 // This is a BMv2 JSON extension: specify a
118 // control-plane name for this key
119 keyelement->emplace("name", name->value);
120 }
121
122 auto jk = ctxt->conv->convert(expr);
123 keyelement->emplace("target", jk->to<Util::JsonObject>()->get("value"));
124 if (mask != 0)
125 keyelement->emplace("mask",
126 stringRepr(mask, ROUNDUP(expr->type->width_bits(), 8)));
127 else
128 keyelement->emplace("mask", Util::JsonValue::null);
129 tkey->append(keyelement);
130 }
131 }
132 LOG3("table_match_type: " << table_match_type);
133 result->emplace("match_type", table_match_type);
134 ctxt->conv->simpleExpressionsOnly = false;
135
137 auto impl = table->properties->getProperty(propertyName);
138 bool simple = handleTableImplementation(impl, key, result, action_profiles, selector_check);
139
140 unsigned size = 0;
141 auto sz = table->properties->getProperty("size");
142 if (sz != nullptr) {
143 if (sz->value->is<IR::ExpressionValue>()) {
144 auto expr = sz->value->to<IR::ExpressionValue>()->expression;
145 if (!expr->is<IR::Constant>()) {
146 ::P4::error(ErrorType::ERR_EXPECTED, "%1% must be a constant", sz);
147 size = 0;
148 } else {
149 size = expr->to<IR::Constant>()->asInt();
150 }
151 } else {
152 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a number", sz);
153 }
154 }
155 if (auto entries = table->getEntries()) {
156 size = entries->entries.size();
157 }
158 if (size == 0) size = BMV2::TableAttributes::defaultTableSize;
159
160 result->emplace("max_size", size);
161 auto ctrs = table->properties->getProperty("counters");
162 if (ctrs != nullptr) {
163 // The counters attribute should list the counters of the table, accessed in
164 // actions of the table. We should be checking that this attribute and the
165 // actions are consistent?
166 if (ctrs->value->is<IR::ExpressionValue>()) {
167 auto expr = ctrs->value->to<IR::ExpressionValue>()->expression;
168 if (expr->is<IR::ConstructorCallExpression>()) {
169 auto type = ctxt->typeMap->getType(expr, true);
170 if (type == nullptr) return result;
171 if (!type->is<IR::Type_Extern>()) {
172 ::P4::error(ErrorType::ERR_UNEXPECTED,
173 "%1%: Unexpected type %2% for property. "
174 "Must be extern.",
175 ctrs, type);
176 return result;
177 }
178 auto te = type->to<IR::Type_Extern>();
179 if (te->name != "direct_counter" && te->name != "counter") {
180 ::P4::error(ErrorType::ERR_UNEXPECTED,
181 "%1%: Unexpected type %2% for property. "
182 "Must be 'counter' or 'direct_counter'.",
183 ctrs, type);
184 return result;
185 }
186 auto jctr = new Util::JsonObject();
187 cstring ctrname = ctrs->controlPlaneName("counter"_cs);
188 jctr->emplace("name", ctrname);
189 jctr->emplace("id", nextId("counter_arrays"_cs));
190 jctr->emplace_non_null("source_info"_cs, ctrs->sourceInfoJsonObj());
191 // TODO(jafingerhut) - what kind of P4_16 code causes this
192 // code to run, if any?
193 bool direct = te->name == "direct_counter";
194 jctr->emplace("is_direct", direct);
195 jctr->emplace("binding", table->controlPlaneName());
196 ctxt->json->counters->append(jctr);
197 } else if (expr->is<IR::PathExpression>()) {
198 auto pe = expr->to<IR::PathExpression>();
199 auto decl = ctxt->refMap->getDeclaration(pe->path, true);
200 if (!decl->is<IR::Declaration_Instance>()) {
201 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected an instance",
202 decl->getNode());
203 return result;
204 }
205 cstring ctrname = decl->controlPlaneName();
206 auto it = ctxt->structure->directCounterMap.find(ctrname);
207 LOG3("Looking up " << ctrname);
208 if (it != ctxt->structure->directCounterMap.end()) {
209 ::P4::error(ErrorType::ERR_INVALID,
210 "%1%: Direct counters cannot be attached to multiple tables"
211 " %2% and %3%",
212 decl, it->second, table);
213 return result;
214 }
215 ctxt->structure->directCounterMap.emplace(ctrname, table);
216 } else {
217 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a counter", ctrs);
218 }
219 }
220 result->emplace("with_counters", true);
221 } else {
222 result->emplace("with_counters", false);
223 }
224
225 bool sup_to = false;
226 auto timeout = table->properties->getProperty("support_timeout");
227 if (timeout != nullptr) {
228 if (timeout->value->is<IR::ExpressionValue>()) {
229 auto expr = timeout->value->to<IR::ExpressionValue>()->expression;
230 if (!expr->is<IR::BoolLiteral>()) {
231 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: must true/false", timeout);
232 } else {
233 sup_to = expr->to<IR::BoolLiteral>()->value;
234 }
235 } else {
236 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a Boolean", timeout);
237 }
238 }
239 result->emplace("support_timeout", sup_to);
240
241 auto dm = table->properties->getProperty("meters");
242 if (dm != nullptr) {
243 if (dm->value->is<IR::ExpressionValue>()) {
244 auto expr = dm->value->to<IR::ExpressionValue>()->expression;
245 if (!expr->is<IR::PathExpression>()) {
246 ::P4::error(ErrorType::ERR_EXPECTED,
247 "%1%: expected a reference to a meter declaration", expr);
248 } else {
249 auto pe = expr->to<IR::PathExpression>();
250 auto decl = ctxt->refMap->getDeclaration(pe->path, true);
251 auto type = ctxt->typeMap->getType(expr, true);
252 if (type == nullptr) return result;
253 if (type->is<IR::Type_SpecializedCanonical>())
254 type = type->to<IR::Type_SpecializedCanonical>()->baseType;
255 if (!type->is<IR::Type_Extern>()) {
256 ::P4::error(ErrorType::ERR_UNEXPECTED,
257 "%1%: Unexpected type %2% for property", dm, type);
258 return result;
259 }
260 auto te = type->to<IR::Type_Extern>();
261 if (te->name != "direct_meter") {
262 ::P4::error(ErrorType::ERR_UNEXPECTED,
263 "%1%: Unexpected type %2% for property", dm, type);
264 return result;
265 }
266 if (!decl->is<IR::Declaration_Instance>()) {
267 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected an instance",
268 decl->getNode());
269 return result;
270 }
271 ctxt->structure->directMeterMap.setTable(decl, table);
272 ctxt->structure->directMeterMap.setSize(decl, size);
273 BUG_CHECK(decl->is<IR::Declaration_Instance>(), "%1%: expected an instance",
274 decl->getNode());
275 cstring name = decl->controlPlaneName();
276 result->emplace("direct_meters", name);
277 }
278 } else {
279 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a meter", dm);
280 }
281 } else {
282 result->emplace("direct_meters", Util::JsonValue::null);
283 }
284
285 auto action_ids = mkArrayField(result, "action_ids"_cs);
286 auto actions = mkArrayField(result, "actions"_cs);
287 auto al = table->getActionList();
288
289 std::map<cstring, cstring> useActionName;
290 for (auto a : al->actionList) {
291 if (a->expression->is<IR::MethodCallExpression>()) {
292 auto mce = a->expression->to<IR::MethodCallExpression>();
293 if (mce->arguments->size() > 0)
294 ::P4::error(ErrorType::ERR_UNSUPPORTED,
295 "%1%: actions in action list with arguments not supported", a);
296 }
297 auto decl = ctxt->refMap->getDeclaration(a->getPath(), true);
298 BUG_CHECK(decl->is<IR::P4Action>(), "%1%: should be an action name", a);
299 auto action = decl->to<IR::P4Action>();
300 unsigned id = get(ctxt->structure->ids, action, INVALID_ACTION_ID);
301 LOG3("look up id " << action << " " << id);
302 BUG_CHECK(id != INVALID_ACTION_ID, "Could not find id for %1%", action);
303 action_ids->append(id);
304 auto name = action->controlPlaneName();
305 actions->append(name);
306 useActionName.emplace(action->name, name);
307 }
308
309 auto next_tables = new Util::JsonObject();
310
311 CFG::Node *nextDestination = nullptr; // if no action is executed
312 CFG::Node *defaultLabelDestination = nullptr; // if the "default" label is executed
313 // Note: the "default" label is not the default_action.
314 bool hitMiss = false;
315 for (auto s : node->successors.edges) {
316 if (s->isUnconditional()) {
317 nextDestination = s->endpoint;
318 LOG3("nextDestination " << s->endpoint);
319 } else if (s->isBool()) {
320 hitMiss = true;
321 LOG3("hitmiss");
322 } else if (s->label == "default") {
323 defaultLabelDestination = s->endpoint;
324 LOG3("default " << s->endpoint);
325 }
326 }
327
328 Util::IJson *nextLabel = nullptr;
329 if (!hitMiss) {
330 BUG_CHECK(nextDestination, "Could not find default destination for %1%",
331 node->invocation);
332 nextLabel = nodeName(nextDestination);
333 result->emplace("base_default_next", nextLabel);
334 // So if a "default:" switch case exists we set the nextLabel
335 // to be the destination of the default: label.
336 if (defaultLabelDestination != nullptr) nextLabel = nodeName(defaultLabelDestination);
337 } else {
338 result->emplace("base_default_next", Util::JsonValue::null);
339 }
340
341 std::set<cstring> labelsDone;
342 for (auto s : node->successors.edges) {
343 cstring label;
344 if (s->isBool()) {
345 label = s->getBool() ? "__HIT__"_cs : "__MISS__"_cs;
346 } else if (s->isUnconditional()) {
347 continue;
348 } else {
349 label = s->label;
350 if (label == "default") continue;
351 label = ::P4::get(useActionName, label);
352 }
353 next_tables->emplace(label, nodeName(s->endpoint));
354 labelsDone.emplace(label);
355 }
356
357 // Generate labels which don't show up and send them to
358 // the nextLabel.
359 if (!hitMiss) {
360 for (auto a : al->actionList) {
361 cstring name = a->getName().name;
362 cstring label = ::P4::get(useActionName, name);
363 if (labelsDone.find(label) == labelsDone.end())
364 next_tables->emplace(label, nextLabel);
365 }
366 }
367
368 result->emplace("next_tables", next_tables);
369 auto defact =
370 table->properties->getProperty(IR::TableProperties::defaultActionPropertyName);
371 if (defact != nullptr) {
372 if (!simple) {
374 ErrorType::WARN_UNSUPPORTED,
375 "Target does not support default_action for %1% (due to action profiles)",
376 table);
377 return result;
378 }
379
380 if (!defact->value->is<IR::ExpressionValue>()) {
381 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected an action", defact);
382 return result;
383 }
384 auto expr = defact->value->to<IR::ExpressionValue>()->expression;
385 const IR::P4Action *action = nullptr;
386 const IR::Vector<IR::Argument> *args = nullptr;
387
388 if (expr->is<IR::PathExpression>()) {
389 auto path = expr->to<IR::PathExpression>()->path;
390 auto decl = ctxt->refMap->getDeclaration(path, true);
391 BUG_CHECK(decl->is<IR::P4Action>(), "%1%: should be an action name", expr);
392 action = decl->to<IR::P4Action>();
393 } else if (expr->is<IR::MethodCallExpression>()) {
394 auto mce = expr->to<IR::MethodCallExpression>();
395 auto mi = P4::MethodInstance::resolve(mce, ctxt->refMap, ctxt->typeMap);
396 BUG_CHECK(mi->is<P4::ActionCall>(), "%1%: expected an action", expr);
397 action = mi->to<P4::ActionCall>()->action;
398 args = mce->arguments;
399 } else {
400 BUG("%1%: unexpected expression", expr);
401 }
402
403 unsigned actionid = get(ctxt->structure->ids, action, INVALID_ACTION_ID);
404 BUG_CHECK(actionid != INVALID_ACTION_ID, "Could not find id for %1%", action);
405 auto entry = new Util::JsonObject();
406 entry->emplace("action_id", actionid);
407 entry->emplace("action_const", defact->isConstant);
408 auto fields = mkArrayField(entry, "action_data"_cs);
409 if (args != nullptr) {
410 // TODO: use argument names
411 for (auto a : *args) {
412 if (a->expression->is<IR::Constant>()) {
413 cstring repr = stringRepr(a->expression->to<IR::Constant>()->value);
414 fields->append(repr);
415 } else {
416 ::P4::error(ErrorType::ERR_EXPECTED,
417 "%1%: argument must evaluate to a constant integer", a);
418 return result;
419 }
420 }
421 }
422 entry->emplace("action_entry_const", defact->isConstant);
423 result->emplace("default_entry", entry);
424 }
425 convertTableEntries(table, result);
426 return result;
427 }
428 void convertTableEntries(const IR::P4Table *table, Util::JsonObject *jsonTable) {
429 auto entriesList = table->getEntries();
430 if (entriesList == nullptr) return;
431
432 auto entries = mkArrayField(jsonTable, "entries"_cs);
433 int entryPriority = 1; // default priority is defined by index position
434 for (auto e : entriesList->entries) {
435 auto entry = new Util::JsonObject();
436 entry->emplace_non_null("source_info"_cs, e->sourceInfoJsonObj());
437
438 auto keyset = e->getKeys();
439 auto matchKeys = mkArrayField(entry, "match_key"_cs);
440 int keyIndex = 0;
441 for (auto k : keyset->components) {
442 auto key = new Util::JsonObject();
443 auto tableKey = table->getKey()->keyElements.at(keyIndex);
444 int keyWidth = 0;
445 if (tableKey->expression->type->is<IR::Type_Error>()) {
446 // error type doesn't have a width, and will fail below, checking the key
447 // expression k, so it doesn't matter what keyWidth is.
448 } else {
449 keyWidth = tableKey->expression->type->width_bits();
450 }
451 auto k8 = ROUNDUP(keyWidth, 8);
452 auto matchType = getKeyMatchType(tableKey);
453 // Table key fields with match_kind optional will be
454 // represented in the BMv2 JSON file the same as a ternary
455 // field would be.
456 if (matchType == "optional") {
457 key->emplace("match_type", "ternary");
458 } else {
459 key->emplace("match_type", matchType);
460 }
461 if (matchType == corelib.exactMatch.name) {
462 if (k->is<IR::Constant>())
463 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
464 else if (k->is<IR::BoolLiteral>())
465 // booleans are converted to ints
466 key->emplace("key",
467 stringRepr(k->to<IR::BoolLiteral>()->value ? 1 : 0, k8));
468 else
469 ::P4::error(ErrorType::ERR_UNSUPPORTED,
470 "%1%: unsupported exact key expression", k);
471 } else if (matchType == corelib.ternaryMatch.name) {
472 if (k->is<IR::Mask>()) {
473 auto km = k->to<IR::Mask>();
474 key->emplace("key", stringRepr(km->left->to<IR::Constant>()->value, k8));
475 key->emplace("mask", stringRepr(km->right->to<IR::Constant>()->value, k8));
476 } else if (k->is<IR::Constant>()) {
477 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
478 key->emplace("mask", stringRepr(Util::mask(keyWidth), k8));
479 } else if (k->is<IR::BoolLiteral>()) {
480 key->emplace("key",
481 stringRepr(k->to<IR::BoolLiteral>()->value ? 1 : 0, k8));
482 key->emplace("mask", stringRepr(Util::mask(keyWidth), k8));
483 } else if (k->is<IR::DefaultExpression>()) {
484 key->emplace("key", stringRepr(0, k8));
485 key->emplace("mask", stringRepr(0, k8));
486 } else {
487 ::P4::error(ErrorType::ERR_UNSUPPORTED,
488 "%1%: unsupported ternary key expression", k);
489 }
490 } else if (matchType == corelib.lpmMatch.name) {
491 if (k->is<IR::Mask>()) {
492 auto km = k->to<IR::Mask>();
493 key->emplace("key", stringRepr(km->left->to<IR::Constant>()->value, k8));
494 auto trailing_zeros = [](unsigned long n, unsigned long keyWidth) {
495 return n ? __builtin_ctzl(n) : static_cast<int>(keyWidth);
496 };
497 auto count_ones = [](unsigned long n) {
498 return n ? __builtin_popcountl(n) : 0;
499 };
500 auto mask =
501 static_cast<unsigned long>(km->right->to<IR::Constant>()->value);
502 auto len = trailing_zeros(mask, keyWidth);
503 if (len + count_ones(mask) != keyWidth) // any remaining 0s in the prefix?
504 ::P4::error(ErrorType::ERR_INVALID, "%1%: invalid mask for LPM key", k);
505 else
506 key->emplace("prefix_length", keyWidth - len);
507 } else if (k->is<IR::Constant>()) {
508 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
509 key->emplace("prefix_length", keyWidth);
510 } else if (k->is<IR::DefaultExpression>()) {
511 key->emplace("key", stringRepr(0, k8));
512 key->emplace("prefix_length", 0);
513 } else {
514 ::P4::error(ErrorType::ERR_UNSUPPORTED,
515 "%1%: unsupported LPM key expression", k);
516 }
517 } else if (matchType == "range") {
518 if (k->is<IR::Range>()) {
519 auto kr = k->to<IR::Range>();
520 key->emplace("start", stringRepr(kr->left->to<IR::Constant>()->value, k8));
521 key->emplace("end", stringRepr(kr->right->to<IR::Constant>()->value, k8));
522 } else if (k->is<IR::Constant>()) {
523 key->emplace("start", stringRepr(k->to<IR::Constant>()->value, k8));
524 key->emplace("end", stringRepr(k->to<IR::Constant>()->value, k8));
525 } else if (k->is<IR::DefaultExpression>()) {
526 key->emplace("start", stringRepr(0, k8));
527 key->emplace("end", stringRepr((1 << keyWidth) - 1, k8)); // 2^N -1
528 } else {
529 ::P4::error(ErrorType::ERR_UNSUPPORTED,
530 "%1% unsupported range key expression", k);
531 }
532 } else if (matchType == "optional") {
533 // Table key fields with match_kind optional with
534 // "const entries" in the P4 source code will be
535 // represented using the same "key" and "mask" keys in
536 // the BMv2 JSON file as table key fields with
537 // match_kind ternary. In the P4 source code we only
538 // allow exact values or a DefaultExpression (_ or
539 // default), no &&& expression.
540 if (k->is<IR::Constant>()) {
541 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
542 key->emplace("mask", stringRepr(Util::mask(keyWidth), k8));
543 } else if (k->is<IR::DefaultExpression>()) {
544 key->emplace("key", stringRepr(0, k8));
545 key->emplace("mask", stringRepr(0, k8));
546 } else {
547 ::P4::error(ErrorType::ERR_UNSUPPORTED,
548 "%1%: unsupported optional key expression", k);
549 }
550 } else {
551 ::P4::error(ErrorType::ERR_UNKNOWN, "unknown key match type '%2%' for key %1%",
552 k, matchType);
553 }
554 matchKeys->append(key);
555 keyIndex++;
556 }
557
558 auto action = new Util::JsonObject();
559 auto actionRef = e->getAction();
560 if (!actionRef->is<IR::MethodCallExpression>())
561 ::P4::error(ErrorType::ERR_INVALID, "Invalid action '%1%' in entries list.",
562 actionRef);
563 auto actionCall = actionRef->to<IR::MethodCallExpression>();
564 auto method = actionCall->method->to<IR::PathExpression>()->path;
565 auto decl = ctxt->refMap->getDeclaration(method, true);
566 auto actionDecl = decl->to<IR::P4Action>();
567 unsigned id = get(ctxt->structure->ids, actionDecl, INVALID_ACTION_ID);
568 BUG_CHECK(id != INVALID_ACTION_ID, "Could not find id for %1%", actionDecl);
569 action->emplace("action_id", id);
570 auto actionData = mkArrayField(action, "action_data"_cs);
571 for (auto arg : *actionCall->arguments) {
572 actionData->append(stringRepr(arg->expression->to<IR::Constant>()->value, 0));
573 }
574 entry->emplace("action_entry", action);
575
576 if (auto priorityAnnotation = e->getAnnotation("priority"_cs)) {
577 const auto &expr = priorityAnnotation->getExpr();
578 if (expr.size() > 1)
579 ::P4::error(ErrorType::ERR_INVALID, "Invalid priority value %1%", expr);
580 auto priValue = expr.front();
581 if (!priValue->is<IR::Constant>())
582 ::P4::error(ErrorType::ERR_INVALID,
583 "Invalid priority value %1%; must be constant.", expr);
584 entry->emplace("priority", priValue->to<IR::Constant>()->value);
585 } else {
586 entry->emplace("priority", entryPriority);
587 }
588 entryPriority += 1;
589
590 entries->append(entry);
591 }
592 }
593 cstring getKeyMatchType(const IR::KeyElement *ke) {
594 auto path = ke->matchType->path;
595 auto mt = ctxt->refMap->getDeclaration(path, true)->to<IR::Declaration_ID>();
596 BUG_CHECK(mt != nullptr, "%1%: could not find declaration", ke->matchType);
597
598 if (mt->name.name == corelib.exactMatch.name ||
599 mt->name.name == corelib.ternaryMatch.name || mt->name.name == corelib.lpmMatch.name ||
600 ctxt->structure->match_kinds.count(mt->name.name)) {
601 return mt->name.name;
602 }
603
604 ::P4::error(ErrorType::ERR_UNSUPPORTED, "%1%: match type not supported on this target", mt);
605 return "invalid"_cs;
606 }
608 bool handleTableImplementation(const IR::Property *implementation, const IR::Key *key,
609 Util::JsonObject *table, Util::JsonArray *action_profiles,
611 if (implementation == nullptr) {
612 table->emplace("type", "simple");
613 return true;
614 }
615
616 if (!implementation->value->is<IR::ExpressionValue>()) {
617 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected expression for property",
618 implementation);
619 return false;
620 }
621 auto propv = implementation->value->to<IR::ExpressionValue>();
622
623 bool isSimpleTable = true;
624 Util::JsonObject *action_profile;
625 cstring apname;
626
627 if (propv->expression->is<IR::ConstructorCallExpression>()) {
628 auto cc =
629 P4::ConstructorCall::resolve(propv->expression->to<IR::ConstructorCallExpression>(),
630 ctxt->refMap, ctxt->typeMap);
631 if (!cc->is<P4::ExternConstructorCall>()) {
632 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected extern object for property",
633 implementation);
634 return false;
635 }
636 auto ecc = cc->to<P4::ExternConstructorCall>();
637 auto implementationType = ecc->type;
638 auto arguments = ecc->cce->arguments;
639 apname = implementation->controlPlaneName(ctxt->refMap->newName("action_profile"));
640 action_profile = new Util::JsonObject();
641 action_profiles->append(action_profile);
642 action_profile->emplace("name", apname);
643 action_profile->emplace("id", nextId("action_profiles"_cs));
644 action_profile->emplace_non_null("source_info"_cs,
645 propv->expression->sourceInfoJsonObj());
646 // TODO(jafingerhut) - add line/col here?
647 // TBD what about the else if cases below?
648
649 auto add_size = [&action_profile, &arguments](size_t arg_index) {
650 auto size_expr = arguments->at(arg_index)->expression;
651 int size;
652 if (!size_expr->is<IR::Constant>()) {
653 ::P4::error(ErrorType::ERR_EXPECTED, "%1% must be a constant", size_expr);
654 size = 0;
655 } else {
656 size = size_expr->to<IR::Constant>()->asInt();
657 }
658 action_profile->emplace("max_size", size);
659 };
660
661 auto actionSelectorName = Standard::ActionSelectorTraits<arch>::typeName();
662 if (implementationType->name == actionSelectorName) {
663 BUG_CHECK(arguments->size() == 3, "%1%: expected 3 arguments", arguments);
664 isSimpleTable = false;
665 auto selector = new Util::JsonObject();
666 table->emplace("type", "indirect_ws");
667 action_profile->emplace("selector"_cs, selector);
668 add_size(1);
669 auto hash = arguments->at(0)->expression;
670 auto ei = P4::EnumInstance::resolve(hash, ctxt->typeMap);
671 if (ei == nullptr) {
672 ::P4::error(ErrorType::ERR_EXPECTED,
673 "%1%: hash must be a constant on this target", hash);
674 } else {
675 cstring algo = ei->name;
676 selector->emplace("algo", algo);
677 }
678 auto input = mkArrayField(selector, "input"_cs);
679 for (auto ke : key->keyElements) {
680 auto mt = ctxt->refMap->getDeclaration(ke->matchType->path, true)
681 ->to<IR::Declaration_ID>();
682 BUG_CHECK(mt != nullptr, "%1%: could not find declaration", ke->matchType);
683 if (mt->name.name != BMV2::MatchImplementation::selectorMatchTypeName) continue;
684
685 auto expr = ke->expression;
686 auto jk = ctxt->conv->convert(expr);
687 input->append(jk);
688 }
689 } else if (implementationType->name ==
691 isSimpleTable = false;
692 table->emplace("type", "indirect");
693 add_size(0);
694 } else {
695 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: expected value for property", propv);
696 }
697 } else if (propv->expression->is<IR::PathExpression>()) {
698 auto pathe = propv->expression->to<IR::PathExpression>();
699 auto decl = ctxt->refMap->getDeclaration(pathe->path, true);
700 if (!decl->is<IR::Declaration_Instance>()) {
701 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a reference to an instance",
702 pathe);
703 return false;
704 }
705 apname = decl->controlPlaneName();
706 auto dcltype = ctxt->typeMap->getType(pathe, true);
707 if (!dcltype->is<IR::Type_Extern>()) {
708 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected type for implementation",
709 dcltype);
710 return false;
711 }
712 auto type_extern_name = dcltype->to<IR::Type_Extern>()->name;
713 auto actionProfileName = Standard::ActionProfileTraits<arch>::typeName();
714 auto actionSelectorName = Standard::ActionSelectorTraits<arch>::typeName();
715 if (type_extern_name == actionProfileName) {
716 table->emplace("type", "indirect");
717 } else if (type_extern_name == actionSelectorName) {
718 table->emplace("type", "indirect_ws");
719 } else {
720 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected type for implementation",
721 dcltype);
722 return false;
723 }
724 isSimpleTable = false;
725 if (ctxt->toplevel->hasValue(decl->getNode())) {
726 auto eb = ctxt->toplevel->getValue(decl->getNode());
727 BUG_CHECK(eb->is<IR::ExternBlock>(), "Not an extern block?");
728 ExternConverter::cvtExternInstance(ctxt, decl->to<IR::Declaration>(),
729 eb->to<IR::ExternBlock>(), emitExterns);
730 }
731 } else {
732 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected value for property", propv);
733 return false;
734 }
735 table->emplace("action_profile", apname);
736 return isSimpleTable;
737 }
738
739 Util::IJson *convertIf(const CFG::IfNode *node, cstring prefix) {
740 (void)prefix;
741 auto result = new Util::JsonObject();
742 result->emplace("name", node->name);
743 result->emplace("id", nextId("conditionals"_cs));
744 result->emplace_non_null("source_info"_cs, node->statement->condition->sourceInfoJsonObj());
745 auto j = ctxt->conv->convert(node->statement->condition, true, false);
746 CHECK_NULL(j);
747 result->emplace("expression"_cs, j);
748 for (auto e : node->successors.edges) {
749 Util::IJson *dest = nodeName(e->endpoint);
750 cstring label = Util::toString(e->getBool());
751 label += "_next";
752 result->emplace(label, dest);
753 }
754 return result;
755 }
756
757 public:
758 const bool emitExterns;
759 bool preorder(const IR::P4Control *cont) override {
760 auto result = new Util::JsonObject();
761
762 result->emplace("name", name);
763 result->emplace("id", nextId("control"_cs));
764 result->emplace_non_null("source_info"_cs, cont->sourceInfoJsonObj());
765
766 auto cfg = new CFG();
767 cfg->build(cont, ctxt->refMap, ctxt->typeMap);
768 bool success = cfg->checkImplementable();
769 if (!success) return false;
770
771 if (cfg->entryPoint->successors.size() == 0) {
772 result->emplace("init_table", Util::JsonValue::null);
773 } else {
774 BUG_CHECK(cfg->entryPoint->successors.size() == 1, "Expected 1 start node for %1%",
775 cont);
776 auto start = (*(cfg->entryPoint->successors.edges.begin()))->endpoint;
777 result->emplace("init_table", nodeName(start));
778 }
779
780 auto tables = mkArrayField(result, "tables"_cs);
781 auto action_profiles = mkArrayField(result, "action_profiles"_cs);
782 auto conditionals = mkArrayField(result, "conditionals"_cs);
783 ctxt->action_profiles = action_profiles;
784
785 auto selector_check = new BMV2::SharedActionSelectorCheck<arch>(ctxt);
786 cont->apply(*selector_check);
787
788 std::set<const IR::P4Table *> done;
789
790 // Tables are created prior to the other local declarations
791 for (auto node : cfg->allNodes) {
792 auto tn = node->to<CFG::TableNode>();
793 if (tn != nullptr) {
794 if (done.find(tn->table) != done.end())
795 // The same table may appear in multiple nodes in the CFG.
796 // We emit it only once. Other checks should ensure that
797 // the CFG is implementable.
798 continue;
799 done.emplace(tn->table);
800 auto j = convertTable(tn, action_profiles, selector_check);
801 if (::P4::errorCount() > 0) return false;
802 tables->append(j);
803 } else if (node->is<CFG::IfNode>()) {
804 auto j = convertIf(node->to<CFG::IfNode>(), cont->name);
805 if (::P4::errorCount() > 0) return false;
806 conditionals->append(j);
807 }
808 }
809
810 for (auto c : cont->controlLocals) {
811 if (c->is<IR::Declaration_Constant>() || c->is<IR::Declaration_Variable>() ||
812 c->is<IR::P4Action>() || c->is<IR::P4Table>())
813 continue;
814 if (c->is<IR::Declaration_Instance>()) {
815 auto bl = ctxt->structure->resourceMap.at(c);
816 CHECK_NULL(bl);
817 if (bl->is<IR::ControlBlock>() || bl->is<IR::ParserBlock>())
818 // Since this block has not been inlined, it is probably unused
819 // So we don't do anything.
820 continue;
821 if (bl->is<IR::ExternBlock>()) {
822 auto eb = bl->to<IR::ExternBlock>();
823 ExternConverter::cvtExternInstance(ctxt, c, eb, emitExterns);
824 continue;
825 }
826 }
827 P4C_UNIMPLEMENTED("%1%: not yet handled", c);
828 }
829
830 ctxt->json->pipelines->append(result);
831 return false;
832 }
833
834 explicit ControlConverter(ConversionContext *ctxt, cstring name, const bool &emitExterns_)
835 : ctxt(ctxt),
836 name(name),
837 corelib(P4::P4CoreLibrary::instance()),
838 emitExterns(emitExterns_) {
839 setName("ControlConverter");
840 }
841};
842
843} // namespace P4::BMV2
844
845#endif /* BACKENDS_BMV2_COMMON_CONTROL_H_ */
Definition methodInstance.h:209
Definition controlFlowGraph.h:88
Definition controlFlowGraph.h:51
Definition controlFlowGraph.h:75
bool handleTableImplementation(const IR::Property *implementation, const IR::Key *key, Util::JsonObject *table, Util::JsonArray *action_profiles, BMV2::SharedActionSelectorCheck< arch > *)
Return 'true' if the table is 'simple'.
Definition control.h:608
Util::IJson * convert(const IR::Expression *e, bool doFixup=true, bool wrap=true, bool convertBool=false)
Definition backends/bmv2/common/expression.cpp:758
static const cstring selectorMatchTypeName
constant definition for bmv2
Definition bmv2/common/helpers.h:32
Definition sharedActionSelectorCheck.h:32
static EnumInstance * resolve(const IR::Expression *expression, const P4::TypeMap *typeMap)
Definition enumInstance.cpp:11
Definition methodInstance.h:301
Definition ir/vector.h:59
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 coreLibrary.h:103
Definition lib/json.h:41
Definition lib/json.h:128
Definition lib/json.h:177
Definition cstring.h:85
TODO: this is not really specific to BMV2, it should reside somewhere else.
Definition action.cpp:9
void warning(const char *format, Args &&...args)
Report a warning with the given message.
Definition lib/error.h:128
void error(const char *format, Args &&...args)
Report an error with the given message.
Definition lib/error.h:58
unsigned errorCount()
Definition lib/error.h:34
Definition bmv2/common/helpers.h:288
ExpressionConverter * conv
Expression converter is used in many places.
Definition bmv2/common/helpers.h:298
P4::ReferenceMap * refMap
context
Definition bmv2/common/helpers.h:290
Definition bmv2/common/helpers.h:78
Definition bmv2/common/helpers.h:107
T * to() noexcept
Definition rtti.h:226
bool is() const noexcept
Definition rtti.h:216
Definition register_reference.h:28