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 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"));
53 result->emplace_non_null("source_info", table->sourceInfoJsonObj());
54 cstring table_match_type = corelib.exactMatch.name;
55 auto key = table->getKey();
56 auto tkey = mkArrayField(result, "key");
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 ::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 ::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 ::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 ::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 ::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 ::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 ::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");
197 jctr->emplace("name", ctrname);
198 jctr->emplace("id", nextId("counter_arrays"));
199 jctr->emplace_non_null("source_info", 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 ::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 ::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 ::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 ::error(ErrorType::ERR_EXPECTED, "%1%: must true/false", timeout);
241 } else {
242 sup_to = expr->to<IR::BoolLiteral>()->value;
243 }
244 } else {
245 ::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 ::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 ::error(ErrorType::ERR_UNEXPECTED, "%1%: Unexpected type %2% for property",
266 dm, type);
267 return result;
268 }
269 auto te = type->to<IR::Type_Extern>();
270 if (te->name != "direct_meter") {
271 ::error(ErrorType::ERR_UNEXPECTED, "%1%: Unexpected type %2% for property",
272 dm, type);
273 return result;
274 }
275 if (!decl->is<IR::Declaration_Instance>()) {
276 ::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 ::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");
295 auto actions = mkArrayField(result, "actions");
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 ::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__" : "__MISS__";
355 } else if (s->isUnconditional()) {
356 continue;
357 } else {
358 label = s->label;
359 if (label == "default") continue;
360 label = ::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 = ::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) {
382 ::warning(ErrorType::WARN_UNSUPPORTED,
383 "Target does not support default_action for %1% (due to action profiles)",
384 table);
385 return result;
386 }
387
388 if (!defact->value->is<IR::ExpressionValue>()) {
389 ::error(ErrorType::ERR_EXPECTED, "%1%: expected an action", defact);
390 return result;
391 }
392 auto expr = defact->value->to<IR::ExpressionValue>()->expression;
393 const IR::P4Action *action = nullptr;
394 const IR::Vector<IR::Argument> *args = nullptr;
395
396 if (expr->is<IR::PathExpression>()) {
397 auto path = expr->to<IR::PathExpression>()->path;
398 auto decl = ctxt->refMap->getDeclaration(path, true);
399 BUG_CHECK(decl->is<IR::P4Action>(), "%1%: should be an action name", expr);
400 action = decl->to<IR::P4Action>();
401 } else if (expr->is<IR::MethodCallExpression>()) {
402 auto mce = expr->to<IR::MethodCallExpression>();
403 auto mi = P4::MethodInstance::resolve(mce, ctxt->refMap, ctxt->typeMap);
404 BUG_CHECK(mi->is<P4::ActionCall>(), "%1%: expected an action", expr);
405 action = mi->to<P4::ActionCall>()->action;
406 args = mce->arguments;
407 } else {
408 BUG("%1%: unexpected expression", expr);
409 }
410
411 unsigned actionid = get(ctxt->structure->ids, action, INVALID_ACTION_ID);
412 BUG_CHECK(actionid != INVALID_ACTION_ID, "Could not find id for %1%", action);
413 auto entry = new Util::JsonObject();
414 entry->emplace("action_id", actionid);
415 entry->emplace("action_const", defact->isConstant);
416 auto fields = mkArrayField(entry, "action_data");
417 if (args != nullptr) {
418 // TODO: use argument names
419 for (auto a : *args) {
420 if (a->expression->is<IR::Constant>()) {
421 cstring repr = stringRepr(a->expression->to<IR::Constant>()->value);
422 fields->append(repr);
423 } else {
424 ::error(ErrorType::ERR_EXPECTED,
425 "%1%: argument must evaluate to a constant integer", a);
426 return result;
427 }
428 }
429 }
430 entry->emplace("action_entry_const", defact->isConstant);
431 result->emplace("default_entry", entry);
432 }
433 convertTableEntries(table, result);
434 return result;
435 }
436 void convertTableEntries(const IR::P4Table *table, Util::JsonObject *jsonTable) {
437 auto entriesList = table->getEntries();
438 if (entriesList == nullptr) return;
439
440 auto entries = mkArrayField(jsonTable, "entries");
441 int entryPriority = 1; // default priority is defined by index position
442 for (auto e : entriesList->entries) {
443 auto entry = new Util::JsonObject();
444 entry->emplace_non_null("source_info", e->sourceInfoJsonObj());
445
446 auto keyset = e->getKeys();
447 auto matchKeys = mkArrayField(entry, "match_key");
448 int keyIndex = 0;
449 for (auto k : keyset->components) {
450 auto key = new Util::JsonObject();
451 auto tableKey = table->getKey()->keyElements.at(keyIndex);
452 auto keyWidth = tableKey->expression->type->width_bits();
453 auto k8 = ROUNDUP(keyWidth, 8);
454 auto matchType = getKeyMatchType(tableKey);
455 // Table key fields with match_kind optional will be
456 // represented in the BMv2 JSON file the same as a ternary
457 // field would be.
458 if (matchType == "optional") {
459 key->emplace("match_type", "ternary");
460 } else {
461 key->emplace("match_type", matchType);
462 }
463 if (matchType == corelib.exactMatch.name) {
464 if (k->is<IR::Constant>())
465 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
466 else if (k->is<IR::BoolLiteral>())
467 // booleans are converted to ints
468 key->emplace("key",
469 stringRepr(k->to<IR::BoolLiteral>()->value ? 1 : 0, k8));
470 else
471 ::error(ErrorType::ERR_UNSUPPORTED, "%1%: unsupported exact key expression",
472 k);
473 } else if (matchType == corelib.ternaryMatch.name) {
474 if (k->is<IR::Mask>()) {
475 auto km = k->to<IR::Mask>();
476 key->emplace("key", stringRepr(km->left->to<IR::Constant>()->value, k8));
477 key->emplace("mask", stringRepr(km->right->to<IR::Constant>()->value, k8));
478 } else if (k->is<IR::Constant>()) {
479 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
480 key->emplace("mask", stringRepr(Util::mask(keyWidth), k8));
481 } else if (k->is<IR::DefaultExpression>()) {
482 key->emplace("key", stringRepr(0, k8));
483 key->emplace("mask", stringRepr(0, k8));
484 } else {
485 ::error(ErrorType::ERR_UNSUPPORTED,
486 "%1%: unsupported ternary key expression", k);
487 }
488 } else if (matchType == corelib.lpmMatch.name) {
489 if (k->is<IR::Mask>()) {
490 auto km = k->to<IR::Mask>();
491 key->emplace("key", stringRepr(km->left->to<IR::Constant>()->value, k8));
492 auto trailing_zeros = [](unsigned long n, unsigned long keyWidth) {
493 return n ? __builtin_ctzl(n) : static_cast<int>(keyWidth);
494 };
495 auto count_ones = [](unsigned long n) {
496 return n ? __builtin_popcountl(n) : 0;
497 };
498 auto mask =
499 static_cast<unsigned long>(km->right->to<IR::Constant>()->value);
500 auto len = trailing_zeros(mask, keyWidth);
501 if (len + count_ones(mask) != keyWidth) // any remaining 0s in the prefix?
502 ::error(ErrorType::ERR_INVALID, "%1%: invalid mask for LPM key", k);
503 else
504 key->emplace("prefix_length", keyWidth - len);
505 } else if (k->is<IR::Constant>()) {
506 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
507 key->emplace("prefix_length", keyWidth);
508 } else if (k->is<IR::DefaultExpression>()) {
509 key->emplace("key", stringRepr(0, k8));
510 key->emplace("prefix_length", 0);
511 } else {
512 ::error(ErrorType::ERR_UNSUPPORTED, "%1%: unsupported LPM key expression",
513 k);
514 }
515 } else if (matchType == "range") {
516 if (k->is<IR::Range>()) {
517 auto kr = k->to<IR::Range>();
518 key->emplace("start", stringRepr(kr->left->to<IR::Constant>()->value, k8));
519 key->emplace("end", stringRepr(kr->right->to<IR::Constant>()->value, k8));
520 } else if (k->is<IR::Constant>()) {
521 key->emplace("start", stringRepr(k->to<IR::Constant>()->value, k8));
522 key->emplace("end", stringRepr(k->to<IR::Constant>()->value, k8));
523 } else if (k->is<IR::DefaultExpression>()) {
524 key->emplace("start", stringRepr(0, k8));
525 key->emplace("end", stringRepr((1 << keyWidth) - 1, k8)); // 2^N -1
526 } else {
527 ::error(ErrorType::ERR_UNSUPPORTED, "%1% unsupported range key expression",
528 k);
529 }
530 } else if (matchType == "optional") {
531 // Table key fields with match_kind optional with
532 // "const entries" in the P4 source code will be
533 // represented using the same "key" and "mask" keys in
534 // the BMv2 JSON file as table key fields with
535 // match_kind ternary. In the P4 source code we only
536 // allow exact values or a DefaultExpression (_ or
537 // default), no &&& expression.
538 if (k->is<IR::Constant>()) {
539 key->emplace("key", stringRepr(k->to<IR::Constant>()->value, k8));
540 key->emplace("mask", stringRepr(Util::mask(keyWidth), k8));
541 } else if (k->is<IR::DefaultExpression>()) {
542 key->emplace("key", stringRepr(0, k8));
543 key->emplace("mask", stringRepr(0, k8));
544 } else {
545 ::error(ErrorType::ERR_UNSUPPORTED,
546 "%1%: unsupported optional key expression", k);
547 }
548 } else {
549 ::error(ErrorType::ERR_UNKNOWN, "unknown key match type '%2%' for key %1%", k,
550 matchType);
551 }
552 matchKeys->append(key);
553 keyIndex++;
554 }
555
556 auto action = new Util::JsonObject();
557 auto actionRef = e->getAction();
558 if (!actionRef->is<IR::MethodCallExpression>())
559 ::error(ErrorType::ERR_INVALID, "Invalid action '%1%' in entries list.", actionRef);
560 auto actionCall = actionRef->to<IR::MethodCallExpression>();
561 auto method = actionCall->method->to<IR::PathExpression>()->path;
562 auto decl = ctxt->refMap->getDeclaration(method, true);
563 auto actionDecl = decl->to<IR::P4Action>();
564 unsigned id = get(ctxt->structure->ids, actionDecl, INVALID_ACTION_ID);
565 BUG_CHECK(id != INVALID_ACTION_ID, "Could not find id for %1%", actionDecl);
566 action->emplace("action_id", id);
567 auto actionData = mkArrayField(action, "action_data");
568 for (auto arg : *actionCall->arguments) {
569 actionData->append(stringRepr(arg->expression->to<IR::Constant>()->value, 0));
570 }
571 entry->emplace("action_entry", action);
572
573 auto priorityAnnotation = e->getAnnotation("priority");
574 if (priorityAnnotation != nullptr) {
575 if (priorityAnnotation->expr.size() > 1)
576 ::error(ErrorType::ERR_INVALID, "Invalid priority value %1%",
577 priorityAnnotation->expr);
578 auto priValue = priorityAnnotation->expr.front();
579 if (!priValue->is<IR::Constant>())
580 ::error(ErrorType::ERR_INVALID, "Invalid priority value %1%; must be constant.",
581 priorityAnnotation->expr);
582 entry->emplace("priority", priValue->to<IR::Constant>()->value);
583 } else {
584 entry->emplace("priority", entryPriority);
585 }
586 entryPriority += 1;
587
588 entries->append(entry);
589 }
590 }
591 cstring getKeyMatchType(const IR::KeyElement *ke) {
592 auto path = ke->matchType->path;
593 auto mt = ctxt->refMap->getDeclaration(path, true)->to<IR::Declaration_ID>();
594 BUG_CHECK(mt != nullptr, "%1%: could not find declaration", ke->matchType);
595
596 if (mt->name.name == corelib.exactMatch.name ||
597 mt->name.name == corelib.ternaryMatch.name || mt->name.name == corelib.lpmMatch.name ||
598 ctxt->structure->match_kinds.count(mt->name.name)) {
599 return mt->name.name;
600 }
601
602 ::error(ErrorType::ERR_UNSUPPORTED, "%1%: match type not supported on this target", mt);
603 return "invalid";
604 }
606 bool handleTableImplementation(const IR::Property *implementation, const IR::Key *key,
607 Util::JsonObject *table, Util::JsonArray *action_profiles,
609 if (implementation == nullptr) {
610 table->emplace("type", "simple");
611 return true;
612 }
613
614 if (!implementation->value->is<IR::ExpressionValue>()) {
615 ::error(ErrorType::ERR_EXPECTED, "%1%: expected expression for property",
616 implementation);
617 return false;
618 }
619 auto propv = implementation->value->to<IR::ExpressionValue>();
620
621 bool isSimpleTable = true;
622 Util::JsonObject *action_profile;
623 cstring apname;
624
625 if (propv->expression->is<IR::ConstructorCallExpression>()) {
626 auto cc =
627 P4::ConstructorCall::resolve(propv->expression->to<IR::ConstructorCallExpression>(),
628 ctxt->refMap, ctxt->typeMap);
629 if (!cc->is<P4::ExternConstructorCall>()) {
630 ::error(ErrorType::ERR_EXPECTED, "%1%: expected extern object for property",
631 implementation);
632 return false;
633 }
634 auto ecc = cc->to<P4::ExternConstructorCall>();
635 auto implementationType = ecc->type;
636 auto arguments = ecc->cce->arguments;
637 apname = implementation->controlPlaneName(ctxt->refMap->newName("action_profile"));
638 action_profile = new Util::JsonObject();
639 action_profiles->append(action_profile);
640 action_profile->emplace("name", apname);
641 action_profile->emplace("id", nextId("action_profiles"));
642 action_profile->emplace_non_null("source_info", propv->expression->sourceInfoJsonObj());
643 // TODO(jafingerhut) - add line/col here?
644 // TBD what about the else if cases below?
645
646 auto add_size = [&action_profile, &arguments](size_t arg_index) {
647 auto size_expr = arguments->at(arg_index)->expression;
648 int size;
649 if (!size_expr->is<IR::Constant>()) {
650 ::error(ErrorType::ERR_EXPECTED, "%1% must be a constant", size_expr);
651 size = 0;
652 } else {
653 size = size_expr->to<IR::Constant>()->asInt();
654 }
655 action_profile->emplace("max_size", size);
656 };
657
658 auto actionSelectorName = Standard::ActionSelectorTraits<arch>::typeName();
659 if (implementationType->name == actionSelectorName) {
660 BUG_CHECK(arguments->size() == 3, "%1%: expected 3 arguments", arguments);
661 isSimpleTable = false;
662 auto selector = new Util::JsonObject();
663 table->emplace("type", "indirect_ws");
664 action_profile->emplace("selector", selector);
665 add_size(1);
666 auto hash = arguments->at(0)->expression;
667 auto ei = P4::EnumInstance::resolve(hash, ctxt->typeMap);
668 if (ei == nullptr) {
669 ::error(ErrorType::ERR_EXPECTED, "%1%: hash must be a constant on this target",
670 hash);
671 } else {
672 cstring algo = ei->name;
673 selector->emplace("algo", algo);
674 }
675 auto input = mkArrayField(selector, "input");
676 for (auto ke : key->keyElements) {
677 auto mt = ctxt->refMap->getDeclaration(ke->matchType->path, true)
678 ->to<IR::Declaration_ID>();
679 BUG_CHECK(mt != nullptr, "%1%: could not find declaration", ke->matchType);
680 if (mt->name.name != BMV2::MatchImplementation::selectorMatchTypeName) continue;
681
682 auto expr = ke->expression;
683 auto jk = ctxt->conv->convert(expr);
684 input->append(jk);
685 }
686 } else if (implementationType->name ==
688 isSimpleTable = false;
689 table->emplace("type", "indirect");
690 add_size(0);
691 } else {
692 ::error(ErrorType::ERR_UNEXPECTED, "%1%: expected value for property", propv);
693 }
694 } else if (propv->expression->is<IR::PathExpression>()) {
695 auto pathe = propv->expression->to<IR::PathExpression>();
696 auto decl = ctxt->refMap->getDeclaration(pathe->path, true);
697 if (!decl->is<IR::Declaration_Instance>()) {
698 ::error(ErrorType::ERR_EXPECTED, "%1%: expected a reference to an instance", pathe);
699 return false;
700 }
701 apname = decl->controlPlaneName();
702 auto dcltype = ctxt->typeMap->getType(pathe, true);
703 if (!dcltype->is<IR::Type_Extern>()) {
704 ::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected type for implementation",
705 dcltype);
706 return false;
707 }
708 auto type_extern_name = dcltype->to<IR::Type_Extern>()->name;
709 auto actionProfileName = Standard::ActionProfileTraits<arch>::typeName();
710 auto actionSelectorName = Standard::ActionSelectorTraits<arch>::typeName();
711 if (type_extern_name == actionProfileName) {
712 table->emplace("type", "indirect");
713 } else if (type_extern_name == actionSelectorName) {
714 table->emplace("type", "indirect_ws");
715 } else {
716 ::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected type for implementation",
717 dcltype);
718 return false;
719 }
720 isSimpleTable = false;
721 if (ctxt->toplevel->hasValue(decl->getNode())) {
722 auto eb = ctxt->toplevel->getValue(decl->getNode());
723 BUG_CHECK(eb->is<IR::ExternBlock>(), "Not an extern block?");
724 ExternConverter::cvtExternInstance(ctxt, decl->to<IR::Declaration>(),
725 eb->to<IR::ExternBlock>(), emitExterns);
726 }
727 } else {
728 ::error(ErrorType::ERR_UNEXPECTED, "%1%: unexpected value for property", propv);
729 return false;
730 }
731 table->emplace("action_profile", apname);
732 return isSimpleTable;
733 }
734
735 Util::IJson *convertIf(const CFG::IfNode *node, cstring prefix) {
736 (void)prefix;
737 auto result = new Util::JsonObject();
738 result->emplace("name", node->name);
739 result->emplace("id", nextId("conditionals"));
740 result->emplace_non_null("source_info", node->statement->condition->sourceInfoJsonObj());
741 auto j = ctxt->conv->convert(node->statement->condition, true, false);
742 CHECK_NULL(j);
743 result->emplace("expression", j);
744 for (auto e : node->successors.edges) {
745 Util::IJson *dest = nodeName(e->endpoint);
746 cstring label = Util::toString(e->getBool());
747 label += "_next";
748 result->emplace(label, dest);
749 }
750 return result;
751 }
752
753 public:
754 const bool emitExterns;
755 bool preorder(const IR::P4Control *cont) override {
756 auto result = new Util::JsonObject();
757
758 result->emplace("name", name);
759 result->emplace("id", nextId("control"));
760 result->emplace_non_null("source_info", cont->sourceInfoJsonObj());
761
762 auto cfg = new CFG();
763 cfg->build(cont, ctxt->refMap, ctxt->typeMap);
764 bool success = cfg->checkImplementable();
765 if (!success) return false;
766
767 if (cfg->entryPoint->successors.size() == 0) {
768 result->emplace("init_table", Util::JsonValue::null);
769 } else {
770 BUG_CHECK(cfg->entryPoint->successors.size() == 1, "Expected 1 start node for %1%",
771 cont);
772 auto start = (*(cfg->entryPoint->successors.edges.begin()))->endpoint;
773 result->emplace("init_table", nodeName(start));
774 }
775
776 auto tables = mkArrayField(result, "tables");
777 auto action_profiles = mkArrayField(result, "action_profiles");
778 auto conditionals = mkArrayField(result, "conditionals");
779 ctxt->action_profiles = action_profiles;
780
781 auto selector_check = new BMV2::SharedActionSelectorCheck<arch>(ctxt);
782 cont->apply(*selector_check);
783
784 std::set<const IR::P4Table *> done;
785
786 // Tables are created prior to the other local declarations
787 for (auto node : cfg->allNodes) {
788 auto tn = node->to<CFG::TableNode>();
789 if (tn != nullptr) {
790 if (done.find(tn->table) != done.end())
791 // The same table may appear in multiple nodes in the CFG.
792 // We emit it only once. Other checks should ensure that
793 // the CFG is implementable.
794 continue;
795 done.emplace(tn->table);
796 auto j = convertTable(tn, action_profiles, selector_check);
797 if (::errorCount() > 0) return false;
798 tables->append(j);
799 } else if (node->is<CFG::IfNode>()) {
800 auto j = convertIf(node->to<CFG::IfNode>(), cont->name);
801 if (::errorCount() > 0) return false;
802 conditionals->append(j);
803 }
804 }
805
806 for (auto c : cont->controlLocals) {
807 if (c->is<IR::Declaration_Constant>() || c->is<IR::Declaration_Variable>() ||
808 c->is<IR::P4Action>() || c->is<IR::P4Table>())
809 continue;
810 if (c->is<IR::Declaration_Instance>()) {
811 auto bl = ctxt->structure->resourceMap.at(c);
812 CHECK_NULL(bl);
813 if (bl->is<IR::ControlBlock>() || bl->is<IR::ParserBlock>())
814 // Since this block has not been inlined, it is probably unused
815 // So we don't do anything.
816 continue;
817 if (bl->is<IR::ExternBlock>()) {
818 auto eb = bl->to<IR::ExternBlock>();
819 ExternConverter::cvtExternInstance(ctxt, c, eb, emitExterns);
820 continue;
821 }
822 }
823 P4C_UNIMPLEMENTED("%1%: not yet handled", c);
824 }
825
826 ctxt->json->pipelines->append(result);
827 return false;
828 }
829
830 explicit ControlConverter(ConversionContext *ctxt, cstring name, const bool &emitExterns_)
831 : ctxt(ctxt),
832 name(name),
833 corelib(P4::P4CoreLibrary::instance()),
834 emitExterns(emitExterns_) {
835 setName("ControlConverter");
836 }
837};
838
839} // namespace BMV2
840
841#endif /* BACKENDS_BMV2_COMMON_CONTROL_H_ */
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:606
void setTable(const IR::IDeclaration *meter, const IR::P4Table *table)
Definition metermap.cpp:39
void setSize(const IR::IDeclaration *meter, unsigned size)
Definition metermap.cpp:92
Util::IJson * convert(const IR::Expression *e, bool doFixup=true, bool wrap=true, bool convertBool=false)
Definition expression.cpp:767
bool simpleExpressionsOnly
Definition expression.h:84
static const cstring selectorMatchTypeName
constant definition for bmv2
Definition helpers.h:39
ResourceMap resourceMap
map IR node to compile-time allocated resource blocks.
Definition programStructure.h:66
ordered_map< const IR::P4Action *, unsigned > ids
For each action its json id.
Definition programStructure.h:50
std::set< cstring > match_kinds
All match kinds.
Definition programStructure.h:64
DirectMeterMap directMeterMap
All the direct meters.
Definition programStructure.h:60
ordered_map< cstring, const IR::P4Table * > directCounterMap
All the direct counters.
Definition programStructure.h:62
Definition sharedActionSelectorCheck.h:40
Definition externInstance.h:33
Definition methodInstance.h:198
static EnumInstance * resolve(const IR::Expression *expression, const P4::TypeMap *typeMap)
Definition enumInstance.cpp:22
Definition methodInstance.h:290
static MethodInstance * resolve(const IR::MethodCallExpression *mce, DeclarationLookup *refMap, TypeMap *typeMap, bool useExpressionType=false, const Visitor::Context *ctxt=nullptr, bool incomplete=false)
Definition methodInstance.cpp:26
Definition coreLibrary.h:100
cstring newName(cstring base) override
Generate a name from base that fresh for the program.
Definition referenceMap.cpp:96
const IR::IDeclaration * getDeclaration(const IR::Path *path, bool notNull=false) const override
Definition referenceMap.cpp:78
Definition json.h:40
Definition json.h:111
Definition json.h:158
Definition cstring.h:72
Definition applyOptionsPragmas.cpp:24
Definition helpers.h:261
Definition helpers.h:100
T * to() noexcept
Definition rtti.h:226
bool is() const noexcept
Definition rtti.h:216