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