diff --git a/control-plane/p4RuntimeSerializer.cpp b/control-plane/p4RuntimeSerializer.cpp index d7f8dbed0a..1bed039ad2 100644 --- a/control-plane/p4RuntimeSerializer.cpp +++ b/control-plane/p4RuntimeSerializer.cpp @@ -28,6 +28,7 @@ limitations under the License. #include "frontends/common/resolveReferences/referenceMap.h" #include "frontends/p4/coreLibrary.h" +#include "frontends/p4/externInstance.h" #include "frontends/p4/fromv1.0/v1model.h" #include "frontends/p4/methodInstance.h" #include "frontends/p4/typeMap.h" @@ -41,11 +42,6 @@ namespace P4 { namespace ControlPlaneAPI { // XXX(seth): Here are the known issues: -// - Direct counters are not yet supported. -// - Direct meters are not yet supported. -// - We currently don't handle the case where, in P4-16, a table's -// implementation property refers to an action profile that is instantiated -// elsewhere. See Github issue #297. // - The @id pragma is not supported for header instances and especially not for // header stack instances, which currently requires allowing the programmer to // provide a sequence of ids in the @id pragma. Note that @id *is* supported @@ -240,6 +236,145 @@ struct ActionProfile { } }; +/// @return @table's size property if available, falling back to the default size. +static int64_t getTableSize(const IR::P4Table* table) { + const int64_t defaultTableSize = + P4V1::V1Model::instance.tableAttributes.defaultTableSize; + + auto sizeProperty = table->properties->getProperty("size"); + if (sizeProperty == nullptr) { + return defaultTableSize; + } + + if (!sizeProperty->value->is()) { + ::error("Expected an expression for table size property: %1%", sizeProperty); + return defaultTableSize; + } + + auto expression = sizeProperty->value->to()->expression; + if (!expression->is()) { + ::error("Expected a constant for table size property: %1%", sizeProperty); + return defaultTableSize; + } + + const int64_t tableSize = expression->to()->asInt(); + return tableSize == 0 ? defaultTableSize : tableSize; +} + +/// A traits class describing the properties of "counterlike" things. +/// XXX(seth): We could avoid needing this by refactoring the Model code a +/// little; it's currently not friendly to metaprogramming. +template struct CounterlikeTraits; + +template<> struct CounterlikeTraits { + static const cstring name() { return "counter"; } + static const cstring directPropertyName() { + return P4V1::V1Model::instance.tableAttributes.directCounter.name; + } + static const cstring typeName() { + return P4V1::V1Model::instance.counter.name; + } + static const cstring directTypeName() { + return P4V1::V1Model::instance.directCounter.name; + } +}; + +template<> struct CounterlikeTraits { + static const cstring name() { return "meter"; } + static const cstring directPropertyName() { + return P4V1::V1Model::instance.tableAttributes.directMeter.name; + } + static const cstring typeName() { + return P4V1::V1Model::instance.meter.name; + } + static const cstring directTypeName() { + return P4V1::V1Model::instance.directMeter.name; + } +}; + +/** + * The information about a counter or meter instance which is necessary to + * serialize it. @Kind must be a class with a CounterlikeTraits<> + * specialization. + */ +template +struct Counterlike { + /// The name of the instance. + const cstring name; + /// If non-null, the instance's annotations. + const IR::IAnnotated* annotations; + /// The units parameter to the instance; valid values vary depending on @Kind. + const cstring unit; + /// The size parameter to the instance. + const int64_t size; + /// If not none, the instance is a direct resource associated with @table. + const boost::optional table; + + /// @return the information required to serialize an explicit @instance of + /// @Kind, which is defined inside a control block. + static boost::optional> + from(const IR::ExternBlock* instance) { + CHECK_NULL(instance); + auto declaration = instance->node->to(); + + // Counter and meter externs refer to their unit as a "type"; this is + // (confusingly) unrelated to the "type" field of a counter or meter in + // P4Info. + auto unit = instance->getParameterValue("type"); + if (!unit->is()) { + ::error("%1% '%2%' has a unit type which is not an enum constant: %3%", + CounterlikeTraits::name(), declaration, unit); + return boost::none; + } + + auto size = instance->getParameterValue("size")->to(); + if (!size->is()) { + ::error("%1% '%2%' has a non-constant size: %3%", + CounterlikeTraits::name(), declaration, size); + return boost::none; + } + + return Counterlike{declaration->externalName(), + declaration->to(), + unit->to()->name, + size->to()->value.get_si(), + boost::none}; + } + + /// @return the information required to serialize an @instance of @Kind which + /// is either defined in or referenced by a property value of @table. (This + /// implies that @instance is a direct resource of @table.) + static boost::optional> + fromDirect(const ExternInstance& instance, const IR::P4Table* table) { + CHECK_NULL(table); + BUG_CHECK(instance.name != boost::none, + "Caller should've ensured we have a name"); + + if (instance.type->name != CounterlikeTraits::directTypeName()) { + ::error("Expected a direct %1%: %2%", CounterlikeTraits::name(), + instance.expression); + return boost::none; + } + + auto unitArgument = instance.arguments->at(0); + if (unitArgument == nullptr) { + ::error("Direct %1% instance %2% should take a constructor argument", + CounterlikeTraits::name(), instance.expression); + return boost::none; + } + if (!unitArgument->is()) { + ::error("Direct %1% instance %2% has an unexpected constructor argument", + CounterlikeTraits::name(), instance.expression); + return boost::none; + } + + auto unit = unitArgument->to()->member.name; + return Counterlike{*instance.name, instance.annotations, + unit, getTableSize(table), + table->externalName()}; + } +}; + /// @return the value of any P4 '@id' annotation @declaration may have. The name /// 'externalId' is in analogy with externalName(). static boost::optional @@ -714,69 +849,53 @@ class P4RuntimeSerializer { headerField->set_bitwidth(field->bitwidth); } - void addCounter(const IR::IDeclaration* counterDeclaration, - const IR::CompileTimeValue* size, - const IR::CompileTimeValue* unit) { - CHECK_NULL(counterDeclaration); - CHECK_NULL(size); - CHECK_NULL(unit); - BUG_CHECK(size->is(), - "Counter size should be a constant: %1%", size); - BUG_CHECK(unit->is(), - "Counter unit should be an enum constant: %1%", unit); - - auto name = counterDeclaration->externalName(); - auto annotations = counterDeclaration->to(); - auto unitName = unit->to()->name; - + void addCounter(const Counterlike& counterInstance) { auto counter = p4Info->add_counters(); - counter->mutable_preamble()->set_id(symbols.getId(P4RuntimeSymbolType::COUNTER, name)); - counter->mutable_preamble()->set_name(name); - addAnnotations(counter->mutable_preamble(), annotations); - counter->set_size(size->to()->value.get_si()); - counter->set_direct_table_id(0); // XXX(seth): Not yet supported. + auto id = symbols.getId(P4RuntimeSymbolType::COUNTER, counterInstance.name); + counter->mutable_preamble()->set_id(id); + counter->mutable_preamble()->set_name(counterInstance.name); + addAnnotations(counter->mutable_preamble(), counterInstance.annotations); + counter->set_size(counterInstance.size); - if (unitName == "packets") { + if (counterInstance.unit == "packets") { counter->set_unit(Counter::PACKETS); - } else if (unitName == "bytes") { + } else if (counterInstance.unit == "bytes") { counter->set_unit(Counter::BYTES); - } else if (unitName == "packets_and_bytes") { + } else if (counterInstance.unit == "packets_and_bytes") { counter->set_unit(Counter::BOTH); } else { counter->set_unit(Counter::UNSPECIFIED); } - } - void addMeter(const IR::IDeclaration* meterDeclaration, - const IR::CompileTimeValue* size, - const IR::CompileTimeValue* unit) { - CHECK_NULL(meterDeclaration); - CHECK_NULL(size); - CHECK_NULL(unit); - BUG_CHECK(size->is(), - "Meter size should be a constant: %1%", size); - BUG_CHECK(unit->is(), - "Meter unit should be an enum constant: %1%", unit); - - auto name = meterDeclaration->externalName(); - auto annotations = meterDeclaration->to(); - auto unitName = unit->to()->name; + if (counterInstance.table) { + auto id = symbols.getId(P4RuntimeSymbolType::TABLE, + *counterInstance.table); + counter->set_direct_table_id(id); + } + } + void addMeter(const Counterlike& meterInstance) { auto meter = p4Info->add_meters(); - meter->mutable_preamble()->set_id(symbols.getId(P4RuntimeSymbolType::METER, name)); - meter->mutable_preamble()->set_name(name); - addAnnotations(meter->mutable_preamble(), annotations); - meter->set_size(size->to()->value.get_si()); - meter->set_direct_table_id(0); // XXX(seth): Not yet supported. + auto id = symbols.getId(P4RuntimeSymbolType::METER, meterInstance.name); + meter->mutable_preamble()->set_id(id); + meter->mutable_preamble()->set_name(meterInstance.name); + addAnnotations(meter->mutable_preamble(), meterInstance.annotations); + meter->set_size(meterInstance.size); meter->set_type(Meter::COLOR_UNAWARE); // A default; this isn't exposed. - if (unitName == "packets") { + if (meterInstance.unit == "packets") { meter->set_unit(Meter::PACKETS); - } else if (unitName == "bytes") { + } else if (meterInstance.unit == "bytes") { meter->set_unit(Meter::BYTES); } else { meter->set_unit(Meter::UNSPECIFIED); } + + if (meterInstance.table) { + auto id = symbols.getId(P4RuntimeSymbolType::TABLE, + *meterInstance.table); + meter->set_direct_table_id(id); + } } void addAction(const IR::P4Action* actionDeclaration) { @@ -835,6 +954,8 @@ class P4RuntimeSerializer { void addTable(const IR::P4Table* tableDeclaration, uint64_t tableSize, const boost::optional& implementation, + const boost::optional>& directCounter, + const boost::optional>& directMeter, const boost::optional& defaultAction, const std::vector& actions, const std::vector>& matchFields) { @@ -853,6 +974,20 @@ class P4RuntimeSerializer { table->set_implementation_id(id); } + if (directCounter) { + auto id = symbols.getId(P4RuntimeSymbolType::COUNTER, + directCounter->name); + table->add_direct_resource_ids(id); + addCounter(*directCounter); + } + + if (directMeter) { + auto id = symbols.getId(P4RuntimeSymbolType::METER, + directMeter->name); + table->add_direct_resource_ids(id); + addMeter(*directMeter); + } + if (defaultAction) { auto id = symbols.getId(P4RuntimeSymbolType::ACTION, *defaultAction); table->set_const_default_action_id(id); @@ -1220,14 +1355,12 @@ static void serializeExtern(P4RuntimeSerializer& serializer, CHECK_NULL(externBlock); CHECK_NULL(typeMap); - if (externBlock->type->name == "counter") { - serializer.addCounter(externBlock->node->to(), - externBlock->getParameterValue("size"), - externBlock->getParameterValue("type")); - } else if (externBlock->type->name == "meter") { - serializer.addMeter(externBlock->node->to(), - externBlock->getParameterValue("size"), - externBlock->getParameterValue("type")); + if (externBlock->type->name == CounterlikeTraits::typeName()) { + auto counter = Counterlike::from(externBlock); + if (counter) serializer.addCounter(*counter); + } else if (externBlock->type->name == CounterlikeTraits::typeName()) { + auto meter = Counterlike::from(externBlock); + if (meter) serializer.addMeter(*meter); } } @@ -1277,8 +1410,50 @@ static void serializeParser(P4RuntimeSerializer& serializer, } } +/// @return an extern instance defined or referenced by the value of @table's +/// @propertyName property, or boost::none if no extern was referenced. +static boost::optional +getExternInstanceFromProperty(const IR::P4Table* table, + const cstring& propertyName, + ReferenceMap* refMap, + TypeMap* typeMap) { + auto property = table->properties->getProperty(propertyName); + if (property == nullptr) return boost::none; + if (!property->value->is()) { + ::error("Expected %1% property value for table %2% to be an expression: %3%", + propertyName, table->externalName(), property); + return boost::none; + } + + auto expr = property->value->to()->expression; + auto name = property->externalName(); + auto externInstance = ExternInstance::resolve(expr, refMap, typeMap, name); + if (!externInstance) { + ::error("Expected %1% property value for table %2% to resolve to an " + "extern instance: %3%", propertyName, table->externalName(), + property); + return boost::none; + } + + return externInstance; +} + +/// @return the direct counter associated with @table, if it has one, or +/// boost::none otherwise. +template +static boost::optional> +getDirectCounterlike(const IR::P4Table* table, ReferenceMap* refMap, TypeMap* typeMap) { + auto propertyName = CounterlikeTraits::directPropertyName(); + auto instance = + getExternInstanceFromProperty(table, propertyName, refMap, typeMap); + if (!instance) return boost::none; + return Counterlike::fromDirect(*instance, table); +} + static void collectTableSymbols(P4RuntimeSymbolTable& symbols, - const IR::TableBlock* tableBlock) { + const IR::TableBlock* tableBlock, + ReferenceMap* refMap, + TypeMap* typeMap) { CHECK_NULL(tableBlock); auto table = tableBlock->container; symbols.add(P4RuntimeSymbolType::TABLE, table); @@ -1293,31 +1468,16 @@ static void collectTableSymbols(P4RuntimeSymbolTable& symbols, if (impl) { symbols.add(P4RuntimeSymbolType::ACTION_PROFILE, impl->externalName()); } -} - -/// @return @table's size property if available, falling back to the default size. -static int64_t getTableSize(const IR::P4Table* table) { - const int64_t defaultTableSize = - P4V1::V1Model::instance.tableAttributes.defaultTableSize; - - auto sizeProperty = table->properties->getProperty("size"); - if (sizeProperty == nullptr) { - return defaultTableSize; - } - if (!sizeProperty->value->is()) { - ::error("Expected an expression for table size property: %1%", sizeProperty); - return defaultTableSize; + auto directCounter = getDirectCounterlike(table, refMap, typeMap); + if (directCounter) { + symbols.add(P4RuntimeSymbolType::COUNTER, directCounter->name); } - auto expression = sizeProperty->value->to()->expression; - if (!expression->is()) { - ::error("Expected a constant for table size property: %1%", sizeProperty); - return defaultTableSize; + auto directMeter = getDirectCounterlike(table, refMap, typeMap); + if (directMeter) { + symbols.add(P4RuntimeSymbolType::METER, directMeter->name); } - - const int64_t tableSize = expression->to()->asInt(); - return tableSize == 0 ? defaultTableSize : tableSize; } /// @return true iff @expression is a call to the 'isValid()' builtin. @@ -1487,8 +1647,11 @@ static void serializeTable(P4RuntimeSerializer& serializer, boost::optional implementation; if (impl) implementation = impl->externalName(); - serializer.addTable(table, tableSize, implementation, defaultAction, - actions, matchFields); + auto directCounter = getDirectCounterlike(table, refMap, typeMap); + auto directMeter = getDirectCounterlike(table, refMap, typeMap); + + serializer.addTable(table, tableSize, implementation, directCounter, + directMeter, defaultAction, actions, matchFields); } /// Visit evaluated blocks under the provided top-level block. Guarantees that @@ -1521,64 +1684,34 @@ static void forAllEvaluatedBlocks(const IR::ToplevelBlock* aToplevelBlock, /// if it has one, or boost::none otherwise. static boost::optional getActionProfile(const IR::P4Table* table, ReferenceMap* refMap, TypeMap* typeMap) { - auto impl = table->properties->getProperty(P4V1::V1Model::instance - .tableAttributes - .tableImplementation.name); - if (!impl) return boost::none; - - cstring name = impl->externalName(); - - // In P4-16, an action profile is just an instantiation of an extern. In - // P4-14, it's a language construct, but it's desugared into an extern - // instantiation in the frontend. Either way, we currently expect the - // implementation property to contain an extern constructor call. - // XXX(seth): You should also be able to refer to an action profile that's - // instantiated elsewhere (i.e., we should accept a Declaration_Instance - // here), but that currently crashes the compiler - see Github issue #297. - // Once that's fixed, we'll be able to support it here. - if (!impl->value->is()) { - ::error("Table '%1%' has an implementation which is not an expression: %2%", - table->externalName(), impl); - return boost::none; - } - const auto propValue = impl->value->to(); - if (!propValue->expression->is()) { - ::error("Table '%1%' has an implementation which is not a constructor call: %2%", - table->externalName(), impl); - return boost::none; - } - const auto constructorCallExpr = - propValue->expression->to(); - const auto constructorCall = - P4::ConstructorCall::resolve(constructorCallExpr, refMap, typeMap); - if (!constructorCall->is()) { - ::error("Table '%1%' has an implementation which doesn't resolve to an extern: %2%", - table->externalName(), impl); - return boost::none; - } + auto propertyName = P4V1::V1Model::instance.tableAttributes + .tableImplementation.name; + auto instance = + getExternInstanceFromProperty(table, propertyName, refMap, typeMap); + if (!instance) return boost::none; - const auto externType = constructorCall->to()->type; ActionProfileType actionProfileType; const IR::Expression* sizeExpression; - if (externType->name == P4V1::V1Model::instance.action_selector.name) { + if (instance->type->name == P4V1::V1Model::instance.action_selector.name) { actionProfileType = ActionProfileType::INDIRECT_WITH_SELECTOR; - sizeExpression = constructorCallExpr->arguments->at(1); - } else if (externType->name == P4V1::V1Model::instance.action_profile.name) { + sizeExpression = instance->arguments->at(1); + } else if (instance->type->name == P4V1::V1Model::instance.action_profile.name) { actionProfileType = ActionProfileType::INDIRECT; - sizeExpression = constructorCallExpr->arguments->at(0); + sizeExpression = instance->arguments->at(0); } else { ::error("Table '%1%' has an implementation which doesn't resolve to an " - "action profile: %2%", table->externalName(), impl); + "action profile: %2%", table->externalName(), instance->expression); return boost::none; } if (!sizeExpression->is()) { - ::error("Action profile '%1%' has non-constant size '%1%'", name, sizeExpression); + ::error("Action profile '%1%' has non-constant size '%1%'", + *instance->name, sizeExpression); return boost::none; } const int64_t size = sizeExpression->to()->asInt(); - return ActionProfile{name, actionProfileType, size}; + return ActionProfile{*instance->name, actionProfileType, size}; } static void serializeActionProfiles(P4RuntimeSerializer& serializer, @@ -1631,7 +1764,7 @@ void serializeP4Runtime(std::ostream* destination, } else if (block->is()) { collectParserSymbols(symbols, block->to(), typeMap); } else if (block->is()) { - collectTableSymbols(symbols, block->to()); + collectTableSymbols(symbols, block->to(), refMap, typeMap); } }); }); diff --git a/frontends/Makefile.am b/frontends/Makefile.am index 7c9f66a224..8286e7ba11 100644 --- a/frontends/Makefile.am +++ b/frontends/Makefile.am @@ -32,6 +32,7 @@ p4_frontend_UNIFIED = \ frontends/p4/parserControlFlow.cpp \ frontends/p4/reservedWords.cpp \ frontends/p4/coreLibrary.cpp \ + frontends/p4/externInstance.cpp \ frontends/p4/methodInstance.cpp \ frontends/p4/parserCallGraph.cpp \ frontends/p4/tableApply.cpp \ @@ -67,6 +68,7 @@ noinst_HEADERS += \ frontends/p4/enumInstance.h \ frontends/p4/evaluator/evaluator.h \ frontends/p4/evaluator/substituteParameters.h \ + frontends/p4/externInstance.h \ frontends/p4/fromv1.0/converters.h \ frontends/p4/fromv1.0/programStructure.h \ frontends/p4/fromv1.0/v1model.h \ diff --git a/frontends/p4/externInstance.cpp b/frontends/p4/externInstance.cpp new file mode 100644 index 0000000000..3017ac8e59 --- /dev/null +++ b/frontends/p4/externInstance.cpp @@ -0,0 +1,101 @@ +/* +Copyright 2013-present Barefoot Networks, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "externInstance.h" + +#include "frontends/common/resolveReferences/referenceMap.h" +#include "frontends/p4/methodInstance.h" +#include "frontends/p4/typeMap.h" +#include "ir/ir.h" + +namespace P4 { + + +boost::optional +ExternInstance::resolve(const IR::Expression* expr, + ReferenceMap* refMap, + TypeMap* typeMap, + const boost::optional& defaultName + /* = boost::none */) { + CHECK_NULL(expr); + CHECK_NULL(refMap); + CHECK_NULL(typeMap); + + if (expr->is()) { + return resolve(expr->to(), refMap, typeMap); + } else if (expr->is()) { + return resolve(expr->to(), + refMap, typeMap, defaultName); + } else { + return boost::none; + } +} + +boost::optional +ExternInstance::resolve(const IR::PathExpression* path, + ReferenceMap* refMap, + TypeMap* typeMap) { + CHECK_NULL(path); + CHECK_NULL(refMap); + CHECK_NULL(typeMap); + + auto decl = refMap->getDeclaration(path->path, true); + if (!decl->is()) return boost::none; + + auto instance = decl->to(); + auto type = typeMap->getType(instance); + if (!type) { + BUG("Couldn't determine the type of expression: %1%", path); + return boost::none; + } + + // If this is a type instantiation, extract the underlying generic type. + if (type->is()) { + type = typeMap->getTypeType(type->to() + ->baseType, true); + } else if (type->is()) { + type = typeMap->getTypeType(type->to() + ->baseType, true); + } + + if (!type->is()) return boost::none; + + return ExternInstance{instance->externalName(), path, + type->to(), + instance->arguments, + instance->to()}; +} + +boost::optional +ExternInstance::resolve(const IR::ConstructorCallExpression* constructorCallExpr, + ReferenceMap* refMap, + TypeMap* typeMap, + const boost::optional& name /* = boost::none */) { + CHECK_NULL(constructorCallExpr); + CHECK_NULL(refMap); + CHECK_NULL(typeMap); + + auto constructorCall = + P4::ConstructorCall::resolve(constructorCallExpr, refMap, typeMap); + if (!constructorCall->is()) return boost::none; + + auto type = constructorCall->to()->type; + return ExternInstance{name, constructorCallExpr, type, + constructorCallExpr->arguments, + constructorCallExpr->to()}; +} + +} // namespace P4 diff --git a/frontends/p4/externInstance.h b/frontends/p4/externInstance.h new file mode 100644 index 0000000000..22ed059cff --- /dev/null +++ b/frontends/p4/externInstance.h @@ -0,0 +1,91 @@ +/* +Copyright 2013-present Barefoot Networks, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef _FRONTENDS_P4_EXTERNINSTANCE_H_ +#define _FRONTENDS_P4_EXTERNINSTANCE_H_ + +#include +#include "lib/cstring.h" + +namespace IR { +class Expression; +class IAnnotated; +class Type_Extern; +class TypeMap; +template class Vector; +} // namespace IR + +namespace P4 { + +class ReferenceMap; +class TypeMap; + +/** + * ExternInstance is a utility class that allows you to gather information about + * an expression that resolves to an extern instance. + * + * The name is in analogy to MethodInstance. It provides information about the + * name of the extern instance, the underlying extern type, the arguments it was + * instantiated with, and the annotations that we applied to it. It can gather + * this information from either a reference to a named instance, or from an + * expression which constructs an anonymous instance. + */ +struct ExternInstance final { + const boost::optional name; // The instance's name, if any. + const IR::Expression* expression; // The original expression passed to resolve(). + const IR::Type_Extern* type; // The type of the instance. + const IR::Vector* arguments; // The instance's constructor arguments. + const IR::IAnnotated* annotations; // If non-null, the instance's annotations. + + /** + * @return the extern instance that @expr resolves to, if any, or + * boost::none otherwise. + * + * @expr may either refer to a named extern instance (i.e., it may be a + * PathExpression), or it may construct an anonymous instance directly + * (i.e., it may be a ConstructorCallExpression). In the latter case, the + * instance will not have a name; @defaultName may optionally be used to + * specify a default name to return. + */ + static boost::optional + resolve(const IR::Expression* expr, + ReferenceMap* refMap, + TypeMap* typeMap, + const boost::optional& defaultName = boost::none); + + /** + * @return the extern instance that @path resolves to, if any, or + * boost::none otherwise. + */ + static boost::optional + resolve(const IR::PathExpression* path, ReferenceMap* refMap, TypeMap* typeMap); + + /** + * @return the extern instance that @constructorCallExpr resolves to, if any, or + * boost::none otherwise. + * + * Anonymous instances do not have a name, but the caller may provide one + * via @name if it has external information about what the name should be. + */ + static boost::optional + resolve(const IR::ConstructorCallExpression* constructorCallExpr, + ReferenceMap* refMap, TypeMap* typeMap, + const boost::optional& name = boost::none); +}; + +} // namespace P4 + +#endif /* _FRONTENDS_P4_EXTERNINSTANCE_H_ */