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->getExpr().size() == 1, "%1%: expected 1 name", na);
124 auto name = na->getExpr(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 if (auto priorityAnnotation = e->getAnnotation("priority"_cs)) {
582 const auto &expr = priorityAnnotation->getExpr();
583 if (expr.size() > 1)
584 ::P4::error(ErrorType::ERR_INVALID, "Invalid priority value %1%", expr);
585 auto priValue = expr.front();
586 if (!priValue->is<IR::Constant>())
587 ::P4::error(ErrorType::ERR_INVALID,
588 "Invalid priority value %1%; must be constant.", expr);
589 entry->emplace("priority", priValue->to<IR::Constant>()->value);
590 } else {
591 entry->emplace("priority", entryPriority);
592 }
593 entryPriority += 1;
594
595 entries->append(entry);
596 }
597 }
598 cstring getKeyMatchType(const IR::KeyElement *ke) {
599 auto path = ke->matchType->path;
600 auto mt = ctxt->refMap->getDeclaration(path, true)->to<IR::Declaration_ID>();
601 BUG_CHECK(mt != nullptr, "%1%: could not find declaration", ke->matchType);
602
603 if (mt->name.name == corelib.exactMatch.name ||
604 mt->name.name == corelib.ternaryMatch.name || mt->name.name == corelib.lpmMatch.name ||
605 ctxt->structure->match_kinds.count(mt->name.name)) {
606 return mt->name.name;
607 }
608
609 ::P4::error(ErrorType::ERR_UNSUPPORTED, "%1%: match type not supported on this target", mt);
610 return "invalid"_cs;
611 }
613 bool handleTableImplementation(const IR::Property *implementation, const IR::Key *key,
614 Util::JsonObject *table, Util::JsonArray *action_profiles,
616 if (implementation == nullptr) {
617 table->emplace("type", "simple");
618 return true;
619 }
620
621 if (!implementation->value->is<IR::ExpressionValue>()) {
622 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected expression for property",
623 implementation);
624 return false;
625 }
626 auto propv = implementation->value->to<IR::ExpressionValue>();
627
628 bool isSimpleTable = true;
629 Util::JsonObject *action_profile;
630 cstring apname;
631
632 if (propv->expression->is<IR::ConstructorCallExpression>()) {
633 auto cc =
634 P4::ConstructorCall::resolve(propv->expression->to<IR::ConstructorCallExpression>(),
635 ctxt->refMap, ctxt->typeMap);
636 if (!cc->is<P4::ExternConstructorCall>()) {
637 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected extern object for property",
638 implementation);
639 return false;
640 }
641 auto ecc = cc->to<P4::ExternConstructorCall>();
642 auto implementationType = ecc->type;
643 auto arguments = ecc->cce->arguments;
644 apname = implementation->controlPlaneName(ctxt->refMap->newName("action_profile"));
645 action_profile = new Util::JsonObject();
646 action_profiles->append(action_profile);
647 action_profile->emplace("name", apname);
648 action_profile->emplace("id", nextId("action_profiles"_cs));
649 action_profile->emplace_non_null("source_info"_cs,
650 propv->expression->sourceInfoJsonObj());
651 // TODO(jafingerhut) - add line/col here?
652 // TBD what about the else if cases below?
653
654 auto add_size = [&action_profile, &arguments](size_t arg_index) {
655 auto size_expr = arguments->at(arg_index)->expression;
656 int size;
657 if (!size_expr->is<IR::Constant>()) {
658 ::P4::error(ErrorType::ERR_EXPECTED, "%1% must be a constant", size_expr);
659 size = 0;
660 } else {
661 size = size_expr->to<IR::Constant>()->asInt();
662 }
663 action_profile->emplace("max_size", size);
664 };
665
666 auto actionSelectorName = Standard::ActionSelectorTraits<arch>::typeName();
667 if (implementationType->name == actionSelectorName) {
668 BUG_CHECK(arguments->size() == 3, "%1%: expected 3 arguments", arguments);
669 isSimpleTable = false;
670 auto selector = new Util::JsonObject();
671 table->emplace("type", "indirect_ws");
672 action_profile->emplace("selector"_cs, selector);
673 add_size(1);
674 auto hash = arguments->at(0)->expression;
675 auto ei = P4::EnumInstance::resolve(hash, ctxt->typeMap);
676 if (ei == nullptr) {
677 ::P4::error(ErrorType::ERR_EXPECTED,
678 "%1%: hash must be a constant on this target", hash);
679 } else {
680 cstring algo = ei->name;
681 selector->emplace("algo", algo);
682 }
683 auto input = mkArrayField(selector, "input"_cs);
684 for (auto ke : key->keyElements) {
685 auto mt = ctxt->refMap->getDeclaration(ke->matchType->path, true)
686 ->to<IR::Declaration_ID>();
687 BUG_CHECK(mt != nullptr, "%1%: could not find declaration", ke->matchType);
688 if (mt->name.name != BMV2::MatchImplementation::selectorMatchTypeName) continue;
689
690 auto expr = ke->expression;
691 auto jk = ctxt->conv->convert(expr);
692 input->append(jk);
693 }
694 } else if (implementationType->name ==
696 isSimpleTable = false;
697 table->emplace("type", "indirect");
698 add_size(0);
699 } else {
700 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: expected value for property", propv);
701 }
702 } else if (propv->expression->is<IR::PathExpression>()) {
703 auto pathe = propv->expression->to<IR::PathExpression>();
704 auto decl = ctxt->refMap->getDeclaration(pathe->path, true);
705 if (!decl->is<IR::Declaration_Instance>()) {
706 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a reference to an instance",
707 pathe);
708 return false;
709 }
710 apname = decl->controlPlaneName();
711 auto dcltype = ctxt->typeMap->getType(pathe, true);
712 if (!dcltype->is<IR::Type_Extern>()) {
713 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected type for implementation",
714 dcltype);
715 return false;
716 }
717 auto type_extern_name = dcltype->to<IR::Type_Extern>()->name;
718 auto actionProfileName = Standard::ActionProfileTraits<arch>::typeName();
719 auto actionSelectorName = Standard::ActionSelectorTraits<arch>::typeName();
720 if (type_extern_name == actionProfileName) {
721 table->emplace("type", "indirect");
722 } else if (type_extern_name == actionSelectorName) {
723 table->emplace("type", "indirect_ws");
724 } else {
725 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected type for implementation",
726 dcltype);
727 return false;
728 }
729 isSimpleTable = false;
730 if (ctxt->toplevel->hasValue(decl->getNode())) {
731 auto eb = ctxt->toplevel->getValue(decl->getNode());
732 BUG_CHECK(eb->is<IR::ExternBlock>(), "Not an extern block?");
733 ExternConverter::cvtExternInstance(ctxt, decl->to<IR::Declaration>(),
734 eb->to<IR::ExternBlock>(), emitExterns);
735 }
736 } else {
737 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected value for property", propv);
738 return false;
739 }
740 table->emplace("action_profile", apname);
741 return isSimpleTable;
742 }
743
744 Util::IJson *convertIf(const CFG::IfNode *node, cstring prefix) {
745 (void)prefix;
746 auto result = new Util::JsonObject();
747 result->emplace("name", node->name);
748 result->emplace("id", nextId("conditionals"_cs));
749 result->emplace_non_null("source_info"_cs, node->statement->condition->sourceInfoJsonObj());
750 auto j = ctxt->conv->convert(node->statement->condition, true, false);
751 CHECK_NULL(j);
752 result->emplace("expression"_cs, j);
753 for (auto e : node->successors.edges) {
754 Util::IJson *dest = nodeName(e->endpoint);
755 cstring label = Util::toString(e->getBool());
756 label += "_next";
757 result->emplace(label, dest);
758 }
759 return result;
760 }
761
762 public:
763 const bool emitExterns;
764 bool preorder(const IR::P4Control *cont) override {
765 auto result = new Util::JsonObject();
766
767 result->emplace("name", name);
768 result->emplace("id", nextId("control"_cs));
769 result->emplace_non_null("source_info"_cs, cont->sourceInfoJsonObj());
770
771 auto cfg = new CFG();
772 cfg->build(cont, ctxt->refMap, ctxt->typeMap);
773 bool success = cfg->checkImplementable();
774 if (!success) return false;
775
776 if (cfg->entryPoint->successors.size() == 0) {
777 result->emplace("init_table", Util::JsonValue::null);
778 } else {
779 BUG_CHECK(cfg->entryPoint->successors.size() == 1, "Expected 1 start node for %1%",
780 cont);
781 auto start = (*(cfg->entryPoint->successors.edges.begin()))->endpoint;
782 result->emplace("init_table", nodeName(start));
783 }
784
785 auto tables = mkArrayField(result, "tables"_cs);
786 auto action_profiles = mkArrayField(result, "action_profiles"_cs);
787 auto conditionals = mkArrayField(result, "conditionals"_cs);
788 ctxt->action_profiles = action_profiles;
789
790 auto selector_check = new BMV2::SharedActionSelectorCheck<arch>(ctxt);
791 cont->apply(*selector_check);
792
793 std::set<const IR::P4Table *> done;
794
795 // Tables are created prior to the other local declarations
796 for (auto node : cfg->allNodes) {
797 auto tn = node->to<CFG::TableNode>();
798 if (tn != nullptr) {
799 if (done.find(tn->table) != done.end())
800 // The same table may appear in multiple nodes in the CFG.
801 // We emit it only once. Other checks should ensure that
802 // the CFG is implementable.
803 continue;
804 done.emplace(tn->table);
805 auto j = convertTable(tn, action_profiles, selector_check);
806 if (::P4::errorCount() > 0) return false;
807 tables->append(j);
808 } else if (node->is<CFG::IfNode>()) {
809 auto j = convertIf(node->to<CFG::IfNode>(), cont->name);
810 if (::P4::errorCount() > 0) return false;
811 conditionals->append(j);
812 }
813 }
814
815 for (auto c : cont->controlLocals) {
816 if (c->is<IR::Declaration_Constant>() || c->is<IR::Declaration_Variable>() ||
817 c->is<IR::P4Action>() || c->is<IR::P4Table>())
818 continue;
819 if (c->is<IR::Declaration_Instance>()) {
820 auto bl = ctxt->structure->resourceMap.at(c);
821 CHECK_NULL(bl);
822 if (bl->is<IR::ControlBlock>() || bl->is<IR::ParserBlock>())
823 // Since this block has not been inlined, it is probably unused
824 // So we don't do anything.
825 continue;
826 if (bl->is<IR::ExternBlock>()) {
827 auto eb = bl->to<IR::ExternBlock>();
828 ExternConverter::cvtExternInstance(ctxt, c, eb, emitExterns);
829 continue;
830 }
831 }
832 P4C_UNIMPLEMENTED("%1%: not yet handled", c);
833 }
834
835 ctxt->json->pipelines->append(result);
836 return false;
837 }
838
839 explicit ControlConverter(ConversionContext *ctxt, cstring name, const bool &emitExterns_)
840 : ctxt(ctxt),
841 name(name),
842 corelib(P4::P4CoreLibrary::instance()),
843 emitExterns(emitExterns_) {
844 setName("ControlConverter");
845 }
846};
847
848} // namespace P4::BMV2
849
850#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:613
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