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>
39class ControlConverter : public Inspector {
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::BoolLiteral>()) {
489 key->emplace("key",
490 stringRepr(k->to<IR::BoolLiteral>()->value ? 1 : 0, k8));
491 key->emplace("mask", stringRepr(Util::mask(keyWidth), k8));
492 } else if (k->is<IR::DefaultExpression>()) {
493 key->emplace("key", stringRepr(0, k8));
494 key->emplace("mask", stringRepr(0, k8));
495 } else {
496 ::P4::error(ErrorType::ERR_UNSUPPORTED,
497 "%1%: unsupported ternary key expression", k);
498 }
499 } else if (matchType == corelib.lpmMatch.name) {
500 if (k->is<IR::Mask>()) {
501 auto km = k->to<IR::Mask>();
502 key->emplace("key", stringRepr(km->left->to<IR::Constant>()->value, k8));
503 auto trailing_zeros = [](unsigned long n, unsigned long keyWidth) {
504 return n ? __builtin_ctzl(n) : static_cast<int>(keyWidth);
505 };
506 auto count_ones = [](unsigned long n) {
507 return n ? __builtin_popcountl(n) : 0;
508 };
509 auto mask =
510 static_cast<unsigned long>(km->right->to<IR::Constant>()->value);
511 auto len = trailing_zeros(mask, keyWidth);
512 if (len + count_ones(mask) != keyWidth) // any remaining 0s in the prefix?
513 ::P4::error(ErrorType::ERR_INVALID, "%1%: invalid mask for LPM key", k);
514 else
515 key->emplace("prefix_length", keyWidth - len);
516 } else if (k->is<IR::Constant>()) {
517 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
518 key->emplace("prefix_length", keyWidth);
519 } else if (k->is<IR::DefaultExpression>()) {
520 key->emplace("key", stringRepr(0, k8));
521 key->emplace("prefix_length", 0);
522 } else {
523 ::P4::error(ErrorType::ERR_UNSUPPORTED,
524 "%1%: unsupported LPM key expression", k);
525 }
526 } else if (matchType == "range") {
527 if (k->is<IR::Range>()) {
528 auto kr = k->to<IR::Range>();
529 key->emplace("start", stringRepr(kr->left->to<IR::Constant>()->value, k8));
530 key->emplace("end", stringRepr(kr->right->to<IR::Constant>()->value, k8));
531 } else if (k->is<IR::Constant>()) {
532 key->emplace("start", stringRepr(k->to<IR::Constant>()->value, k8));
533 key->emplace("end", stringRepr(k->to<IR::Constant>()->value, k8));
534 } else if (k->is<IR::DefaultExpression>()) {
535 key->emplace("start", stringRepr(0, k8));
536 key->emplace("end", stringRepr((1 << keyWidth) - 1, k8)); // 2^N -1
537 } else {
538 ::P4::error(ErrorType::ERR_UNSUPPORTED,
539 "%1% unsupported range key expression", k);
540 }
541 } else if (matchType == "optional") {
542 // Table key fields with match_kind optional with
543 // "const entries" in the P4 source code will be
544 // represented using the same "key" and "mask" keys in
545 // the BMv2 JSON file as table key fields with
546 // match_kind ternary. In the P4 source code we only
547 // allow exact values or a DefaultExpression (_ or
548 // default), no &&& expression.
549 if (k->is<IR::Constant>()) {
550 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
551 key->emplace("mask", stringRepr(Util::mask(keyWidth), k8));
552 } else if (k->is<IR::DefaultExpression>()) {
553 key->emplace("key", stringRepr(0, k8));
554 key->emplace("mask", stringRepr(0, k8));
555 } else {
556 ::P4::error(ErrorType::ERR_UNSUPPORTED,
557 "%1%: unsupported optional key expression", k);
558 }
559 } else {
560 ::P4::error(ErrorType::ERR_UNKNOWN, "unknown key match type '%2%' for key %1%",
561 k, matchType);
562 }
563 matchKeys->append(key);
564 keyIndex++;
565 }
566
567 auto action = new Util::JsonObject();
568 auto actionRef = e->getAction();
569 if (!actionRef->is<IR::MethodCallExpression>())
570 ::P4::error(ErrorType::ERR_INVALID, "Invalid action '%1%' in entries list.",
571 actionRef);
572 auto actionCall = actionRef->to<IR::MethodCallExpression>();
573 auto method = actionCall->method->to<IR::PathExpression>()->path;
574 auto decl = ctxt->refMap->getDeclaration(method, true);
575 auto actionDecl = decl->to<IR::P4Action>();
576 unsigned id = get(ctxt->structure->ids, actionDecl, INVALID_ACTION_ID);
577 BUG_CHECK(id != INVALID_ACTION_ID, "Could not find id for %1%", actionDecl);
578 action->emplace("action_id", id);
579 auto actionData = mkArrayField(action, "action_data"_cs);
580 for (auto arg : *actionCall->arguments) {
581 actionData->append(stringRepr(arg->expression->to<IR::Constant>()->value, 0));
582 }
583 entry->emplace("action_entry", action);
584
585 if (auto priorityAnnotation = e->getAnnotation("priority"_cs)) {
586 const auto &expr = priorityAnnotation->getExpr();
587 if (expr.size() > 1)
588 ::P4::error(ErrorType::ERR_INVALID, "Invalid priority value %1%", expr);
589 auto priValue = expr.front();
590 if (!priValue->is<IR::Constant>())
591 ::P4::error(ErrorType::ERR_INVALID,
592 "Invalid priority value %1%; must be constant.", expr);
593 entry->emplace("priority", priValue->to<IR::Constant>()->value);
594 } else {
595 entry->emplace("priority", entryPriority);
596 }
597 entryPriority += 1;
598
599 entries->append(entry);
600 }
601 }
602 cstring getKeyMatchType(const IR::KeyElement *ke) {
603 auto path = ke->matchType->path;
604 auto mt = ctxt->refMap->getDeclaration(path, true)->to<IR::Declaration_ID>();
605 BUG_CHECK(mt != nullptr, "%1%: could not find declaration", ke->matchType);
606
607 if (mt->name.name == corelib.exactMatch.name ||
608 mt->name.name == corelib.ternaryMatch.name || mt->name.name == corelib.lpmMatch.name ||
609 ctxt->structure->match_kinds.count(mt->name.name)) {
610 return mt->name.name;
611 }
612
613 ::P4::error(ErrorType::ERR_UNSUPPORTED, "%1%: match type not supported on this target", mt);
614 return "invalid"_cs;
615 }
617 bool handleTableImplementation(const IR::Property *implementation, const IR::Key *key,
618 Util::JsonObject *table, Util::JsonArray *action_profiles,
620 if (implementation == nullptr) {
621 table->emplace("type", "simple");
622 return true;
623 }
624
625 if (!implementation->value->is<IR::ExpressionValue>()) {
626 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected expression for property",
627 implementation);
628 return false;
629 }
630 auto propv = implementation->value->to<IR::ExpressionValue>();
631
632 bool isSimpleTable = true;
633 Util::JsonObject *action_profile;
634 cstring apname;
635
636 if (propv->expression->is<IR::ConstructorCallExpression>()) {
637 auto cc =
638 P4::ConstructorCall::resolve(propv->expression->to<IR::ConstructorCallExpression>(),
639 ctxt->refMap, ctxt->typeMap);
640 if (!cc->is<P4::ExternConstructorCall>()) {
641 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected extern object for property",
642 implementation);
643 return false;
644 }
645 auto ecc = cc->to<P4::ExternConstructorCall>();
646 auto implementationType = ecc->type;
647 auto arguments = ecc->cce->arguments;
648 apname = implementation->controlPlaneName(ctxt->refMap->newName("action_profile"));
649 action_profile = new Util::JsonObject();
650 action_profiles->append(action_profile);
651 action_profile->emplace("name", apname);
652 action_profile->emplace("id", nextId("action_profiles"_cs));
653 action_profile->emplace_non_null("source_info"_cs,
654 propv->expression->sourceInfoJsonObj());
655 // TODO(jafingerhut) - add line/col here?
656 // TBD what about the else if cases below?
657
658 auto add_size = [&action_profile, &arguments](size_t arg_index) {
659 auto size_expr = arguments->at(arg_index)->expression;
660 int size;
661 if (!size_expr->is<IR::Constant>()) {
662 ::P4::error(ErrorType::ERR_EXPECTED, "%1% must be a constant", size_expr);
663 size = 0;
664 } else {
665 size = size_expr->to<IR::Constant>()->asInt();
666 }
667 action_profile->emplace("max_size", size);
668 };
669
670 auto actionSelectorName = Standard::ActionSelectorTraits<arch>::typeName();
671 if (implementationType->name == actionSelectorName) {
672 BUG_CHECK(arguments->size() == 3, "%1%: expected 3 arguments", arguments);
673 isSimpleTable = false;
674 auto selector = new Util::JsonObject();
675 table->emplace("type", "indirect_ws");
676 action_profile->emplace("selector"_cs, selector);
677 add_size(1);
678 auto hash = arguments->at(0)->expression;
679 auto ei = P4::EnumInstance::resolve(hash, ctxt->typeMap);
680 if (ei == nullptr) {
681 ::P4::error(ErrorType::ERR_EXPECTED,
682 "%1%: hash must be a constant on this target", hash);
683 } else {
684 cstring algo = ei->name;
685 selector->emplace("algo", algo);
686 }
687 auto input = mkArrayField(selector, "input"_cs);
688 for (auto ke : key->keyElements) {
689 auto mt = ctxt->refMap->getDeclaration(ke->matchType->path, true)
690 ->to<IR::Declaration_ID>();
691 BUG_CHECK(mt != nullptr, "%1%: could not find declaration", ke->matchType);
692 if (mt->name.name != BMV2::MatchImplementation::selectorMatchTypeName) continue;
693
694 auto expr = ke->expression;
695 auto jk = ctxt->conv->convert(expr);
696 input->append(jk);
697 }
698 } else if (implementationType->name ==
700 isSimpleTable = false;
701 table->emplace("type", "indirect");
702 add_size(0);
703 } else {
704 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: expected value for property", propv);
705 }
706 } else if (propv->expression->is<IR::PathExpression>()) {
707 auto pathe = propv->expression->to<IR::PathExpression>();
708 auto decl = ctxt->refMap->getDeclaration(pathe->path, true);
709 if (!decl->is<IR::Declaration_Instance>()) {
710 ::P4::error(ErrorType::ERR_EXPECTED, "%1%: expected a reference to an instance",
711 pathe);
712 return false;
713 }
714 apname = decl->controlPlaneName();
715 auto dcltype = ctxt->typeMap->getType(pathe, true);
716 if (!dcltype->is<IR::Type_Extern>()) {
717 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected type for implementation",
718 dcltype);
719 return false;
720 }
721 auto type_extern_name = dcltype->to<IR::Type_Extern>()->name;
722 auto actionProfileName = Standard::ActionProfileTraits<arch>::typeName();
723 auto actionSelectorName = Standard::ActionSelectorTraits<arch>::typeName();
724 if (type_extern_name == actionProfileName) {
725 table->emplace("type", "indirect");
726 } else if (type_extern_name == actionSelectorName) {
727 table->emplace("type", "indirect_ws");
728 } else {
729 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected type for implementation",
730 dcltype);
731 return false;
732 }
733 isSimpleTable = false;
734 if (ctxt->toplevel->hasValue(decl->getNode())) {
735 auto eb = ctxt->toplevel->getValue(decl->getNode());
736 BUG_CHECK(eb->is<IR::ExternBlock>(), "Not an extern block?");
737 ExternConverter::cvtExternInstance(ctxt, decl->to<IR::Declaration>(),
738 eb->to<IR::ExternBlock>(), emitExterns);
739 }
740 } else {
741 ::P4::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected value for property", propv);
742 return false;
743 }
744 table->emplace("action_profile", apname);
745 return isSimpleTable;
746 }
747
748 Util::IJson *convertIf(const CFG::IfNode *node, cstring prefix) {
749 (void)prefix;
750 auto result = new Util::JsonObject();
751 result->emplace("name", node->name);
752 result->emplace("id", nextId("conditionals"_cs));
753 result->emplace_non_null("source_info"_cs, node->statement->condition->sourceInfoJsonObj());
754 auto j = ctxt->conv->convert(node->statement->condition, true, false);
755 CHECK_NULL(j);
756 result->emplace("expression"_cs, j);
757 for (auto e : node->successors.edges) {
758 Util::IJson *dest = nodeName(e->endpoint);
759 cstring label = Util::toString(e->getBool());
760 label += "_next";
761 result->emplace(label, dest);
762 }
763 return result;
764 }
765
766 public:
767 const bool emitExterns;
768 bool preorder(const IR::P4Control *cont) override {
769 auto result = new Util::JsonObject();
770
771 result->emplace("name", name);
772 result->emplace("id", nextId("control"_cs));
773 result->emplace_non_null("source_info"_cs, cont->sourceInfoJsonObj());
774
775 auto cfg = new CFG();
776 cfg->build(cont, ctxt->refMap, ctxt->typeMap);
777 bool success = cfg->checkImplementable();
778 if (!success) return false;
779
780 if (cfg->entryPoint->successors.size() == 0) {
781 result->emplace("init_table", Util::JsonValue::null);
782 } else {
783 BUG_CHECK(cfg->entryPoint->successors.size() == 1, "Expected 1 start node for %1%",
784 cont);
785 auto start = (*(cfg->entryPoint->successors.edges.begin()))->endpoint;
786 result->emplace("init_table", nodeName(start));
787 }
788
789 auto tables = mkArrayField(result, "tables"_cs);
790 auto action_profiles = mkArrayField(result, "action_profiles"_cs);
791 auto conditionals = mkArrayField(result, "conditionals"_cs);
792 ctxt->action_profiles = action_profiles;
793
794 auto selector_check = new BMV2::SharedActionSelectorCheck<arch>(ctxt);
795 cont->apply(*selector_check);
796
797 std::set<const IR::P4Table *> done;
798
799 // Tables are created prior to the other local declarations
800 for (auto node : cfg->allNodes) {
801 auto tn = node->to<CFG::TableNode>();
802 if (tn != nullptr) {
803 if (done.find(tn->table) != done.end())
804 // The same table may appear in multiple nodes in the CFG.
805 // We emit it only once. Other checks should ensure that
806 // the CFG is implementable.
807 continue;
808 done.emplace(tn->table);
809 auto j = convertTable(tn, action_profiles, selector_check);
810 if (::P4::errorCount() > 0) return false;
811 tables->append(j);
812 } else if (node->is<CFG::IfNode>()) {
813 auto j = convertIf(node->to<CFG::IfNode>(), cont->name);
814 if (::P4::errorCount() > 0) return false;
815 conditionals->append(j);
816 }
817 }
818
819 for (auto c : cont->controlLocals) {
820 if (c->is<IR::Declaration_Constant>() || c->is<IR::Declaration_Variable>() ||
821 c->is<IR::P4Action>() || c->is<IR::P4Table>())
822 continue;
823 if (c->is<IR::Declaration_Instance>()) {
824 auto bl = ctxt->structure->resourceMap.at(c);
825 CHECK_NULL(bl);
826 if (bl->is<IR::ControlBlock>() || bl->is<IR::ParserBlock>())
827 // Since this block has not been inlined, it is probably unused
828 // So we don't do anything.
829 continue;
830 if (bl->is<IR::ExternBlock>()) {
831 auto eb = bl->to<IR::ExternBlock>();
832 ExternConverter::cvtExternInstance(ctxt, c, eb, emitExterns);
833 continue;
834 }
835 }
836 P4C_UNIMPLEMENTED("%1%: not yet handled", c);
837 }
838
839 ctxt->json->pipelines->append(result);
840 return false;
841 }
842
843 explicit ControlConverter(ConversionContext *ctxt, cstring name, const bool &emitExterns_)
844 : ctxt(ctxt),
845 name(name),
846 corelib(P4::P4CoreLibrary::instance()),
847 emitExterns(emitExterns_) {
848 setName("ControlConverter");
849 }
850};
851
852} // namespace P4::BMV2
853
854#endif /* BACKENDS_BMV2_COMMON_CONTROL_H_ */
Definition methodInstance.h:209
Definition controlFlowGraph.h:97
Definition controlFlowGraph.h:60
Definition controlFlowGraph.h:84
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:617
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
static EnumInstance * resolve(const IR::Expression *expression, const P4::TypeMap *typeMap)
Definition enumInstance.cpp:22
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:21
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: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
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
Definition register_reference.h:28