From 22a5a13ee347dd40e6b7b6472e00ea2205db5358 Mon Sep 17 00:00:00 2001 From: Mihai Budiu Date: Wed, 28 Nov 2018 09:03:46 -0800 Subject: [PATCH] Fix for issue #562 (#1605) * Fix for issue #562 --- backends/bmv2/simple_switch/midend.cpp | 2 + ir/ir.def | 2 + midend/CMakeLists.txt | 2 + midend/flattenInterfaceStructs.cpp | 179 +++++++++++++++++ midend/flattenInterfaceStructs.h | 188 ++++++++++++++++++ midend/nestedStructs.cpp | 2 +- midend/nestedStructs.h | 10 +- testdata/p4_16_samples/issue562-bmv2.p4 | 59 ++++++ .../issue562-bmv2-first.p4 | 57 ++++++ .../issue562-bmv2-frontend.p4 | 57 ++++++ .../issue562-bmv2-midend.p4 | 67 +++++++ .../p4_16_samples_outputs/issue562-bmv2.p4 | 57 ++++++ .../issue562-bmv2.p4-stderr | 0 13 files changed, 678 insertions(+), 4 deletions(-) create mode 100644 midend/flattenInterfaceStructs.cpp create mode 100644 midend/flattenInterfaceStructs.h create mode 100644 testdata/p4_16_samples/issue562-bmv2.p4 create mode 100644 testdata/p4_16_samples_outputs/issue562-bmv2-first.p4 create mode 100644 testdata/p4_16_samples_outputs/issue562-bmv2-frontend.p4 create mode 100644 testdata/p4_16_samples_outputs/issue562-bmv2-midend.p4 create mode 100644 testdata/p4_16_samples_outputs/issue562-bmv2.p4 create mode 100644 testdata/p4_16_samples_outputs/issue562-bmv2.p4-stderr diff --git a/backends/bmv2/simple_switch/midend.cpp b/backends/bmv2/simple_switch/midend.cpp index 8b7d0758f4..9f18f0b773 100644 --- a/backends/bmv2/simple_switch/midend.cpp +++ b/backends/bmv2/simple_switch/midend.cpp @@ -35,6 +35,7 @@ limitations under the License. #include "midend/eliminateTuples.h" #include "midend/eliminateNewtype.h" #include "midend/eliminateSerEnums.h" +#include "midend/flattenInterfaceStructs.h" #include "midend/local_copyprop.h" #include "midend/nestedStructs.h" #include "midend/removeLeftSlices.h" @@ -84,6 +85,7 @@ SimpleSwitchMidEnd::SimpleSwitchMidEnd(CompilerOptions& options) : MidEnd(option new P4::NestedStructs(&refMap, &typeMap), new P4::SimplifySelectList(&refMap, &typeMap), new P4::RemoveSelectBooleans(&refMap, &typeMap), + new P4::FlattenInterfaceStructs(&refMap, &typeMap), new P4::Predication(&refMap), new P4::MoveDeclarations(), // more may have been introduced new P4::ConstantFolding(&refMap, &typeMap), diff --git a/ir/ir.def b/ir/ir.def index 3511796392..c1f1781609 100644 --- a/ir/ir.def +++ b/ir/ir.def @@ -371,6 +371,8 @@ class Declaration_Constant : Declaration, IAnnotated { /// Like a variable, but for a statically allocated instance. /// The syntax is Contructor(args) name = initializer; +/// Initializers are an experimental features, used for externs with +/// abstract methods. class Declaration_Instance : Declaration, IAnnotated, IInstance { optional Annotations annotations = Annotations::empty; Type type; // Either Type_Name or Type_Specialized or Type_Extern diff --git a/midend/CMakeLists.txt b/midend/CMakeLists.txt index bc7854f608..ab6f6d02ee 100644 --- a/midend/CMakeLists.txt +++ b/midend/CMakeLists.txt @@ -22,6 +22,7 @@ set (MIDEND_SRCS eliminateSerEnums.cpp expandEmit.cpp expandLookahead.cpp + flattenInterfaceStructs.cpp interpreter.cpp local_copyprop.cpp nestedStructs.cpp @@ -55,6 +56,7 @@ set (MIDEND_HDRS expandEmit.h expandLookahead.h expr_uses.h + flattenInterfaceStructs.h has_side_effects.h interpreter.h local_copyprop.h diff --git a/midend/flattenInterfaceStructs.cpp b/midend/flattenInterfaceStructs.cpp new file mode 100644 index 0000000000..d49c3a3b43 --- /dev/null +++ b/midend/flattenInterfaceStructs.cpp @@ -0,0 +1,179 @@ +/* +Copyright 2018 VMware, 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 "flattenInterfaceStructs.h" + +namespace P4 { + +void StructTypeReplacement::flatten(const P4::TypeMap* typeMap, + cstring prefix, + const IR::Type* type, + IR::IndexedVector *fields) { + if (auto st = type->to()) { + structFieldMap.emplace(prefix, st); + for (auto f : st->fields) + flatten(typeMap, prefix + "." + f->name, f->type, fields); + return; + } + cstring fieldName = prefix.replace(".", "_") + + cstring::to_cstring(fieldNameRemap.size()); + fieldNameRemap.emplace(prefix, fieldName); + fields->push_back(new IR::StructField(IR::ID(fieldName), type)); +} + +StructTypeReplacement::StructTypeReplacement( + const P4::TypeMap* typeMap, const IR::Type_Struct* type) { + auto vec = new IR::IndexedVector(); + flatten(typeMap, "", type, vec); + replacementType = new IR::Type_Struct(type->name, IR::Annotations::empty, *vec); +} + +const IR::StructInitializerExpression* StructTypeReplacement::explode( + const IR::Expression *root, cstring prefix) { + auto vec = new IR::IndexedVector(); + auto fieldType = ::get(structFieldMap, prefix); + CHECK_NULL(fieldType); + for (auto f : fieldType->fields) { + cstring fieldName = prefix + "." + f->name.name; + auto newFieldname = ::get(fieldNameRemap, fieldName); + const IR::Expression* expr; + if (!newFieldname.isNullOrEmpty()) { + expr = new IR::Member(root, newFieldname); + } else { + expr = explode(root, fieldName); + } + vec->push_back(new IR::NamedExpression(f->name, expr)); + } + return new IR::StructInitializerExpression(fieldType->name, *vec, false); +} + +static const IR::Type_Struct* isNestedStruct(const P4::TypeMap* typeMap, const IR::Type* type) { + if (auto st = type->to()) { + for (auto f : st->fields) { + auto ft = typeMap->getType(f, true); + if (ft->is()) + return st; + } + } + return nullptr; +} + +void NestedStructMap::createReplacement(const IR::Type_Struct* type) { + auto repl = ::get(replacement, type); + if (repl != nullptr) + return; + repl = new StructTypeReplacement(typeMap, type); + LOG3("Replacement for " << type << " is " << repl); + replacement.emplace(type, repl); +} + +bool FindTypesToReplace::preorder(const IR::Declaration_Instance* inst) { + auto type = map->typeMap->getTypeType(inst->type, true); + auto ts = type->to(); + if (ts == nullptr) + return false; + if (!ts->baseType->is()) + return false; + for (auto t : *ts->arguments) { + if (auto st = isNestedStruct(map->typeMap, t)) + map->createReplacement(st); + } + return false; +} + +///////////////////////////////// + +const IR::Node* ReplaceStructs::preorder(IR::P4Program* program) { + if (replacementMap->empty()) { + // nothing to do + prune(); + } + return program; +} + +const IR::Node* ReplaceStructs::postorder(IR::Type_Struct* type) { + auto canon = replacementMap->typeMap->getTypeType(getOriginal(), true); + auto repl = replacementMap->getReplacement(canon); + if (repl != nullptr) + return repl->replacementType; + return type; +} + +const IR::Node* ReplaceStructs::postorder(IR::Member* expression) { + // Find out if this applies to one of the parameters that are being replaced. + if (getParent() != nullptr) + // We only want to process the outermost Member + return expression; + const IR::Expression* e = expression; + cstring prefix = ""; + while (auto mem = e->to()) { + e = mem->expr; + prefix = cstring(".") + mem->member + prefix; + } + auto pe = e->to(); + if (pe == nullptr) + return expression; + // At this point we know that pe is an expression of the form + // param.field1.etc.fieldN, where param has a type that needs to be replaced. + auto decl = replacementMap->refMap->getDeclaration(pe->path, true); + auto param = decl->to(); + if (param == nullptr) + return expression; + auto repl = ::get(toReplace, param); + if (repl == nullptr) + return expression; + auto newFieldName = ::get(repl->fieldNameRemap, prefix); + const IR::Expression* result; + if (newFieldName.isNullOrEmpty()) { + // Prefix is a reference to a field of the original struct whose + // type is actually a struct itself. We need to replace the field + // with a struct initializer expression. (This won't work if the + // field is being used as a left-value. Hopefully, all such uses + // of a struct-valued field as a left-value have been already + // replaced by the NestedStructs pass.) + result = repl->explode(pe, prefix); + } else { + result = new IR::Member(pe, newFieldName); + } + LOG3("Replacing " << expression << " with " << result); + return result; +} + +const IR::Node* ReplaceStructs::preorder(IR::P4Parser* parser) { + for (auto p : parser->getApplyParameters()->parameters) { + auto pt = replacementMap->typeMap->getType(p, true); + auto repl = replacementMap->getReplacement(pt); + if (repl != nullptr) { + toReplace.emplace(p, repl); + LOG3("Replacing parameter " << dbp(p) << " of " << dbp(parser)); + } + } + return parser; +} + +const IR::Node* ReplaceStructs::preorder(IR::P4Control* control) { + for (auto p : control->getApplyParameters()->parameters) { + auto pt = replacementMap->typeMap->getType(p, true); + auto repl = replacementMap->getReplacement(pt); + if (repl != nullptr) { + toReplace.emplace(p, repl); + LOG3("Replacing parameter " << dbp(p) << " of " << dbp(control)); + } + } + return control; +} + +} // namespace P4 diff --git a/midend/flattenInterfaceStructs.h b/midend/flattenInterfaceStructs.h new file mode 100644 index 0000000000..d9ebf0361e --- /dev/null +++ b/midend/flattenInterfaceStructs.h @@ -0,0 +1,188 @@ +/* +Copyright 2018 VMware, 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 _MIDEND_FLATTENINTERFACESTRUCTS_H_ +#define _MIDEND_FLATTENINTERFACESTRUCTS_H_ + +#include "ir/ir.h" +#include "frontends/p4/typeChecking/typeChecker.h" + +namespace P4 { + +/** +Describes how a nested struct type is replaced: the new type to +replace it and how each field is renamed. For example, consider +the following: + +struct S { + bit a; + bool b; +} + +struct T { + S s; + bit<6> y; +} + +struct M { + T t; + bit<3> x; +} +*/ +struct StructTypeReplacement : public IHasDbPrint { + StructTypeReplacement(const P4::TypeMap* typeMap, const IR::Type_Struct* type); + + // Maps nested field names to final field names. + // In our example this could be: + // .t.s.a -> _t_s_a0; + // .t.s.b -> _t_s_b1; + // .t.y -> _t_y2; + // .x -> _x3; + std::map fieldNameRemap; + // Maps internal fields names to types. + // .t -> T + // .t.s -> S + std::map structFieldMap; + // Holds a new flat type + // struct M { + // bit _t_s_a0; + // bool _t_s_b1; + // bit<6> _t_y2; + // bit<3> _x3; + // } + const IR::Type* replacementType; + virtual void dbprint(std::ostream& out) const { + out << replacementType; + } + + // Helper for constructor + void flatten(const P4::TypeMap* typeMap, + cstring prefix, + const IR::Type* type, + IR::IndexedVector *fields); + + /// Returns a StructInitializerExpression suitable for + /// initializing a struct for the fields that start with the + /// given prefix. For example, for prefix .t and root R this returns + /// { .s = { .a = R._t_s_a0, .b = R._t_s_b1 }, .y = R._t_y2 } + const IR::StructInitializerExpression* explode( + const IR::Expression* root, cstring prefix); +}; + +/** + * Maintains a map between an original struct type and its + * replacement. + */ +struct NestedStructMap { + P4::ReferenceMap* refMap; + P4::TypeMap* typeMap; + + ordered_map replacement; + + NestedStructMap(P4::ReferenceMap* refMap, P4::TypeMap* typeMap): + refMap(refMap), typeMap(typeMap) + { CHECK_NULL(refMap); CHECK_NULL(typeMap); } + void createReplacement(const IR::Type_Struct* type); + StructTypeReplacement* getReplacement(const IR::Type* type) const + { return ::get(replacement, type); } + bool empty() const { return replacement.empty(); } +}; + +/** +Find the types to replace and insert them in the nested struct map. +This pass only looks at type arguments used in package instantiations. + */ +class FindTypesToReplace : public Inspector { + NestedStructMap* map; + public: + explicit FindTypesToReplace(NestedStructMap* map): map(map) { + setName("FindTypesToReplace"); + CHECK_NULL(map); + } + bool preorder(const IR::Declaration_Instance* inst) override; +}; + +/** +This pass transforms the type signatures of instantiated controls, +parsers, and packages. It does not transform methods, functions or +actions. It starts from package instantiations: every type argument +that is a nested structure is replaced with "simpler" flat type. + +Should be run after the NestedStructs pass. + +struct S { bit b; } +struct T { S s; } + +control c(inout T arg) { + apply { + ... arg.s.b ... + } +} + +control proto(inout V arg); +package top(proto ctrl); + +top(c()) main; + +This is transformed into: + +struct S { bit b; } +struct T { + bit _s_b0; +} + +control c(inout T arg) { + apply { + ... arg._s_b0; ... + } +} + +top(c()) main; + + */ +class ReplaceStructs : public Transform { + NestedStructMap* replacementMap; + std::map toReplace; + + public: + explicit ReplaceStructs(NestedStructMap* sm): replacementMap(sm) { + CHECK_NULL(sm); + setName("ReplaceStructs"); + } + + const IR::Node* preorder(IR::P4Program* program) override; + const IR::Node* postorder(IR::Member* expression) override; + const IR::Node* preorder(IR::P4Parser* parser) override; + const IR::Node* preorder(IR::P4Control* control) override; + const IR::Node* postorder(IR::Type_Struct* type) override; +}; + +class FlattenInterfaceStructs final : public PassManager { + public: + FlattenInterfaceStructs(ReferenceMap* refMap, TypeMap* typeMap) { + auto sm = new NestedStructMap(refMap, typeMap); + passes.push_back(new TypeChecking(refMap, typeMap)); + passes.push_back(new FindTypesToReplace(sm)); + passes.push_back(new ReplaceStructs(sm)); + passes.push_back(new ClearTypeMap(typeMap)); + setName("FlattenInterfaceStructs"); + } +}; + + +} // namespace P4 + +#endif /* _MIDEND_FLATTENINTERFACESTRUCTS_H_ */ diff --git a/midend/nestedStructs.cpp b/midend/nestedStructs.cpp index b02c863063..3597ef860e 100644 --- a/midend/nestedStructs.cpp +++ b/midend/nestedStructs.cpp @@ -60,7 +60,7 @@ const IR::Node* RemoveNestedStructs::postorder(IR::Member* expression) { auto left = values->getTranslation(expression->expr); if (left == nullptr) return expression; - auto comp = left->get(expression->member.name); + auto comp = left->getComponent(expression->member.name); if (comp == nullptr) { auto l = left->convertToExpression(); auto e = new IR::Member(expression->srcInfo, l, expression->member); diff --git a/midend/nestedStructs.h b/midend/nestedStructs.h index f74562c911..a27f56b558 100644 --- a/midend/nestedStructs.h +++ b/midend/nestedStructs.h @@ -24,9 +24,13 @@ namespace P4 { class ComplexValues final { public: + /** + * Represents a field or a collection of fields of a value that + * has a struct type. + */ struct Component : public IHasDbPrint { virtual const IR::Expression* convertToExpression() = 0; - virtual Component* get(cstring name) = 0; + virtual Component* getComponent(cstring name) = 0; virtual void dbprint(std::ostream& out) const = 0; }; @@ -35,7 +39,7 @@ class ComplexValues final { explicit FinalName(cstring name) : newName(name) {} const IR::Expression* convertToExpression() override { return new IR::PathExpression(IR::ID(newName)); } - Component* get(cstring) override + Component* getComponent(cstring) override { return nullptr; } void dbprint(std::ostream& out) const override { out << newName << IndentCtl::endl; } @@ -52,7 +56,7 @@ class ComplexValues final { } return vec; } - Component* get(cstring name) override + Component* getComponent(cstring name) override { return ::get(members, name); } void dbprint(std::ostream& out) const override { out << IndentCtl::indent; diff --git a/testdata/p4_16_samples/issue562-bmv2.p4 b/testdata/p4_16_samples/issue562-bmv2.p4 new file mode 100644 index 0000000000..2609e27deb --- /dev/null +++ b/testdata/p4_16_samples/issue562-bmv2.p4 @@ -0,0 +1,59 @@ +#include + +struct alt_t { + bit<1> valid; + bit<7> port; +}; + +struct row_t { + alt_t alt0; + alt_t alt1; +}; + +struct parsed_packet_t {}; + +struct local_metadata_t { + row_t row; +}; + +parser parse(packet_in pk, out parsed_packet_t hdr, + inout local_metadata_t local_metadata, + inout standard_metadata_t standard_metadata) { + state start { + transition accept; + } +} + +control ingress(inout parsed_packet_t hdr, + inout local_metadata_t local_metadata, + inout standard_metadata_t standard_metadata) { + apply { + local_metadata.row.alt0 = local_metadata.row.alt1; + local_metadata.row.alt0.valid = 1; + local_metadata.row.alt1.port = local_metadata.row.alt1.port + 1; + clone3(CloneType.I2E, 0, local_metadata.row); + } +} + +control egress(inout parsed_packet_t hdr, + inout local_metadata_t local_metadata, + inout standard_metadata_t standard_metadata) { + apply { } +} + +control deparser(packet_out b, in parsed_packet_t hdr) { + apply { } +} + +control verify_checksum(inout parsed_packet_t hdr, + inout local_metadata_t local_metadata) { + apply { } +} + +control compute_checksum(inout parsed_packet_t hdr, + inout local_metadata_t local_metadata) { + apply { } +} + +V1Switch(parse(), verify_checksum(), ingress(), egress(), + compute_checksum(), deparser()) main; diff --git a/testdata/p4_16_samples_outputs/issue562-bmv2-first.p4 b/testdata/p4_16_samples_outputs/issue562-bmv2-first.p4 new file mode 100644 index 0000000000..8c2f2a4e58 --- /dev/null +++ b/testdata/p4_16_samples_outputs/issue562-bmv2-first.p4 @@ -0,0 +1,57 @@ +#include +#include + +struct alt_t { + bit<1> valid; + bit<7> port; +} + +struct row_t { + alt_t alt0; + alt_t alt1; +} + +struct parsed_packet_t { +} + +struct local_metadata_t { + row_t row; +} + +parser parse(packet_in pk, out parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + state start { + transition accept; + } +} + +control ingress(inout parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + apply { + local_metadata.row.alt0 = local_metadata.row.alt1; + local_metadata.row.alt0.valid = 1w1; + local_metadata.row.alt1.port = local_metadata.row.alt1.port + 7w1; + clone3(CloneType.I2E, 32w0, local_metadata.row); + } +} + +control egress(inout parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + apply { + } +} + +control deparser(packet_out b, in parsed_packet_t hdr) { + apply { + } +} + +control verify_checksum(inout parsed_packet_t hdr, inout local_metadata_t local_metadata) { + apply { + } +} + +control compute_checksum(inout parsed_packet_t hdr, inout local_metadata_t local_metadata) { + apply { + } +} + +V1Switch(parse(), verify_checksum(), ingress(), egress(), compute_checksum(), deparser()) main; + diff --git a/testdata/p4_16_samples_outputs/issue562-bmv2-frontend.p4 b/testdata/p4_16_samples_outputs/issue562-bmv2-frontend.p4 new file mode 100644 index 0000000000..8c2f2a4e58 --- /dev/null +++ b/testdata/p4_16_samples_outputs/issue562-bmv2-frontend.p4 @@ -0,0 +1,57 @@ +#include +#include + +struct alt_t { + bit<1> valid; + bit<7> port; +} + +struct row_t { + alt_t alt0; + alt_t alt1; +} + +struct parsed_packet_t { +} + +struct local_metadata_t { + row_t row; +} + +parser parse(packet_in pk, out parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + state start { + transition accept; + } +} + +control ingress(inout parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + apply { + local_metadata.row.alt0 = local_metadata.row.alt1; + local_metadata.row.alt0.valid = 1w1; + local_metadata.row.alt1.port = local_metadata.row.alt1.port + 7w1; + clone3(CloneType.I2E, 32w0, local_metadata.row); + } +} + +control egress(inout parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + apply { + } +} + +control deparser(packet_out b, in parsed_packet_t hdr) { + apply { + } +} + +control verify_checksum(inout parsed_packet_t hdr, inout local_metadata_t local_metadata) { + apply { + } +} + +control compute_checksum(inout parsed_packet_t hdr, inout local_metadata_t local_metadata) { + apply { + } +} + +V1Switch(parse(), verify_checksum(), ingress(), egress(), compute_checksum(), deparser()) main; + diff --git a/testdata/p4_16_samples_outputs/issue562-bmv2-midend.p4 b/testdata/p4_16_samples_outputs/issue562-bmv2-midend.p4 new file mode 100644 index 0000000000..513b168da9 --- /dev/null +++ b/testdata/p4_16_samples_outputs/issue562-bmv2-midend.p4 @@ -0,0 +1,67 @@ +#include +#include + +struct alt_t { + bit<1> valid; + bit<7> port; +} + +struct row_t { + alt_t alt0; + alt_t alt1; +} + +struct parsed_packet_t { +} + +struct local_metadata_t { + row_t row; +} + +parser parse(packet_in pk, out parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + state start { + transition accept; + } +} + +control ingress(inout parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + @hidden action act() { + local_metadata.row.alt0.valid = local_metadata.row.alt1.valid; + local_metadata.row.alt0.port = local_metadata.row.alt1.port; + local_metadata.row.alt0.valid = 1w1; + local_metadata.row.alt1.port = local_metadata.row.alt1.port + 7w1; + clone3(CloneType.I2E, 32w0, local_metadata.row); + } + @hidden table tbl_act { + actions = { + act(); + } + const default_action = act(); + } + apply { + tbl_act.apply(); + } +} + +control egress(inout parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + apply { + } +} + +control deparser(packet_out b, in parsed_packet_t hdr) { + apply { + } +} + +control verify_checksum(inout parsed_packet_t hdr, inout local_metadata_t local_metadata) { + apply { + } +} + +control compute_checksum(inout parsed_packet_t hdr, inout local_metadata_t local_metadata) { + apply { + } +} + +V1Switch(parse(), verify_checksum(), ingress(), egress(), compute_checksum(), deparser()) main; + diff --git a/testdata/p4_16_samples_outputs/issue562-bmv2.p4 b/testdata/p4_16_samples_outputs/issue562-bmv2.p4 new file mode 100644 index 0000000000..dad66b0000 --- /dev/null +++ b/testdata/p4_16_samples_outputs/issue562-bmv2.p4 @@ -0,0 +1,57 @@ +#include +#include + +struct alt_t { + bit<1> valid; + bit<7> port; +} + +struct row_t { + alt_t alt0; + alt_t alt1; +} + +struct parsed_packet_t { +} + +struct local_metadata_t { + row_t row; +} + +parser parse(packet_in pk, out parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + state start { + transition accept; + } +} + +control ingress(inout parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + apply { + local_metadata.row.alt0 = local_metadata.row.alt1; + local_metadata.row.alt0.valid = 1; + local_metadata.row.alt1.port = local_metadata.row.alt1.port + 1; + clone3(CloneType.I2E, 0, local_metadata.row); + } +} + +control egress(inout parsed_packet_t hdr, inout local_metadata_t local_metadata, inout standard_metadata_t standard_metadata) { + apply { + } +} + +control deparser(packet_out b, in parsed_packet_t hdr) { + apply { + } +} + +control verify_checksum(inout parsed_packet_t hdr, inout local_metadata_t local_metadata) { + apply { + } +} + +control compute_checksum(inout parsed_packet_t hdr, inout local_metadata_t local_metadata) { + apply { + } +} + +V1Switch(parse(), verify_checksum(), ingress(), egress(), compute_checksum(), deparser()) main; + diff --git a/testdata/p4_16_samples_outputs/issue562-bmv2.p4-stderr b/testdata/p4_16_samples_outputs/issue562-bmv2.p4-stderr new file mode 100644 index 0000000000..e69de29bb2