From b1430115ebaceb37bc50f6bd5628628caf2ea317 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Fri, 7 Apr 2017 18:09:06 -0700 Subject: [PATCH 1/3] A few more gtest cases for psa --- frontends/p4/p4-parse.ypp | 5 +- lib/source_file.cpp | 5 + lib/source_file.h | 3 + test/gtest/Makefile.am | 4 +- test/gtest/arch_test.cpp | 318 ++++++++++++++++++++++++++++++++++ test/gtest/typecheck_test.cpp | 75 -------- 6 files changed, 331 insertions(+), 79 deletions(-) create mode 100644 test/gtest/arch_test.cpp delete mode 100644 test/gtest/typecheck_test.cpp diff --git a/frontends/p4/p4-parse.ypp b/frontends/p4/p4-parse.ypp index 5a2c70ab46a..ca06a89757a 100644 --- a/frontends/p4/p4-parse.ypp +++ b/frontends/p4/p4-parse.ypp @@ -1035,13 +1035,14 @@ const IR::P4Program *parse_P4_16_file(const char *name, FILE *in) { const IR::P4Program *parse_string(const std::string &pgm) { int errors = 0; + Util::InputSources::reset(); clearErrorReporter(); + structure.clear(); + allErrors = nullptr; declarations = new IR::IndexedVector(); YY_BUFFER_STATE state = yy_scan_string(pgm.c_str()); errors |= yyparse(); yy_delete_buffer(state); - structure.clear(); - allErrors = nullptr; if (errors) { return nullptr; } diff --git a/lib/source_file.cpp b/lib/source_file.cpp index eb1927de42e..cf398e8ae41 100644 --- a/lib/source_file.cpp +++ b/lib/source_file.cpp @@ -62,6 +62,11 @@ InputSources::InputSources() : this->contents.push_back(""); } +/* static */ +void InputSources::reset() { + instance = new InputSources; +} + // prevent further changes void InputSources::seal() { if (this->sealed) diff --git a/lib/source_file.h b/lib/source_file.h index 452e44a803d..95cc866c8e4 100644 --- a/lib/source_file.h +++ b/lib/source_file.h @@ -271,6 +271,9 @@ class InputSources final { cstring toDebugString() const; + // Reset all source information, so that a new file can be parsed. + static void reset(); + static InputSources* instance; private: diff --git a/test/gtest/Makefile.am b/test/gtest/Makefile.am index 5d7f80b7c4e..4d1d45ae31b 100644 --- a/test/gtest/Makefile.am +++ b/test/gtest/Makefile.am @@ -16,8 +16,8 @@ # elsewhere in the codebase. gtest_unittest_UNIFIED = \ test/gtest/opeq_test.cpp \ - test/gtest/typecheck_test.cpp \ - test/gtest/midend_test.cpp + test/gtest/midend_test.cpp \ + test/gtest/arch_test.cpp cpplint_FILES += $(gtest_unittest_UNIFIED) gtest_SOURCES += $(gtest_unittest_SOURCES) diff --git a/test/gtest/arch_test.cpp b/test/gtest/arch_test.cpp new file mode 100644 index 00000000000..d21657386fb --- /dev/null +++ b/test/gtest/arch_test.cpp @@ -0,0 +1,318 @@ +/* +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 "gtest/gtest.h" +#include "ir/ir.h" +#include "helper.h" +#include "lib/log.h" + +#include "frontends/p4/typeMap.h" +#include "frontends/common/resolveReferences/referenceMap.h" +#include "frontends/common/resolveReferences/resolveReferences.h" + +#include "p4/createBuiltins.h" +#include "p4/typeChecking/typeChecker.h" + +using namespace P4; + +TEST(arch, packet_out) { + std::string program = R"( + // simplified core.p4 + extern packet_out { + void emit (in T hdr); + } + // simplified v1model.p4 + control Deparser (packet_out b, in H hdr); + package PSA (Deparser p); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + control MyDeparser(packet_out b, in ParsedHeaders h){ + apply { + b.emit(h.hdr); + } + } + PSA(MyDeparser()) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +// Potential bug +TEST(arch, duplicatedDeclarationBug) { + std::string program = R"( + // simplified core.p4 + extern packet_out { + void emit (in T hdr); + } + // simplified v1model.p4 + control Deparser (packet_out b, in H hdr); + package PSA (Deparser p); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + // BUG: duplicated name with 'Deparser' + control Deparser(packet_out b, in ParsedHeaders h){ + apply { + b.emit(h.hdr); + } + } + PSA(Deparser()) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + + pgm = pgm->apply(passes); + ASSERT_EQ(nullptr, pgm); +} + +TEST(arch, instantiation) { + std::string program = R"( + // simplified core.p4 + extern packet_in { + void extract (out T hdr); + } + extern packet_out { + void emit (in T hdr); + } + // simplified v1model.p4 + parser Parser (packet_in b, out M meta); + control Ingress (inout H hdr, inout M meta); + control Deparser (packet_out b, in H hdr); + package PSA (Parser p, Ingress ig, Deparser dp); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> hdr; + } + parser MyParser(packet_in b, out Metadata m) { + state start {} + } + control MyIngress(inout ParsedHeaders h, inout Metadata m) { + apply { + } + } + control MyDeparser(packet_out b, in ParsedHeaders h){ + apply { + b.emit(h.hdr); + } + } + MyParser() p; + MyIngress() ig; + MyDeparser() dp; + PSA(p, ig, dp) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +TEST(arch, package_with_body) { + std::string program = R"( + // simplified core.p4 + // simplified v1model.p4 + control Ingress (inout H hdr, inout M meta); + package PSA (Ingress ig) {} + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> hdr; + } + control MyIngress(inout ParsedHeaders h, inout Metadata m) (bit<32> x) { + apply { + } + } + MyIngress(2) ig; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_EQ(nullptr, pgm); +} + +TEST(arch, control_in_control) { + std::string program = R"( + // simplified core.p4 + // simplified v1model.p4 + control Ingress (inout H hdr, inout M meta); + control Egress (inout M meta); + package PSA (Ingress ig); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> hdr; + } + control MyIngress(inout ParsedHeaders h, inout Metadata m) { + apply { + } + } + control MyEgress(inout Metadata m) (MyIngress ig) { + apply {} + } + MyIngress() ig; + MyEgress(ig) eg; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +TEST(arch, psa_clone_as_param_to_package) { + std::string program = R"( + extern clone { + clone(); + void execute(in bit<32> sessions); + } + package PSA (clone c); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + clone() c; + PSA(c) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +TEST(arch, psa_clone_as_param_to_control) { + std::string program = R"( + extern clone { + // constructor + clone(); + // methods + void clone(in bit<32> sessions); + void clone(in bit<32> sessions, in T data); + } + control ingress (inout H hdr, inout M meta); + package PSA (ingress ig); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> md; + } + control MyIngress (inout ParsedHeaders p, inout Metadata m) (clone> c) { + apply{} + } + MyIngress(clone>()) ig; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); + + dump(pgm); +} + +TEST(arch, psa_clone_as_param_to_extern) { + std::string program = R"( + extern clone { + // constructor + clone(); + // methods + void clone(in bit<32> sessions); + void clone(in bit<32> sessions, in T data); + } + extern PRE { + PRE(); + void clone(in bit<32> sessions); /* same as invoking c.clone(sessions); */ + void clone(in bit<32> sessions, in T data); /* same as invoking c.clone(sessions, data) */ + // ... + // drop, multicast api here + } + control ingress (inout H hdr, inout M meta); + package PSA (ingress ig); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> md; + } + control MyIngress (inout ParsedHeaders p, inout Metadata m) (PRE> pre) { + apply{} + } + PRE>() pre; + MyIngress(pre) ig; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + diff --git a/test/gtest/typecheck_test.cpp b/test/gtest/typecheck_test.cpp deleted file mode 100644 index 8331fa814c1..00000000000 --- a/test/gtest/typecheck_test.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* -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 "gtest/gtest.h" - -#include "ir/ir.h" -#include "p4/p4-parse.h" -#include "frontends/p4/typeMap.h" -#include "frontends/common/constantFolding.h" -#include "frontends/p4/typeChecking/bindVariables.h" -#include "frontends/common/resolveReferences/resolveReferences.h" - -#include "p4/validateParsedProgram.h" -#include "p4/createBuiltins.h" -#include "p4/unusedDeclarations.h" -#include "p4/typeChecking/typeChecker.h" - -#include "helper.h" - -using namespace P4; - -TEST(UNITTEST, helloworld) { - std::string program = with_core_p4( - "parser Parser (packet_in p){ state start{} };\n" - "control empty() { apply {} };\n" - "package top(empty e);\n" - "top(empty()) main;\n"); - - const IR::P4Program* pgm = parse_string(program); - ASSERT_NE(nullptr, pgm); - - ReferenceMap refMap; - TypeMap typeMap; - - PassManager passes = { - new ValidateParsedProgram(false), - new CreateBuiltins(), - new ResolveReferences(&refMap, true), - new ConstantFolding(&refMap, nullptr), - new ResolveReferences(&refMap), -// new TypeInference(&refMap, &typeMap), - }; - pgm = pgm->apply(passes); - - ASSERT_NE(nullptr, pgm); -} - -TEST(UNITTEST, package) { - std::string program = with_core_p4( - "parser Parser (packet_in p){ state start{} };\n" - "control empty() { apply {} };\n" - "package top(empty e);\n"); - - const IR::P4Program* pgm = parse_string(program); - - ASSERT_NE(nullptr, pgm); - - PassManager passes = { - new CreateBuiltins(), - }; - pgm = pgm->apply(passes); -} From 7cb42b520db215a03a77f43beeab4b1b1a77624e Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 8 Apr 2017 16:01:00 -0700 Subject: [PATCH 2/3] reset parser symbol table after failure --- frontends/p4/symbol_table.cpp | 2 ++ test/gtest/Makefile.am | 1 + test/gtest/arch_test.cpp | 40 ++++++++++++++++++++++++---- test/gtest/{helper.h => helpers.cpp} | 4 --- test/gtest/helpers.h | 25 +++++++++++++++++ test/gtest/midend_test.cpp | 2 +- 6 files changed, 64 insertions(+), 10 deletions(-) rename test/gtest/{helper.h => helpers.cpp} (93%) create mode 100644 test/gtest/helpers.h diff --git a/frontends/p4/symbol_table.cpp b/frontends/p4/symbol_table.cpp index 6e432a8aed0..70acd835a0d 100644 --- a/frontends/p4/symbol_table.cpp +++ b/frontends/p4/symbol_table.cpp @@ -244,5 +244,7 @@ cstring ProgramStructure::toString() const { void ProgramStructure::clear() { rootNamespace->clear(); + currentNamespace = rootNamespace; + debugStream = stderr; } } // namespace Util diff --git a/test/gtest/Makefile.am b/test/gtest/Makefile.am index 4d1d45ae31b..fc097f5c7ff 100644 --- a/test/gtest/Makefile.am +++ b/test/gtest/Makefile.am @@ -15,6 +15,7 @@ # General GTest unit tests. Add tests here if they don't have a logical home # elsewhere in the codebase. gtest_unittest_UNIFIED = \ + test/gtest/helpers.cpp \ test/gtest/opeq_test.cpp \ test/gtest/midend_test.cpp \ test/gtest/arch_test.cpp diff --git a/test/gtest/arch_test.cpp b/test/gtest/arch_test.cpp index d21657386fb..1da02f35add 100644 --- a/test/gtest/arch_test.cpp +++ b/test/gtest/arch_test.cpp @@ -16,7 +16,7 @@ limitations under the License. #include "gtest/gtest.h" #include "ir/ir.h" -#include "helper.h" +#include "helpers.h" #include "lib/log.h" #include "frontends/p4/typeMap.h" @@ -144,7 +144,7 @@ TEST(arch, instantiation) { ASSERT_NE(nullptr, pgm); } -TEST(arch, package_with_body) { +TEST(arch, psa_package_with_body) { std::string program = R"( // simplified core.p4 // simplified v1model.p4 @@ -175,7 +175,7 @@ TEST(arch, package_with_body) { ASSERT_EQ(nullptr, pgm); } -TEST(arch, control_in_control) { +TEST(arch, psa_control_in_control) { std::string program = R"( // simplified core.p4 // simplified v1model.p4 @@ -269,8 +269,6 @@ TEST(arch, psa_clone_as_param_to_control) { }); pgm = pgm->apply(passes); ASSERT_NE(nullptr, pgm); - - dump(pgm); } TEST(arch, psa_clone_as_param_to_extern) { @@ -316,3 +314,35 @@ TEST(arch, psa_clone_as_param_to_extern) { ASSERT_NE(nullptr, pgm); } +TEST(arch, clone_as_extern_method) { + std::string program = R"( + extern void clone(in bit<32> sessions); + extern void clone3(in bit<32> sessions, in T data); + control ingress (inout H hdr, inout M meta); + package PSA (ingress ig); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> md; + } + control MyIngress (inout ParsedHeaders p, inout Metadata m) { + apply{ + // invoke clone3 triggers a compiler bug. + } + } + MyIngress() ig; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes ({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + diff --git a/test/gtest/helper.h b/test/gtest/helpers.cpp similarity index 93% rename from test/gtest/helper.h rename to test/gtest/helpers.cpp index 28e1e9354c9..a641970330a 100644 --- a/test/gtest/helper.h +++ b/test/gtest/helpers.cpp @@ -14,9 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -#ifndef _TEST_GTEST_HELPER_ -#define _TEST_GTEST_HELPER_ - #include /* preprocessing by prepending the content of core.p4 to test program */ @@ -27,4 +24,3 @@ std::string with_core_p4 (const std::string& pgm) { return sstr.str() + pgm; } -#endif diff --git a/test/gtest/helpers.h b/test/gtest/helpers.h new file mode 100644 index 00000000000..2699edd9853 --- /dev/null +++ b/test/gtest/helpers.h @@ -0,0 +1,25 @@ +/* +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 _TEST_GTEST_HELPER_ +#define _TEST_GTEST_HELPER_ + +#include + +/* preprocessing by prepending the content of core.p4 to test program */ +std::string with_core_p4 (const std::string& pgm); + +#endif diff --git a/test/gtest/midend_test.cpp b/test/gtest/midend_test.cpp index ce7563e6704..1afaeda5a15 100644 --- a/test/gtest/midend_test.cpp +++ b/test/gtest/midend_test.cpp @@ -16,7 +16,7 @@ limitations under the License. #include "gtest/gtest.h" #include "ir/ir.h" -#include "helper.h" +#include "helpers.h" #include "lib/log.h" #include "frontends/p4/typeMap.h" From 768990b7633b9915a8290e9e6ef9294b8808a6cb Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 8 Apr 2017 22:00:47 -0700 Subject: [PATCH 3/3] cpplint --- test/gtest/arch_test.cpp | 18 +++++++++--------- test/gtest/helpers.cpp | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/gtest/arch_test.cpp b/test/gtest/arch_test.cpp index 1da02f35add..0d5a7fd4b45 100644 --- a/test/gtest/arch_test.cpp +++ b/test/gtest/arch_test.cpp @@ -52,7 +52,7 @@ TEST(arch, packet_out) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); @@ -86,7 +86,7 @@ TEST(arch, duplicatedDeclarationBug) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); @@ -136,7 +136,7 @@ TEST(arch, instantiation) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); @@ -168,7 +168,7 @@ TEST(arch, psa_package_with_body) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); pgm = pgm->apply(passes); @@ -204,7 +204,7 @@ TEST(arch, psa_control_in_control) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); pgm = pgm->apply(passes); @@ -229,7 +229,7 @@ TEST(arch, psa_clone_as_param_to_package) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); pgm = pgm->apply(passes); @@ -264,7 +264,7 @@ TEST(arch, psa_clone_as_param_to_control) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); pgm = pgm->apply(passes); @@ -307,7 +307,7 @@ TEST(arch, psa_clone_as_param_to_extern) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); pgm = pgm->apply(passes); @@ -339,7 +339,7 @@ TEST(arch, clone_as_extern_method) { ReferenceMap refMap; TypeMap typeMap; - PassManager passes ({ + PassManager passes({ new TypeChecking(&refMap, &typeMap) }); pgm = pgm->apply(passes); diff --git a/test/gtest/helpers.cpp b/test/gtest/helpers.cpp index a641970330a..658715066e1 100644 --- a/test/gtest/helpers.cpp +++ b/test/gtest/helpers.cpp @@ -17,10 +17,10 @@ limitations under the License. #include /* preprocessing by prepending the content of core.p4 to test program */ -std::string with_core_p4 (const std::string& pgm) { +std::string with_core_p4(const std::string& pgm) { std::ifstream input("p4include/core.p4"); std::stringstream sstr; - while(input >> sstr.rdbuf()); + while (input >> sstr.rdbuf()) {} return sstr.str() + pgm; }