diff --git a/backends/p4tools/README.md b/backends/p4tools/README.md index 1dc194160ce..dc5a5a3f155 100644 --- a/backends/p4tools/README.md +++ b/backends/p4tools/README.md @@ -6,13 +6,13 @@ p4tools ├─ cmake ── common P4Tools CMake modules. ├─ common ── common code for the various P4Tools modules. - │ └─ compiler ── transformation passes for P4 code. - │ └─ control_plane ── code concerning P4Tool's control plane semantics. - │ └─ core ── definitions for core parts of the P4Tools modules. + │ ├─ compiler ── transformation passes for P4 code. + │ ├─ control_plane ── code concerning P4Tool's control plane semantics. + │ ├─ core ── definitions for core parts of the P4Tools modules. │ └─ lib ── helper functions and utilities for P4Tools modules. └─ modules ── P4Tools extensions. + ├─ smith ── P4Smith: a random P4 program generator. └─ testgen ── P4Testgen: a test-case generator for P4 programs. - ``` ## P4Tools @@ -20,6 +20,8 @@ P4Tools is a collection of tools that make testing P4 targets and programs a lit - [P4Testgen](https://github.com/p4lang/p4c/tree/main/backends/p4tools/modules/testgen): An input-output test case generator for P4. +- [P4Smith](https://github.com/p4lang/p4c/tree/main/backends/p4tools/modules/smith): A random P4 program generator in the spirit of Csmith. + ## Building Please see the general installation instructions [here](https://github.com/p4lang/p4c#installing-p4c-from-source). P4Tools can be built using the following CMAKE configuration in the P4C repository. @@ -31,7 +33,6 @@ make ``` ## Dependencies -* [inja](https://github.com/pantor/inja) template engine for testcase generation. * [z3](https://github.com/Z3Prover/z3) SMT solver to compute path constraints. * Important: We currently only support Z3 versions 4.8.14 to 4.12.0. diff --git a/backends/p4tools/common/lib/util.cpp b/backends/p4tools/common/lib/util.cpp index c6b6a5df08a..3155e917eeb 100644 --- a/backends/p4tools/common/lib/util.cpp +++ b/backends/p4tools/common/lib/util.cpp @@ -4,8 +4,8 @@ #include #include #include +#include #include -#include #include #include @@ -64,7 +64,30 @@ uint64_t Utils::getRandInt(uint64_t max) { return dist(rng); } -big_int Utils::getRandBigInt(big_int max) { +int64_t Utils::getRandInt(int64_t min, int64_t max) { + boost::random::uniform_int_distribution distribution(min, max); + return distribution(rng); +} + +int64_t Utils::getRandInt(const std::vector &percent) { + int sum = std::accumulate(percent.begin(), percent.end(), 0); + + // Do not pick zero since that conflicts with zero percentage values. + auto randNum = getRandInt(1, sum); + int ret = 0; + + int64_t retSum = 0; + for (auto i : percent) { + retSum += i; + if (retSum >= randNum) { + break; + } + ret = ret + 1; + } + return ret; +} + +big_int Utils::getRandBigInt(const big_int &max) { if (!currentSeed) { return 0; } @@ -72,6 +95,14 @@ big_int Utils::getRandBigInt(big_int max) { return dist(rng); } +big_int Utils::getRandBigInt(const big_int &min, const big_int &max) { + if (!currentSeed) { + return 0; + } + boost::random::uniform_int_distribution dist(min, max); + return dist(rng); +} + const IR::Constant *Utils::getRandConstantForWidth(int bitWidth) { auto maxVal = IR::getMaxBvVal(bitWidth); auto randInt = Utils::getRandBigInt(maxVal); @@ -145,8 +176,10 @@ std::vector argumentsToTypeDeclarations( const IR::IDeclaration *findProgramDecl(const IR::IGeneralNamespace *ns, const IR::Path *path) { auto name = path->name.name; - auto *decl = ns->getDeclsByName(name)->singleOrDefault(); - if (decl != nullptr) return decl; + const auto *decl = ns->getDeclsByName(name)->singleOrDefault(); + if (decl != nullptr) { + return decl; + } BUG("Variable %1% not found in the available namespaces.", path); } diff --git a/backends/p4tools/common/lib/util.h b/backends/p4tools/common/lib/util.h index b007fb67cfa..f7a8f032aa7 100644 --- a/backends/p4tools/common/lib/util.h +++ b/backends/p4tools/common/lib/util.h @@ -70,9 +70,18 @@ class Utils { /// @returns a random integer in the range [0, @param max]. Always return 0 if no seed is set. static uint64_t getRandInt(uint64_t max); + /// @returns a random integer between min and max. + static int64_t getRandInt(int64_t min, int64_t max); + + /// @returns a random integer based on the percent vector. + static int64_t getRandInt(const std::vector &percent); + /// @returns a random big integer in the range [0, @param max]. Always return 0 if no seed is /// set. - static big_int getRandBigInt(big_int max); + static big_int getRandBigInt(const big_int &max); + + /// This is a big_int version of getRndInt. + static big_int getRandBigInt(const big_int &min, const big_int &max); /// @returns a IR::Constant with a random big integer that fits the specified bit width. /// The type will be an unsigned Type_Bits with @param bitWidth. diff --git a/backends/p4tools/common/p4ctool.h b/backends/p4tools/common/p4ctool.h index 3d67d7bb0bb..e40bb718b01 100644 --- a/backends/p4tools/common/p4ctool.h +++ b/backends/p4tools/common/p4ctool.h @@ -38,12 +38,11 @@ class AbstractP4cTool { auto &toolOptions = Options::get(); auto compileContext = toolOptions.process(args); if (!compileContext) { - return 1; + return EXIT_FAILURE; } // Set up the compilation context. AutoCompileContext autoContext(*compileContext); - // If not explicitly disabled, print basic information to standard output. if (!toolOptions.disableInformationLogging) { enableInformationLogging(); diff --git a/backends/p4tools/modules/smith/CMakeLists.txt b/backends/p4tools/modules/smith/CMakeLists.txt new file mode 100644 index 00000000000..83411dbe967 --- /dev/null +++ b/backends/p4tools/modules/smith/CMakeLists.txt @@ -0,0 +1,84 @@ +# CMake file for P4Smith. + +include(common) + +project(smith) + +# Declare common P4Testgen variables. +set(P4SMITH_DIR ${P4C_BINARY_DIR}/smith) +set(P4SMITH_DRIVER "${CMAKE_CURRENT_BINARY_DIR}/p4smith") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/version.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/version.h" @ONLY +) +# Source files for smith. +set(SMITH_SOURCES + core/target.cpp + common/declarations.cpp + common/expressions.cpp + common/parser.cpp + common/scope.cpp + common/statements.cpp + common/table.cpp + util/util.cpp + util/wordlist.cpp + options.cpp + smith.cpp +) + +set(SMITH_LIBS + PRIVATE p4tools-common +) + +file(GLOB tools_targets RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/targets + ${CMAKE_CURRENT_SOURCE_DIR}/targets/* +) + +foreach(ext ${tools_targets}) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/targets/${ext}/CMakeLists.txt) + # Generate an option that makes it possible to disable this extension. + string(MAKE_C_IDENTIFIER ${ext} EXT_AS_IDENTIFIER) + string(TOUPPER ${EXT_AS_IDENTIFIER} EXT_AS_OPTION_NAME) + string(CONCAT ENABLE_EXT_OPTION "ENABLE_TOOLS_TARGET_" ${EXT_AS_OPTION_NAME}) + string(CONCAT EXT_HELP_TEXT "Build the " ${ext} " target") + option(${ENABLE_EXT_OPTION} ${EXT_HELP_TEXT} ON) + if(${ENABLE_EXT_OPTION}) + message("-- Enabling target ${ext}") + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/targets/${ext}) + set(include_statements_var + "${include_statements_var}#include \"backends/p4tools/modules/smith/targets/${ext}/register.h\"\n" + ) + set(smith_targets_var "${smith_targets_var} ${ext}RegisterSmithTarget();\n") + endif() + endif() +endforeach(ext) + +# Fill the template +configure_file(register.h.in register.h) + +add_library(smith ${SMITH_SOURCES}) +target_link_libraries(smith + ${SMITH_LIBS} + # For Abseil includes. + PRIVATE frontend +) + +add_dependencies(smith p4tools-common) + +add_p4tools_executable(p4smith main.cpp) + +target_link_libraries( + p4smith + PRIVATE smith + ${SMITH_LIBS} + PRIVATE ${P4C_LIBRARIES} + PRIVATE ${P4C_LIB_DEPS} +) + +add_custom_target( + linkp4smith # Add some convenience links for invoking p4smith. + COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/p4smith + ${CMAKE_BINARY_DIR}/p4smith +) + +add_dependencies(p4smith linkp4smith) diff --git a/backends/p4tools/modules/smith/README.md b/backends/p4tools/modules/smith/README.md new file mode 100644 index 00000000000..8dfc4b047ae --- /dev/null +++ b/backends/p4tools/modules/smith/README.md @@ -0,0 +1,79 @@ +[![Status](https://github.com/p4lang/p4c/actions/workflows/ci-p4tools.yml/badge.svg)](https://github.com/p4lang/p4c/actions/workflows/ci-p4tools.yml) + +# P4Smith + +## Table of Contents + +- [Installation](#installation) +- [Extensions](#extensions) +- [Usage](#usage) +- [Limitations](#limitations) +- [Further Reading](#further-reading) +- [Contributing](#contributing) +- [License](#license) + +P4Smith is an extensible random P4 program generator in the spirit of [CSmith](https://en.wikipedia.org/wiki/Csmith). P4Smith generates random, but valid P4 programs for various P4 targets, for example the [v1model.p4](https://github.com/p4lang/behavioral-model/blob/main/docs/simple_switch.md) architecture on [BMv2](https://github.com/p4lang/behavioral-model) or `tna.p4` running on Tofino 1. P4Smiths generates programs that are valid according to the latest version of the (P4 specification)[https://p4.org/p4-spec/docs/P4-16-working-spec.html] or the restrictions of the specific target. + +## Installation + +P4Smith depends on the P4Tools framework and is automatically installed with P4Tools. Please follow the instructions listed [here](https://github.com/p4lang/p4c/tree/main/backends/p4tools#building) to install P4Smith. The main binary `p4smith` can be found in the `build` folder after a successful installation. + +P4Smith is available as part of the [official P4C docker image](https://hub.docker.com/r/p4lang/p4c/). On Debian-based systems, it is also possible to install a P4Smith binary by following [these](https://github.com/p4lang/p4c#installing-packaged-versions-of-p4c) instructions. + +## Extensions +P4Smith extensions are instantiations of a particular combination of P4 architecture and the target that executes the P4 code. For example, the `v1model.p4` architecture can be executed on the behavioral model. P4Smith extension make use of the core P4Smith framework to generate programs. Several open-source extensions are available. + +### core.p4 using the test compiler p4test +[targets/generic](targets/generic) + +This random program generator generates random packages and tries to produce all valid P4 code according to the latest P4 specification. Programs should be compiled using [p4test](https://github.com/p4lang/p4c/tree/main/backends/p4est). + +### v1model.p4 and psa.p4 on BMv2 +[targets/bmv2](targets/bmv2) + +P4Smith supports generating P4 programs for the `v1model` and `psa` architecture on [BMv2](https://github.com/p4lang/behavioral-model). + +### pna.p4 on the DPDK SoftNIC +[targets/pna](targets/pna) + +The [DPDK-SoftNIC](https://github.com/p4lang/p4-dpdk-target) is a new target implemented using the [Data Plane Development Kit (DPDK)](https://www.dpdk.org/). The SoftNIC can be programmed using the P4 `pna.p4` architecture. + +### tna.p4 on Tofino 1 +[targets/ebpf](targets/ebpf) + +P4Smith can also generate programs for the `tna` architecture on Tofino 1. The programs are intended to be compiled on the proprietary Barefoot Tofino compiler. + +## Usage +To access the possible options for `p4smith` use `p4smith --help`. To generate a test for a particular target and P4 architecture, run the following command: + +```bash +./p4smith --target [TARGET] --arch [ARCH] prog.p4 +``` +Where `ARCH` specifies the P4 architecture (e.g., v1model.p4) and `TARGET` represents the targeted network device (e.g., BMv2). `prog.p4` is the name of the generated program. + +## Further Reading +P4Smith was originally titled Bludgeon and part of the Gauntlet compiler testing framework. Section 4 of the [paper](https://arxiv.org/abs/2006.01074) provides a high-level overview of the tool. + + +If you would like to cite this tool please use this citation format: +```latex +@inproceedings{ruffy-osdi2020, +author = {Ruffy, Fabian and Wang, Tao and Sivaraman, Anirudh}, +title = {Gauntlet: Finding Bugs in Compilers for Programmable Packet Processing}, +booktitle = {14th {USENIX} Symposium on Operating Systems Design and Implementation ({OSDI} 20)}, +year = {2020}, +publisher = {{USENIX} Association}, +month = nov, + abstract = { + Programmable packet-processing devices such as programmable switches and network interface cards are becoming mainstream. These devices are configured in a domain-specific language such as P4, using a compiler to translate packet-processing programs into instructions for different targets. As networks with programmable devices become widespread, it is critical that these compilers be dependable. This paper considers the problem of finding bugs in compilers for packet processing in the context of P4-16. We introduce domain-specific techniques to induce both abnormal termination of the compiler (crash bugs) and miscompilation (semantic bugs). We apply these techniques to (1) the opensource P4 compiler (P4C) infrastructure, which serves as a common base for different P4 back ends; (2) the P4 back end for the P4 reference software switch; and (3) the P4 back end for the Barefoot Tofino switch. Across the 3 platforms, over 8 months of bug finding, our tool Gauntlet detected 96 new and distinct bugs (62 crash and 34 semantic), which we confirmed with the respective compiler developers. 54 have been fixed (31 crash and 23 semantic); the remaining have been assigned to a developer. Our bug-finding efforts also led to 6 P4 specification changes. We have open sourced Gauntlet at p4gauntlet.github.io and it now runs within P4C’s continuous integration pipeline.} +} + +``` + +## Contributing + +Contributions to P4Smith in any form are welcome! Please follow the guidelines listed [here](https://github.com/p4lang/p4c/blob/main/CONTRIBUTING.md) to contribute. + +## License + +This project is licensed under the Apache License 2.0. See the [LICENSE](https://github.com/p4lang/p4c/blob/main/backends/p4tools/LICENSE) file for details. diff --git a/backends/p4tools/modules/smith/common/declarations.cpp b/backends/p4tools/modules/smith/common/declarations.cpp new file mode 100644 index 00000000000..2b9a1be9563 --- /dev/null +++ b/backends/p4tools/modules/smith/common/declarations.cpp @@ -0,0 +1,749 @@ +#include "backends/p4tools/modules/smith/common/declarations.h" + +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/common/table.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/vector.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" +#include "lib/source_file.h" + +namespace P4Tools::P4Smith { + +IR::StatOrDecl *DeclarationGenerator::generateRandomStatementOrDeclaration(bool is_in_func) { + std::vector percent = {PCT.STATEMENTORDECLARATION_VAR, + PCT.STATEMENTORDECLARATION_CONSTANT, + PCT.STATEMENTORDECLARATION_STATEMENT}; + auto val = Utils::getRandInt(percent); + if (val == 0) { + auto *stmt = target().declarationGenerator().genVariableDeclaration(); + if (stmt == nullptr) { + BUG("Declaration in statementOrDeclaration should not be nullptr!"); + } + return stmt; + } + if (val == 1) { + auto *stmt = target().declarationGenerator().genConstantDeclaration(); + if (stmt == nullptr) { + BUG("Declaration in statementOrDeclaration should not be nullptr!"); + } + return stmt; + } + auto genStmt = target().statementGenerator(); + auto *stmt = genStmt.genAssignmentOrMethodCallStatement(is_in_func); + if (stmt == nullptr) { + // it can happen that no statement can be generated + // for example in functions without writable values + // so declare a variable instead + return target().declarationGenerator().genVariableDeclaration(); + } + return stmt; +} + +IR::Annotations *DeclarationGenerator::genAnnotation() { + Util::SourceInfo si; + IR::Vector annotations; + IR::Vector exprs; + cstring name = IR::Annotation::nameAnnotation; + auto *strLiteral = new IR::StringLiteral(IR::Type_String::get(), getRandomString(6)); + + exprs.push_back(strLiteral); + + auto *annotation = new IR::Annotation(si, name, exprs, false); + annotations.push_back(annotation); + + return new IR::Annotations(annotations); +} + +IR::Declaration_Constant *DeclarationGenerator::genConstantDeclaration() { + cstring name = getRandomString(6); + TyperefProbs typePercent = { + PCT.CONSTANTDECLARATION_BASETYPE_BIT, PCT.CONSTANTDECLARATION_BASETYPE_SIGNED_BIT, + PCT.CONSTANTDECLARATION_BASETYPE_VARBIT, PCT.CONSTANTDECLARATION_BASETYPE_INT, + PCT.CONSTANTDECLARATION_BASETYPE_ERROR, PCT.CONSTANTDECLARATION_BASETYPE_BOOL, + PCT.CONSTANTDECLARATION_BASETYPE_STRING, PCT.CONSTANTDECLARATION_DERIVED_ENUM, + PCT.CONSTANTDECLARATION_DERIVED_HEADER, PCT.CONSTANTDECLARATION_DERIVED_HEADER_STACK, + PCT.CONSTANTDECLARATION_DERIVED_STRUCT, PCT.CONSTANTDECLARATION_DERIVED_HEADER_UNION, + PCT.CONSTANTDECLARATION_DERIVED_TUPLE, PCT.CONSTANTDECLARATION_TYPE_VOID, + PCT.CONSTANTDECLARATION_TYPE_MATCH_KIND, + }; + + const auto *tp = target().expressionGenerator().pickRndType(typePercent); + + IR::Declaration_Constant *ret = nullptr; + // constant declarations need to be compile-time known + P4Scope::req.compile_time_known = true; + + if (tp->is() || tp->is() || tp->is() || + tp->is()) { + auto *expr = target().expressionGenerator().genExpression(tp); + ret = new IR::Declaration_Constant(name, tp, expr); + } else { + BUG("Type %s not supported!", tp->node_type_name()); + } + P4Scope::req.compile_time_known = false; + + P4Scope::addToScope(ret); + + return ret; +} + +IR::P4Action *DeclarationGenerator::genActionDeclaration() { + cstring name = getRandomString(5); + IR::ParameterList *params = nullptr; + IR::BlockStatement *blk = nullptr; + P4Scope::startLocalScope(); + P4Scope::prop.in_action = true; + params = genParameterList(); + + blk = target().statementGenerator().genBlockStatement(false); + + auto *ret = new IR::P4Action(name, params, blk); + + P4Scope::prop.in_action = false; + P4Scope::endLocalScope(); + + P4Scope::addToScope(ret); + + return ret; +} + +IR::IndexedVector DeclarationGenerator::genLocalControlDecls() { + IR::IndexedVector localDecls; + + auto vars = Utils::getRandInt(DECL.MIN_VAR, DECL.MAX_VAR); + auto decls = Utils::getRandInt(DECL.MIN_INSTANCE, DECL.MAX_INSTANCE); + auto actions = Utils::getRandInt(DECL.MIN_ACTION, DECL.MAX_ACTION); + auto tables = Utils::getRandInt(DECL.MIN_TABLE, DECL.MAX_TABLE); + + // variableDeclarations + for (int i = 0; i <= vars; i++) { + auto *varDecl = genVariableDeclaration(); + localDecls.push_back(varDecl); + } + + // declaration_instance + for (int i = 0; i <= decls; i++) { + auto *declIns = genControlDeclarationInstance(); + + if (declIns == nullptr) { + continue; + } + localDecls.push_back(declIns); + } + + // actionDeclarations + for (int i = 0; i <= actions; i++) { + auto *actDecl = genActionDeclaration(); + localDecls.push_back(actDecl); + } + + for (int i = 0; i <= tables; i++) { + auto *tabDecl = target().tableGenerator().genTableDeclaration(); + localDecls.push_back(tabDecl); + } + return localDecls; + // instantiations +} + +IR::P4Control *DeclarationGenerator::genControlDeclaration() { + // start of new scope + P4Scope::startLocalScope(); + cstring name = getRandomString(7); + IR::ParameterList *params = genParameterList(); + auto *typeCtrl = new IR::Type_Control(name, params); + + IR::IndexedVector localDecls = genLocalControlDecls(); + // apply body + auto *applyBlock = target().statementGenerator().genBlockStatement(false); + + // end of scope + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4ctrl = new IR::P4Control(name, typeCtrl, localDecls, applyBlock); + P4Scope::addToScope(p4ctrl); + + return p4ctrl; +} + +IR::Declaration_Instance *DeclarationGenerator::genControlDeclarationInstance() { + auto p4Ctrls = P4Scope::getDecls(); + size_t size = p4Ctrls.size(); + + if (size == 0) { + // FIXME: Figure out a better way to handle this nullptr + return nullptr; + } + auto *args = new IR::Vector(); + const IR::P4Control *p4ctrl = p4Ctrls.at(Utils::getRandInt(0, size - 1)); + IR::Type *tp = new IR::Type_Name(p4ctrl->name); + auto *decl = new IR::Declaration_Instance(cstring(getRandomString(6)), tp, args); + P4Scope::addToScope(decl); + return decl; +} + +IR::Type *DeclarationGenerator::genDerivedTypeDeclaration() { return genHeaderTypeDeclaration(); } + +IR::IndexedVector DeclarationGenerator::genIdentifierList(size_t len) { + IR::IndexedVector declIds; + std::set declIdsName; + + for (size_t i = 0; i < len; i++) { + cstring name = getRandomString(2); + auto *declId = new IR::Declaration_ID(name); + + if (declIdsName.find(name) != declIdsName.end()) { + delete name; + delete declId; + continue; + } + + declIds.push_back(declId); + } + + return declIds; +} + +IR::IndexedVector DeclarationGenerator::genSpecifiedIdentifier(size_t len) { + IR::IndexedVector members; + std::set membersName; + + for (size_t i = 0; i < len; i++) { + cstring name = getRandomString(2); + IR::Expression *ex = P4Tools::P4Smith::ExpressionGenerator::genIntLiteral(); + + if (membersName.find(name) != membersName.end()) { + delete ex; + continue; + } + + auto *member = new IR::SerEnumMember(name, ex); + + members.push_back(member); + } + + return members; +} +IR::IndexedVector DeclarationGenerator::genSpecifiedIdentifierList(size_t len) { + IR::IndexedVector members; + std::set membersName; + + for (size_t i = 0; i < len; i++) { + cstring name = getRandomString(2); + IR::Expression *ex = target().expressionGenerator().genIntLiteral(); + + if (membersName.find(name) != membersName.end()) { + delete ex; + continue; + } + + auto *member = new IR::SerEnumMember(name, ex); + + members.push_back(member); + } + + return members; +} + +IR::Type_Enum *DeclarationGenerator::genEnumDeclaration(cstring name) { + auto declIds = genIdentifierList(3); + auto *ret = new IR::Type_Enum(name, declIds); + + P4Scope::addToScope(ret); + return ret; +} + +IR::Type_SerEnum *DeclarationGenerator::genSerEnumDeclaration(cstring name) { + auto members = genSpecifiedIdentifierList(3); + const auto *tp = ExpressionGenerator::genBitType(false); + + auto *ret = new IR::Type_SerEnum(name, tp, members); + + P4Scope::addToScope(ret); + return ret; +} + +IR::Type *DeclarationGenerator::genEnumTypeDeclaration(int type) { + cstring name = getRandomString(4); + if (type == 0) { + return genEnumDeclaration(name); + } + return genSerEnumDeclaration(name); +} + +IR::Method *DeclarationGenerator::genExternDeclaration() { + cstring name = getRandomString(7); + IR::Type_Method *tm = nullptr; + P4Scope::startLocalScope(); + IR::ParameterList *params = genParameterList(); + + // externs have the same type restrictions as functions + TyperefProbs typePercent = { + PCT.FUNCTIONDECLARATION_BASETYPE_BIT, PCT.FUNCTIONDECLARATION_BASETYPE_SIGNED_BIT, + PCT.FUNCTIONDECLARATION_BASETYPE_VARBIT, PCT.FUNCTIONDECLARATION_BASETYPE_INT, + PCT.FUNCTIONDECLARATION_BASETYPE_ERROR, PCT.FUNCTIONDECLARATION_BASETYPE_BOOL, + PCT.FUNCTIONDECLARATION_BASETYPE_STRING, PCT.FUNCTIONDECLARATION_DERIVED_ENUM, + PCT.FUNCTIONDECLARATION_DERIVED_HEADER, PCT.FUNCTIONDECLARATION_DERIVED_HEADER_STACK, + PCT.FUNCTIONDECLARATION_DERIVED_STRUCT, PCT.FUNCTIONDECLARATION_DERIVED_HEADER_UNION, + PCT.FUNCTIONDECLARATION_DERIVED_TUPLE, PCT.FUNCTIONDECLARATION_TYPE_VOID, + PCT.FUNCTIONDECLARATION_TYPE_MATCH_KIND, + }; + const auto *returnType = target().expressionGenerator().pickRndType(typePercent); + tm = new IR::Type_Method(returnType, params, name); + auto *ret = new IR::Method(name, tm); + P4Scope::endLocalScope(); + P4Scope::addToScope(ret); + return ret; +} + +IR::Function *DeclarationGenerator::genFunctionDeclaration() { + cstring name = getRandomString(7); + IR::Type_Method *tm = nullptr; + IR::BlockStatement *blk = nullptr; + P4Scope::startLocalScope(); + IR::ParameterList *params = genParameterList(); + + TyperefProbs typePercent = { + PCT.FUNCTIONDECLARATION_BASETYPE_BIT, PCT.FUNCTIONDECLARATION_BASETYPE_SIGNED_BIT, + PCT.FUNCTIONDECLARATION_BASETYPE_VARBIT, PCT.FUNCTIONDECLARATION_BASETYPE_INT, + PCT.FUNCTIONDECLARATION_BASETYPE_ERROR, PCT.FUNCTIONDECLARATION_BASETYPE_BOOL, + PCT.FUNCTIONDECLARATION_BASETYPE_STRING, PCT.FUNCTIONDECLARATION_DERIVED_ENUM, + PCT.FUNCTIONDECLARATION_DERIVED_HEADER, PCT.FUNCTIONDECLARATION_DERIVED_HEADER_STACK, + PCT.FUNCTIONDECLARATION_DERIVED_STRUCT, PCT.FUNCTIONDECLARATION_DERIVED_HEADER_UNION, + PCT.FUNCTIONDECLARATION_DERIVED_TUPLE, PCT.FUNCTIONDECLARATION_TYPE_VOID, + PCT.FUNCTIONDECLARATION_TYPE_MATCH_KIND, + }; + const auto *returnType = target().expressionGenerator().pickRndType(typePercent); + tm = new IR::Type_Method(returnType, params, name); + + P4Scope::prop.ret_type = returnType; + blk = target().statementGenerator().genBlockStatement(true); + P4Scope::prop.ret_type = nullptr; + + auto *ret = new IR::Function(name, tm, blk); + P4Scope::endLocalScope(); + P4Scope::addToScope(ret); + return ret; +} + +IR::Type_Header *DeclarationGenerator::genEthernetHeaderType() { + IR::IndexedVector fields; + auto *ethDst = new IR::StructField("dst_addr", IR::Type_Bits::get(48, false)); + auto *ethSrc = new IR::StructField("src_addr", IR::Type_Bits::get(48, false)); + auto *ethType = new IR::StructField("eth_type", IR::Type_Bits::get(16, false)); + + fields.push_back(ethDst); + fields.push_back(ethSrc); + fields.push_back(ethType); + + auto *ret = new IR::Type_Header(IR::ID(ETH_HEADER_T), fields); + P4Scope::addToScope(ret); + + return ret; +} + +IR::Type_Header *DeclarationGenerator::genHeaderTypeDeclaration() { + cstring name = getRandomString(6); + IR::IndexedVector fields; + TyperefProbs typePercent = { + PCT.HEADERTYPEDECLARATION_BASETYPE_BIT, PCT.HEADERTYPEDECLARATION_BASETYPE_SIGNED_BIT, + PCT.HEADERTYPEDECLARATION_BASETYPE_VARBIT, PCT.HEADERTYPEDECLARATION_BASETYPE_INT, + PCT.HEADERTYPEDECLARATION_BASETYPE_ERROR, PCT.HEADERTYPEDECLARATION_BASETYPE_BOOL, + PCT.HEADERTYPEDECLARATION_BASETYPE_STRING, PCT.HEADERTYPEDECLARATION_DERIVED_ENUM, + PCT.HEADERTYPEDECLARATION_DERIVED_HEADER, PCT.HEADERTYPEDECLARATION_DERIVED_HEADER_STACK, + PCT.HEADERTYPEDECLARATION_DERIVED_STRUCT, PCT.HEADERTYPEDECLARATION_DERIVED_HEADER_UNION, + PCT.HEADERTYPEDECLARATION_DERIVED_TUPLE, PCT.HEADERTYPEDECLARATION_TYPE_VOID, + PCT.HEADERTYPEDECLARATION_TYPE_MATCH_KIND, + }; + + size_t len = Utils::getRandInt(1, 5); + for (size_t i = 0; i < len; i++) { + cstring fieldName = getRandomString(4); + const auto *fieldTp = target().expressionGenerator().pickRndType(typePercent); + + if (const auto *structTp = fieldTp->to()) { + fieldTp = new IR::Type_Name(structTp->name); + } + auto *sf = new IR::StructField(fieldName, fieldTp); + fields.push_back(sf); + } + auto *ret = new IR::Type_Header(name, fields); + if (P4Scope::req.byte_align_headers) { + auto remainder = ret->width_bits() % 8; + if (remainder != 0) { + const auto *padBit = IR::Type_Bits::get(8 - remainder, false); + auto *padField = new IR::StructField("padding", padBit); + ret->fields.push_back(padField); + } + } + P4Scope::addToScope(ret); + + return ret; +} + +IR::Type_HeaderUnion *DeclarationGenerator::genHeaderUnionDeclaration() { + cstring name = getRandomString(6); + + IR::IndexedVector fields; + auto lTypes = P4Scope::getDecls(); + if (lTypes.size() < 2) { + BUG("Creating a header union assumes at least two headers!"); + } + // !sure if this correct... + size_t len = Utils::getRandInt(2, lTypes.size() - 2); + std::set visitedHeaders; + // we need to guarantee correct execution so try as long as we can + // this is a bit dicey... do !like it + int attempts = 0; + while (true) { + if (attempts >= 100) { + BUG("We should not need this many attempts!"); + } + attempts++; + cstring fieldName = getRandomString(4); + const auto *hdrTp = lTypes.at(Utils::getRandInt(0, lTypes.size() - 1)); + // check if we have already added this header + if (visitedHeaders.find(hdrTp->name) != visitedHeaders.end()) { + continue; + } + auto *tpName = new IR::Type_Name(hdrTp->name); + auto *sf = new IR::StructField(fieldName, tpName); + visitedHeaders.insert(hdrTp->name); + fields.push_back(sf); + if (fields.size() == len) { + break; + } + } + + auto *ret = new IR::Type_HeaderUnion(name, fields); + + P4Scope::addToScope(ret); + + return ret; +} + +IR::Type *DeclarationGenerator::genHeaderStackType() { + auto lTypes = P4Scope::getDecls(); + if (lTypes.empty()) { + BUG("Creating a header stacks assumes at least one declared header!"); + } + const auto *hdrTp = lTypes.at(Utils::getRandInt(0, lTypes.size() - 1)); + auto stackSize = Utils::getRandInt(1, MAX_HEADER_STACK_SIZE); + auto *hdrTypeName = new IR::Type_Name(hdrTp->name); + auto *ret = + new IR::Type_Stack(hdrTypeName, new IR::Constant(IR::Type_InfInt::get(), stackSize)); + + P4Scope::addToScope(ret); + + return ret; +} + +IR::Type_Struct *DeclarationGenerator::genStructTypeDeclaration() { + cstring name = getRandomString(6); + + IR::IndexedVector fields; + TyperefProbs typePercent = { + PCT.STRUCTTYPEDECLARATION_BASETYPE_BIT, PCT.STRUCTTYPEDECLARATION_BASETYPE_SIGNED_BIT, + PCT.STRUCTTYPEDECLARATION_BASETYPE_VARBIT, PCT.STRUCTTYPEDECLARATION_BASETYPE_INT, + PCT.STRUCTTYPEDECLARATION_BASETYPE_ERROR, PCT.STRUCTTYPEDECLARATION_BASETYPE_BOOL, + PCT.STRUCTTYPEDECLARATION_BASETYPE_STRING, PCT.STRUCTTYPEDECLARATION_DERIVED_ENUM, + PCT.STRUCTTYPEDECLARATION_DERIVED_HEADER, PCT.STRUCTTYPEDECLARATION_DERIVED_HEADER_STACK, + PCT.STRUCTTYPEDECLARATION_DERIVED_STRUCT, PCT.STRUCTTYPEDECLARATION_DERIVED_HEADER_UNION, + PCT.STRUCTTYPEDECLARATION_DERIVED_TUPLE, PCT.STRUCTTYPEDECLARATION_TYPE_VOID, + PCT.STRUCTTYPEDECLARATION_TYPE_MATCH_KIND, + }; + auto lTypes = P4Scope::getDecls(); + if (lTypes.empty()) { + return nullptr; + } + size_t len = Utils::getRandInt(1, 5); + + for (size_t i = 0; i < len; i++) { + const auto *fieldTp = target().expressionGenerator().pickRndType(typePercent); + cstring fieldName = getRandomString(4); + if (fieldTp->to() != nullptr) { + // Right now there is now way to initialize a header stack + // So we have to add the entire structure to the banned expressions + P4Scope::notInitializedStructs.insert(name); + } + auto *sf = new IR::StructField(fieldName, fieldTp); + fields.push_back(sf); + } + + auto *ret = new IR::Type_Struct(name, fields); + + P4Scope::addToScope(ret); + + return ret; +} + +IR::Type_Struct *DeclarationGenerator::genHeaderStruct() { + IR::IndexedVector fields; + + // Tao: hard code for ethernet_t eth_hdr; + auto *ethSf = new IR::StructField(ETH_HDR, new IR::Type_Name(ETH_HEADER_T)); + fields.push_back(ethSf); + + size_t len = Utils::getRandInt(1, 5); + // we can only generate very specific types for headers + // header, header stack, header union + std::vector percent = {PCT.STRUCTTYPEDECLARATION_HEADERS_HEADER, + PCT.STRUCTTYPEDECLARATION_HEADERS_STACK}; + for (size_t i = 0; i < len; i++) { + cstring fieldName = getRandomString(4); + IR::Type *tp = nullptr; + switch (Utils::getRandInt(percent)) { + case 0: { + // TODO(fruffy): We have to assume that this works + auto lTypes = P4Scope::getDecls(); + if (lTypes.empty()) { + BUG("structTypeDeclaration: No available header for Headers!"); + } + const auto *candidateType = lTypes.at(Utils::getRandInt(0, lTypes.size() - 1)); + tp = new IR::Type_Name(candidateType->name.name); + break; + } + case 1: { + tp = genHeaderStackType(); + // Right now there is now way to initialize a header stack + // So we have to add the entire structure to the banned expressions + P4Scope::notInitializedStructs.insert(cstring("Headers")); + } + } + fields.push_back(new IR::StructField(fieldName, tp)); + } + auto *ret = new IR::Type_Struct("Headers", fields); + + P4Scope::addToScope(ret); + + return ret; +} + +IR::Type_Declaration *DeclarationGenerator::genTypeDeclaration() { + std::vector percent = {PCT.TYPEDECLARATION_HEADER, PCT.TYPEDECLARATION_STRUCT, + PCT.TYPEDECLARATION_UNION}; + IR::Type_Declaration *decl = nullptr; + bool useDefaultDecl = false; + switch (Utils::getRandInt(percent)) { + case 0: { + useDefaultDecl = true; + break; + } + case 1: { + decl = genStructTypeDeclaration(); + break; + } + case 2: { + // header unions are disabled for now, need to fix assignments + auto hdrs = P4Scope::getDecls(); + // we can only generate a union if we have at least two headers + if (hdrs.size() > 1) { + decl = genHeaderUnionDeclaration(); + if (decl == nullptr) { + useDefaultDecl = true; + } + } else { + useDefaultDecl = true; + } + break; + } + } + if (useDefaultDecl) { + decl = genHeaderTypeDeclaration(); + } + + return decl; +} + +const IR::Type *DeclarationGenerator::genType() { + std::vector percent = {PCT.TYPEDEFDECLARATION_BASE, PCT.TYPEDEFDECLARATION_STRUCTLIKE, + PCT.TYPEDEFDECLARATION_STACK}; + + std::vector typeProbs = { + PCT.TYPEDEFDECLARATION_BASETYPE_BOOL, PCT.TYPEDEFDECLARATION_BASETYPE_ERROR, + PCT.TYPEDEFDECLARATION_BASETYPE_INT, PCT.TYPEDEFDECLARATION_BASETYPE_STRING, + PCT.TYPEDEFDECLARATION_BASETYPE_BIT, PCT.TYPEDEFDECLARATION_BASETYPE_SIGNED_BIT, + PCT.TYPEDEFDECLARATION_BASETYPE_VARBIT}; + const IR::Type *tp = nullptr; + switch (Utils::getRandInt(percent)) { + case 0: { + std::vector bTypes = {1}; // only bit<> + tp = P4Tools::P4Smith::ExpressionGenerator::pickRndBaseType(typeProbs); + break; + } + case 1: { + auto lTypes = P4Scope::getDecls(); + if (lTypes.empty()) { + return nullptr; + } + const auto *candidateType = lTypes.at(Utils::getRandInt(0, lTypes.size() - 1)); + tp = new IR::Type_Name(candidateType->name.name); + break; + } + case 2: { + // tp = headerStackType::gen(); + break; + } + } + return tp; +} + +IR::Type_Typedef *DeclarationGenerator::genTypeDef() { + cstring name = getRandomString(5); + auto *ret = new IR::Type_Typedef(name, genType()); + P4Scope::addToScope(ret); + return ret; +} + +IR::Type_Newtype *DeclarationGenerator::genNewtype() { + cstring name = getRandomString(5); + IR::Type *type = nullptr; + + auto *ret = new IR::Type_Newtype(name, type); + P4Scope::addToScope(ret); + return ret; +} + +IR::Type *DeclarationGenerator::genTypeDefOrNewType() { + // TODO(fruffy): we only have typedef now, no newtype + return genTypeDef(); +} + +IR::Declaration_Variable *DeclarationGenerator::genVariableDeclaration() { + cstring name = getRandomString(6); + + TyperefProbs typePercent = { + PCT.VARIABLEDECLARATION_BASETYPE_BIT, PCT.VARIABLEDECLARATION_BASETYPE_SIGNED_BIT, + PCT.VARIABLEDECLARATION_BASETYPE_VARBIT, PCT.VARIABLEDECLARATION_BASETYPE_INT, + PCT.VARIABLEDECLARATION_BASETYPE_ERROR, PCT.VARIABLEDECLARATION_BASETYPE_BOOL, + PCT.VARIABLEDECLARATION_BASETYPE_STRING, PCT.VARIABLEDECLARATION_DERIVED_ENUM, + PCT.VARIABLEDECLARATION_DERIVED_HEADER, PCT.VARIABLEDECLARATION_DERIVED_HEADER_STACK, + PCT.VARIABLEDECLARATION_DERIVED_STRUCT, PCT.VARIABLEDECLARATION_DERIVED_HEADER_UNION, + PCT.VARIABLEDECLARATION_DERIVED_TUPLE, PCT.VARIABLEDECLARATION_TYPE_VOID, + PCT.VARIABLEDECLARATION_TYPE_MATCH_KIND, + }; + + const IR::Type *tp = target().expressionGenerator().pickRndType(typePercent); + + IR::Declaration_Variable *ret = nullptr; + + if (tp->is() || tp->is() || tp->is() || + tp->is()) { + auto *expr = target().expressionGenerator().genExpression(tp); + ret = new IR::Declaration_Variable(name, tp, expr); + } else if (tp->is()) { + // header stacks do !have an initializer yet + ret = new IR::Declaration_Variable(name, tp); + } else { + BUG("Type %s not supported!", tp->node_type_name()); + } + + P4Scope::addToScope(ret); + + return ret; +} + +IR::Parameter *DeclarationGenerator::genTypedParameter(bool if_none_dir) { + cstring name = getRandomString(4); + const IR::Type *tp = nullptr; + IR::Direction dir; + TyperefProbs typePercent; + + if (if_none_dir) { + typePercent = { + PCT.PARAMETER_NONEDIR_BASETYPE_BIT, PCT.PARAMETER_NONEDIR_BASETYPE_SIGNED_BIT, + PCT.PARAMETER_NONEDIR_BASETYPE_VARBIT, PCT.PARAMETER_NONEDIR_BASETYPE_INT, + PCT.PARAMETER_NONEDIR_BASETYPE_ERROR, PCT.PARAMETER_NONEDIR_BASETYPE_BOOL, + PCT.PARAMETER_NONEDIR_BASETYPE_STRING, PCT.PARAMETER_NONEDIR_DERIVED_ENUM, + PCT.PARAMETER_NONEDIR_DERIVED_HEADER, PCT.PARAMETER_NONEDIR_DERIVED_HEADER_STACK, + PCT.PARAMETER_NONEDIR_DERIVED_STRUCT, PCT.PARAMETER_NONEDIR_DERIVED_HEADER_UNION, + PCT.PARAMETER_NONEDIR_DERIVED_TUPLE, PCT.PARAMETER_NONEDIR_TYPE_VOID, + PCT.PARAMETER_NONEDIR_TYPE_MATCH_KIND, + }; + dir = IR::Direction::None; + } else { + typePercent = { + PCT.PARAMETER_BASETYPE_BIT, PCT.PARAMETER_BASETYPE_SIGNED_BIT, + PCT.PARAMETER_BASETYPE_VARBIT, PCT.PARAMETER_BASETYPE_INT, + PCT.PARAMETER_BASETYPE_ERROR, PCT.PARAMETER_BASETYPE_BOOL, + PCT.PARAMETER_BASETYPE_STRING, PCT.PARAMETER_DERIVED_ENUM, + PCT.PARAMETER_DERIVED_HEADER, PCT.PARAMETER_DERIVED_HEADER_STACK, + PCT.PARAMETER_DERIVED_STRUCT, PCT.PARAMETER_DERIVED_HEADER_UNION, + PCT.PARAMETER_DERIVED_TUPLE, PCT.PARAMETER_TYPE_VOID, + PCT.PARAMETER_TYPE_MATCH_KIND, + }; + std::vector dirPercent = {PCT.PARAMETER_DIR_IN, PCT.PARAMETER_DIR_OUT, + PCT.PARAMETER_DIR_INOUT}; + switch (Utils::getRandInt(dirPercent)) { + case 0: + dir = IR::Direction::In; + break; + case 1: + dir = IR::Direction::Out; + break; + case 2: + dir = IR::Direction::InOut; + break; + default: + dir = IR::Direction::None; + } + } + tp = target().expressionGenerator().pickRndType(typePercent); + + return new IR::Parameter(name, dir, tp); +} + +IR::Parameter *DeclarationGenerator::genParameter(IR::Direction dir, cstring p_name, + cstring t_name) { + IR::Type *tp = new IR::Type_Name(new IR::Path(t_name)); + return new IR::Parameter(p_name, dir, tp); +} + +IR::ParameterList *DeclarationGenerator::genParameterList() { + IR::IndexedVector params; + size_t totalParams = Utils::getRandInt(0, 3); + size_t numDirParams = (totalParams != 0U) ? Utils::getRandInt(0, totalParams - 1) : 0; + size_t numDirectionlessParams = totalParams - numDirParams; + for (size_t i = 0; i < numDirParams; i++) { + IR::Parameter *param = genTypedParameter(false); + if (param == nullptr) { + BUG("param is null"); + } + params.push_back(param); + // add to the scope + P4Scope::addToScope(param); + // only add values that are not read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + for (size_t i = 0; i < numDirectionlessParams; i++) { + IR::Parameter *param = genTypedParameter(true); + + if (param == nullptr) { + BUG("param is null"); + } + params.push_back(param); + // add to the scope + P4Scope::addToScope(param); + P4Scope::addLval(param->type, param->name.name, true); + } + + return new IR::ParameterList(params); +} + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/common/declarations.h b/backends/p4tools/modules/smith/common/declarations.h new file mode 100644 index 00000000000..06000eb1dad --- /dev/null +++ b/backends/p4tools/modules/smith/common/declarations.h @@ -0,0 +1,86 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_DECLARATIONS_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_DECLARATIONS_H_ + +#include + +#include "backends/p4tools/modules/smith/common/generator.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" +#include "lib/cstring.h" + +namespace P4Tools::P4Smith { + +class DeclarationGenerator : public Generator { + public: + explicit DeclarationGenerator(const SmithTarget &target) : Generator(target) {} + + virtual ~DeclarationGenerator() = default; + + virtual IR::StatOrDecl *generateRandomStatementOrDeclaration(bool is_in_func); + + virtual IR::Annotations *genAnnotation(); + + virtual IR::P4Action *genActionDeclaration(); + + virtual IR::Declaration_Constant *genConstantDeclaration(); + + virtual IR::IndexedVector genLocalControlDecls(); + + virtual IR::P4Control *genControlDeclaration(); + + virtual IR::Declaration_Instance *genControlDeclarationInstance(); + + virtual IR::Type *genDerivedTypeDeclaration(); + + virtual IR::IndexedVector genIdentifierList(size_t len); + + virtual IR::IndexedVector genSpecifiedIdentifier(size_t len); + + virtual IR::IndexedVector genSpecifiedIdentifierList(size_t len); + + virtual IR::Type_Enum *genEnumDeclaration(cstring name); + + virtual IR::Type_SerEnum *genSerEnumDeclaration(cstring name); + + virtual IR::Type *genEnumTypeDeclaration(int type); + + virtual IR::Method *genExternDeclaration(); + + virtual IR::Function *genFunctionDeclaration(); + + static IR::Type_Header *genEthernetHeaderType(); + + virtual IR::Type_Header *genHeaderTypeDeclaration(); + + virtual IR::Type_HeaderUnion *genHeaderUnionDeclaration(); + + static constexpr size_t MAX_HEADER_STACK_SIZE = 10; + + virtual IR::Type *genHeaderStackType(); + + virtual IR::Type_Struct *genStructTypeDeclaration(); + + virtual IR::Type_Struct *genHeaderStruct(); + + virtual IR::Type_Declaration *genTypeDeclaration(); + + virtual const IR::Type *genType(); + + virtual IR::Type_Typedef *genTypeDef(); + + virtual IR::Type_Newtype *genNewtype(); + + virtual IR::Type *genTypeDefOrNewType(); + + virtual IR::Declaration_Variable *genVariableDeclaration(); + + virtual IR::Parameter *genTypedParameter(bool if_none_dir); + + virtual IR::Parameter *genParameter(IR::Direction dir, cstring p_name, cstring t_name); + + virtual IR::ParameterList *genParameterList(); +}; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_DECLARATIONS_H_ */ diff --git a/backends/p4tools/modules/smith/common/expressions.cpp b/backends/p4tools/modules/smith/common/expressions.cpp new file mode 100644 index 00000000000..2805dfe57ee --- /dev/null +++ b/backends/p4tools/modules/smith/common/expressions.cpp @@ -0,0 +1,1246 @@ +#include "backends/p4tools/modules/smith/common/expressions.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "ir/indexed_vector.h" +#include "ir/vector.h" +#include "lib/exceptions.h" + +namespace P4Tools::P4Smith { + +const IR::Type_Boolean *ExpressionGenerator::genBoolType() { return IR::Type_Boolean::get(); } + +const IR::Type_InfInt *ExpressionGenerator::genIntType() { return IR::Type_InfInt::get(); } + +const IR::Type *ExpressionGenerator::pickRndBaseType(const std::vector &type_probs) { + if (type_probs.size() != 7) { + BUG("pickRndBaseType: Type probabilities must be exact"); + } + const IR::Type *tb = nullptr; + switch (Utils::getRandInt(type_probs)) { + case 0: { + // bool + tb = genBoolType(); + break; + } + case 1: { + // error, this is not supported right now + break; + } + case 2: { + // int, this is not supported right now + tb = genIntType(); + break; + } + case 3: { + // string, this is not supported right now + break; + } + case 4: { + // bit<> + tb = genBitType(false); + break; + } + case 5: { + // int<> + tb = genBitType(true); + break; + } + case 6: { + // varbit<>, this is not supported right now + break; + } + } + return tb; +} + +const IR::Type *ExpressionGenerator::pickRndType(TyperefProbs type_probs) { + const std::vector &typeProbsVector = { + type_probs.p4_bit, type_probs.p4_signed_bit, type_probs.p4_varbit, + type_probs.p4_int, type_probs.p4_error, type_probs.p4_bool, + type_probs.p4_string, type_probs.p4_enum, type_probs.p4_header, + type_probs.p4_header_stack, type_probs.p4_struct, type_probs.p4_header_union, + type_probs.p4_tuple, type_probs.p4_void, type_probs.p4_match_kind}; + + const std::vector &basetypeProbs = { + type_probs.p4_bool, type_probs.p4_error, type_probs.p4_int, type_probs.p4_string, + type_probs.p4_bit, type_probs.p4_signed_bit, type_probs.p4_varbit}; + + if (typeProbsVector.size() != 15) { + BUG("pickRndType: Type probabilities must be exact"); + } + const IR::Type *tp = nullptr; + size_t idx = Utils::getRandInt(typeProbsVector); + switch (idx) { + case 0: { + // bit<> + tp = ExpressionGenerator::genBitType(false); + break; + } + case 1: { + // int<> + tp = ExpressionGenerator::genBitType(true); + break; + } + case 2: { + // varbit<>, this is not supported right now + break; + } + case 3: { + tp = ExpressionGenerator::genIntType(); + break; + } + case 4: { + // error, this is not supported right now + break; + } + case 5: { + // bool + tp = ExpressionGenerator::genBoolType(); + break; + } + case 6: { + // string, this is not supported right now + break; + } + case 7: { + // enum, this is not supported right now + break; + } + case 8: { + // header + auto lTypes = P4Scope::getDecls(); + if (lTypes.empty()) { + tp = pickRndBaseType(basetypeProbs); + break; + } + const auto *candidateType = lTypes.at(Utils::getRandInt(0, lTypes.size() - 1)); + auto typeName = candidateType->name.name; + // check if struct is forbidden + if (P4Scope::notInitializedStructs.count(typeName) == 0) { + tp = new IR::Type_Name(candidateType->name.name); + } else { + tp = pickRndBaseType(basetypeProbs); + } + break; + } + case 9: { + tp = target().declarationGenerator().genHeaderStackType(); + break; + } + case 10: { + // struct + auto lTypes = P4Scope::getDecls(); + if (lTypes.empty()) { + tp = pickRndBaseType(basetypeProbs); + break; + } + const auto *candidateType = lTypes.at(Utils::getRandInt(0, lTypes.size() - 1)); + auto typeName = candidateType->name.name; + // check if struct is forbidden + if (P4Scope::notInitializedStructs.count(typeName) == 0) { + tp = new IR::Type_Name(candidateType->name.name); + } else { + tp = pickRndBaseType(basetypeProbs); + } + break; + } + case 11: { + // header union, this is not supported right now + break; + } + case 12: { + // tuple, this is not supported right now + break; + } + case 13: { + // void + tp = IR::Type_Void::get(); + break; + } + case 14: { + // match kind, this is not supported right now + break; + } + } + if (tp == nullptr) { + BUG("pickRndType: Chosen type is Null!"); + } + + return tp; +} + +IR::BoolLiteral *ExpressionGenerator::genBoolLiteral() { + if (Utils::getRandInt(0, 1) != 0) { + return new IR::BoolLiteral(false); + } + return new IR::BoolLiteral(true); +} + +const IR::Type_Bits *ExpressionGenerator::genBitType(bool isSigned) { + auto size = Utils::getRandInt(0, sizeof(BIT_WIDTHS) / sizeof(int) - 1); + + return IR::Type_Bits::get(BIT_WIDTHS[size], isSigned); +} + +IR::Constant *ExpressionGenerator::genIntLiteral(size_t bit_width) { + big_int min = -((big_int(1) << bit_width - 1)); + if (P4Scope::req.not_negative) { + min = 0; + } + big_int max = ((big_int(1) << bit_width - 1) - 1); + big_int value = Utils::getRandBigInt(min, max); + while (true) { + if (P4Scope::req.not_zero && value == 0) { + value = Utils::getRandBigInt(min, max); + // retry until we generate a value that is !zero + continue; + } + break; + } + return new IR::Constant(value); +} +IR::Constant *ExpressionGenerator::genBitLiteral(const IR::Type *tb) { + big_int maxSize = (big_int(1U) << tb->width_bits()); + + big_int value; + if (P4Scope::req.not_zero) { + value = Utils::getRandBigInt(1, maxSize - 1); + } else { + value = Utils::getRandBigInt(0, maxSize - 1); + } + return new IR::Constant(tb, value); +} + +IR::Expression *ExpressionGenerator::genExpression(const IR::Type *tp) { + IR::Expression *expr = nullptr; + + // reset the expression depth + P4Scope::prop.depth = 0; + + if (const auto *tb = tp->to()) { + expr = constructBitExpr(tb); + } else if (tp->is()) { + expr = constructIntExpr(); + } else if (tp->is()) { + expr = constructBooleanExpr(); + } else if (const auto *tn = tp->to()) { + expr = constructStructExpr(tn); + } else { + BUG("Expression: Type %s not yet supported", tp->node_type_name()); + } + // reset the expression depth, just to be safe... + P4Scope::prop.depth = 0; + return expr; +} + +IR::MethodCallExpression *ExpressionGenerator::pickFunction( + IR::IndexedVector viable_functions, const IR::Type **ret_type) { + // TODO(fruffy): Make this more sophisticated + if (viable_functions.empty() || P4Scope::req.compile_time_known) { + return nullptr; + } + + size_t idx = Utils::getRandInt(0, viable_functions.size() - 1); + cstring funName; + const IR::ParameterList *params = nullptr; + if (const auto *p4Fun = viable_functions[idx]->to()) { + funName = p4Fun->name.name; + params = p4Fun->getParameters(); + *ret_type = p4Fun->type->returnType; + } else if (const auto *p4Extern = viable_functions[idx]->to()) { + funName = p4Extern->name.name; + params = p4Extern->getParameters(); + *ret_type = p4Extern->type->returnType; + } else { + BUG("Unknown callable: Type %s not yet supported", viable_functions[idx]->node_type_name()); + } + auto *expr = genFunctionCall(funName, *params); + // sometimes, functions may !be callable + // because we do !have the right return values + if ((expr == nullptr) || (ret_type == nullptr)) { + return nullptr; + } + return expr; +} + +IR::MethodCallExpression *ExpressionGenerator::genFunctionCall(cstring method_name, + IR::ParameterList params) { + auto *args = new IR::Vector(); + IR::IndexedVector decls; + + bool canCall = true; + + for (const auto *par : params) { + if (!checkInputArg(par)) { + canCall = false; + } else { + IR::Argument *arg = nullptr; + arg = new IR::Argument(genInputArg(par)); + args->push_back(arg); + } + } + if (canCall) { + auto *pathExpr = new IR::PathExpression(method_name); + return new IR::MethodCallExpression(pathExpr, args); + } + return nullptr; +} + +IR::ListExpression *ExpressionGenerator::genExpressionList(IR::Vector types, + bool only_lval) { + IR::Vector components; + for (const auto *tb : types) { + IR::Expression *expr = nullptr; + if (only_lval) { + cstring lvalName = P4Scope::pickLval(tb); + expr = new IR::PathExpression(lvalName); + } else { + expr = genExpression(tb); + } + components.push_back(expr); + } + return new IR::ListExpression(components); +} + +IR::Expression *ExpressionGenerator::constructUnaryExpr(const IR::Type_Bits *tb) { + IR::Expression *expr = nullptr; + + if (P4Scope::prop.depth > MAX_DEPTH) { + return genBitLiteral(tb); + } + P4Scope::prop.depth++; + + // we want to avoid negation when we require no negative values + int64_t negPct = PCT.EXPRESSION_BIT_UNARY_NEG; + if (P4Scope::req.not_negative) { + negPct = 0; + } + + std::vector percent = {negPct, PCT.EXPRESSION_BIT_UNARY_CMPL, + PCT.EXPRESSION_BIT_UNARY_CAST, + PCT.EXPRESSION_BIT_UNARY_FUNCTION}; + + switch (Utils::getRandInt(percent)) { + case 0: { + // pick a negation that matches the type + expr = new IR::Neg(tb, constructBitExpr(tb)); + } break; + case 1: { + // pick a complement that matches the type + // width must be known so we cast + expr = constructBitExpr(tb); + if (P4Scope::prop.width_unknown) { + expr = new IR::Cast(tb, expr); + P4Scope::prop.width_unknown = false; + } + expr = new IR::Cmpl(tb, expr); + } break; + case 2: { + // pick a cast to the type that matches the type + // new bit type can be random here + expr = new IR::Cast(tb, constructBitExpr(tb)); + } break; + case 3: { + auto p4Functions = P4Scope::getDecls(); + auto p4Externs = P4Scope::getDecls(); + + IR::IndexedVector viableFunctions; + for (const auto *fun : p4Functions) { + if (fun->type->returnType->to() != nullptr) { + viableFunctions.push_back(fun); + } + } + for (const auto *fun : p4Externs) { + if (fun->type->returnType->to() != nullptr) { + viableFunctions.push_back(fun); + } + } + const IR::Type *retType = nullptr; + expr = pickFunction(viableFunctions, &retType); + // can !find a suitable function, generate a default value + if (expr == nullptr) { + expr = genBitLiteral(tb); + break; + } + // if the return value does !match try to cast it + if (retType != tb) { + expr = new IR::Cast(tb, expr); + } + } break; + } + return expr; +} + +IR::Expression *ExpressionGenerator::createSaturationOperand(const IR::Type_Bits *tb) { + IR::Expression *expr = constructBitExpr(tb); + + int width = P4Scope::constraints.max_phv_container_width; + if (width != 0) { + if (tb->width_bits() > width) { + const auto *type = IR::Type_Bits::get(width, false); + expr = new IR::Cast(type, expr); + expr->type = type; + P4Scope::prop.width_unknown = false; + return expr; + } + } + + // width must be known so we cast + if (P4Scope::prop.width_unknown) { + expr = new IR::Cast(tb, expr); + P4Scope::prop.width_unknown = false; + } + + expr->type = tb; + return expr; +} + +IR::Expression *ExpressionGenerator::constructBinaryBitExpr(const IR::Type_Bits *tb) { + IR::Expression *expr = nullptr; + + if (P4Scope::prop.depth > MAX_DEPTH) { + return genBitLiteral(tb); + } + P4Scope::prop.depth++; + + auto pctSub = PCT.EXPRESSION_BIT_BINARY_SUB; + auto pctSubsat = PCT.EXPRESSION_BIT_BINARY_SUBSAT; + // we want to avoid subtraction when we require no negative values + if (P4Scope::req.not_negative) { + pctSub = 0; + pctSubsat = 0; + } + + std::vector percent = {PCT.EXPRESSION_BIT_BINARY_MUL, + PCT.EXPRESSION_BIT_BINARY_DIV, + PCT.EXPRESSION_BIT_BINARY_MOD, + PCT.EXPRESSION_BIT_BINARY_ADD, + pctSub, + PCT.EXPRESSION_BIT_BINARY_ADDSAT, + pctSubsat, + PCT.EXPRESSION_BIT_BINARY_LSHIFT, + PCT.EXPRESSION_BIT_BINARY_RSHIFT, + PCT.EXPRESSION_BIT_BINARY_BAND, + PCT.EXPRESSION_BIT_BINARY_BOR, + PCT.EXPRESSION_BIT_BINARY_BXOR, + PCT.EXPRESSION_BIT_BINARY_CONCAT}; + + switch (Utils::getRandInt(percent)) { + case 0: { + IR::Expression *left = constructBitExpr(tb); + IR::Expression *right = constructBitExpr(tb); + // pick a multiplication that matches the type + expr = new IR::Mul(tb, left, right); + } break; + case 1: { + // pick a division that matches the type + // TODO(fruffy): Make more sophisticated + // this requires only compile time known values + IR::Expression *left = genBitLiteral(tb); + P4Scope::req.not_zero = true; + IR::Expression *right = genBitLiteral(tb); + P4Scope::req.not_zero = false; + expr = new IR::Div(tb, left, right); + } break; + case 2: { + // pick a modulo that matches the type + // TODO(fruffy): Make more sophisticated + // this requires only compile time known values + IR::Expression *left = genBitLiteral(tb); + P4Scope::req.not_zero = true; + IR::Expression *right = genBitLiteral(tb); + P4Scope::req.not_zero = false; + expr = new IR::Mod(tb, left, right); + } break; + case 3: { + IR::Expression *left = constructBitExpr(tb); + IR::Expression *right = constructBitExpr(tb); + // pick an addition that matches the type + expr = new IR::Add(tb, left, right); + } break; + case 4: { + IR::Expression *left = constructBitExpr(tb); + IR::Expression *right = constructBitExpr(tb); + // pick a subtraction that matches the type + expr = new IR::Sub(tb, left, right); + } break; + case 5: { + IR::Expression *left = createSaturationOperand(tb); + IR::Expression *right = createSaturationOperand(tb); + // pick a saturating addition that matches the type + BUG_CHECK(left->type->width_bits() == right->type->width_bits(), + "Operator must have same left and right types: %s, %s", left->type, + right->type); + expr = new IR::AddSat(tb, left, right); + // If we ended up constraining the operand width, the expr might not be the correct + // type. + if (left->type != tb) { + expr = new IR::Cast(tb, expr); + } + } break; + case 6: { + IR::Expression *left = createSaturationOperand(tb); + IR::Expression *right = createSaturationOperand(tb); + // pick a saturating addition that matches the type + BUG_CHECK(left->type->width_bits() == right->type->width_bits(), + "Operator must have same left and right types: %s, %s", left->type, + right->type); + expr = new IR::SubSat(tb, left, right); + // If we ended up constraining the operand width, the expr might not be the correct + // type. + if (left->type != tb) { + expr = new IR::Cast(tb, expr); + } + } break; + case 7: { + // width must be known so we cast + IR::Expression *left = constructBitExpr(tb); + if (P4Scope::prop.width_unknown) { + left = new IR::Cast(tb, left); + P4Scope::prop.width_unknown = false; + } + // TODO(fruffy): Make this more sophisticated, + P4Scope::req.not_negative = true; + IR::Expression *right = constructBitExpr(tb); + P4Scope::req.not_negative = false; + // TODO(fruffy): Make this more sophisticated + // shifts are limited to 8 bits + if (P4Scope::constraints.const_lshift_count) { + right = genBitLiteral(IR::Type_Bits::get(P4Scope::req.shift_width, false)); + } else { + right = new IR::Cast(IR::Type_Bits::get(8, false), right); + } + // pick a left-shift that matches the type + expr = new IR::Shl(tb, left, right); + } break; + case 8: { + // width must be known so we cast + IR::Expression *left = constructBitExpr(tb); + if (P4Scope::prop.width_unknown) { + left = new IR::Cast(tb, left); + P4Scope::prop.width_unknown = false; + } + + // TODO(fruffy): Make this more sophisticated, + P4Scope::req.not_negative = true; + IR::Expression *right = constructBitExpr(tb); + P4Scope::req.not_negative = false; + // shifts are limited to 8 bits + right = new IR::Cast(IR::Type_Bits::get(8, false), right); + // pick a right-shift that matches the type + expr = new IR::Shr(tb, left, right); + } break; + case 9: { + IR::Expression *left = constructBitExpr(tb); + IR::Expression *right = constructBitExpr(tb); + // pick an binary And that matches the type + expr = new IR::BAnd(tb, left, right); + } break; + case 10: { + IR::Expression *left = constructBitExpr(tb); + IR::Expression *right = constructBitExpr(tb); + // pick a binary Or and that matches the type + expr = new IR::BOr(tb, left, right); + } break; + case 11: { + IR::Expression *left = constructBitExpr(tb); + IR::Expression *right = constructBitExpr(tb); + // pick an binary Xor that matches the type + expr = new IR::BXor(tb, left, right); + } break; + case 12: { + // pick an concatenation that matches the type + size_t typeWidth = tb->width_bits(); + size_t split = Utils::getRandInt(1, typeWidth - 1); + // TODO(fruffy): lazy fallback + if (split >= typeWidth) { + return genBitLiteral(tb); + } + const auto *tl = IR::Type_Bits::get(typeWidth - split, false); + const auto *tr = IR::Type_Bits::get(split, false); + // width must be known so we cast + // width must be known so we cast + IR::Expression *left = constructBitExpr(tl); + if (P4Scope::prop.width_unknown) { + left = new IR::Cast(tl, left); + P4Scope::prop.width_unknown = false; + } + IR::Expression *right = constructBitExpr(tr); + if (P4Scope::prop.width_unknown) { + right = new IR::Cast(tr, right); + P4Scope::prop.width_unknown = false; + } + expr = new IR::Concat(tb, left, right); + } break; + } + return expr; +} + +IR::Expression *ExpressionGenerator::constructTernaryBitExpr(const IR::Type_Bits *tb) { + IR::Expression *expr = nullptr; + + if (P4Scope::prop.depth > MAX_DEPTH) { + return genBitLiteral(tb); + } + P4Scope::prop.depth++; + + std::vector percent = {PCT.EXPRESSION_BIT_BINARY_SLICE, PCT.EXPRESSION_BIT_BINARY_MUX}; + + switch (Utils::getRandInt(percent)) { + case 0: { + // TODO(fruffy): Refine this... + // pick a slice that matches the type + auto typeWidth = tb->width_bits(); + // TODO(fruffy): this is some arbitrary value... + auto newTypeSize = Utils::getRandInt(1, 128) + typeWidth; + const auto *sliceType = IR::Type_Bits::get(newTypeSize, false); + auto *sliceExpr = constructBitExpr(sliceType); + if (P4Scope::prop.width_unknown) { + sliceExpr = new IR::Cast(sliceType, sliceExpr); + P4Scope::prop.width_unknown = false; + } + auto margin = newTypeSize - typeWidth; + auto high = Utils::getRandInt(0, margin - 1) + typeWidth; + auto low = high - typeWidth + 1; + expr = new IR::Slice(sliceExpr, high, low); + break; + } + case 1: { + // pick a mux that matches the type + IR::Expression *cond = constructBooleanExpr(); + IR::Expression *left = constructBitExpr(tb); + if (P4Scope::prop.width_unknown) { + left = new IR::Cast(tb, left); + P4Scope::prop.width_unknown = false; + } + IR::Expression *right = constructBitExpr(tb); + if (P4Scope::prop.width_unknown) { + right = new IR::Cast(tb, right); + P4Scope::prop.width_unknown = false; + } + expr = new IR::Mux(tb, cond, left, right); + } break; + } + return expr; +} + +IR::Expression *ExpressionGenerator::pickBitVar(const IR::Type_Bits *tb) { + cstring nodeName = tb->node_type_name(); + auto availBitTypes = P4Scope::lvalMap[nodeName].size(); + if (P4Scope::checkLval(tb)) { + cstring name = P4Scope::pickLval(tb); + return new IR::PathExpression(name); + } + if (availBitTypes > 0) { + // even if we do !find anything we can still cast other bits + auto *newTb = P4Scope::pickDeclaredBitType(); + cstring name = P4Scope::pickLval(newTb); + return new IR::Cast(tb, new IR::PathExpression(name)); + } + + // fallback, just generate a literal + return genBitLiteral(tb); +} + +IR::Expression *ExpressionGenerator::constructBitExpr(const IR::Type_Bits *tb) { + IR::Expression *expr = nullptr; + + std::vector percent = {PCT.EXPRESSION_BIT_VAR, PCT.EXPRESSION_BIT_INT_LITERAL, + PCT.EXPRESSION_BIT_BIT_LITERAL, PCT.EXPRESSION_BIT_UNARY, + PCT.EXPRESSION_BIT_BINARY, PCT.EXPRESSION_BIT_TERNARY}; + + switch (Utils::getRandInt(percent)) { + case 0: { + // pick a variable that matches the type + // do !pick, if the requirement is to be a compile time known value + // TODO(fruffy): This is lazy, we can easily check + if (P4Scope::req.compile_time_known) { + expr = genBitLiteral(tb); + } else { + expr = pickBitVar(tb); + } + } break; + case 1: { + // pick an int literal, if allowed + if (P4Scope::req.require_scalar) { + expr = genBitLiteral(tb); + } else { + expr = constructIntExpr(); + P4Scope::prop.width_unknown = true; + } + } break; + case 2: { + // pick a bit literal that matches the type + expr = genBitLiteral(tb); + } break; + case 3: { + // pick a unary expression that matches the type + expr = constructUnaryExpr(tb); + } break; + case 4: { + // pick a binary expression that matches the type + expr = constructBinaryBitExpr(tb); + } break; + case 5: { + // pick a ternary expression that matches the type + expr = constructTernaryBitExpr(tb); + } break; + } + return expr; +} + +IR::Expression *ExpressionGenerator::constructCmpExpr() { + IR::Expression *expr = nullptr; + + // gen some random type + // can be either bits, int, bool, or structlike + // for now it is just bits + auto newTypeSize = Utils::getRandInt(1, 128); + const auto *newType = IR::Type_Bits::get(newTypeSize, false); + IR::Expression *left = constructBitExpr(newType); + IR::Expression *right = constructBitExpr(newType); + + std::vector percent = {PCT.EXPRESSION_BOOLEAN_CMP_EQU, PCT.EXPRESSION_BOOLEAN_CMP_NEQ}; + + switch (Utils::getRandInt(percent)) { + case 0: { + expr = new IR::Equ(left, right); + // pick an equals that matches the type + } break; + case 1: { + expr = new IR::Neq(left, right); + // pick a not-equals that matches the type + } break; + } + return expr; +} + +IR::Expression *ExpressionGenerator::constructBooleanExpr() { + IR::Expression *expr = nullptr; + IR::Expression *left = nullptr; + IR::Expression *right = nullptr; + + std::vector percent = { + PCT.EXPRESSION_BOOLEAN_VAR, PCT.EXPRESSION_BOOLEAN_LITERAL, PCT.EXPRESSION_BOOLEAN_NOT, + PCT.EXPRESSION_BOOLEAN_LAND, PCT.EXPRESSION_BOOLEAN_LOR, PCT.EXPRESSION_BOOLEAN_CMP, + PCT.EXPRESSION_BOOLEAN_FUNCTION, PCT.EXPRESSION_BOOLEAN_BUILT_IN}; + + switch (Utils::getRandInt(percent)) { + case 0: { + const auto *tb = IR::Type_Boolean::get(); + // TODO(fruffy): This is lazy, we can easily check + if (P4Scope::req.compile_time_known) { + expr = genBoolLiteral(); + break; + } + if (P4Scope::checkLval(tb)) { + cstring name = P4Scope::pickLval(tb); + expr = new IR::TypeNameExpression(name); + } else { + expr = genBoolLiteral(); + } + } break; + case 1: { + // pick a boolean literal + expr = genBoolLiteral(); + } break; + case 2: { + // pick a Not expression + expr = new IR::LNot(constructBooleanExpr()); + } break; + case 3: { + // pick an And expression + left = constructBooleanExpr(); + right = constructBooleanExpr(); + expr = new IR::LAnd(left, right); + } break; + case 4: { + // pick an Or expression + left = constructBooleanExpr(); + right = constructBooleanExpr(); + expr = new IR::LOr(left, right); + } break; + case 5: { + // pick a comparison + expr = constructCmpExpr(); + } break; + case 6: { + auto p4Functions = P4Scope::getDecls(); + auto p4Externs = P4Scope::getDecls(); + + IR::IndexedVector viableFunctions; + for (const auto *fun : p4Functions) { + if (fun->type->returnType->to() != nullptr) { + viableFunctions.push_back(fun); + } + } + for (const auto *fun : p4Externs) { + if (fun->type->returnType->to() != nullptr) { + viableFunctions.push_back(fun); + } + } + const IR::Type *retType = nullptr; + expr = pickFunction(viableFunctions, &retType); + // can !find a suitable function, generate a default value + if (expr == nullptr) { + expr = genBoolLiteral(); + } + } break; + case 7: { + // get the expression + auto *tblSet = P4Scope::getCallableTables(); + + // just generate a literal if there are no tables left + if (tblSet->empty() || P4Scope::req.compile_time_known) { + expr = genBoolLiteral(); + break; + } + auto idx = Utils::getRandInt(0, tblSet->size() - 1); + auto tblIter = std::begin(*tblSet); + + std::advance(tblIter, idx); + const IR::P4Table *tbl = *tblIter; + expr = new IR::Member(new IR::MethodCallExpression( + new IR::Member(new IR::PathExpression(tbl->name), "apply")), + "hit"); + tblSet->erase(tblIter); + } + } + return expr; +} + +IR::Expression *ExpressionGenerator::constructUnaryIntExpr() { + IR::Expression *expr = nullptr; + + if (P4Scope::prop.depth > MAX_DEPTH) { + return genIntLiteral(); + } + const auto *tp = IR::Type_InfInt::get(); + P4Scope::prop.depth++; + + // we want to avoid negation when we require no negative values + int64_t negPct = PCT.EXPRESSION_INT_UNARY_NEG; + if (P4Scope::req.not_negative) { + negPct = 0; + } + + std::vector percent = {negPct, PCT.EXPRESSION_INT_UNARY_FUNCTION}; + + switch (Utils::getRandInt(percent)) { + case 0: { + // pick a negation that matches the type + expr = new IR::Neg(tp, constructIntExpr()); + } break; + case 1: { + auto p4Functions = P4Scope::getDecls(); + auto p4Externs = P4Scope::getDecls(); + + IR::IndexedVector viableFunctions; + for (const auto *fun : p4Functions) { + if (fun->type->returnType->to() != nullptr) { + viableFunctions.push_back(fun); + } + } + for (const auto *fun : p4Externs) { + if (fun->type->returnType->to() != nullptr) { + viableFunctions.push_back(fun); + } + } + const IR::Type *retType = nullptr; + expr = pickFunction(viableFunctions, &retType); + // can !find a suitable function, generate a default value + if (expr == nullptr) { + expr = genIntLiteral(); + break; + } + } break; + } + return expr; +} + +IR::Expression *ExpressionGenerator::constructBinaryIntExpr() { + IR::Expression *expr = nullptr; + if (P4Scope::prop.depth > MAX_DEPTH) { + return genIntLiteral(); + } + const auto *tp = IR::Type_InfInt::get(); + P4Scope::prop.depth++; + + auto pctSub = PCT.EXPRESSION_INT_BINARY_SUB; + // we want to avoid subtraction when we require no negative values + if (P4Scope::req.not_negative) { + pctSub = 0; + } + + std::vector percent = {PCT.EXPRESSION_INT_BINARY_MUL, + PCT.EXPRESSION_INT_BINARY_DIV, + PCT.EXPRESSION_INT_BINARY_MOD, + PCT.EXPRESSION_INT_BINARY_ADD, + pctSub, + PCT.EXPRESSION_INT_BINARY_LSHIFT, + PCT.EXPRESSION_INT_BINARY_RSHIFT, + PCT.EXPRESSION_INT_BINARY_BAND, + PCT.EXPRESSION_INT_BINARY_BOR, + PCT.EXPRESSION_INT_BINARY_BXOR}; + + switch (Utils::getRandInt(percent)) { + case 0: { + IR::Expression *left = constructIntExpr(); + IR::Expression *right = constructIntExpr(); + // pick a multiplication that matches the type + expr = new IR::Mul(tp, left, right); + } break; + case 1: { + // pick a division that matches the type + // TODO(fruffy): Make more sophisticated + P4Scope::req.not_negative = true; + IR::Expression *left = genIntLiteral(); + P4Scope::req.not_zero = true; + IR::Expression *right = genIntLiteral(); + P4Scope::req.not_zero = false; + P4Scope::req.not_negative = false; + expr = new IR::Div(tp, left, right); + } break; + case 2: { + // pick a modulo that matches the type + // TODO(fruffy): Make more sophisticated + P4Scope::req.not_negative = true; + IR::Expression *left = genIntLiteral(); + P4Scope::req.not_zero = true; + IR::Expression *right = genIntLiteral(); + P4Scope::req.not_zero = false; + P4Scope::req.not_negative = false; + expr = new IR::Mod(tp, left, right); + } break; + case 3: { + IR::Expression *left = constructIntExpr(); + IR::Expression *right = constructIntExpr(); + // pick an addition that matches the type + expr = new IR::Add(tp, left, right); + } break; + case 4: { + IR::Expression *left = constructIntExpr(); + IR::Expression *right = constructIntExpr(); + // pick a subtraction that matches the type + expr = new IR::Sub(tp, left, right); + } break; + case 5: { + // width must be known so we cast + IR::Expression *left = constructIntExpr(); + // TODO(fruffy): Make this more sophisticated, + P4Scope::req.not_negative = true; + IR::Expression *right = constructIntExpr(); + // shifts are limited to 8 bits + right = new IR::Cast(IR::Type_Bits::get(8, false), right); + P4Scope::req.not_negative = false; + expr = new IR::Shl(tp, left, right); + } break; + case 6: { + // width must be known so we cast + IR::Expression *left = constructIntExpr(); + // TODO(fruffy): Make this more sophisticated, + P4Scope::req.not_negative = true; + IR::Expression *right = constructIntExpr(); + // shifts are limited to 8 bits + right = new IR::Cast(IR::Type_Bits::get(8, false), right); + P4Scope::req.not_negative = false; + expr = new IR::Shr(tp, left, right); + } break; + case 7: { + IR::Expression *left = constructIntExpr(); + IR::Expression *right = constructIntExpr(); + // pick an binary And that matches the type + expr = new IR::BAnd(tp, left, right); + } break; + case 8: { + IR::Expression *left = constructIntExpr(); + IR::Expression *right = constructIntExpr(); + // pick a binary Or and that matches the type + expr = new IR::BOr(tp, left, right); + } break; + case 9: { + IR::Expression *left = constructIntExpr(); + IR::Expression *right = constructIntExpr(); + // pick an binary Xor that matches the type + expr = new IR::BXor(tp, left, right); + } break; + } + return expr; +} + +IR::Expression *ExpressionGenerator::pickIntVar() { + const auto *tp = IR::Type_InfInt::get(); + if (P4Scope::checkLval(tp)) { + cstring name = P4Scope::pickLval(tp); + return new IR::PathExpression(name); + } + + // fallback, just generate a literal + return genIntLiteral(); +} + +IR::Expression *ExpressionGenerator::constructIntExpr() { + IR::Expression *expr = nullptr; + + std::vector percent = {PCT.EXPRESSION_INT_VAR, PCT.EXPRESSION_INT_INT_LITERAL, + PCT.EXPRESSION_INT_UNARY, PCT.EXPRESSION_INT_BINARY}; + + switch (Utils::getRandInt(percent)) { + case 0: { + expr = pickIntVar(); + } break; + case 1: { + // pick an int literal that matches the type + expr = genIntLiteral(); + } break; + case 2: { + // pick a unary expression that matches the type + expr = constructUnaryIntExpr(); + } break; + case 3: { + // pick a binary expression that matches the type + expr = constructBinaryIntExpr(); + } break; + } + return expr; +} + +IR::ListExpression *ExpressionGenerator::genStructListExpr(const IR::Type_Name *tn) { + IR::Vector components; + cstring tnName = tn->path->name.name; + + if (const auto *td = P4Scope::getTypeByName(tnName)) { + if (const auto *tnType = td->to()) { + for (const auto *sf : tnType->fields) { + IR::Expression *expr = nullptr; + if (const auto *fieldTn = sf->type->to()) { + // can!use another type here yet + expr = genStructListExpr(fieldTn); + components.push_back(expr); + } else if (const auto *fieldTs = sf->type->to()) { + auto stackSize = fieldTs->getSize(); + const auto *stackType = fieldTs->elementType; + if (const auto *sTypeName = stackType->to()) { + for (size_t idx = 0; idx < stackSize; ++idx) { + expr = genStructListExpr(sTypeName); + components.push_back(expr); + } + + } else { + BUG("genStructListExpr: Stack Type %s unsupported", tnName); + } + } else { + expr = genExpression(sf->type); + components.push_back(expr); + } + } + } else { + BUG("genStructListExpr: Requested Type %s not a struct-like type", tnName); + } + } else { + BUG("genStructListExpr: Requested Type %s not found", tnName); + } + return new IR::ListExpression(components); +} + +IR::Expression *ExpressionGenerator::constructStructExpr(const IR::Type_Name *tn) { + IR::Expression *expr = nullptr; + std::vector percent = {PCT.EXPRESSION_STRUCT_VAR, PCT.EXPRESSION_STRUCT_LITERAL, + PCT.EXPRESSION_STRUCT_FUNCTION}; + + // because fallthrough is !very portable... + bool useDefaultExpr = false; + + switch (Utils::getRandInt(percent)) { + case 0: + // pick a type from the available list + // do !pick, if the requirement is to be a compile time known value + if (P4Scope::checkLval(tn) && !P4Scope::req.compile_time_known) { + cstring lval = P4Scope::pickLval(tn); + expr = new IR::TypeNameExpression(lval); + } else { + // if there is no suitable declaration we fall through + useDefaultExpr = true; + } + break; + case 1: { + // construct a list expression out of base-types + useDefaultExpr = true; + } break; + case 2: { + // run a function call + auto p4Functions = P4Scope::getDecls(); + auto p4Externs = P4Scope::getDecls(); + + IR::IndexedVector viableFunctions; + for (const auto *fun : p4Functions) { + if (fun->type->returnType == tn) { + viableFunctions.push_back(fun); + } + } + for (const auto *fun : p4Externs) { + if (fun->type->returnType == tn) { + viableFunctions.push_back(fun); + } + } + + const IR::Type *retType = nullptr; + expr = pickFunction(viableFunctions, &retType); + // can !find a suitable function, generate a default value + if (expr == nullptr) { + useDefaultExpr = true; + break; + } + } + } + if (useDefaultExpr) { + expr = genStructListExpr(tn); + } + return expr; +} + +IR::Expression *ExpressionGenerator::genInputArg(const IR::Parameter *param) { + if (param->direction == IR::Direction::In) { + // this can be any value + return genExpression(param->type); + } + if (param->direction == IR::Direction::None) { + // such args can only be compile-time constants + P4Scope::req.compile_time_known = true; + auto *expr = genExpression(param->type); + P4Scope::req.compile_time_known = false; + return expr; + } + // for inout and out the value must be writeable + return pickLvalOrSlice(param->type); +} + +bool ExpressionGenerator::checkInputArg(const IR::Parameter *param) { + if (param->direction == IR::Direction::In || param->direction == IR::Direction::None) { + return P4Scope::checkLval(param->type, false); + } + return P4Scope::checkLval(param->type, true); +} + +size_t split(const std::string &txt, std::vector &strs, char ch) { + size_t pos = txt.find(ch); + size_t initialPos = 0; + strs.clear(); + + // Decompose statement + while (pos != std::string::npos) { + strs.emplace_back(txt.substr(initialPos, pos - initialPos)); + initialPos = pos + 1; + + pos = txt.find(ch, initialPos); + } + + // Add the last one + strs.emplace_back(txt.substr(initialPos, std::min(pos, txt.size()) - initialPos + 1)); + + return strs.size(); +} + +IR::Expression *ExpressionGenerator::editHdrStack(cstring lval) { + // Check if there is a stack bracket inside the string. + // Also, if we can not have variables inside the header stack index, + // then just return the original expression. + // FIXME: terrible but at least works for now + if ((lval.find('[') == nullptr) || P4Scope::constraints.const_header_stack_index) { + return new IR::PathExpression(lval); + } + + std::vector splitStr; + int size = split(lval.c_str(), splitStr, '.'); + if (size < 1) { + BUG("Unexpected split size. %d", size); + } + auto sIter = std::begin(splitStr); + IR::Expression *expr = new IR::PathExpression(*sIter); + for (advance(sIter, 1); sIter != splitStr.end(); ++sIter) { + // if there is an index, convert it towards the proper expression + auto subStr = *sIter; + const auto *hdrBrkt = subStr.find('['); + if (hdrBrkt != nullptr) { + auto stackStr = subStr.substr(static_cast(hdrBrkt - subStr + 1)); + const auto *stackSzEnd = stackStr.find(']'); + if (stackSzEnd == nullptr) { + BUG("There should be a closing bracket."); + } + int stackSz = std::stoi(stackStr.before(stackSzEnd).c_str()); + expr = new IR::Member(expr, subStr.before(hdrBrkt)); + auto *tb = IR::Type_Bits::get(3, false); + IR::Expression *idx = genExpression(tb); + auto *args = new IR::Vector(); + args->push_back(new IR::Argument(idx)); + args->push_back(new IR::Argument(new IR::Constant(tb, stackSz))); + idx = new IR::MethodCallExpression(new IR::PathExpression("max"), args); + + expr = new IR::ArrayIndex(expr, idx); + } else { + expr = new IR::Member(expr, subStr); + } + } + return expr; +} + +IR::Expression *ExpressionGenerator::pickLvalOrSlice(const IR::Type *tp) { + cstring lvalStr = P4Scope::pickLval(tp, true); + IR::Expression *expr = editHdrStack(lvalStr); + + if (const auto *tb = tp->to()) { + std::vector percent = {PCT.SCOPE_LVAL_PATH, PCT.SCOPE_LVAL_SLICE}; + switch (Utils::getRandInt(percent)) { + case 0: { + break; + } + case 1: { + auto keyTypesOpt = + P4Scope::getWriteableLvalForTypeKey(IR::Type_Bits::static_type_name()); + if (!keyTypesOpt) { + break; + } + auto keyTypes = keyTypesOpt.value(); + size_t targetWidth = tb->width_bits(); + + std::vector> candidates; + + for (const auto &bitBucket : keyTypes) { + size_t keySize = bitBucket.first; + if (keySize > targetWidth) { + for (cstring lval : keyTypes[keySize]) { + candidates.emplace_back(keySize, lval); + } + } + } + if (candidates.empty()) { + break; + } + size_t idx = Utils::getRandInt(0, candidates.size() - 1); + auto lval = std::begin(candidates); + // "advance" the iterator idx times + std::advance(lval, idx); + size_t candidateSize = lval->first; + auto *sliceExpr = new IR::PathExpression(lval->second); + size_t low = Utils::getRandInt(0, candidateSize - targetWidth); + size_t high = low + targetWidth - 1; + expr = new IR::Slice(sliceExpr, high, low); + } break; + } + } + return expr; +} + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/common/expressions.h b/backends/p4tools/modules/smith/common/expressions.h new file mode 100644 index 00000000000..c6104b1fca1 --- /dev/null +++ b/backends/p4tools/modules/smith/common/expressions.h @@ -0,0 +1,119 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_EXPRESSIONS_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_EXPRESSIONS_H_ + +#include +#include +#include + +#include "backends/p4tools/modules/smith/common/generator.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" +#include "ir/vector.h" +#include "lib/cstring.h" + +namespace P4Tools::P4Smith { + +using TyperefProbs = struct TyperefProbs { + int64_t p4_bit; + int64_t p4_signed_bit; + int64_t p4_varbit; + int64_t p4_int; + int64_t p4_error; + int64_t p4_bool; + int64_t p4_string; + // derived types + int64_t p4_enum; + int64_t p4_header; + int64_t p4_header_stack; + int64_t p4_struct; + int64_t p4_header_union; + int64_t p4_tuple; + int64_t p4_void; + int64_t p4_match_kind; +}; + +class ExpressionGenerator : public Generator { + public: + virtual ~ExpressionGenerator() = default; + explicit ExpressionGenerator(const SmithTarget &target) : Generator(target) {} + + static constexpr size_t MAX_DEPTH = 3; + + static constexpr int BIT_WIDTHS[6] = {4, 8, 16, 32, 64, 128}; + + static const IR::Type_Boolean *genBoolType(); + + static const IR::Type_InfInt *genIntType(); + + // isSigned, true -> int<>, false -> bit<> + static const IR::Type_Bits *genBitType(bool isSigned); + + static const IR::Type *pickRndBaseType(const std::vector &type_probs); + + virtual const IR::Type *pickRndType(TyperefProbs type_probs); + + static IR::BoolLiteral *genBoolLiteral(); + + static IR::Constant *genIntLiteral(size_t bit_width = INTEGER_WIDTH); + + static IR::Constant *genBitLiteral(const IR::Type *tb); + + private: + IR::Expression *constructUnaryExpr(const IR::Type_Bits *tb); + + IR::Expression *createSaturationOperand(const IR::Type_Bits *tb); + + IR::Expression *constructBinaryBitExpr(const IR::Type_Bits *tb); + + IR::Expression *constructTernaryBitExpr(const IR::Type_Bits *tb); + + public: + virtual IR::Expression *pickBitVar(const IR::Type_Bits *tb); + + virtual IR::Expression *constructBitExpr(const IR::Type_Bits *tb); + + private: + IR::Expression *constructCmpExpr(); + + public: + virtual IR::Expression *constructBooleanExpr(); + + private: + IR::Expression *constructUnaryIntExpr(); + + IR::Expression *constructBinaryIntExpr(); + + static IR::Expression *pickIntVar(); + + public: + IR::Expression *constructIntExpr(); + + private: + IR::ListExpression *genStructListExpr(const IR::Type_Name *tn); + + IR::Expression *editHdrStack(cstring lval); + + public: + virtual IR::Expression *constructStructExpr(const IR::Type_Name *tn); + + virtual IR::MethodCallExpression *genFunctionCall(cstring method_name, + IR::ParameterList params); + + virtual IR::MethodCallExpression *pickFunction( + IR::IndexedVector viable_functions, const IR::Type **ret_type); + + virtual IR::Expression *genExpression(const IR::Type *tp); + + virtual IR::ListExpression *genExpressionList(IR::Vector types, bool only_lval); + + virtual IR::Expression *genInputArg(const IR::Parameter *param); + + virtual IR::Expression *pickLvalOrSlice(const IR::Type *tp); + + virtual bool checkInputArg(const IR::Parameter *param); +}; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_EXPRESSIONS_H_ */ diff --git a/backends/p4tools/modules/smith/common/generator.h b/backends/p4tools/modules/smith/common/generator.h new file mode 100644 index 00000000000..37abd6ff111 --- /dev/null +++ b/backends/p4tools/modules/smith/common/generator.h @@ -0,0 +1,20 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_GENERATOR_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_GENERATOR_H_ + +#include + +namespace P4Tools::P4Smith { + +class SmithTarget; + +class Generator { + std::reference_wrapper _target; + + public: + explicit Generator(const SmithTarget &target) : _target(target) {} + + const SmithTarget &target() { return _target; } +}; +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_GENERATOR_H_ */ diff --git a/backends/p4tools/modules/smith/common/parser.cpp b/backends/p4tools/modules/smith/common/parser.cpp new file mode 100644 index 00000000000..4e140b69850 --- /dev/null +++ b/backends/p4tools/modules/smith/common/parser.cpp @@ -0,0 +1,271 @@ +#include "backends/p4tools/modules/smith/common/parser.h" + +#include +#include +#include +#include + +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/vector.h" +#include "lib/big_int_util.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" + +namespace P4Tools::P4Smith { + +IR::MethodCallStatement *ParserGenerator::genHdrExtract(IR::Member *pkt_call, IR::Expression *mem) { + auto *args = new IR::Vector(); + auto *arg = new IR::Argument(mem); + + args->push_back(arg); + auto *mce = new IR::MethodCallExpression(pkt_call, args); + return new IR::MethodCallStatement(mce); +} + +void ParserGenerator::genHdrUnionExtract(IR::IndexedVector &components, + const IR::Type_HeaderUnion *hdru, IR::ArrayIndex *arr_ind, + IR::Member *pkt_call) { + const auto *sf = hdru->fields.at(0); + // for (auto sf : hdru->fields) { + // auto mem = new IR::Member(arr_ind, + // sf->type->to()->path->name); + auto *mem = new IR::Member(arr_ind, sf->name); + + components.push_back(genHdrExtract(pkt_call, mem)); + // } +} + +IR::ParserState *ParserGenerator::genStartState() { + IR::IndexedVector components; + IR::Expression *transition = new IR::PathExpression("parse_hdrs"); + auto *ret = new IR::ParserState("start", components, transition); + + P4Scope::addToScope(ret); + return ret; +} + +IR::ParserState *ParserGenerator::genHdrStates() { + IR::Expression *transition = nullptr; + IR::IndexedVector components; + std::vector hdrFieldsNames; + std::map hdrFieldsTypes; + + const auto *sysHdrType = P4Scope::getTypeByName(SYS_HDR_NAME); + const auto *sysHdr = sysHdrType->to(); + if (sysHdr == nullptr) { + BUG("Unexpected system header %s", sysHdrType->static_type_name()); + } + for (const auto *sf : sysHdr->fields) { + hdrFieldsNames.push_back(sf->name.name); + hdrFieldsTypes.emplace(sf->name.name, sf->type); + } + + auto *pktCall = new IR::Member(new IR::PathExpression("pkt"), "extract"); + for (auto sfName : hdrFieldsNames) { + const auto *sfType = hdrFieldsTypes[sfName]; + if (const auto *sfTpS = sfType->to()) { + auto *mem = new IR::Member(new IR::PathExpression("hdr"), sfName); + size_t size = sfTpS->getSize(); + const auto *eleTpName = sfTpS->elementType; + const auto *eleTp = + P4Scope::getTypeByName(eleTpName->to()->path->name.name); + if (eleTp->is()) { + for (size_t j = 0; j < size; j++) { + auto *nextMem = new IR::Member(mem, "next"); + components.push_back(genHdrExtract(pktCall, nextMem)); + } + } else if (const auto *hdruTp = eleTp->to()) { + for (size_t j = 0; j < size; j++) { + auto *arrInd = + new IR::ArrayIndex(mem, new IR::Constant(IR::Type_InfInt::get(), j)); + genHdrUnionExtract(components, hdruTp, arrInd, pktCall); + } + } else { + BUG("wtf here %s", sfType->node_type_name()); + } + } else if (sfType->is()) { + auto *mem = new IR::Member(new IR::PathExpression("hdr"), sfName); + const auto *hdrFieldTp = + P4Scope::getTypeByName(sfType->to()->path->name.name); + if (hdrFieldTp->is()) { + const auto *hdruTp = hdrFieldTp->to(); + const auto *sf = hdruTp->fields.at(0); + auto *hdrMem = new IR::Member(mem, sf->name); + components.push_back(genHdrExtract(pktCall, hdrMem)); + } else { + components.push_back(genHdrExtract(pktCall, mem)); + } + } else { + BUG("wtf here %s", sfType->node_type_name()); + } + } + + // transition part + // transition = new IR::PathExpression(new IR::Path(IR::ID("state_0"))); + /* cstring next_state = getRandomString(6); + genState(next_state); + transition = new IR::PathExpression(next_state);*/ + + transition = new IR::PathExpression("accept"); + + auto *ret = new IR::ParserState("parse_hdrs", components, transition); + P4Scope::addToScope(ret); + return ret; +} + +IR::ListExpression *ParserGenerator::buildMatchExpr(IR::Vector types) { + IR::Vector components; + for (const auto *tb : types) { + IR::Expression *expr = nullptr; + switch (Utils::getRandInt(0, 2)) { + case 0: { + // TODO(fruffy): Figure out allowed expressions + // if (P4Scope::checkLval(tb)) { + // cstring lval_name = P4Scope::pickLval(tb); + // expr = new IR::PathExpression(lval_name); + // } else { + // expr = target().expressionGenerator().genBitLiteral(tb); + // } + expr = target().expressionGenerator().genBitLiteral(tb); + break; + } + case 1: { + // Range + big_int maxRange = big_int(1U) << tb->width_bits(); + // FIXME: disable large ranges for now + maxRange = min(big_int(1U) << 32, maxRange); + big_int lower = Utils::getRandBigInt(0, maxRange - 1); + big_int higher = Utils::getRandBigInt(lower, maxRange - 1); + auto *lowerExpr = new IR::Constant(lower); + auto *higherExpr = new IR::Constant(higher); + expr = new IR::Range(tb, lowerExpr, higherExpr); + break; + } + case 2: { + // Mask + auto *left = target().expressionGenerator().genBitLiteral(tb); + auto *right = target().expressionGenerator().genBitLiteral(tb); + expr = new IR::Mask(tb, left, right); + break; + } + } + components.push_back(expr); + } + return new IR::ListExpression(components); +} + +void ParserGenerator::genState(cstring name) { + IR::IndexedVector components; + + P4Scope::startLocalScope(); + + // variable decls + for (int i = 0; i < 5; i++) { + auto *varDecl = target().declarationGenerator().genVariableDeclaration(); + components.push_back(varDecl); + } + // statements + for (int i = 0; i < 5; i++) { + auto *ass = target().statementGenerator().genAssignmentStatement(); + if (ass != nullptr) { + components.push_back(ass); + } + break; + } + + // expression + IR::Expression *transition = nullptr; + + std::vector percent = {PCT.P4STATE_TRANSITION_ACCEPT, PCT.P4STATE_TRANSITION_REJECT, + PCT.P4STATE_TRANSITION_STATE, PCT.P4STATE_TRANSITION_SELECT}; + + P4Scope::endLocalScope(); + switch (Utils::getRandInt(percent)) { + case 0: { + transition = new IR::PathExpression("accept"); + break; + } + case 1: { + transition = new IR::PathExpression("reject"); + break; + } + case 2: { + cstring nextState = getRandomString(6); + genState(nextState); + transition = new IR::PathExpression(nextState); + break; + } + case 3: { + IR::Vector exprs; + IR::Vector cases; + size_t numTransitions = Utils::getRandInt(1, 3); + size_t keySetLen = Utils::getRandInt(1, 4); + + IR::Vector types; + for (size_t i = 0; i <= keySetLen; i++) { + auto *tb = ExpressionGenerator::genBitType(false); + types.push_back(tb); + } + + for (size_t i = 0; i < numTransitions; i++) { + IR::Expression *matchSet = nullptr; + // TODO(fruffy): Do !always have a default + if (i == (numTransitions - 1)) { + P4Scope::req.compile_time_known = true; + matchSet = buildMatchExpr(types); + P4Scope::req.compile_time_known = false; + } else { + matchSet = new IR::DefaultExpression(); + } + switch (Utils::getRandInt(0, 2)) { + case 0: { + cases.push_back( + new IR::SelectCase(matchSet, new IR::PathExpression("accept"))); + break; + } + case 1: { + cases.push_back( + new IR::SelectCase(matchSet, new IR::PathExpression("reject"))); + break; + } + case 2: { + cstring nextState = getRandomString(6); + genState(nextState); + auto *swCase = + new IR::SelectCase(matchSet, new IR::PathExpression(nextState)); + cases.push_back(swCase); + break; + } + } + } + P4Scope::req.require_scalar = true; + IR::ListExpression *keySet = + target().expressionGenerator().genExpressionList(types, false); + P4Scope::req.require_scalar = false; + transition = new IR::SelectExpression(keySet, cases); + break; + } + } + + // add to scope + auto *ret = new IR::ParserState(name, components, transition); + P4Scope::addToScope(ret); + state_list.push_back(ret); +} + +void ParserGenerator::buildParserTree() { + state_list.push_back(ParserGenerator::genStartState()); + state_list.push_back(ParserGenerator::genHdrStates()); +} + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/common/parser.h b/backends/p4tools/modules/smith/common/parser.h new file mode 100644 index 00000000000..59da5007ce5 --- /dev/null +++ b/backends/p4tools/modules/smith/common/parser.h @@ -0,0 +1,35 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_PARSER_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_PARSER_H_ + +#include "backends/p4tools/modules/smith/common/generator.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" +#include "ir/vector.h" +#include "lib/cstring.h" + +namespace P4Tools::P4Smith { + +class ParserGenerator : public Generator { + private: + IR::IndexedVector state_list; + + public: + virtual ~ParserGenerator() = default; + explicit ParserGenerator(const SmithTarget &target) : Generator(target) {} + + virtual IR::MethodCallStatement *genHdrExtract(IR::Member *pkt_call, IR::Expression *mem); + virtual void genHdrUnionExtract(IR::IndexedVector &components, + const IR::Type_HeaderUnion *hdru, IR::ArrayIndex *arr_ind, + IR::Member *pkt_call); + virtual IR::ListExpression *buildMatchExpr(IR::Vector types); + virtual IR::ParserState *genStartState(); + virtual IR::ParserState *genHdrStates(); + virtual void genState(cstring name); + virtual void buildParserTree(); + + [[nodiscard]] IR::IndexedVector getStates() const { return state_list; } +}; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_PARSER_H_ */ diff --git a/backends/p4tools/modules/smith/common/probabilities.h b/backends/p4tools/modules/smith/common/probabilities.h new file mode 100644 index 00000000000..ac0b40a2268 --- /dev/null +++ b/backends/p4tools/modules/smith/common/probabilities.h @@ -0,0 +1,328 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_PROBABILITIES_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_PROBABILITIES_H_ + +#include + +static struct Probabilities { + // assignment or method call + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_ASSIGN = 75; + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_CALL = 25; + // probabilities of assignment types + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_ASSIGN_BIT = 100; + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_ASSIGN_STRUCTLIKE = 0; + // probabilities of method calls + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_ACTION = 44; + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_FUNCTION = 45; + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_TABLE = 10; + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_CTRL = 5; + uint16_t ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_BUILT_IN = 1; + + // probabilities for statements + uint16_t STATEMENT_SWITCH = 5; + uint16_t STATEMENT_ASSIGNMENTORMETHODCALL = 85; + uint16_t STATEMENT_IF = 3; + uint16_t STATEMENT_RETURN = 2; + uint16_t STATEMENT_EXIT = 0; + uint16_t STATEMENT_BLOCK = 2; + + // probabilities to pick a slice when requesting an lval + uint16_t SCOPE_LVAL_PATH = 90; + uint16_t SCOPE_LVAL_SLICE = 10; + + // probabilities for statements or declarations + uint16_t STATEMENTORDECLARATION_VAR = 10; + uint16_t STATEMENTORDECLARATION_CONSTANT = 10; + uint16_t STATEMENTORDECLARATION_STATEMENT = 80; + + // probabilities for unary bit expressions + uint16_t EXPRESSION_BIT_UNARY_NEG = 20; + uint16_t EXPRESSION_BIT_UNARY_CMPL = 20; + uint16_t EXPRESSION_BIT_UNARY_CAST = 10; + uint16_t EXPRESSION_BIT_UNARY_FUNCTION = 50; + // probabilities for binary bit expressions + uint16_t EXPRESSION_BIT_BINARY_MUL = 5; + uint16_t EXPRESSION_BIT_BINARY_DIV = 5; + uint16_t EXPRESSION_BIT_BINARY_MOD = 5; + uint16_t EXPRESSION_BIT_BINARY_ADD = 10; + uint16_t EXPRESSION_BIT_BINARY_SUB = 10; + uint16_t EXPRESSION_BIT_BINARY_ADDSAT = 10; + uint16_t EXPRESSION_BIT_BINARY_SUBSAT = 10; + uint16_t EXPRESSION_BIT_BINARY_LSHIFT = 5; + uint16_t EXPRESSION_BIT_BINARY_RSHIFT = 5; + uint16_t EXPRESSION_BIT_BINARY_BAND = 10; + uint16_t EXPRESSION_BIT_BINARY_BOR = 10; + uint16_t EXPRESSION_BIT_BINARY_BXOR = 10; + uint16_t EXPRESSION_BIT_BINARY_CONCAT = 5; + // probabilities for ternary bit expressions + uint16_t EXPRESSION_BIT_BINARY_SLICE = 50; + uint16_t EXPRESSION_BIT_BINARY_MUX = 50; + // probabilities for bit expressions + uint16_t EXPRESSION_BIT_VAR = 20; + uint16_t EXPRESSION_BIT_INT_LITERAL = 5; + uint16_t EXPRESSION_BIT_BIT_LITERAL = 25; + uint16_t EXPRESSION_BIT_UNARY = 10; + uint16_t EXPRESSION_BIT_BINARY = 20; + uint16_t EXPRESSION_BIT_TERNARY = 10; + + // probabilities for unary int expressions + uint16_t EXPRESSION_INT_UNARY_NEG = 20; + uint16_t EXPRESSION_INT_UNARY_CMPL = 20; + uint16_t EXPRESSION_INT_UNARY_FUNCTION = 50; + // probabilities for binary int expressions + uint16_t EXPRESSION_INT_BINARY_MUL = 5; + uint16_t EXPRESSION_INT_BINARY_DIV = 5; + uint16_t EXPRESSION_INT_BINARY_MOD = 5; + uint16_t EXPRESSION_INT_BINARY_ADD = 10; + uint16_t EXPRESSION_INT_BINARY_SUB = 10; + uint16_t EXPRESSION_INT_BINARY_LSHIFT = 0; + uint16_t EXPRESSION_INT_BINARY_RSHIFT = 0; + uint16_t EXPRESSION_INT_BINARY_BAND = 10; + uint16_t EXPRESSION_INT_BINARY_BOR = 10; + uint16_t EXPRESSION_INT_BINARY_BXOR = 10; + // probabilities for ternary int expressions + uint16_t EXPRESSION_INT_BINARY_MUX = 50; + // probabilities for int expressions + uint16_t EXPRESSION_INT_VAR = 20; + uint16_t EXPRESSION_INT_INT_LITERAL = 5; + uint16_t EXPRESSION_INT_UNARY = 10; + uint16_t EXPRESSION_INT_BINARY = 20; + + // probabilities for boolean expressions + uint16_t EXPRESSION_BOOLEAN_VAR = 15; + uint16_t EXPRESSION_BOOLEAN_LITERAL = 20; + uint16_t EXPRESSION_BOOLEAN_NOT = 35; + uint16_t EXPRESSION_BOOLEAN_LAND = 5; + uint16_t EXPRESSION_BOOLEAN_LOR = 5; + uint16_t EXPRESSION_BOOLEAN_CMP = 10; + uint16_t EXPRESSION_BOOLEAN_FUNCTION = 5; + uint16_t EXPRESSION_BOOLEAN_BUILT_IN = 5; + // probabilities for comparisons + uint16_t EXPRESSION_BOOLEAN_CMP_EQU = 50; + uint16_t EXPRESSION_BOOLEAN_CMP_NEQ = 50; + + // probabilities for structlike expressions + uint16_t EXPRESSION_STRUCT_VAR = 50; + uint16_t EXPRESSION_STRUCT_LITERAL = 30; + uint16_t EXPRESSION_STRUCT_FUNCTION = 20; + + // probabilities for state transitions in the parser + uint16_t P4STATE_TRANSITION_ACCEPT = 50; + uint16_t P4STATE_TRANSITION_REJECT = 10; + uint16_t P4STATE_TRANSITION_STATE = 30; + uint16_t P4STATE_TRANSITION_SELECT = 10; + + uint16_t BASETYPE_BIT = 50; + uint16_t BASETYPE_SIGNED_BIT = 0; + uint16_t BASETYPE_VARBIT = 0; + uint16_t BASETYPE_INT = 10; + uint16_t BASETYPE_ERROR = 0; + uint16_t BASETYPE_BOOL = 10; + uint16_t BASETYPE_STRING = 0; + uint16_t DERIVED_ENUM = 0; + uint16_t DERIVED_HEADER = 5; + uint16_t DERIVED_HEADER_STACK = 2; + uint16_t DERIVED_STRUCT = 5; + uint16_t DERIVED_HEADER_UNION = 0; + uint16_t DERIVED_TUPLE = 0; + uint16_t TYPE_VOID = 0; + uint16_t TYPE_MATCH_KIND = 0; + + // probabilities for the types of constant declarations + uint16_t CONSTANTDECLARATION_TYPE_BASE = 80; + uint16_t CONSTANTDECLARATION_TYPE_STRUCT = 0; + // probabilities for the base types in constant declarations + uint16_t CONSTANTDECLARATION_BASETYPE_BIT = BASETYPE_BIT; + uint16_t CONSTANTDECLARATION_BASETYPE_SIGNED_BIT = BASETYPE_SIGNED_BIT; + uint16_t CONSTANTDECLARATION_BASETYPE_VARBIT = BASETYPE_VARBIT; + uint16_t CONSTANTDECLARATION_BASETYPE_INT = BASETYPE_INT; + uint16_t CONSTANTDECLARATION_BASETYPE_ERROR = BASETYPE_ERROR; + uint16_t CONSTANTDECLARATION_BASETYPE_BOOL = BASETYPE_BOOL; + uint16_t CONSTANTDECLARATION_BASETYPE_STRING = BASETYPE_STRING; + uint16_t CONSTANTDECLARATION_DERIVED_ENUM = DERIVED_ENUM; + uint16_t CONSTANTDECLARATION_DERIVED_HEADER = DERIVED_HEADER; + uint16_t CONSTANTDECLARATION_DERIVED_HEADER_STACK = 0; + uint16_t CONSTANTDECLARATION_DERIVED_STRUCT = DERIVED_STRUCT; + uint16_t CONSTANTDECLARATION_DERIVED_HEADER_UNION = DERIVED_HEADER_UNION; + uint16_t CONSTANTDECLARATION_DERIVED_TUPLE = DERIVED_TUPLE; + uint16_t CONSTANTDECLARATION_TYPE_VOID = TYPE_VOID; + uint16_t CONSTANTDECLARATION_TYPE_MATCH_KIND = TYPE_MATCH_KIND; + + // probabilities for the function return types + uint16_t FUNCTIONDECLARATION_TYPE_BASE = 90; + uint16_t FUNCTIONDECLARATION_TYPE_STRUCT = 9; + // probabilities for the base types in function declarations + uint16_t FUNCTIONDECLARATION_BASETYPE_BIT = BASETYPE_BIT; + uint16_t FUNCTIONDECLARATION_BASETYPE_SIGNED_BIT = BASETYPE_SIGNED_BIT; + uint16_t FUNCTIONDECLARATION_BASETYPE_VARBIT = BASETYPE_VARBIT; + uint16_t FUNCTIONDECLARATION_BASETYPE_INT = 0; + uint16_t FUNCTIONDECLARATION_BASETYPE_ERROR = BASETYPE_ERROR; + uint16_t FUNCTIONDECLARATION_BASETYPE_BOOL = BASETYPE_BOOL; + uint16_t FUNCTIONDECLARATION_BASETYPE_STRING = BASETYPE_STRING; + uint16_t FUNCTIONDECLARATION_DERIVED_ENUM = DERIVED_ENUM; + uint16_t FUNCTIONDECLARATION_DERIVED_HEADER = DERIVED_HEADER; + // there is no support yet for header stack initialization + uint16_t FUNCTIONDECLARATION_DERIVED_HEADER_STACK = 0; + uint16_t FUNCTIONDECLARATION_DERIVED_STRUCT = DERIVED_STRUCT; + uint16_t FUNCTIONDECLARATION_DERIVED_HEADER_UNION = DERIVED_HEADER_UNION; + uint16_t FUNCTIONDECLARATION_DERIVED_TUPLE = DERIVED_TUPLE; + uint16_t FUNCTIONDECLARATION_TYPE_VOID = 1; + uint16_t FUNCTIONDECLARATION_TYPE_MATCH_KIND = TYPE_MATCH_KIND; + + // probabilities for types in header structures + uint16_t HEADERTYPEDECLARATION_FIELD_BASE = 100; + uint16_t HEADERTYPEDECLARATION_FIELD_STRUCT = 0; + // probabilities for the base types in header type declarations + uint16_t HEADERTYPEDECLARATION_BASETYPE_BIT = BASETYPE_BIT; + uint16_t HEADERTYPEDECLARATION_BASETYPE_SIGNED_BIT = BASETYPE_SIGNED_BIT; + uint16_t HEADERTYPEDECLARATION_BASETYPE_VARBIT = BASETYPE_VARBIT; + uint16_t HEADERTYPEDECLARATION_BASETYPE_INT = 0; + uint16_t HEADERTYPEDECLARATION_BASETYPE_ERROR = BASETYPE_ERROR; + uint16_t HEADERTYPEDECLARATION_BASETYPE_BOOL = 0; + uint16_t HEADERTYPEDECLARATION_BASETYPE_STRING = BASETYPE_STRING; + uint16_t HEADERTYPEDECLARATION_DERIVED_ENUM = DERIVED_ENUM; + uint16_t HEADERTYPEDECLARATION_DERIVED_HEADER = 0; + uint16_t HEADERTYPEDECLARATION_DERIVED_HEADER_STACK = 0; + // FIXME: Headers should be able to have structs? + uint16_t HEADERTYPEDECLARATION_DERIVED_STRUCT = 0; + uint16_t HEADERTYPEDECLARATION_DERIVED_HEADER_UNION = 0; + uint16_t HEADERTYPEDECLARATION_DERIVED_TUPLE = 0; + uint16_t HEADERTYPEDECLARATION_TYPE_VOID = 0; + uint16_t HEADERTYPEDECLARATION_TYPE_MATCH_KIND = 0; + + // probabilities for the parameter direction + uint16_t PARAMETER_DIR_IN = 33; + uint16_t PARAMETER_DIR_OUT = 33; + uint16_t PARAMETER_DIR_INOUT = 33; + // probabilities for the base types in parameter declarations + uint16_t PARAMETER_BASETYPE_BIT = BASETYPE_BIT; + uint16_t PARAMETER_BASETYPE_SIGNED_BIT = BASETYPE_SIGNED_BIT; + uint16_t PARAMETER_BASETYPE_VARBIT = BASETYPE_VARBIT; + uint16_t PARAMETER_BASETYPE_INT = 0; + uint16_t PARAMETER_BASETYPE_ERROR = BASETYPE_ERROR; + uint16_t PARAMETER_BASETYPE_BOOL = 0; + uint16_t PARAMETER_BASETYPE_STRING = BASETYPE_STRING; + uint16_t PARAMETER_DERIVED_ENUM = DERIVED_ENUM; + uint16_t PARAMETER_DERIVED_HEADER = DERIVED_HEADER; + uint16_t PARAMETER_DERIVED_HEADER_STACK = 0; + uint16_t PARAMETER_DERIVED_STRUCT = DERIVED_STRUCT; + uint16_t PARAMETER_DERIVED_HEADER_UNION = DERIVED_HEADER_UNION; + uint16_t PARAMETER_DERIVED_TUPLE = DERIVED_TUPLE; + uint16_t PARAMETER_TYPE_VOID = TYPE_VOID; + uint16_t PARAMETER_TYPE_MATCH_KIND = TYPE_MATCH_KIND; + // probabilities for the directionless base types in parameter declarations + // the value is coming from the control, back ends can adjust these probs + uint16_t PARAMETER_NONEDIR_BASETYPE_BIT = PARAMETER_BASETYPE_BIT; + uint16_t PARAMETER_NONEDIR_BASETYPE_SIGNED_BIT = PARAMETER_BASETYPE_SIGNED_BIT; + uint16_t PARAMETER_NONEDIR_BASETYPE_VARBIT = PARAMETER_BASETYPE_VARBIT; + uint16_t PARAMETER_NONEDIR_BASETYPE_INT = PARAMETER_BASETYPE_INT; + uint16_t PARAMETER_NONEDIR_BASETYPE_ERROR = PARAMETER_BASETYPE_ERROR; + uint16_t PARAMETER_NONEDIR_BASETYPE_BOOL = PARAMETER_BASETYPE_BOOL; + uint16_t PARAMETER_NONEDIR_BASETYPE_STRING = PARAMETER_BASETYPE_STRING; + uint16_t PARAMETER_NONEDIR_DERIVED_ENUM = PARAMETER_DERIVED_ENUM; + uint16_t PARAMETER_NONEDIR_DERIVED_HEADER = PARAMETER_DERIVED_HEADER; + uint16_t PARAMETER_NONEDIR_DERIVED_HEADER_STACK = PARAMETER_DERIVED_HEADER_STACK; + uint16_t PARAMETER_NONEDIR_DERIVED_HEADER_UNION = DERIVED_HEADER_UNION; + uint16_t PARAMETER_NONEDIR_DERIVED_STRUCT = DERIVED_STRUCT; + uint16_t PARAMETER_NONEDIR_DERIVED_TUPLE = DERIVED_TUPLE; + uint16_t PARAMETER_NONEDIR_TYPE_VOID = TYPE_VOID; + uint16_t PARAMETER_NONEDIR_TYPE_MATCH_KIND = TYPE_MATCH_KIND; + + // probabilities for types in struct structures + uint16_t STRUCTTYPEDECLARATION_FIELD_BASE = 75; + uint16_t STRUCTTYPEDECLARATION_FIELD_STRUCT = 25; + uint16_t STRUCTTYPEDECLARATION_FIELD_STACK = 0; + // probabilities for the headers struct type + uint16_t STRUCTTYPEDECLARATION_HEADERS_HEADER = 90; + uint16_t STRUCTTYPEDECLARATION_HEADERS_STACK = 10; + // probabilities for the base types in struct type declarations + uint16_t STRUCTTYPEDECLARATION_BASETYPE_BIT = BASETYPE_BIT; + uint16_t STRUCTTYPEDECLARATION_BASETYPE_SIGNED_BIT = BASETYPE_SIGNED_BIT; + uint16_t STRUCTTYPEDECLARATION_BASETYPE_VARBIT = BASETYPE_VARBIT; + uint16_t STRUCTTYPEDECLARATION_BASETYPE_INT = 0; + uint16_t STRUCTTYPEDECLARATION_BASETYPE_ERROR = BASETYPE_ERROR; + uint16_t STRUCTTYPEDECLARATION_BASETYPE_BOOL = BASETYPE_BOOL; + uint16_t STRUCTTYPEDECLARATION_BASETYPE_STRING = BASETYPE_STRING; + uint16_t STRUCTTYPEDECLARATION_DERIVED_ENUM = DERIVED_ENUM; + uint16_t STRUCTTYPEDECLARATION_DERIVED_HEADER = DERIVED_HEADER; + uint16_t STRUCTTYPEDECLARATION_DERIVED_HEADER_STACK = DERIVED_HEADER_STACK; + uint16_t STRUCTTYPEDECLARATION_DERIVED_STRUCT = DERIVED_STRUCT; + uint16_t STRUCTTYPEDECLARATION_DERIVED_HEADER_UNION = DERIVED_HEADER_UNION; + uint16_t STRUCTTYPEDECLARATION_DERIVED_TUPLE = DERIVED_TUPLE; + uint16_t STRUCTTYPEDECLARATION_TYPE_VOID = 0; + uint16_t STRUCTTYPEDECLARATION_TYPE_MATCH_KIND = 0; + // probabilities for types in struct structures + uint16_t TYPEDECLARATION_HEADER = 75; + uint16_t TYPEDECLARATION_STRUCT = 25; + uint16_t TYPEDECLARATION_UNION = 0; + + // probabilities for types in struct structures + uint16_t TYPEDEFDECLARATION_BASE = 75; + uint16_t TYPEDEFDECLARATION_STRUCTLIKE = 25; + uint16_t TYPEDEFDECLARATION_STACK = 0; + // probabilities for the base types in typedef declarations + uint16_t TYPEDEFDECLARATION_BASETYPE_BIT = BASETYPE_BIT; + uint16_t TYPEDEFDECLARATION_BASETYPE_SIGNED_BIT = BASETYPE_SIGNED_BIT; + uint16_t TYPEDEFDECLARATION_BASETYPE_VARBIT = BASETYPE_VARBIT; + uint16_t TYPEDEFDECLARATION_BASETYPE_INT = 0; + uint16_t TYPEDEFDECLARATION_BASETYPE_ERROR = BASETYPE_ERROR; + uint16_t TYPEDEFDECLARATION_BASETYPE_BOOL = 0; + uint16_t TYPEDEFDECLARATION_BASETYPE_STRING = BASETYPE_STRING; + uint16_t TYPEDEFDECLARATION_DERIVED_ENUM = DERIVED_ENUM; + uint16_t TYPEDEFDECLARATION_DERIVED_HEADER = DERIVED_HEADER; + uint16_t TYPEDEFDECLARATION_DERIVED_HEADER_STACK = DERIVED_HEADER_STACK; + uint16_t TYPEDEFDECLARATION_STRUCT = DERIVED_STRUCT; + uint16_t TYPEDEFDECLARATION_HEADER_UNION = DERIVED_HEADER_UNION; + uint16_t TYPEDEFDECLARATION_TUPLE = DERIVED_TUPLE; + uint16_t TYPEDEFDECLARATION_TYPE_VOID = TYPE_VOID; + uint16_t TYPEDEFDECLARATION_TYPE_MATCH_KIND = TYPE_MATCH_KIND; + + // probabilities for the types of constant declarations + uint16_t VARIABLEDECLARATION_TYPE_BASE = 80; + uint16_t VARIABLEDECLARATION_TYPE_STRUCT = 15; + uint16_t VARIABLEDECLARATION_TYPE_STACK = 5; + // probabilities for the base types in variable declarations + uint16_t VARIABLEDECLARATION_BASETYPE_BIT = BASETYPE_BIT; + uint16_t VARIABLEDECLARATION_BASETYPE_SIGNED_BIT = BASETYPE_SIGNED_BIT; + uint16_t VARIABLEDECLARATION_BASETYPE_VARBIT = BASETYPE_VARBIT; + uint16_t VARIABLEDECLARATION_BASETYPE_INT = 0; + uint16_t VARIABLEDECLARATION_BASETYPE_ERROR = BASETYPE_ERROR; + uint16_t VARIABLEDECLARATION_BASETYPE_BOOL = BASETYPE_BOOL; + uint16_t VARIABLEDECLARATION_BASETYPE_STRING = BASETYPE_STRING; + uint16_t VARIABLEDECLARATION_DERIVED_ENUM = DERIVED_ENUM; + uint16_t VARIABLEDECLARATION_DERIVED_HEADER = DERIVED_HEADER; + uint16_t VARIABLEDECLARATION_DERIVED_HEADER_STACK = DERIVED_HEADER_STACK; + uint16_t VARIABLEDECLARATION_DERIVED_STRUCT = DERIVED_STRUCT; + uint16_t VARIABLEDECLARATION_DERIVED_HEADER_UNION = DERIVED_HEADER_UNION; + uint16_t VARIABLEDECLARATION_DERIVED_TUPLE = DERIVED_TUPLE; + uint16_t VARIABLEDECLARATION_TYPE_VOID = TYPE_VOID; + uint16_t VARIABLEDECLARATION_TYPE_MATCH_KIND = TYPE_MATCH_KIND; +} PCT; + +static struct Declarations { + // minimum and maximum number of type declarations + uint16_t MIN_TYPE = 1; + uint16_t MAX_TYPE = 8; + + // minimum and maximum number of statements in a block statement + uint16_t BLOCKSTATEMENT_MIN_STAT = 3; + uint16_t BLOCKSTATEMENT_MAX_STAT = 10; + + // minimum and maximum number of callable declarations + uint16_t MIN_CALLABLES = 0; + uint16_t MAX_CALLABLES = 4; + + // minimum and maximum variable declarations + uint16_t MIN_VAR = 0; + uint16_t MAX_VAR = 5; + + uint16_t MIN_INSTANCE = 0; + uint16_t MAX_INSTANCE = 2; + + uint16_t MIN_ACTION = 0; + uint16_t MAX_ACTION = 2; + + uint16_t MIN_TABLE = 0; + uint16_t MAX_TABLE = 3; +} DECL; + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_PROBABILITIES_H_ */ diff --git a/backends/p4tools/modules/smith/common/scope.cpp b/backends/p4tools/modules/smith/common/scope.cpp new file mode 100644 index 00000000000..b5c38cd8b86 --- /dev/null +++ b/backends/p4tools/modules/smith/common/scope.cpp @@ -0,0 +1,342 @@ +#include "backends/p4tools/modules/smith/common/scope.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/logging.h" +#include "backends/p4tools/common/lib/util.h" +#include "ir/node.h" +#include "ir/vector.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" + +namespace P4Tools::P4Smith { + +std::vector *> P4Scope::scope; +std::set P4Scope::usedNames; +std::map>> P4Scope::lvalMap; +std::map>> P4Scope::lvalMapRw; +std::set P4Scope::callableTables; + +// TODO(fruffy): This should be set by the back end +std::set P4Scope::notInitializedStructs; +Properties P4Scope::prop; +Requirements P4Scope::req; +Constraints P4Scope::constraints; +void P4Scope::addToScope(const IR::Node *node) { + auto *lScope = P4Scope::scope.back(); + lScope->push_back(node); + + if (const auto *dv = node->to()) { + addLval(dv->type, dv->name.name); + } else if (const auto *dc = node->to()) { + addLval(dc->type, dc->name.name, true); + } +} + +void P4Scope::startLocalScope() { + auto *localScope = new IR::Vector(); + scope.push_back(localScope); +} + +void P4Scope::endLocalScope() { + IR::Vector *localScope = scope.back(); + + for (const auto *node : *localScope) { + if (const auto *decl = node->to()) { + deleteLval(decl->type, decl->name.name); + } else if (const auto *dc = node->to()) { + deleteLval(dc->type, dc->name.name); + } else if (const auto *param = node->to()) { + deleteLval(param->type, param->name.name); + } else if (const auto *tbl = node->to()) { + callableTables.erase(tbl); + } + } + + delete localScope; + scope.pop_back(); +} + +void addCompoundLvals(const IR::Type_StructLike *sl_type, cstring sl_name, bool read_only) { + for (const auto *field : sl_type->fields) { + std::stringstream ss; + ss.str(""); + ss << sl_name << "." << field->name.name; + cstring fieldName(ss.str()); + P4Scope::addLval(field->type, fieldName, read_only); + } +} + +void deleteCompoundLvals(const IR::Type_StructLike *sl_type, cstring sl_name) { + for (const auto *field : sl_type->fields) { + std::stringstream ss; + ss.str(""); + ss << sl_name << "." << field->name.name; + cstring fieldName(ss.str()); + P4Scope::deleteLval(field->type, fieldName); + } +} + +void P4Scope::deleteLval(const IR::Type *tp, cstring name) { + cstring typeKey; + int bitBucket = 0; + + if (const auto *tb = tp->to()) { + typeKey = IR::Type_Bits::static_type_name(); + bitBucket = tb->width_bits(); + } else if (const auto *tb = tp->to()) { + typeKey = IR::Type_Boolean::static_type_name(); + bitBucket = tb->width_bits(); + } else if (tp->is()) { + typeKey = IR::Type_InfInt::static_type_name(); + bitBucket = 1; + } else if (const auto *ts = tp->to()) { + size_t stackSize = ts->getSize(); + typeKey = IR::Type_Stack::static_type_name(); + bitBucket = 1; + if (const auto *tnType = ts->elementType->to()) { + std::stringstream ss; + ss.str(""); + ss << name << "[" << (stackSize - 1) << "]"; + cstring stackName(ss.str()); + deleteLval(tnType, stackName); + } else { + BUG("Type_Name %s not yet supported", ts->elementType->node_type_name()); + } + } else if (const auto *tn = tp->to()) { + auto tnName = tn->path->name.name; + if (tnName == "packet_in") { + return; + } + if (const auto *td = getTypeByName(tnName)) { + if (const auto *tnType = td->to()) { + typeKey = tnName; + // width_bits should work here, do !know why not... + // bit_bucket = P4Scope::compound_type[tn_name]->width_bits(); + bitBucket = 1; + deleteCompoundLvals(tnType, name); + } else { + BUG("Type_Name %s !found!", td->node_type_name()); + } + } else { + printInfo("Type %s does not exist!\n", tnName.c_str()); + return; + } + } else { + BUG("Type %s not yet supported", tp->node_type_name()); + } + lvalMap[typeKey][bitBucket].erase(name); + + // delete values in the normal map + if (lvalMap[typeKey][bitBucket].empty()) { + lvalMap[typeKey].erase(bitBucket); + } + + // delete values in the rw map + if (lvalMapRw.count(typeKey) != 0) { + if (lvalMapRw[typeKey].count(bitBucket) != 0) { + lvalMapRw[typeKey][bitBucket].erase(name); + if (lvalMapRw[typeKey][bitBucket].empty()) { + lvalMapRw[typeKey].erase(bitBucket); + } + } + } + // delete empty type entries + if (lvalMap[typeKey].empty()) { + lvalMap.erase(typeKey); + } + if (lvalMapRw[typeKey].empty()) { + lvalMapRw.erase(typeKey); + } +} + +void P4Scope::addLval(const IR::Type *tp, cstring name, bool read_only) { + cstring typeKey; + int bitBucket = 0; + + if (const auto *tb = tp->to()) { + typeKey = IR::Type_Bits::static_type_name(); + bitBucket = tb->width_bits(); + } else if (const auto *tb = tp->to()) { + typeKey = IR::Type_Boolean::static_type_name(); + bitBucket = tb->width_bits(); + } else if (tp->is()) { + typeKey = IR::Type_InfInt::static_type_name(); + bitBucket = 1; + } else if (const auto *ts = tp->to()) { + size_t stackSize = ts->getSize(); + typeKey = IR::Type_Stack::static_type_name(); + bitBucket = 1; + if (const auto *tnType = ts->elementType->to()) { + std::stringstream ss; + ss.str(""); + ss << name << "[" << (stackSize - 1) << "]"; + cstring stackName(ss.str()); + addLval(tnType, stackName, read_only); + } else { + BUG("Type_Name %s not yet supported", ts->elementType->node_type_name()); + } + } else if (const auto *tn = tp->to()) { + auto tnName = tn->path->name.name; + // FIXME: this could be better + if (tnName == "packet_in") { + return; + } + if (const auto *td = getTypeByName(tnName)) { + if (const auto *tnType = td->to()) { + // width_bits should work here, do !know why not... + typeKey = tnName; + // does !work for some reason... + // bit_bucket = P4Scope::compound_type[tn_name]->width_bits(); + bitBucket = 1; + addCompoundLvals(tnType, name, read_only); + } else { + BUG("Type_Name %s not yet supported", td->node_type_name()); + } + } else { + BUG("Type %s does not exist", tnName); + } + } else { + BUG("Type %s not yet supported", tp->node_type_name()); + } + if (!read_only) { + lvalMapRw[typeKey][bitBucket].insert(name); + } + lvalMap[typeKey][bitBucket].insert(name); +} + +std::set P4Scope::getCandidateLvals(const IR::Type *tp, bool must_write) { + cstring typeKey; + int bitBucket = 0; + + if (const auto *tb = tp->to()) { + typeKey = IR::Type_Bits::static_type_name(); + bitBucket = tb->width_bits(); + } else if (const auto *tb = tp->to()) { + typeKey = IR::Type_Boolean::static_type_name(); + bitBucket = tb->width_bits(); + } else if (tp->is()) { + typeKey = IR::Type_InfInt::static_type_name(); + bitBucket = 1; + } else if (const auto *ts = tp->to()) { + typeKey = ts->name.name; + bitBucket = 1; + } else if (const auto *tn = tp->to()) { + auto tnName = tn->path->name.name; + if (getTypeByName(tnName) != nullptr) { + typeKey = tnName; + // bit_bucket = td->width_bits(); + bitBucket = 1; + } else { + BUG("Type_name refers to unknown type %s", tnName); + } + } else { + BUG("Type %s not yet supported", tp->node_type_name()); + } + + std::map>> lookupMap; + + if (must_write) { + lookupMap = lvalMapRw; + } else { + lookupMap = lvalMap; + } + + if (lookupMap.count(typeKey) == 0) { + return {}; + } + auto keyTypes = lookupMap[typeKey]; + if (keyTypes.count(bitBucket) == 0) { + return {}; + } + + return keyTypes[bitBucket]; +} + +std::optional>> P4Scope::getWriteableLvalForTypeKey( + cstring typeKey) { + if (lvalMapRw.find(typeKey) == lvalMapRw.end()) { + return std::nullopt; + } + return lvalMapRw.at(typeKey); +} +bool P4Scope::hasWriteableLval(cstring typeKey) { + return lvalMapRw.find(typeKey) != lvalMapRw.end(); +} + +bool P4Scope::checkLval(const IR::Type *tp, bool must_write) { + std::set candidates = getCandidateLvals(tp, must_write); + return !candidates.empty(); +} + +cstring P4Scope::pickLval(const IR::Type *tp, bool must_write) { + std::set candidates = getCandidateLvals(tp, must_write); + + if (candidates.empty()) { + BUG("Invalid Type Query, %s not found", tp->toString()); + } + size_t idx = Utils::getRandInt(0, candidates.size() - 1); + auto lval = std::begin(candidates); + // "advance" the iterator idx times + std::advance(lval, idx); + + return *lval; +} + +const IR::Type_Bits *P4Scope::pickDeclaredBitType(bool must_write) { + std::map>> lookupMap; + + if (must_write) { + lookupMap = lvalMapRw; + } else { + lookupMap = lvalMap; + } + + cstring bitKey = IR::Type_Bits::static_type_name(); + if (lookupMap.count(bitKey) == 0) { + return nullptr; + } + auto keyTypes = lookupMap[bitKey]; + size_t idx = Utils::getRandInt(0, keyTypes.size() - 1); + int bitWidth = next(keyTypes.begin(), idx)->first; + return IR::Type_Bits::get(bitWidth, false); +} + +std::vector P4Scope::getFilteredDecls(std::set filter) { + std::vector ret; + for (auto *subScope : scope) { + for (const auto *node : *subScope) { + cstring name = node->node_type_name(); + if (filter.find(name) == filter.end()) { + if (const auto *decl = node->to()) { + ret.push_back(decl); + } + } + } + } + return ret; +} + +std::set *P4Scope::getCallableTables() { return &callableTables; } + +const IR::Type_Declaration *P4Scope::getTypeByName(cstring name) { + for (auto *subScope : scope) { + for (const auto *node : *subScope) { + if (const auto *decl = node->to()) { + if (decl->name.name == name) { + return decl; + } + } + } + } + return nullptr; +} +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/common/scope.h b/backends/p4tools/modules/smith/common/scope.h new file mode 100644 index 00000000000..d0e6c5e7a81 --- /dev/null +++ b/backends/p4tools/modules/smith/common/scope.h @@ -0,0 +1,124 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_SCOPE_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_SCOPE_H_ + +#include +#include +#include +#include +#include + +#include "ir/ir.h" +#include "ir/node.h" +#include "ir/vector.h" +#include "lib/cstring.h" + +namespace P4Tools::P4Smith { + +struct Requirements { + bool require_scalar{false}; + bool compile_time_known{false}; + bool no_methodcalls{false}; + bool not_zero{false}; + bool not_negative{false}; + bool byte_align_headers{false}; + int shift_width{8}; + Requirements() + + = default; +}; + +struct Constraints { + bool const_header_stack_index{false}; + bool const_lshift_count{false}; + bool single_stage_actions{false}; + int max_phv_container_width{0}; + Constraints() + + = default; +}; + +struct Properties { + bool width_unknown{false}; + bool has_methodcall{false}; + bool in_action{false}; + size_t depth = 0; + // This means we are in a block that returns. + // We need to return an expression with the specified type. + const IR::Type *ret_type = nullptr; + Properties() = default; +}; + +class P4Scope { + public: + /// This is a list of subscopes. + static std::vector *> scope; + + /// Maintain a set of names we have already used to avoid duplicates. + static std::set usedNames; + + /// This is a map of usable lvalues we store to be used for references. + static std::map>> lvalMap; + + /// A subset of the lval map that includes rw values. + static std::map>> lvalMapRw; + + /// TODO: Maybe we can just remove tables from the declarations list? + /// This is back-end specific. + static std::set callableTables; + + /// Structs that should not be initialized because they are incomplete. + static std::set notInitializedStructs; + + /// Properties that define the current state of the program. + /// For example, when should a return expression must be returned in a block. + static Properties prop; + + /// Back-end or node-specific restrictions. + static Requirements req; + + /// This defines all constraints specific to various targets or back-ends. + static Constraints constraints; + + P4Scope() = default; + + ~P4Scope() = default; + + static void addToScope(const IR::Node *n); + static void startLocalScope(); + static void endLocalScope(); + + static void addLval(const IR::Type *tp, cstring name, bool read_only = false); + static bool checkLval(const IR::Type *tp, bool must_write = false); + static cstring pickLval(const IR::Type *tp, bool must_write = false); + static void deleteLval(const IR::Type *tp, cstring name); + static std::set getCandidateLvals(const IR::Type *tp, bool must_write = true); + static bool hasWriteableLval(cstring typeKey); + static std::optional>> getWriteableLvalForTypeKey( + cstring typeKey); + + static const IR::Type_Bits *pickDeclaredBitType(bool must_write = false); + + static const IR::Type_Declaration *getTypeByName(cstring name); + + // template to get all declarations + // C++ is so shit... templates must be inlined to be generally usable. + template + static std::vector getDecls() { + std::vector ret; + + for (auto *subScope : scope) { + for (const auto *node : *subScope) { + if (const T *tmpObj = node->to()) { + ret.push_back(tmpObj); + } + } + } + return ret; + } + + static std::vector getFilteredDecls(std::set filter); + static std::set *getCallableTables(); +}; +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_SCOPE_H_ */ diff --git a/backends/p4tools/modules/smith/common/statements.cpp b/backends/p4tools/modules/smith/common/statements.cpp new file mode 100644 index 00000000000..2b54ba482a6 --- /dev/null +++ b/backends/p4tools/modules/smith/common/statements.cpp @@ -0,0 +1,391 @@ +#include "backends/p4tools/modules/smith/common/statements.h" + +#include +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/ir-generated.h" +#include "ir/vector.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" +#include "lib/log.h" + +namespace P4Tools::P4Smith { + +IR::Statement *StatementGenerator::genStatement(bool is_in_func) { + // functions can!have exit statements so set their probability to zero + int64_t pctExit = PCT.STATEMENT_EXIT; + if (is_in_func) { + pctExit = 0; + } + std::vector percent = {PCT.STATEMENT_SWITCH, + PCT.STATEMENT_ASSIGNMENTORMETHODCALL, + PCT.STATEMENT_IF, + PCT.STATEMENT_RETURN, + pctExit, + PCT.STATEMENT_BLOCK}; + IR::Statement *stmt = nullptr; + bool useDefaultStmt = false; + + switch (Utils::getRandInt(percent)) { + case 0: { + stmt = genSwitchStatement(); + if (stmt == nullptr) { + useDefaultStmt = true; + } + break; + } + case 1: { + useDefaultStmt = true; + break; + } + case 2: { + stmt = genConditionalStatement(is_in_func); + break; + } + case 3: { + stmt = genReturnStatement(P4Scope::prop.ret_type); + break; + } + case 4: { + stmt = genExitStatement(); + break; + } + case 5: { + stmt = genBlockStatement(is_in_func); + break; + } + } + if (useDefaultStmt) { + stmt = genAssignmentOrMethodCallStatement(is_in_func); + } + return stmt; +} + +IR::IndexedVector StatementGenerator::genBlockStatementHelper(bool is_in_func) { + // Randomize the total number of statements. + int maxStatements = + Utils::getRandInt(DECL.BLOCKSTATEMENT_MIN_STAT, DECL.BLOCKSTATEMENT_MAX_STAT); + IR::IndexedVector statOrDecls; + + // Put tab_name .apply() after some initializations. + for (int numStat = 0; numStat <= maxStatements; numStat++) { + IR::StatOrDecl *stmt = + target().declarationGenerator().generateRandomStatementOrDeclaration(is_in_func); + if (stmt == nullptr) { + BUG("Statement in BlockStatement should not be nullptr!"); + } + statOrDecls.push_back(stmt); + } + return statOrDecls; +} + +IR::BlockStatement *StatementGenerator::genBlockStatement(bool is_in_func) { + P4Scope::startLocalScope(); + + auto statOrDecls = genBlockStatementHelper(is_in_func); + + if (is_in_func && (P4Scope::prop.ret_type->to() == nullptr)) { + auto *retStat = genReturnStatement(P4Scope::prop.ret_type); + statOrDecls.push_back(retStat); + } + P4Scope::endLocalScope(); + + return new IR::BlockStatement(statOrDecls); +} + +IR::IfStatement *StatementGenerator::genConditionalStatement(bool is_in_func) { + IR::Expression *cond = nullptr; + IR::Statement *ifTrue = nullptr; + IR::Statement *ifFalse = nullptr; + + cond = target().expressionGenerator().genExpression(IR::Type_Boolean::get()); + if (cond == nullptr) { + BUG("cond in IfStatement should !be nullptr!"); + } + ifTrue = genStatement(is_in_func); + if (ifTrue == nullptr) { + // could !generate a statement + // this happens when there is now way to generate an assignment + ifTrue = new IR::EmptyStatement(); + } + ifFalse = genStatement(is_in_func); + if (ifFalse == nullptr) { + // could !generate a statement + // this happens when there is now way to generate an assignment + ifFalse = new IR::EmptyStatement(); + } + return new IR::IfStatement(cond, ifTrue, ifFalse); +} + +void StatementGenerator::removeLval(const IR::Expression *left, const IR::Type *type) { + cstring lvalStr = nullptr; + if (const auto *path = left->to()) { + lvalStr = path->path->name.name; + } else if (const auto *mem = left->to()) { + lvalStr = mem->member.name; + } else if (const auto *slice = left->to()) { + lvalStr = slice->e0->to()->path->name.name; + } else if (const auto *arrIdx = left->to()) { + lvalStr = arrIdx->left->to()->path->name.name; + } + + P4Scope::deleteLval(type, lvalStr); +} + +IR::Statement *StatementGenerator::genAssignmentStatement() { + IR::AssignmentStatement *assignstat = nullptr; + IR::Expression *left = nullptr; + IR::Expression *right = nullptr; + + std::vector percent = {PCT.ASSIGNMENTORMETHODCALLSTATEMENT_ASSIGN_BIT, + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_ASSIGN_STRUCTLIKE}; + + switch (Utils::getRandInt(percent)) { + case 0: { + const auto *bitType = P4Scope::pickDeclaredBitType(true); + // Ideally this should have a fallback option + if (bitType == nullptr) { + LOG3("Could not find writable bit lval for assignment!\n"); + // TODO(fruffy): Find a more meaningful assignment statement + return nullptr; + } + left = target().expressionGenerator().pickLvalOrSlice(bitType); + if (P4Scope::constraints.single_stage_actions) { + removeLval(left, bitType); + } + right = target().expressionGenerator().genExpression(bitType); + return new IR::AssignmentStatement(left, right); + } + case 1: + // TODO(fruffy): Compound types + break; + } + + return assignstat; +} + +IR::Statement *StatementGenerator::genMethodCallExpression(const IR::PathExpression *methodName, + const IR::ParameterList ¶ms) { + auto *args = new IR::Vector(); + IR::IndexedVector decls; + + // all this boilerplate should be somewhere else... + P4Scope::startLocalScope(); + + for (const auto *par : params) { + IR::Argument *arg = nullptr; + // TODO(fruffy): Fix the direction none issue here. + if (!target().expressionGenerator().checkInputArg(par) && + par->direction != IR::Direction::None) { + auto name = cstring(getRandomString(6)); + auto *expr = target().expressionGenerator().genExpression(par->type); + // all this boilerplate should be somewhere else... + auto *decl = new IR::Declaration_Variable(name, par->type, expr); + P4Scope::addToScope(decl); + decls.push_back(decl); + } + arg = new IR::Argument(target().expressionGenerator().genInputArg(par)); + args->push_back(arg); + } + auto *mce = new IR::MethodCallExpression(methodName, args); + auto *mcs = new IR::MethodCallStatement(mce); + P4Scope::endLocalScope(); + if (decls.empty()) { + return mcs; + } + auto *blkStmt = new IR::BlockStatement(decls); + blkStmt->push_back(mcs); + return blkStmt; +} + +IR::Statement *StatementGenerator::genMethodCallStatement(bool is_in_func) { + IR::MethodCallExpression *mce = nullptr; + + // functions cannot call actions or tables so set their chance to zero + int16_t tmpActionPct = PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_ACTION; + int16_t tmpTblPct = PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_TABLE; + int16_t tmpCtrlPct = PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_CTRL; + if (is_in_func) { + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_ACTION = 0; + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_TABLE = 0; + } + if (P4Scope::prop.in_action) { + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_CTRL = 0; + } + std::vector percent = {PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_ACTION, + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_FUNCTION, + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_TABLE, + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_CTRL, + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_BUILT_IN}; + + switch (Utils::getRandInt(percent)) { + case 0: { + auto actions = P4Scope::getDecls(); + if (actions.empty()) { + break; + } + size_t idx = Utils::getRandInt(0, actions.size() - 1); + const auto *p4Fun = actions.at(idx); + auto params = p4Fun->getParameters()->parameters; + auto *methodName = new IR::PathExpression(p4Fun->name); + return genMethodCallExpression(methodName, params); + } + case 1: { + auto funcs = P4Scope::getDecls(); + if (funcs.empty()) { + break; + } + size_t idx = Utils::getRandInt(0, funcs.size() - 1); + const auto *p4Fun = funcs.at(idx); + auto params = p4Fun->getParameters()->parameters; + auto *methodName = new IR::PathExpression(p4Fun->name); + return genMethodCallExpression(methodName, params); + } + case 2: { + auto *tblSet = P4Scope::getCallableTables(); + if (tblSet->empty()) { + break; + } + auto idx = Utils::getRandInt(0, tblSet->size() - 1); + auto tblIter = std::begin(*tblSet); + std::advance(tblIter, idx); + const IR::P4Table *tbl = *tblIter; + auto *mem = new IR::Member(new IR::PathExpression(tbl->name), "apply"); + mce = new IR::MethodCallExpression(mem); + tblSet->erase(tblIter); + break; + } + case 3: { + auto decls = P4Scope::getDecls(); + if (decls.empty()) { + break; + } + auto idx = Utils::getRandInt(0, decls.size() - 1); + auto declIter = std::begin(decls); + std::advance(declIter, idx); + const IR::Declaration_Instance *declInstance = *declIter; + // avoid member here + std::stringstream tmpMethodStr; + tmpMethodStr << declInstance->name << ".apply"; + cstring tmpMethodCstr(tmpMethodStr.str()); + auto *methodName = new IR::PathExpression(tmpMethodCstr); + const auto *typeName = declInstance->type->to(); + + const auto *resolvedType = P4Scope::getTypeByName(typeName->path->name); + if (resolvedType == nullptr) { + BUG("Type Name %s not found", typeName->path->name); + } + if (const auto *ctrl = resolvedType->to()) { + auto params = ctrl->getApplyParameters()->parameters; + return genMethodCallExpression(methodName, params); + } + BUG("Declaration Instance type %s not yet supported", + declInstance->type->node_type_name()); + } + case 4: { + auto hdrs = P4Scope::getDecls(); + if (hdrs.empty()) { + break; + } + std::set hdrLvals; + for (const auto *hdr : hdrs) { + auto availableLvals = P4Scope::getCandidateLvals(hdr, true); + hdrLvals.insert(availableLvals.begin(), availableLvals.end()); + } + if (hdrLvals.empty()) { + break; + } + auto idx = Utils::getRandInt(0, hdrLvals.size() - 1); + auto hdrLvalIter = std::begin(hdrLvals); + std::advance(hdrLvalIter, idx); + cstring hdrLval = *hdrLvalIter; + const IR::Expression *member = nullptr; + if (Utils::getRandInt(0, 1) != 0) { + member = new IR::Member(new IR::PathExpression(hdrLval), "setValid"); + } else { + member = new IR::Member(new IR::PathExpression(hdrLval), "setInvalid"); + } + mce = new IR::MethodCallExpression(member); + break; + } + } + // restore previous probabilities + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_ACTION = tmpActionPct; + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_TABLE = tmpTblPct; + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_CTRL = tmpCtrlPct; + if (mce != nullptr) { + return new IR::MethodCallStatement(mce); + } + // unable to return a methodcall, return an assignment instead + return genAssignmentStatement(); +} + +IR::Statement *StatementGenerator::genAssignmentOrMethodCallStatement(bool is_in_func) { + std::vector percent = {PCT.ASSIGNMENTORMETHODCALLSTATEMENT_ASSIGN, + PCT.ASSIGNMENTORMETHODCALLSTATEMENT_METHOD_CALL}; + auto val = Utils::getRandInt(percent); + if (val == 0) { + return genAssignmentStatement(); + } + return genMethodCallStatement(is_in_func); +} + +IR::ExitStatement *StatementGenerator::genExitStatement() { return new IR::ExitStatement(); } + +IR::SwitchStatement *StatementGenerator::genSwitchStatement() { + // get the expression + auto *tblSet = P4Scope::getCallableTables(); + + // return nullptr if there are no tables left + if (tblSet->empty()) { + return nullptr; + } + auto idx = Utils::getRandInt(0, tblSet->size() - 1); + auto tblIter = std::begin(*tblSet); + + std::advance(tblIter, idx); + const IR::P4Table *tbl = *tblIter; + auto *expr = new IR::Member( + new IR::MethodCallExpression(new IR::Member(new IR::PathExpression(tbl->name), "apply")), + "action_run"); + tblSet->erase(tblIter); + + // get the switch cases + IR::Vector switchCases; + for (const auto *tabProperty : tbl->properties->properties) { + if (tabProperty->name.name == IR::TableProperties::actionsPropertyName) { + const auto *property = tabProperty->value->to(); + for (const auto *action : property->actionList) { + cstring actName = action->getName(); + auto *blkStat = genBlockStatement(false); + auto *switchCase = new IR::SwitchCase(new IR::PathExpression(actName), blkStat); + switchCases.push_back(switchCase); + } + } + } + + return new IR::SwitchStatement(expr, switchCases); +} + +IR::ReturnStatement *StatementGenerator::genReturnStatement(const IR::Type *tp) { + IR::Expression *expr = nullptr; + + // Type_Void is also empty + if ((tp != nullptr) && (tp->to() == nullptr)) { + expr = target().expressionGenerator().genExpression(tp); + } + return new IR::ReturnStatement(expr); +} + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/common/statements.h b/backends/p4tools/modules/smith/common/statements.h new file mode 100644 index 00000000000..2f2f7588eb5 --- /dev/null +++ b/backends/p4tools/modules/smith/common/statements.h @@ -0,0 +1,44 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_STATEMENTS_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_STATEMENTS_H_ + +#include "backends/p4tools/modules/smith/common/generator.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" + +namespace P4Tools::P4Smith { + +class StatementGenerator : public Generator { + public: + explicit StatementGenerator(const SmithTarget &target) : Generator(target) {} + + virtual ~StatementGenerator() = default; + + virtual IR::Statement *genStatement(bool is_in_func); + + IR::IndexedVector genBlockStatementHelper(bool is_in_func); + + virtual IR::BlockStatement *genBlockStatement(bool is_in_func); + + virtual IR::IfStatement *genConditionalStatement(bool is_in_func); + + static void removeLval(const IR::Expression *left, const IR::Type *type); + + virtual IR::Statement *genAssignmentStatement(); + + virtual IR::Statement *genMethodCallExpression(const IR::PathExpression *methodName, + const IR::ParameterList ¶ms); + + virtual IR::Statement *genMethodCallStatement(bool is_in_func); + + virtual IR::Statement *genAssignmentOrMethodCallStatement(bool is_in_func); + + virtual IR::ExitStatement *genExitStatement(); + + virtual IR::SwitchStatement *genSwitchStatement(); + + IR::ReturnStatement *genReturnStatement(const IR::Type *tp = nullptr); +}; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_STATEMENTS_H_ */ diff --git a/backends/p4tools/modules/smith/common/table.cpp b/backends/p4tools/modules/smith/common/table.cpp new file mode 100644 index 00000000000..c8aa6370873 --- /dev/null +++ b/backends/p4tools/modules/smith/common/table.cpp @@ -0,0 +1,159 @@ +#include "backends/p4tools/modules/smith/common/table.h" + +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/logging.h" +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/vector.h" +#include "lib/cstring.h" +#include "lib/exceptions.h" + +namespace P4Tools::P4Smith { + +IR::P4Table *TableGenerator::genTableDeclaration() { + IR::TableProperties *tbProperties = genTablePropertyList(); + cstring name = getRandomString(6); + auto *ret = new IR::P4Table(name, tbProperties); + P4Scope::addToScope(ret); + P4Scope::callableTables.emplace(ret); + return ret; +} + +IR::TableProperties *TableGenerator::genTablePropertyList() { + IR::IndexedVector tabProperties; + + tabProperties.push_back(genKeyProperty()); + tabProperties.push_back(genActionListProperty()); + + return new IR::TableProperties(tabProperties); +} + +IR::Key *TableGenerator::genKeyElementList(size_t len) { + IR::Vector keys; + + for (size_t i = 0; i < len; i++) { + // TODO(fruffy): More types than just exact + IR::KeyElement *key = genKeyElement("exact"); + if (key == nullptr) { + continue; + } + // @name + // Tao: actually, this may never happen + const auto *keyAnno = key->annotations->annotations.at(0); + const auto *annotExpr = keyAnno->expr.at(0); + cstring keyAnnotatName; + if (annotExpr->is()) { + const auto *strExpr = annotExpr->to(); + keyAnnotatName = strExpr->value; + } else { + BUG("must be a string literal"); + } + + keys.push_back(key); + } + + return new IR::Key(keys); +} + +IR::KeyElement *TableGenerator::genKeyElement(IR::ID match_kind) { + auto *match = new IR::PathExpression(std::move(match_kind)); + auto *annotations = target().declarationGenerator().genAnnotation(); + auto *bitType = P4Scope::pickDeclaredBitType(false); + + // Ideally this should have a fallback option + if (bitType == nullptr) { + printInfo("Could not find key lval for key matches\n"); + return nullptr; + } + // this expression can!be an infinite precision integer + P4Scope::req.require_scalar = true; + auto *expr = target().expressionGenerator().genExpression(bitType); + P4Scope::req.require_scalar = false; + auto *key = new IR::KeyElement(annotations, expr, match); + + return key; +} + +IR::Property *TableGenerator::genKeyProperty() { + cstring name = IR::TableProperties::keyPropertyName; + auto *keys = genKeyElementList(Utils::getRandInt(0, 3)); + + // isConstant --> false + return new IR::Property(name, keys, false); +} + +IR::MethodCallExpression *TableGenerator::genTableActionCall(cstring method_name, + const IR::ParameterList ¶ms) { + auto *args = new IR::Vector(); + IR::IndexedVector decls; + + for (const auto *par : params) { + if (!target().expressionGenerator().checkInputArg(par)) { + return nullptr; + } + if (par->direction == IR::Direction::None) { + // do nothing; in tables directionless parameters are + // set by the control plane + continue; + } + IR::Argument *arg = nullptr; + if (par->direction == IR::Direction::In) { + // the generated expression needs to be compile-time known + P4Scope::req.compile_time_known = true; + arg = new IR::Argument(target().expressionGenerator().genExpression(par->type)); + P4Scope::req.compile_time_known = false; + } else { + arg = new IR::Argument(target().expressionGenerator().pickLvalOrSlice(par->type)); + } + args->push_back(arg); + } + auto *pathExpr = new IR::PathExpression(method_name); + return new IR::MethodCallExpression(pathExpr, args); +} + +IR::ActionList *TableGenerator::genActionList(size_t len) { + IR::IndexedVector actList; + auto p4Actions = P4Scope::getDecls(); + std::set actNames; + + if (p4Actions.empty()) { + return new IR::ActionList(actList); + } + for (size_t i = 0; i < len; i++) { + size_t idx = Utils::getRandInt(0, p4Actions.size() - 1); + const auto *p4Act = p4Actions[idx]; + cstring actName = p4Act->name.name; + + if (actNames.find(actName) != actNames.end()) { + continue; + } + actNames.insert(actName); + + const auto *params = p4Act->parameters; + + IR::MethodCallExpression *mce = genTableActionCall(actName, *params); + if (mce != nullptr) { + auto *actlistEle = new IR::ActionListElement(mce); + actList.push_back(actlistEle); + } + } + return new IR::ActionList(actList); +} + +IR::Property *TableGenerator::genActionListProperty() { + cstring name = IR::TableProperties::actionsPropertyName; + auto *acts = genActionList(Utils::getRandInt(0, 3)); + + return new IR::Property(name, acts, false); +} + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/common/table.h b/backends/p4tools/modules/smith/common/table.h new file mode 100644 index 00000000000..1edcde3332f --- /dev/null +++ b/backends/p4tools/modules/smith/common/table.h @@ -0,0 +1,37 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_TABLE_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_TABLE_H_ +#include + +#include "backends/p4tools/modules/smith/common/generator.h" +#include "ir/ir.h" +#include "lib/cstring.h" + +namespace P4Tools::P4Smith { + +class TableGenerator : public Generator { + public: + explicit TableGenerator(const SmithTarget &target) : Generator(target) {} + + virtual ~TableGenerator() = default; + + virtual IR::P4Table *genTableDeclaration(); + + virtual IR::TableProperties *genTablePropertyList(); + + virtual IR::KeyElement *genKeyElement(IR::ID match_kind); + + virtual IR::Key *genKeyElementList(size_t len); + + virtual IR::Property *genKeyProperty(); + + virtual IR::MethodCallExpression *genTableActionCall(cstring method_name, + const IR::ParameterList ¶ms); + + virtual IR::ActionList *genActionList(size_t len); + + IR::Property *genActionListProperty(); +}; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_COMMON_TABLE_H_ */ diff --git a/backends/p4tools/modules/smith/core/target.cpp b/backends/p4tools/modules/smith/core/target.cpp new file mode 100644 index 00000000000..9aeee340c71 --- /dev/null +++ b/backends/p4tools/modules/smith/core/target.cpp @@ -0,0 +1,15 @@ +#include "backends/p4tools/modules/smith/core/target.h" + +#include + +#include "backends/p4tools/common/compiler/compiler_target.h" +#include "backends/p4tools/common/core/target.h" + +namespace P4Tools::P4Smith { + +SmithTarget::SmithTarget(const std::string &deviceName, const std::string &archName) + : P4Tools::CompilerTarget("smith", deviceName, archName) {} + +const SmithTarget &SmithTarget::get() { return P4Tools::Target::get("smith"); } + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/core/target.h b/backends/p4tools/modules/smith/core/target.h new file mode 100644 index 00000000000..4131f59deac --- /dev/null +++ b/backends/p4tools/modules/smith/core/target.h @@ -0,0 +1,42 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_CORE_TARGET_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_CORE_TARGET_H_ + +#include +#include + +#include "backends/p4tools/common/compiler/compiler_target.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/common/table.h" +#include "ir/ir.h" + +namespace P4Tools::P4Smith { + +class SmithTarget : public CompilerTarget { + public: + /// @returns the singleton instance for the current target. + static const SmithTarget &get(); + + /// Write the necessary #include directives and other helpful constructs to the specified + /// stream. + [[nodiscard]] virtual int writeTargetPreamble(std::ostream *ostream) const = 0; + + [[nodiscard]] virtual const IR::P4Program *generateP4Program() const = 0; + + [[nodiscard]] virtual DeclarationGenerator &declarationGenerator() const = 0; + [[nodiscard]] virtual ExpressionGenerator &expressionGenerator() const = 0; + [[nodiscard]] virtual StatementGenerator &statementGenerator() const = 0; + [[nodiscard]] virtual ParserGenerator &parserGenerator() const = 0; + [[nodiscard]] virtual TableGenerator &tableGenerator() const = 0; + + protected: + explicit SmithTarget(const std::string &deviceName, const std::string &archName); + + private: +}; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_CORE_TARGET_H_ */ diff --git a/backends/p4tools/modules/smith/main.cpp b/backends/p4tools/modules/smith/main.cpp new file mode 100644 index 00000000000..ca1f798313d --- /dev/null +++ b/backends/p4tools/modules/smith/main.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/logging.h" +#include "backends/p4tools/modules/smith/smith.h" +#include "lib/crash.h" +#include "lib/exceptions.h" +#include "lib/timer.h" + +int main(int argc, char **argv) { + setup_signals(); + + std::vector args; + args.reserve(argc); + for (int i = 0; i < argc; ++i) { + args.push_back(argv[i]); + } + + int result = EXIT_SUCCESS; + try { + Util::ScopedTimer timer("P4Smith Main"); + result = P4Tools::P4Smith::Smith().main(args); + } catch (const Util::CompilerBug &e) { + std::cerr << "Internal error: " << e.what() << '\n'; + std::cerr << "Please submit a bug report with your code." << '\n'; + result = EXIT_FAILURE; + } catch (const Util::CompilerUnimplemented &e) { + std::cerr << e.what() << '\n'; + result = EXIT_FAILURE; + } catch (const Util::CompilationError &e) { + std::cerr << e.what() << '\n'; + result = EXIT_FAILURE; + } catch (const std::exception &e) { + std::cerr << "Internal error: " << e.what() << '\n'; + std::cerr << "Please submit a bug report with your code." << '\n'; + result = EXIT_FAILURE; + } catch (...) { + std::cerr << "Internal error. Please submit a bug report with your code." << '\n'; + result = EXIT_FAILURE; + } + P4Tools::printPerformanceReport(); + return result; +} diff --git a/backends/p4tools/modules/smith/options.cpp b/backends/p4tools/modules/smith/options.cpp new file mode 100644 index 00000000000..c3f7ab35f50 --- /dev/null +++ b/backends/p4tools/modules/smith/options.cpp @@ -0,0 +1,45 @@ +#include "backends/p4tools/modules/smith/options.h" + +#include +#include +#include + +#include "backends/p4tools/common/options.h" +#include "backends/p4tools/modules/smith/toolname.h" +#include "lib/compile_context.h" +#include "lib/error.h" +#include "lib/exceptions.h" + +namespace P4Tools { + +SmithOptions &SmithOptions::get() { + static SmithOptions INSTANCE; + return INSTANCE; +} + +const char *SmithOptions::getIncludePath() { + P4C_UNIMPLEMENTED("getIncludePath is not implemented for P4Smith."); +} + +void SmithOptions::processArgs(const std::vector &args) { + // Convert to the standard (argc, argv) pair. + int argc = 0; + char **argv = nullptr; + std::tie(argc, argv) = convertArgs(args); + + // Establish a dummy compilation context so that we can use ::error to report errors while + // processing command-line options. + class DummyCompileContext : public BaseCompileContext { + } dummyContext; + AutoCompileContext autoDummyContext(&dummyContext); + + // Delegate to the hook. + auto *remainingArgs = P4Tools::AbstractP4cToolOptions::process(argc, argv); + if ((remainingArgs == nullptr) || ::errorCount() > 0) { + return; + } +} + +SmithOptions::SmithOptions() : AbstractP4cToolOptions(P4Smith::TOOL_NAME, "P4Smith options.") {} + +} // namespace P4Tools diff --git a/backends/p4tools/modules/smith/options.h b/backends/p4tools/modules/smith/options.h new file mode 100644 index 00000000000..17a1b60e161 --- /dev/null +++ b/backends/p4tools/modules/smith/options.h @@ -0,0 +1,25 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_OPTIONS_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_OPTIONS_H_ +#include + +#include "backends/p4tools/common/options.h" + +namespace P4Tools { + +class SmithOptions : public AbstractP4cToolOptions { + public: + ~SmithOptions() override = default; + static SmithOptions &get(); + + const char *getIncludePath() override; + void processArgs(const std::vector &args); + + private: + SmithOptions(); +}; + +// using P4toZ3Context = P4CContextWithOptions; + +} // namespace P4Tools + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_OPTIONS_H_ */ diff --git a/backends/p4tools/modules/smith/register.h.in b/backends/p4tools/modules/smith/register.h.in new file mode 100644 index 00000000000..8ed1d5400fe --- /dev/null +++ b/backends/p4tools/modules/smith/register.h.in @@ -0,0 +1,13 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_REGISTER_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_REGISTER_H_ + +@include_statements_var@ + +namespace P4Tools::P4Smith { + +inline void registerSmithTargets() { +@smith_targets_var@} + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_REGISTER_H_ */ diff --git a/backends/p4tools/modules/smith/scripts/compilation-test.sh b/backends/p4tools/modules/smith/scripts/compilation-test.sh new file mode 100755 index 00000000000..0f8718f70b1 --- /dev/null +++ b/backends/p4tools/modules/smith/scripts/compilation-test.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e # Exit on error. + +if [ -z "$1" ]; then + echo "- Missing mandatory argument: NUM_ITERATIONS" + echo " - Usage: source compilation-test.sh " + return 1 +fi + +if [ -z "$2" ]; then + echo "- Missing mandatory argument: SMITH_BIN" + echo " - Usage: source compilation-test.sh " + return 1 +fi + +if [ -z "$3" ]; then + echo "- Missing mandatory argument: COMPILER_BIN" + echo " - Usage: source compilation-test.sh " + return 1 +fi + +if [ -z "$4" ]; then + echo "- Missing mandatory argument: TEST_DIR" + echo " - Usage: source compilation-test.sh " + return 1 +fi + +if [ -z "$5" ]; then + echo "- Missing mandatory argument: ARCH" + echo " - Usage: source compilation-test.sh " + return 1 +fi + +if [ -z "$6" ]; then + echo "- Missing mandatory argument: TARGET" + echo " - Usage: source compilation-test.sh " + return 1 +fi + +NUM_ITERATIONS=$1 +SMITH_BIN=$2 +COMPILER_BIN=$3 +TEST_DIR=$4 +ARCH=$5 +TARGET=$6 + +for i in $(seq 1 $NUM_ITERATIONS); do + echo "Generating program $i" + $SMITH_BIN --target $ARCH --arch $TARGET --seed $i $TEST_DIR/out.p4 + # TODO: Do not compile until we have stabilized. + # $COMPILER_BIN $TEST_DIR/out.p4 +done diff --git a/backends/p4tools/modules/smith/smith.cpp b/backends/p4tools/modules/smith/smith.cpp new file mode 100644 index 00000000000..87e6ab53d7c --- /dev/null +++ b/backends/p4tools/modules/smith/smith.cpp @@ -0,0 +1,93 @@ +#include "backends/p4tools/modules/smith/smith.h" + +#include +#include +#include +#include + +#include "backends/p4tools/common/compiler/compiler_result.h" +#include "backends/p4tools/common/lib/logging.h" +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "backends/p4tools/modules/smith/options.h" +#include "backends/p4tools/modules/smith/register.h" +#include "frontends/common/parser_options.h" +#include "frontends/p4/toP4/toP4.h" +#include "ir/ir.h" +#include "lib/compile_context.h" +#include "lib/cstring.h" +#include "lib/error.h" +#include "lib/nullstream.h" + +namespace P4Tools::P4Smith { + +void Smith::registerTarget() { registerSmithTargets(); } + +int Smith::main(const std::vector &args) { + // Register supported compiler targets. + registerTarget(); + + // Process command-line options. + auto &toolOptions = SmithOptions::get(); + auto compileContext = toolOptions.process(args); + if (!compileContext) { + return EXIT_FAILURE; + } + + // If not explicitly disabled, print basic information to standard output. + if (!toolOptions.disableInformationLogging) { + enableInformationLogging(); + } + + // Set up the compilation context. + AutoCompileContext autoContext(*compileContext); + // Instantiate a dummy program for now. In the future this can be a skeleton. + const IR::P4Program program; + return mainImpl(CompilerResult(program)); +} + +int Smith::mainImpl(const CompilerResult & /*result*/) { + registerSmithTargets(); + + auto outputFile = P4CContext::get().options().file; + + auto &smithOptions = P4Tools::SmithOptions::get(); + + // Use a default name if no specific output name is provided. + if (outputFile == nullptr) { + outputFile = cstring("out.p4"); + } + auto *ostream = openFile(outputFile, false); + if (ostream == nullptr) { + ::error("must have [file]"); + exit(EXIT_FAILURE); + } + if (smithOptions.seed.has_value()) { + printInfo("Using provided seed"); + } else { + printInfo("Generating seed..."); + // No seed provided, we generate our own. + std::random_device r; + smithOptions.seed = r(); + Utils::setRandomSeed(*smithOptions.seed); + } + // TODO(fruffy): Remove this. We are setting the seed in two frameworks. + printInfo("============ Program seed %1% =============\n", *smithOptions.seed); + const auto &smithTarget = SmithTarget::get(); + + auto result = smithTarget.writeTargetPreamble(ostream); + if (result != EXIT_SUCCESS) { + return result; + } + const auto *generatedProgram = smithTarget.generateP4Program(); + // Use ToP4 to write the P4 program to the specified stream. + P4::ToP4 top4(ostream, false); + generatedProgram->apply(top4); + ostream->flush(); + P4Scope::endLocalScope(); + + return EXIT_SUCCESS; +} + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/smith.h b/backends/p4tools/modules/smith/smith.h new file mode 100644 index 00000000000..1753049cfd3 --- /dev/null +++ b/backends/p4tools/modules/smith/smith.h @@ -0,0 +1,25 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_SMITH_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_SMITH_H_ + +#include + +#include "backends/p4tools/common/compiler/compiler_result.h" +#include "backends/p4tools/common/p4ctool.h" +#include "backends/p4tools/modules/smith/options.h" + +namespace P4Tools::P4Smith { + +class Smith : public AbstractP4cTool { + protected: + void registerTarget() override; + + int mainImpl(const CompilerResult &compilerResult) override; + + public: + virtual ~Smith() = default; + int main(const std::vector &args); +}; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_SMITH_H_ */ diff --git a/backends/p4tools/modules/smith/targets/bmv2/CMakeLists.txt b/backends/p4tools/modules/smith/targets/bmv2/CMakeLists.txt new file mode 100644 index 00000000000..024c9eef816 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/CMakeLists.txt @@ -0,0 +1,16 @@ +if(ENABLE_TESTING) + # Include the test subdirectory. + message("-- Adding p4smith bmv2 test suite") + include(test/P4Tests.cmake) +endif() + +# Source files for smith. +set( + SMITH_SOURCES + ${SMITH_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/psa.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/target.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/v1model.cpp + PARENT_SCOPE +) + diff --git a/backends/p4tools/modules/smith/targets/bmv2/psa.cpp b/backends/p4tools/modules/smith/targets/bmv2/psa.cpp new file mode 100644 index 00000000000..69adcbbc268 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/psa.cpp @@ -0,0 +1,442 @@ +#include "backends/p4tools/modules/smith/targets/bmv2/psa.h" + +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/targets/bmv2/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" +#include "ir/node.h" +#include "ir/vector.h" + +namespace P4Tools::P4Smith::BMv2 { + +using namespace P4::literals; + +/* ============================================================================================= + * Bmv2PsaSmithTarget implementation + * ============================================================================================= */ + +Bmv2PsaSmithTarget::Bmv2PsaSmithTarget() : AbstractBMv2SmithTarget("bmv2", "psa") {} + +void Bmv2PsaSmithTarget::make() { + static Bmv2PsaSmithTarget *INSTANCE = nullptr; + if (INSTANCE == nullptr) { + INSTANCE = new Bmv2PsaSmithTarget(); + } +} + +IR::P4Parser *Bmv2PsaSmithTarget::generateIngressParserBlock() const { + IR::IndexedVector parserLocals; + P4Scope::startLocalScope(); + + // generate type_parser !that this is labeled "p" + IR::IndexedVector params; + + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_in"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "hdr"_cs, SYS_HDR_NAME)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "user_meta"_cs, "metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "psa_ingress_parser_input_metadata_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "resubmit_meta"_cs, "empty_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "recirculate_meta"_cs, + "empty_t"_cs)); + auto *parList = new IR::ParameterList(params); + + auto *tpParser = new IR::Type_Parser("IngressParserImpl", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + // // generate decls + // for (int i = 0; i < 5; i++) { + // auto var_decl = variableDeclaration::gen(); + // parserLocals.push_back(var_decl); + // } + + // generate states + IR::IndexedVector states; + auto *startState = parserGenerator().genStartState(); + states.push_back(startState); + states.push_back(parserGenerator().genHdrStates()); + + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4parser = new IR::P4Parser("IngressParserImpl", tpParser, parserLocals, states); + P4Scope::addToScope(p4parser); + return p4parser; +} + +IR::P4Control *Bmv2PsaSmithTarget::generateIngressBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "user_meta"_cs, "metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "psa_ingress_input_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "ostd"_cs, + "psa_ingress_output_metadata_t"_cs)); + + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("ingress", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + IR::IndexedVector localDecls = declarationGenerator().genLocalControlDecls(); + // apply body + auto *applyBlock = statementGenerator().genBlockStatement(false); + // hardcode the output port to be zero + auto *outputPort = new IR::PathExpression("ostd.egress_port"); + // PSA requires explicit casts of the output port variable + // actually !sure why this is required... + auto *outPortCast = new IR::Cast(new IR::Type_Name("PortId_t"), new IR::Constant(0)); + auto *assign = new IR::AssignmentStatement(outputPort, outPortCast); + // some hack to insert the expression at the beginning + auto it = applyBlock->components.begin(); + applyBlock->components.insert(it, assign); + // also make sure the packet isn't dropped... + outputPort = new IR::PathExpression("ostd.drop"); + // PSA requires explicit casts of the output port variable + // actually !sure why this is required... + assign = new IR::AssignmentStatement(outputPort, new IR::BoolLiteral(false)); + // some hack to insert the expression at the beginning + it = applyBlock->components.begin(); + applyBlock->components.insert(it, assign); + // end of scope + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4ctrl = new IR::P4Control("ingress", typeCtrl, localDecls, applyBlock); + P4Scope::addToScope(p4ctrl); + return p4ctrl; +} + +IR::MethodCallStatement *genDeparserEmitCall() { + auto *call = new IR::PathExpression("pkt.emit"); + auto *args = new IR::Vector(); + + args->push_back(new IR::Argument(new IR::PathExpression("h"))); + + auto *mce = new IR::MethodCallExpression(call, args); + auto *mst = new IR::MethodCallStatement(mce); + return mst; +} + +IR::P4Control *Bmv2PsaSmithTarget::generateIngressDeparserBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_out"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "clone_i2e_meta"_cs, "empty_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "resubmit_meta"_cs, "empty_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "normal_meta"_cs, "empty_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "user_meta"_cs, "metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "psa_ingress_output_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("IngressDeparserImpl", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + blkStat->push_back(genDeparserEmitCall()); + + return new IR::P4Control("IngressDeparserImpl", typeCtrl, localDecls, blkStat); +} + +IR::P4Parser *Bmv2PsaSmithTarget::generateEgressParserBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_in"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "hdr"_cs, SYS_HDR_NAME)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "user_meta"_cs, "metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "psa_egress_parser_input_metadata_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "normal_meta"_cs, "empty_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "clone_i2e_meta"_cs, "empty_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "clone_e2e_meta"_cs, "empty_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeParser = new IR::Type_Parser("EgressParserImpl", parList); + IR::IndexedVector localDecls; + // TODO(fruffy): this hacky. FIX + // generate states + IR::IndexedVector states; + IR::IndexedVector components; + IR::Expression *transition = new IR::PathExpression("accept"); + auto *startState = new IR::ParserState("start", components, transition); + states.push_back(startState); + return new IR::P4Parser("EgressParserImpl", typeParser, localDecls, states); +} + +IR::P4Control *Bmv2PsaSmithTarget::generateEgressBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "user_meta"_cs, "metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "psa_egress_input_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "ostd"_cs, + "psa_egress_output_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("egress", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + + return new IR::P4Control("egress", typeCtrl, localDecls, blkStat); +} + +IR::P4Control *Bmv2PsaSmithTarget::generateEgressDeparserBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_out"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "clone_e2e_meta"_cs, "empty_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::Out, "recirculate_meta"_cs, + "empty_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "user_meta"_cs, "metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "psa_egress_output_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter( + IR::Direction::In, "edstd"_cs, "psa_egress_deparser_input_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("EgressDeparserImpl", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + blkStat->push_back(genDeparserEmitCall()); + + return new IR::P4Control("EgressDeparserImpl", typeCtrl, localDecls, blkStat); +} + +namespace { + +IR::Declaration_Instance *generateIngressPipeDeclaration() { + auto *args = new IR::Vector(); + + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("IngressParserImpl")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("ingress")))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("IngressDeparserImpl")))); + auto *packageName = new IR::Type_Name("IngressPipeline"); + return new IR::Declaration_Instance("ip", packageName, args); +} + +IR::Declaration_Instance *generateEgressPipeDeclaration() { + auto *args = new IR::Vector(); + + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("EgressParserImpl")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("egress")))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("EgressDeparserImpl")))); + auto *packageName = new IR::Type_Name("EgressPipeline"); + return new IR::Declaration_Instance("ep", packageName, args); +} + +IR::Declaration_Instance *generateMainPsaPackage() { + auto *args = new IR::Vector(); + args->push_back(new IR::Argument(new IR::TypeNameExpression("ip"))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("PacketReplicationEngine")))); + args->push_back(new IR::Argument(new IR::TypeNameExpression("ep"))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("BufferingQueueingEngine")))); + auto *packageName = new IR::Type_Name("PSA_Switch"); + return new IR::Declaration_Instance("main", packageName, args); +} + +IR::Type_Struct *generateUserMetadataType() { + // Do !emit meta fields for now, no need + IR::IndexedVector fields; + auto *ret = new IR::Type_Struct("metadata_t", fields); + P4Scope::addToScope(ret); + return ret; +} + +IR::Type_Struct *generateEmptyMetadataType() { + // Do !emit meta fields for now, no need + IR::IndexedVector fields; + auto *ret = new IR::Type_Struct("empty_t", fields); + P4Scope::addToScope(ret); + return ret; +} + +void generatePsaMetadata() { + IR::ID *name = nullptr; + IR::Type_Struct *ret = nullptr; + IR::IndexedVector fields; + + name = new IR::ID("psa_ingress_parser_input_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); + name = new IR::ID("psa_ingress_input_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); + name = new IR::ID("psa_ingress_output_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); + name = new IR::ID("psa_egress_input_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); + name = new IR::ID("psa_egress_output_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); +} + +void setBmv2PsaProbabilities() { + PCT.PARAMETER_NONEDIR_DERIVED_STRUCT = 0; + PCT.PARAMETER_NONEDIR_DERIVED_HEADER = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_BOOL = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_ERROR = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_STRING = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_VARBIT = 0; +} + +} // namespace + +int Bmv2PsaSmithTarget::writeTargetPreamble(std::ostream *ostream) const { + *ostream << "#include \n"; + *ostream << "#include \n\n"; + *ostream << "bit<3> max(in bit<3> val, in bit<3> bound) {\n"; + *ostream << " return val < bound ? val : bound;\n"; + *ostream << "}\n"; + return EXIT_SUCCESS; +} + +const IR::P4Program *Bmv2PsaSmithTarget::generateP4Program() const { + P4Scope::startLocalScope(); + + // insert banned structures + P4Scope::notInitializedStructs.insert("psa_ingress_parser_input_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("psa_ingress_input_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("psa_ingress_output_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("psa_egress_input_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("psa_egress_output_metadata_t"_cs); + // set psa-specific probabilities + setBmv2PsaProbabilities(); + // insert some dummy metadata + generatePsaMetadata(); + + // start to assemble the model + auto *objects = new IR::Vector(); + + objects->push_back(declarationGenerator().genEthernetHeaderType()); + + // generate some declarations + int typeDecls = Utils::getRandInt(DECL.MIN_TYPE, DECL.MAX_TYPE); + for (int i = 0; i < typeDecls; ++i) { + objects->push_back(declarationGenerator().genTypeDeclaration()); + } + + // generate struct Headers + objects->push_back(declarationGenerator().genHeaderStruct()); + // generate struct metadata_t + objects->push_back(generateUserMetadataType()); + // generate struct empty_t + objects->push_back(generateEmptyMetadataType()); + + // generate some callables + int callableDecls = Utils::getRandInt(DECL.MIN_CALLABLES, DECL.MAX_CALLABLES); + for (int i = 0; i < callableDecls; ++i) { + std::vector percent = {80, 15, 0, 5}; + switch (Utils::getRandInt(percent)) { + case 0: { + objects->push_back(declarationGenerator().genFunctionDeclaration()); + break; + } + case 1: { + objects->push_back(declarationGenerator().genActionDeclaration()); + break; + } + case 2: { + objects->push_back(declarationGenerator().genExternDeclaration()); + break; + } + case 3: { + objects->push_back(declarationGenerator().genControlDeclaration()); + break; + } + } + } + + // generate all the necessary pipelines for the package + objects->push_back(generateIngressParserBlock()); + objects->push_back(generateIngressBlock()); + objects->push_back(generateIngressDeparserBlock()); + objects->push_back(generateEgressParserBlock()); + objects->push_back(generateEgressBlock()); + objects->push_back(generateEgressDeparserBlock()); + + // finally assemble the package + objects->push_back(generateIngressPipeDeclaration()); + objects->push_back(generateEgressPipeDeclaration()); + objects->push_back(generateMainPsaPackage()); + + return new IR::P4Program(*objects); +} + +} // namespace P4Tools::P4Smith::BMv2 diff --git a/backends/p4tools/modules/smith/targets/bmv2/psa.h b/backends/p4tools/modules/smith/targets/bmv2/psa.h new file mode 100644 index 00000000000..a35d950abf7 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/psa.h @@ -0,0 +1,61 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_PSA_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_PSA_H_ + +#include + +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/common/table.h" +#include "backends/p4tools/modules/smith/targets/bmv2/target.h" +#include "ir/ir.h" + +namespace P4Tools::P4Smith::BMv2 { + +class Bmv2PsaSmithTarget : public AbstractBMv2SmithTarget { + private: + DeclarationGenerator *_declarationGenerator = new DeclarationGenerator(*this); + ExpressionGenerator *_expressionGenerator = new ExpressionGenerator(*this); + StatementGenerator *_statementGenerator = new StatementGenerator(*this); + ParserGenerator *_parserGenerator = new ParserGenerator(*this); + TableGenerator *_tableGenerator = new TableGenerator(*this); + + [[nodiscard]] IR::P4Parser *generateIngressParserBlock() const; + [[nodiscard]] IR::P4Control *generateIngressBlock() const; + [[nodiscard]] IR::P4Control *generateIngressDeparserBlock() const; + [[nodiscard]] IR::P4Parser *generateEgressParserBlock() const; + [[nodiscard]] IR::P4Control *generateEgressBlock() const; + [[nodiscard]] IR::P4Control *generateEgressDeparserBlock() const; + + public: + /// Registers this target. + static void make(); + + [[nodiscard]] int writeTargetPreamble(std::ostream *ostream) const override; + + [[nodiscard]] const IR::P4Program *generateP4Program() const override; + + [[nodiscard]] DeclarationGenerator &declarationGenerator() const override { + return *_declarationGenerator; + } + + [[nodiscard]] ExpressionGenerator &expressionGenerator() const override { + return *_expressionGenerator; + } + + [[nodiscard]] StatementGenerator &statementGenerator() const override { + return *_statementGenerator; + } + + [[nodiscard]] ParserGenerator &parserGenerator() const override { return *_parserGenerator; } + + [[nodiscard]] TableGenerator &tableGenerator() const override { return *_tableGenerator; } + + private: + Bmv2PsaSmithTarget(); +}; + +} // namespace P4Tools::P4Smith::BMv2 + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_PSA_H_ */ diff --git a/backends/p4tools/modules/smith/targets/bmv2/register.h b/backends/p4tools/modules/smith/targets/bmv2/register.h new file mode 100644 index 00000000000..a39a1e3a82d --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/register.h @@ -0,0 +1,16 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_REGISTER_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_REGISTER_H_ + +#include "backends/p4tools/modules/smith/targets/bmv2/psa.h" +#include "backends/p4tools/modules/smith/targets/bmv2/v1model.h" + +namespace P4Tools::P4Smith { + +inline void bmv2RegisterSmithTarget() { + BMv2::Bmv2V1modelSmithTarget::make(); + BMv2::Bmv2PsaSmithTarget::make(); +} + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_REGISTER_H_ */ diff --git a/backends/p4tools/modules/smith/targets/bmv2/target.cpp b/backends/p4tools/modules/smith/targets/bmv2/target.cpp new file mode 100644 index 00000000000..fb5e299651e --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/target.cpp @@ -0,0 +1,18 @@ +#include "backends/p4tools/modules/smith/targets/bmv2/target.h" + +#include +#include + +#include "backends/p4tools/modules/smith/core/target.h" + +namespace P4Tools::P4Smith::BMv2 { + +/* ============================================================================================= + * AbstractBMv2SmithTarget implementation + * ============================================================================================= */ + +AbstractBMv2SmithTarget::AbstractBMv2SmithTarget(const std::string &deviceName, + const std::string &archName) + : SmithTarget(deviceName, archName) {} + +} // namespace P4Tools::P4Smith::BMv2 diff --git a/backends/p4tools/modules/smith/targets/bmv2/target.h b/backends/p4tools/modules/smith/targets/bmv2/target.h new file mode 100644 index 00000000000..d99673ab1a6 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/target.h @@ -0,0 +1,16 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_TARGET_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_TARGET_H_ + +#include + +#include "backends/p4tools/modules/smith/core/target.h" + +namespace P4Tools::P4Smith::BMv2 { + +class AbstractBMv2SmithTarget : public SmithTarget { + protected: + explicit AbstractBMv2SmithTarget(const std::string &deviceName, const std::string &archName); +}; +} // namespace P4Tools::P4Smith::BMv2 + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_TARGET_H_ */ diff --git a/backends/p4tools/modules/smith/targets/bmv2/test/P4Tests.cmake b/backends/p4tools/modules/smith/targets/bmv2/test/P4Tests.cmake new file mode 100644 index 00000000000..a9b7ca40983 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/test/P4Tests.cmake @@ -0,0 +1,13 @@ +# This file defines how to execute Smith on P4 programs. +# General test utilities. +include(${P4TOOLS_SOURCE_DIR}/cmake/TestUtils.cmake) + +set(SMITH_BMV2_CMD ${smith_SOURCE_DIR}/scripts/compilation-test.sh) + +set(SMITH_BMV2_V1MODEL_ARGS 100 ${P4SMITH_DRIVER} ${CMAKE_BINARY_DIR}/p4c-bm2-ss ${CMAKE_BINARY_DIR} bmv2 v1model) + +add_test (NAME smith-compile-bmv2-v1model COMMAND ${SMITH_BMV2_CMD} ${SMITH_BMV2_V1MODEL_ARGS} WORKING_DIRECTORY ${P4C_BINARY_DIR}) + +set(SMITH_BMV2_PSA_ARGS 100 ${P4SMITH_DRIVER} ${CMAKE_BINARY_DIR}/p4c-bm2-psa ${CMAKE_BINARY_DIR} bmv2 psa) + +add_test (NAME smith-compile-bmv2-psa COMMAND ${SMITH_BMV2_CMD} ${SMITH_BMV2_PSA_ARGS} WORKING_DIRECTORY ${P4C_BINARY_DIR}) diff --git a/backends/p4tools/modules/smith/targets/bmv2/v1model.cpp b/backends/p4tools/modules/smith/targets/bmv2/v1model.cpp new file mode 100644 index 00000000000..5ecfbfa2b1a --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/v1model.cpp @@ -0,0 +1,385 @@ +#include "backends/p4tools/modules/smith/targets/bmv2/v1model.h" + +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/targets/bmv2/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" +#include "ir/node.h" +#include "ir/vector.h" + +namespace P4Tools::P4Smith::BMv2 { + +using namespace P4::literals; + +/* ============================================================================================= + * Bmv2V1modelSmithTarget implementation + * ============================================================================================= */ + +Bmv2V1modelSmithTarget::Bmv2V1modelSmithTarget() : AbstractBMv2SmithTarget("bmv2", "v1model") {} + +void Bmv2V1modelSmithTarget::make() { + static Bmv2V1modelSmithTarget *INSTANCE = nullptr; + if (INSTANCE == nullptr) { + INSTANCE = new Bmv2V1modelSmithTarget(); + } +} + +IR::P4Parser *Bmv2V1modelSmithTarget::generateParserBlock() const { + IR::IndexedVector parserLocals; + + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_in"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "hdr"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "m"_cs, "Meta"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "sm"_cs, + "standard_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeParser = new IR::Type_Parser("p", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + // generate decls + /* for (int i = 0; i < 5; i++) { + auto var_decl = variableDeclaration::gen(); + parserLocals.push_back(var_decl); + }*/ + + // generate states + IR::IndexedVector states; + states.push_back(parserGenerator().genStartState()); + states.push_back(parserGenerator().genHdrStates()); + + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4parser = new IR::P4Parser("p", typeParser, parserLocals, states); + P4Scope::addToScope(p4parser); + return p4parser; +} + +IR::P4Control *Bmv2V1modelSmithTarget::generateIngressBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "m"_cs, "Meta"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "sm"_cs, + "standard_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("ingress", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + IR::IndexedVector localDecls = declarationGenerator().genLocalControlDecls(); + // apply body + auto *applyBlock = statementGenerator().genBlockStatement(false); + + // end of scope + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4ctrl = new IR::P4Control("ingress", typeCtrl, localDecls, applyBlock); + P4Scope::addToScope(p4ctrl); + return p4ctrl; +} + +IR::P4Control *Bmv2V1modelSmithTarget::generateVerifyBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "m"_cs, "Meta"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("vrfy", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + + return new IR::P4Control("vrfy", typeCtrl, localDecls, blkStat); +} + +IR::P4Control *Bmv2V1modelSmithTarget::generateUpdateBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "m"_cs, "Meta"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("update", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + + return new IR::P4Control("update", typeCtrl, localDecls, blkStat); +} + +IR::P4Control *Bmv2V1modelSmithTarget::generateEgressBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "m"_cs, "Meta"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "sm"_cs, + "standard_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("egress", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + + return new IR::P4Control("egress", typeCtrl, localDecls, blkStat); +} + +IR::MethodCallStatement *generateDeparserEmitCall() { + auto *call = new IR::PathExpression("pkt.emit"); + auto *args = new IR::Vector(); + + args->push_back(new IR::Argument(new IR::PathExpression("h"))); + + auto *mce = new IR::MethodCallExpression(call, args); + auto *mst = new IR::MethodCallStatement(mce); + return mst; +} + +IR::P4Control *Bmv2V1modelSmithTarget::generateDeparserBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_out"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "h"_cs, SYS_HDR_NAME)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("deparser"_cs, parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + blkStat->push_back(generateDeparserEmitCall()); + + return new IR::P4Control("deparser"_cs, typeCtrl, localDecls, blkStat); +} + +IR::Declaration_Instance *generateMainV1ModelPackage() { + auto *args = new IR::Vector(); + + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("p")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("vrfy")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("ingress")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("egress")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("update")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("deparser")))); + auto *packageName = new IR::Type_Name("V1Switch"); + return new IR::Declaration_Instance("main", packageName, args); +} + +IR::Type_Struct *generateMetadataStructure() { + // Do !emit meta fields for now, no need + // FIXME: Design a way to emit these that plays nicely with all targets + // auto sfl = new structFieldList(STRUCT, name->name); + // IR::IndexedVector< IR::StructField > fields = sfl->gen(Utils::getRandInt(1, + // 5)); + IR::IndexedVector fields; + + auto *ret = new IR::Type_Struct("Meta", fields); + + P4Scope::addToScope(ret); + + return ret; +} + +IR::IndexedVector generateStandardMetadataVector() { + IR::IndexedVector fields; + + // IR::ID *name; + // IR::Type *tp; + + /* + name = new IR::ID("ingress_port"); + tp = IR::Type_Bits::get(9, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("egress_spec"); + tp = IR::Type_Bits::get(9, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("egress_port"); + tp = IR::Type_Bits::get(9, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("instance_type"); + tp = IR::Type_Bits::get(32, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("packet_length"); + tp = IR::Type_Bits::get(32, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("enq_timestamp"); + tp = IR::Type_Bits::get(32, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("enq_qdepth"); + tp = IR::Type_Bits::get(19, false); + fields.push_back(new IR::StructField(*name, tp)); + // name = new IR::ID("dep_timedelta"); + // tp = IR::Type_Bits::get(32, false); + // fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("deq_qdepth"); + tp = IR::Type_Bits::get(19, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("ingress_global_timestamp"); + tp = IR::Type_Bits::get(48, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("egress_global_timestamp"); + tp = IR::Type_Bits::get(48, false); + fields.push_back(new IR::StructField(*name, tp)); + name = new IR::ID("egress_rid"); + tp = IR::Type_Bits::get(16, false); + fields.push_back(new IR::StructField(*name, tp)); + // Tao: error is omitted here + name = new IR::ID("priority"); + tp = IR::Type_Bits::get(3, false); + fields.push_back(new IR::StructField(*name, tp)); + */ + + return fields; +} + +IR::Type_Struct *generateStandardMetadataStructure() { + auto fields = generateStandardMetadataVector(); + + auto *ret = new IR::Type_Struct("standard_metadata_t", fields); + + P4Scope::addToScope(ret); + + return ret; +} + +void setProbabilitiesforBmv2V1model() { + PCT.PARAMETER_NONEDIR_DERIVED_STRUCT = 0; + PCT.PARAMETER_NONEDIR_DERIVED_HEADER = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_BOOL = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_ERROR = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_STRING = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_VARBIT = 0; + // V1Model does !support headers that are !multiples of 8 + PCT.STRUCTTYPEDECLARATION_BASETYPE_BOOL = 0; + // V1Model requires headers to be byte-aligned + P4Scope::req.byte_align_headers = true; +} + +int Bmv2V1modelSmithTarget::writeTargetPreamble(std::ostream *ostream) const { + *ostream << "#include \n"; + *ostream << "#include \n\n"; + *ostream << "bit<3> max(in bit<3> val, in bit<3> bound) {\n"; + *ostream << " return val < bound ? val : bound;\n"; + *ostream << "}\n"; + return EXIT_SUCCESS; +} + +const IR::P4Program *Bmv2V1modelSmithTarget::generateP4Program() const { + P4Scope::startLocalScope(); + + // insert banned structures + P4Scope::notInitializedStructs.insert("standard_metadata_t"_cs); + // Set bmv2-v1model-specific probabilities. + setProbabilitiesforBmv2V1model(); + + // start to assemble the model + auto *objects = new IR::Vector(); + + objects->push_back(declarationGenerator().genEthernetHeaderType()); + + // generate some declarations + int typeDecls = Utils::getRandInt(DECL.MIN_TYPE, DECL.MAX_TYPE); + for (int i = 0; i < typeDecls; ++i) { + objects->push_back(declarationGenerator().genTypeDeclaration()); + } + + // generate struct Headers + objects->push_back(declarationGenerator().genHeaderStruct()); + + // generate struct Meta + objects->push_back(generateMetadataStructure()); + // insert standard_metadata_t + generateStandardMetadataStructure(); + + // generate some callables + int callableDecls = Utils::getRandInt(DECL.MIN_CALLABLES, DECL.MAX_CALLABLES); + for (int i = 0; i < callableDecls; ++i) { + std::vector percent = {80, 15, 0, 5}; + switch (Utils::getRandInt(percent)) { + case 0: { + objects->push_back(declarationGenerator().genFunctionDeclaration()); + break; + } + case 1: { + objects->push_back(declarationGenerator().genActionDeclaration()); + break; + } + case 2: { + objects->push_back(declarationGenerator().genExternDeclaration()); + break; + } + case 3: { + objects->push_back(declarationGenerator().genControlDeclaration()); + break; + } + } + } + + // generate all the necessary pipelines for the package + objects->push_back(generateParserBlock()); + objects->push_back(generateIngressBlock()); + objects->push_back(generateVerifyBlock()); + objects->push_back(generateUpdateBlock()); + objects->push_back(generateEgressBlock()); + objects->push_back(generateDeparserBlock()); + + // finally assemble the package + objects->push_back(generateMainV1ModelPackage()); + + return new IR::P4Program(*objects); +} + +} // namespace P4Tools::P4Smith::BMv2 diff --git a/backends/p4tools/modules/smith/targets/bmv2/v1model.h b/backends/p4tools/modules/smith/targets/bmv2/v1model.h new file mode 100644 index 00000000000..4db53fd4c2b --- /dev/null +++ b/backends/p4tools/modules/smith/targets/bmv2/v1model.h @@ -0,0 +1,61 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_V1MODEL_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_V1MODEL_H_ + +#include + +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/common/table.h" +#include "backends/p4tools/modules/smith/targets/bmv2/target.h" +#include "ir/ir.h" + +namespace P4Tools::P4Smith::BMv2 { + +class Bmv2V1modelSmithTarget : public AbstractBMv2SmithTarget { + private: + DeclarationGenerator *_declarationGenerator = new DeclarationGenerator(*this); + ExpressionGenerator *_expressionGenerator = new ExpressionGenerator(*this); + StatementGenerator *_statementGenerator = new StatementGenerator(*this); + ParserGenerator *_parserGenerator = new ParserGenerator(*this); + TableGenerator *_tableGenerator = new TableGenerator(*this); + + [[nodiscard]] IR::P4Parser *generateParserBlock() const; + [[nodiscard]] IR::P4Control *generateIngressBlock() const; + [[nodiscard]] IR::P4Control *generateUpdateBlock() const; + [[nodiscard]] IR::P4Control *generateVerifyBlock() const; + [[nodiscard]] IR::P4Control *generateEgressBlock() const; + [[nodiscard]] IR::P4Control *generateDeparserBlock() const; + + public: + /// Registers this target. + static void make(); + + [[nodiscard]] int writeTargetPreamble(std::ostream *ostream) const override; + + [[nodiscard]] const IR::P4Program *generateP4Program() const override; + + [[nodiscard]] DeclarationGenerator &declarationGenerator() const override { + return *_declarationGenerator; + } + + [[nodiscard]] ExpressionGenerator &expressionGenerator() const override { + return *_expressionGenerator; + } + + [[nodiscard]] StatementGenerator &statementGenerator() const override { + return *_statementGenerator; + } + + [[nodiscard]] ParserGenerator &parserGenerator() const override { return *_parserGenerator; } + + [[nodiscard]] TableGenerator &tableGenerator() const override { return *_tableGenerator; } + + private: + Bmv2V1modelSmithTarget(); +}; + +} // namespace P4Tools::P4Smith::BMv2 + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_BMV2_V1MODEL_H_ */ diff --git a/backends/p4tools/modules/smith/targets/generic/CMakeLists.txt b/backends/p4tools/modules/smith/targets/generic/CMakeLists.txt new file mode 100644 index 00000000000..61e5469efbd --- /dev/null +++ b/backends/p4tools/modules/smith/targets/generic/CMakeLists.txt @@ -0,0 +1,14 @@ +if(ENABLE_TESTING) + # Include the test subdirectory. + message("-- Adding p4smith generic test suite") + include(test/P4Tests.cmake) +endif() + +# Source files for smith. +set( + SMITH_SOURCES + ${SMITH_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/target.cpp + PARENT_SCOPE +) + diff --git a/backends/p4tools/modules/smith/targets/generic/register.h b/backends/p4tools/modules/smith/targets/generic/register.h new file mode 100644 index 00000000000..71f562c9eee --- /dev/null +++ b/backends/p4tools/modules/smith/targets/generic/register.h @@ -0,0 +1,12 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_GENERIC_REGISTER_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_GENERIC_REGISTER_H_ + +#include "backends/p4tools/modules/smith/targets/generic/target.h" + +namespace P4Tools::P4Smith { + +inline void genericRegisterSmithTarget() { Generic::GenericCoreSmithTarget::make(); } + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_GENERIC_REGISTER_H_ */ diff --git a/backends/p4tools/modules/smith/targets/generic/target.cpp b/backends/p4tools/modules/smith/targets/generic/target.cpp new file mode 100644 index 00000000000..5a9303d5c5f --- /dev/null +++ b/backends/p4tools/modules/smith/targets/generic/target.cpp @@ -0,0 +1,225 @@ +#include "backends/p4tools/modules/smith/targets/generic/target.h" + +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" +#include "ir/node.h" +#include "ir/vector.h" + +namespace P4Tools::P4Smith::Generic { + +using namespace P4::literals; + +/* ============================================================================================= + * AbstractGenericSmithTarget implementation + * ============================================================================================= */ + +AbstractGenericSmithTarget::AbstractGenericSmithTarget(const std::string &deviceName, + const std::string &archName) + : SmithTarget(deviceName, archName) {} + +/* ============================================================================================= + * GenericCoreSmithTarget implementation + * ============================================================================================= */ + +GenericCoreSmithTarget::GenericCoreSmithTarget() : AbstractGenericSmithTarget("generic", "core") {} + +void GenericCoreSmithTarget::make() { + static GenericCoreSmithTarget *INSTANCE = nullptr; + if (INSTANCE == nullptr) { + INSTANCE = new GenericCoreSmithTarget(); + } +} + +IR::P4Parser *GenericCoreSmithTarget::generateParserBlock() const { + IR::IndexedVector parserLocals; + + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_in"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "hdr"_cs, SYS_HDR_NAME)); + auto *parList = new IR::ParameterList(params); + auto *typeParser = new IR::Type_Parser("p", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + // generate decls + for (int i = 0; i < 5; i++) { + auto *varDecl = declarationGenerator().genVariableDeclaration(); + parserLocals.push_back(varDecl); + } + + // generate states + parserGenerator().buildParserTree(); + IR::IndexedVector states = parserGenerator().getStates(); + + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4parser = new IR::P4Parser("p", typeParser, parserLocals, states); + P4Scope::addToScope(p4parser); + return p4parser; +} + +IR::P4Control *GenericCoreSmithTarget::generateIngressBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("ingress", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + IR::IndexedVector localDecls = declarationGenerator().genLocalControlDecls(); + // apply body + auto *applyBlock = statementGenerator().genBlockStatement(false); + + // end of scope + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4ctrl = new IR::P4Control("ingress", typeCtrl, localDecls, applyBlock); + P4Scope::addToScope(p4ctrl); + return p4ctrl; +} + +IR::Declaration_Instance *GenericCoreSmithTarget::generateMainPackage() { + auto *args = new IR::Vector(); + + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("p")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("ingress")))); + auto *packageName = new IR::Type_Name("top"); + return new IR::Declaration_Instance("main", packageName, args); +} + +IR::Type_Parser *GenericCoreSmithTarget::generateParserBlockType() const { + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "b"_cs, "packet_in"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "hdr"_cs, SYS_HDR_NAME)); + auto *parList = new IR::ParameterList(params); + return new IR::Type_Parser("Parser", parList); +} + +IR::Type_Control *GenericCoreSmithTarget::generateIngressBlockType() const { + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "hdr"_cs, SYS_HDR_NAME)); + auto *parList = new IR::ParameterList(params); + return new IR::Type_Control("Ingress", parList); +} + +IR::Type_Package *GenericCoreSmithTarget::generatePackageType() const { + IR::IndexedVector params; + params.push_back(declarationGenerator().genParameter(IR::Direction::None, "p"_cs, "Parser"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "ig"_cs, "Ingress"_cs)); + auto *parList = new IR::ParameterList(params); + return new IR::Type_Package("top", parList); +} + +int GenericCoreSmithTarget::writeTargetPreamble(std::ostream *ostream) const { + *ostream << "#include \n"; + *ostream << "bit<3> max(in bit<3> val, in bit<3> bound) {\n"; + *ostream << " return val < bound ? val : bound;\n"; + *ostream << "}\n"; + return EXIT_SUCCESS; +} + +const IR::P4Program *GenericCoreSmithTarget::generateP4Program() const { + P4Scope::startLocalScope(); + + // start to assemble the model + auto *objects = new IR::Vector(); + + objects->push_back(declarationGenerator().genEthernetHeaderType()); + + // generate some declarations + int typeDecls = Utils::getRandInt(DECL.MIN_TYPE, DECL.MAX_TYPE); + for (int i = 0; i < typeDecls; ++i) { + objects->push_back(declarationGenerator().genTypeDeclaration()); + } + + // generate struct Headers + objects->push_back(declarationGenerator().genHeaderStruct()); + + // generate some callables + int callableDecls = Utils::getRandInt(DECL.MIN_CALLABLES, DECL.MAX_CALLABLES); + for (int i = 0; i < callableDecls; ++i) { + std::vector percent = {70, 15, 10, 5}; + switch (Utils::getRandInt(percent)) { + case 0: { + objects->push_back(declarationGenerator().genFunctionDeclaration()); + break; + } + case 1: { + objects->push_back(declarationGenerator().genActionDeclaration()); + break; + } + case 2: { + objects->push_back(declarationGenerator().genExternDeclaration()); + break; + } + case 3: { + objects->push_back(declarationGenerator().genControlDeclaration()); + break; + } + } + } + + // generate all the necessary pipelines for the package + objects->push_back(generateParserBlock()); + objects->push_back(generateIngressBlock()); + + // finally assemble the package + objects->push_back(generateParserBlockType()); + objects->push_back(generateIngressBlockType()); + objects->push_back(generatePackageType()); + objects->push_back(generateMainPackage()); + + return new IR::P4Program(*objects); +} + +} // namespace P4Tools::P4Smith::Generic diff --git a/backends/p4tools/modules/smith/targets/generic/target.h b/backends/p4tools/modules/smith/targets/generic/target.h new file mode 100644 index 00000000000..1a5bef9b69a --- /dev/null +++ b/backends/p4tools/modules/smith/targets/generic/target.h @@ -0,0 +1,68 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_GENERIC_TARGET_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_GENERIC_TARGET_H_ + +#include +#include + +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/common/table.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "ir/ir.h" + +namespace P4Tools::P4Smith::Generic { + +class AbstractGenericSmithTarget : public SmithTarget { + protected: + explicit AbstractGenericSmithTarget(const std::string &deviceName, const std::string &archName); +}; + +class GenericCoreSmithTarget : public AbstractGenericSmithTarget { + private: + DeclarationGenerator *_declarationGenerator = new DeclarationGenerator(*this); + ExpressionGenerator *_expressionGenerator = new ExpressionGenerator(*this); + StatementGenerator *_statementGenerator = new StatementGenerator(*this); + ParserGenerator *_parserGenerator = new ParserGenerator(*this); + TableGenerator *_tableGenerator = new TableGenerator(*this); + + [[nodiscard]] IR::P4Parser *generateParserBlock() const; + [[nodiscard]] IR::P4Control *generateIngressBlock() const; + [[nodiscard]] static IR::Declaration_Instance *generateMainPackage(); + + [[nodiscard]] IR::Type_Parser *generateParserBlockType() const; + [[nodiscard]] IR::Type_Control *generateIngressBlockType() const; + [[nodiscard]] IR::Type_Package *generatePackageType() const; + + public: + /// Registers this target. + static void make(); + + [[nodiscard]] int writeTargetPreamble(std::ostream *ostream) const override; + + [[nodiscard]] const IR::P4Program *generateP4Program() const override; + + [[nodiscard]] DeclarationGenerator &declarationGenerator() const override { + return *_declarationGenerator; + } + + [[nodiscard]] ExpressionGenerator &expressionGenerator() const override { + return *_expressionGenerator; + } + + [[nodiscard]] StatementGenerator &statementGenerator() const override { + return *_statementGenerator; + } + + [[nodiscard]] ParserGenerator &parserGenerator() const override { return *_parserGenerator; } + + [[nodiscard]] TableGenerator &tableGenerator() const override { return *_tableGenerator; } + + private: + GenericCoreSmithTarget(); +}; + +} // namespace P4Tools::P4Smith::Generic + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_GENERIC_TARGET_H_ */ diff --git a/backends/p4tools/modules/smith/targets/generic/test/P4Tests.cmake b/backends/p4tools/modules/smith/targets/generic/test/P4Tests.cmake new file mode 100644 index 00000000000..cf3729ccd3d --- /dev/null +++ b/backends/p4tools/modules/smith/targets/generic/test/P4Tests.cmake @@ -0,0 +1,10 @@ +# This file defines how to execute Smith on P4 programs. +# General test utilities. +include(${P4TOOLS_SOURCE_DIR}/cmake/TestUtils.cmake) + +set(SMITH_BMV2_CMD ${smith_SOURCE_DIR}/scripts/compilation-test.sh) + +set(SMITH_BMV2_ARGS 100 ${P4SMITH_DRIVER} ${CMAKE_BINARY_DIR}/p4test ${CMAKE_BINARY_DIR} generic core) + +add_test(NAME smith-compile-core COMMAND ${SMITH_BMV2_CMD} ${SMITH_BMV2_ARGS} WORKING_DIRECTORY ${P4C_BINARY_DIR}) + diff --git a/backends/p4tools/modules/smith/targets/nic/CMakeLists.txt b/backends/p4tools/modules/smith/targets/nic/CMakeLists.txt new file mode 100644 index 00000000000..cc538cea6f8 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/nic/CMakeLists.txt @@ -0,0 +1,14 @@ +if(ENABLE_TESTING) + # Include the test subdirectory. + message("-- Adding p4smith nic test suite") + include(test/P4Tests.cmake) +endif() + +# Source files for smith. +set( + SMITH_SOURCES + ${SMITH_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/target.cpp + PARENT_SCOPE +) + diff --git a/backends/p4tools/modules/smith/targets/nic/register.h b/backends/p4tools/modules/smith/targets/nic/register.h new file mode 100644 index 00000000000..36c256a884c --- /dev/null +++ b/backends/p4tools/modules/smith/targets/nic/register.h @@ -0,0 +1,12 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_NIC_REGISTER_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_NIC_REGISTER_H_ + +#include "backends/p4tools/modules/smith/targets/nic/target.h" + +namespace P4Tools::P4Smith { + +inline void nicRegisterSmithTarget() { Nic::DpdkPnaSmithTarget::make(); } + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_NIC_REGISTER_H_ */ diff --git a/backends/p4tools/modules/smith/targets/nic/target.cpp b/backends/p4tools/modules/smith/targets/nic/target.cpp new file mode 100644 index 00000000000..9da0b28d6af --- /dev/null +++ b/backends/p4tools/modules/smith/targets/nic/target.cpp @@ -0,0 +1,318 @@ +#include "backends/p4tools/modules/smith/targets/nic/target.h" + +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" +#include "ir/node.h" +#include "ir/vector.h" + +namespace P4Tools::P4Smith::Nic { + +using namespace P4::literals; + +/* ============================================================================================= + * AbstractNicSmithTarget implementation + * ============================================================================================= */ + +AbstractNicSmithTarget::AbstractNicSmithTarget(const std::string &deviceName, + const std::string &archName) + : SmithTarget(deviceName, archName) {} + +/* ============================================================================================= + * DpdkPnaSmithTarget implementation + * ============================================================================================= */ + +DpdkPnaSmithTarget::DpdkPnaSmithTarget() : AbstractNicSmithTarget("dpdk", "pna") {} + +void DpdkPnaSmithTarget::make() { + static DpdkPnaSmithTarget *INSTANCE = nullptr; + if (INSTANCE == nullptr) { + INSTANCE = new DpdkPnaSmithTarget(); + } +} + +IR::P4Parser *DpdkPnaSmithTarget::generateMainParserBlock() const { + IR::IndexedVector parserLocals; + P4Scope::startLocalScope(); + + // generate type_parser !that this is labeled "p" + IR::IndexedVector params; + + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_in"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "hdr"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "user_meta"_cs, + "main_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "pna_main_parser_input_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + + auto *tpParser = new IR::Type_Parser("MainParserImpl", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + // // generate decls + // for (int i = 0; i < 5; i++) { + // auto var_decl = variableDeclaration::gen(); + // parserLocals.push_back(var_decl); + // } + + // generate states + IR::IndexedVector states; + auto *startState = parserGenerator().genStartState(); + states.push_back(startState); + states.push_back(parserGenerator().genHdrStates()); + + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4parser = new IR::P4Parser("MainParserImpl", tpParser, parserLocals, states); + P4Scope::addToScope(p4parser); + return p4parser; +} + +IR::P4Control *DpdkPnaSmithTarget::generatePreControlBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "hdr"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "user_meta"_cs, + "main_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "pna_pre_input_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "ostd"_cs, + "pna_pre_output_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("PreControlImpl", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + + return new IR::P4Control("PreControlImpl", typeCtrl, localDecls, blkStat); +} + +IR::P4Control *DpdkPnaSmithTarget::generateMainControlBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "hdr"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "user_meta"_cs, + "main_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "istd"_cs, + "pna_main_input_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "ostd"_cs, + "pna_main_output_metadata_t"_cs)); + + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("MainControlImpl", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + IR::IndexedVector localDecls = declarationGenerator().genLocalControlDecls(); + // apply body + auto *applyBlock = statementGenerator().genBlockStatement(false); + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4ctrl = new IR::P4Control("MainControlImpl", typeCtrl, localDecls, applyBlock); + P4Scope::addToScope(p4ctrl); + return p4ctrl; +} + +IR::MethodCallStatement *generateDeparserEmitCall() { + auto *call = new IR::PathExpression("pkt.emit"); + auto *args = new IR::Vector(); + + args->push_back(new IR::Argument(new IR::PathExpression("hdr"))); + + auto *mce = new IR::MethodCallExpression(call, args); + auto *mst = new IR::MethodCallStatement(mce); + return mst; +} + +IR::P4Control *DpdkPnaSmithTarget::generateMainDeparserBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_out"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "hdr"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "user_meta"_cs, + "main_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "ostd"_cs, + "pna_main_output_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("MainDeparserImpl", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + blkStat->push_back(generateDeparserEmitCall()); + + return new IR::P4Control("MainDeparserImpl", typeCtrl, localDecls, blkStat); +} + +IR::Declaration_Instance *generateMainPackage() { + auto *args = new IR::Vector(); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("MainParserImpl")))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("PreControlImpl")))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("MainControlImpl")))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("MainDeparserImpl")))); + auto *packageName = new IR::Type_Name("PNA_NIC"); + return new IR::Declaration_Instance("main", packageName, args); +} + +IR::Type_Struct *generateUserMetadata() { + // Do not emit meta fields for now, no need + IR::IndexedVector fields; + auto *ret = new IR::Type_Struct("main_metadata_t", fields); + P4Scope::addToScope(ret); + return ret; +} + +void generateMainMetadata() { + IR::ID *name = nullptr; + IR::Type_Struct *ret = nullptr; + IR::IndexedVector fields; + + name = new IR::ID("pna_main_parser_input_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); + name = new IR::ID("pna_pre_input_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); + name = new IR::ID("pna_pre_output_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); + name = new IR::ID("pna_main_input_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); + name = new IR::ID("pna_main_output_metadata_t"); + ret = new IR::Type_Struct(*name, fields); + P4Scope::addToScope(ret); +} + +void setPnaDpdkProbabilities() { + PCT.PARAMETER_NONEDIR_DERIVED_STRUCT = 0; + PCT.PARAMETER_NONEDIR_DERIVED_HEADER = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_BOOL = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_ERROR = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_STRING = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_VARBIT = 0; +} + +int DpdkPnaSmithTarget::writeTargetPreamble(std::ostream *ostream) const { + *ostream << "#include \n"; + *ostream << "#include \n\n"; + *ostream << "bit<3> max(in bit<3> val, in bit<3> bound) {\n"; + *ostream << " return val < bound ? val : bound;\n"; + *ostream << "}\n"; + return EXIT_SUCCESS; +} + +const IR::P4Program *DpdkPnaSmithTarget::generateP4Program() const { + P4Scope::startLocalScope(); + // insert banned structures + P4Scope::notInitializedStructs.insert("psa_ingress_parser_input_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("psa_ingress_input_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("psa_ingress_output_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("psa_egress_input_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("psa_egress_output_metadata_t"_cs); + // set psa-specific probabilities + setPnaDpdkProbabilities(); + // insert some dummy metadata + generateMainMetadata(); + + // start to assemble the model + auto *objects = new IR::Vector(); + + objects->push_back(declarationGenerator().genEthernetHeaderType()); + + // generate some declarations + int typeDecls = Utils::getRandInt(DECL.MIN_TYPE, DECL.MAX_TYPE); + for (int i = 0; i < typeDecls; ++i) { + objects->push_back(declarationGenerator().genTypeDeclaration()); + } + + // generate struct Headers + objects->push_back(declarationGenerator().genHeaderStruct()); + // generate struct metadata_t + objects->push_back(generateUserMetadata()); + + // generate some callables + int callableDecls = Utils::getRandInt(DECL.MIN_CALLABLES, DECL.MAX_CALLABLES); + for (int i = 0; i < callableDecls; ++i) { + std::vector percent = {80, 15, 0, 5}; + switch (Utils::getRandInt(percent)) { + case 0: { + objects->push_back(declarationGenerator().genFunctionDeclaration()); + break; + } + case 1: { + objects->push_back(declarationGenerator().genActionDeclaration()); + break; + } + case 2: { + objects->push_back(declarationGenerator().genExternDeclaration()); + break; + } + case 3: { + objects->push_back(declarationGenerator().genControlDeclaration()); + break; + } + } + } + + // generate all the necessary pipelines for the package + objects->push_back(generateMainParserBlock()); + objects->push_back(generatePreControlBlock()); + objects->push_back(generateMainControlBlock()); + objects->push_back(generateMainDeparserBlock()); + + // finally assemble the package + objects->push_back(generateMainPackage()); + + return new IR::P4Program(*objects); +} + +} // namespace P4Tools::P4Smith::Nic diff --git a/backends/p4tools/modules/smith/targets/nic/target.h b/backends/p4tools/modules/smith/targets/nic/target.h new file mode 100644 index 00000000000..28542b8da28 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/nic/target.h @@ -0,0 +1,65 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_NIC_TARGET_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_NIC_TARGET_H_ + +#include +#include + +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/common/table.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "ir/ir.h" + +namespace P4Tools::P4Smith::Nic { + +class AbstractNicSmithTarget : public SmithTarget { + protected: + explicit AbstractNicSmithTarget(const std::string &deviceName, const std::string &archName); +}; + +class DpdkPnaSmithTarget : public AbstractNicSmithTarget { + private: + DeclarationGenerator *_declarationGenerator = new DeclarationGenerator(*this); + ExpressionGenerator *_expressionGenerator = new ExpressionGenerator(*this); + StatementGenerator *_statementGenerator = new StatementGenerator(*this); + ParserGenerator *_parserGenerator = new ParserGenerator(*this); + TableGenerator *_tableGenerator = new TableGenerator(*this); + + [[nodiscard]] IR::P4Parser *generateMainParserBlock() const; + [[nodiscard]] IR::P4Control *generatePreControlBlock() const; + [[nodiscard]] IR::P4Control *generateMainControlBlock() const; + [[nodiscard]] IR::P4Control *generateMainDeparserBlock() const; + + public: + /// Registers this target. + static void make(); + + [[nodiscard]] int writeTargetPreamble(std::ostream *ostream) const override; + + [[nodiscard]] const IR::P4Program *generateP4Program() const override; + + [[nodiscard]] DeclarationGenerator &declarationGenerator() const override { + return *_declarationGenerator; + } + + [[nodiscard]] ExpressionGenerator &expressionGenerator() const override { + return *_expressionGenerator; + } + + [[nodiscard]] StatementGenerator &statementGenerator() const override { + return *_statementGenerator; + } + + [[nodiscard]] ParserGenerator &parserGenerator() const override { return *_parserGenerator; } + + [[nodiscard]] TableGenerator &tableGenerator() const override { return *_tableGenerator; } + + private: + DpdkPnaSmithTarget(); +}; + +} // namespace P4Tools::P4Smith::Nic + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_NIC_TARGET_H_ */ diff --git a/backends/p4tools/modules/smith/targets/nic/test/P4Tests.cmake b/backends/p4tools/modules/smith/targets/nic/test/P4Tests.cmake new file mode 100644 index 00000000000..3e142b552cb --- /dev/null +++ b/backends/p4tools/modules/smith/targets/nic/test/P4Tests.cmake @@ -0,0 +1,10 @@ +# This file defines how to execute Smith on P4 programs. +# General test utilities. +include(${P4TOOLS_SOURCE_DIR}/cmake/TestUtils.cmake) + +set(SMITH_BMV2_CMD ${smith_SOURCE_DIR}/scripts/compilation-test.sh) + +set(SMITH_BMV2_ARGS 100 ${P4SMITH_DRIVER} ${CMAKE_BINARY_DIR}/p4c-dpdk ${CMAKE_BINARY_DIR} dpdk pna) + +add_test (NAME smith-compile-dpdk COMMAND ${SMITH_BMV2_CMD} ${SMITH_BMV2_ARGS} WORKING_DIRECTORY ${P4C_BINARY_DIR}) + diff --git a/backends/p4tools/modules/smith/targets/tofino/CMakeLists.txt b/backends/p4tools/modules/smith/targets/tofino/CMakeLists.txt new file mode 100644 index 00000000000..cab87d5a52c --- /dev/null +++ b/backends/p4tools/modules/smith/targets/tofino/CMakeLists.txt @@ -0,0 +1,14 @@ +if(ENABLE_TESTING) + # Include the test subdirectory. + message("-- Adding p4smith tofino test suite") + include(test/P4Tests.cmake) +endif() + +# Source files for smith. +set( + SMITH_SOURCES + ${SMITH_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/target.cpp + PARENT_SCOPE +) + diff --git a/backends/p4tools/modules/smith/targets/tofino/register.h b/backends/p4tools/modules/smith/targets/tofino/register.h new file mode 100644 index 00000000000..0c1ee000929 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/tofino/register.h @@ -0,0 +1,12 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_TOFINO_REGISTER_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_TOFINO_REGISTER_H_ + +#include "backends/p4tools/modules/smith/targets/tofino/target.h" + +namespace P4Tools::P4Smith { + +inline void tofinoRegisterSmithTarget() { Tofino::TofinoTnaSmithTarget::make(); } + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_TOFINO_REGISTER_H_ */ diff --git a/backends/p4tools/modules/smith/targets/tofino/target.cpp b/backends/p4tools/modules/smith/targets/tofino/target.cpp new file mode 100644 index 00000000000..074311d8c1f --- /dev/null +++ b/backends/p4tools/modules/smith/targets/tofino/target.cpp @@ -0,0 +1,619 @@ +#include "backends/p4tools/modules/smith/targets/tofino/target.h" + +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/probabilities.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "backends/p4tools/modules/smith/util/util.h" +#include "ir/indexed_vector.h" +#include "ir/ir.h" +#include "ir/node.h" +#include "ir/vector.h" +#include "lib/cstring.h" + +namespace P4Tools::P4Smith::Tofino { + +using namespace P4::literals; + +/* ============================================================================================= + * AbstractTofinoSmithTarget implementation + * ============================================================================================= */ + +AbstractTofinoSmithTarget::AbstractTofinoSmithTarget(const std::string &deviceName, + const std::string &archName) + : SmithTarget(deviceName, archName) {} + +/* ============================================================================================= + * TofinoTnaSmithTarget implementation + * ============================================================================================= */ + +TofinoTnaSmithTarget::TofinoTnaSmithTarget() : AbstractTofinoSmithTarget("tofino", "tna") {} + +void TofinoTnaSmithTarget::make() { + static TofinoTnaSmithTarget *INSTANCE = nullptr; + if (INSTANCE == nullptr) { + INSTANCE = new TofinoTnaSmithTarget(); + } +} + +namespace { + +IR::Declaration_Instance *generatePackageDeclaration() { + auto *args = new IR::Vector(); + + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("SwitchIngressParser")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("ingress")))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("SwitchIngressDeparser")))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("SwitchEgressParser")))); + args->push_back( + new IR::Argument(new IR::MethodCallExpression(new IR::TypeNameExpression("SwitchEgress")))); + args->push_back(new IR::Argument( + new IR::MethodCallExpression(new IR::TypeNameExpression("SwitchEgressDeparser")))); + auto *packageName = new IR::Type_Name("Pipeline"); + return new IR::Declaration_Instance("pipe", packageName, args); +} + +IR::Declaration_Instance *generateMainPackage() { + auto *args = new IR::Vector(); + args->push_back(new IR::Argument(new IR::TypeNameExpression("pipe"))); + auto *packageName = new IR::Type_Name("Switch"); + return new IR::Declaration_Instance("main", packageName, args); +} + +IR::IndexedVector generateStructFields(std::vector &fields, + std::vector &bit_size, + size_t vec_size) { + IR::IndexedVector retFields; + + for (size_t i = 0; i < vec_size; i++) { + cstring name = fields.at(i); + retFields.push_back(new IR::StructField(name, IR::Type_Bits::get(bit_size.at(i), false))); + } + + return retFields; +} + +IR::IndexedVector generateIngressIntrinsicMetadata() { + std::vector fields = { + // "resubmit_flag", + }; + + std::vector bitSize = { + // 1, + }; + + return generateStructFields(fields, bitSize, fields.size()); +} + +IR::IndexedVector generateIngressIntrinsicMetadataForTrafficManager() { + std::vector fields = { + // "ucast_egress_port", + // "bypass_egress", + // "deflect_on_drop", + // "ingress_cos", + // "qid", + // "icos_for_copy_to_cpu", + // "copy_to_cpu", + // "packet_color", + // "disable_ucast_cutthru", + // "enable_mcast_cutthru", + // "mcast_grp_a", + // "mcast_grp_b", + // "level1_mcast_hash", + // "level2_mcast_hash", + // "level1_exclusion_id", + // "level2_exclusion_id", + // "rid", + }; + + std::vector bitSize = { + // 9, + // 1, + // 1, + // 3, + // 5, + // 3, + // 1, + // 2, + // 1, + // 1, + // 16, + // 16, + // 13, + // 13, + // 16, + // 9, + // 16, + }; + + return generateStructFields(fields, bitSize, fields.size()); +} + +IR::IndexedVector generateIngressIntrinsicMetadataFromParser() { + std::vector fields = { + // "parser_err", + }; + + std::vector bitSize = { + // 16, + }; + + return generateStructFields(fields, bitSize, fields.size()); +} + +IR::IndexedVector generateIngressIntrinsicMetadataForDeparser() { + std::vector fields = { + // "drop_ctl", + // "digest_type", + // "resubmit_type", + // "mirror_type", + }; + + std::vector bitSize = { + // 3, + // 3, + // 3, + // 3, + }; + + return generateStructFields(fields, bitSize, fields.size()); +} + +IR::IndexedVector generateEgressIntrisicMetadata() { + std::vector fields = { + // "egress_port", + }; + + std::vector bitSize = { + // 9, + }; + + return generateStructFields(fields, bitSize, fields.size()); +} + +IR::IndexedVector generateEgressIntrinsicMetadataFromParser() { + std::vector fields = { + // "parser_err", + }; + + std::vector bitSize = { + // 16, + }; + + return generateStructFields(fields, bitSize, fields.size()); +} + +IR::IndexedVector generateEgressIntrinsicMetadataForDeparser() { + std::vector fields = { + // "drop_ctl", + // "mirror_type", + // "coalesce_flush", + // "coalesce_length", + }; + + std::vector bitSize = { + // 3, + // 3, + // 1, + // 7, + }; + + return generateStructFields(fields, bitSize, fields.size()); +} + +IR::IndexedVector generateEgressIntrinsicMetadataForOutputPort() { + std::vector fields = { + // "force_tx_error", + }; + + std::vector bitSize = { + // 1, + }; + + return generateStructFields(fields, bitSize, fields.size()); +} + +void generateTnaMetadata() { + IR::ID *name = nullptr; + // IR::IndexedVector fields; + IR::Type_Struct *ret = nullptr; + + name = new IR::ID("ingress_intrinsic_metadata_t"); + ret = new IR::Type_Struct(*name, generateIngressIntrinsicMetadata()); + P4Scope::addToScope(ret); + name = new IR::ID("ingress_intrinsic_metadata_for_tm_t"); + ret = new IR::Type_Struct(*name, generateIngressIntrinsicMetadataForTrafficManager()); + P4Scope::addToScope(ret); + name = new IR::ID("ingress_intrinsic_metadata_from_parser_t"); + ret = new IR::Type_Struct(*name, generateIngressIntrinsicMetadataFromParser()); + P4Scope::addToScope(ret); + name = new IR::ID("ingress_intrinsic_metadata_for_deparser_t"); + ret = new IR::Type_Struct(*name, generateIngressIntrinsicMetadataForDeparser()); + P4Scope::addToScope(ret); + name = new IR::ID("egress_intrinsic_metadata_t"); + ret = new IR::Type_Struct(*name, generateEgressIntrisicMetadata()); + P4Scope::addToScope(ret); + name = new IR::ID("egress_intrinsic_metadata_from_parser_t"); + ret = new IR::Type_Struct(*name, generateEgressIntrinsicMetadataFromParser()); + P4Scope::addToScope(ret); + name = new IR::ID("egress_intrinsic_metadata_for_deparser_t"); + ret = new IR::Type_Struct(*name, generateEgressIntrinsicMetadataForDeparser()); + P4Scope::addToScope(ret); + name = new IR::ID("egress_intrinsic_metadata_for_output_port_t"); + ret = new IR::Type_Struct(*name, generateEgressIntrinsicMetadataForOutputPort()); + P4Scope::addToScope(ret); +} + +IR::Type_Struct *generateIngressMetadataT() { + // Do !emit meta fields for now, no need + IR::IndexedVector fields; + auto *ret = new IR::Type_Struct("ingress_metadata_t", fields); + P4Scope::addToScope(ret); + return ret; +} + +IR::Type_Struct *generateEgressMetadataT() { + // Do !emit meta fields for now, no need + IR::IndexedVector fields; + auto *ret = new IR::Type_Struct("egress_metadata_t", fields); + P4Scope::addToScope(ret); + return ret; +} + +void setTnaProbabilities() { + PCT.PARAMETER_NONEDIR_DERIVED_STRUCT = 0; + PCT.PARAMETER_NONEDIR_DERIVED_HEADER = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_BOOL = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_ERROR = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_STRING = 0; + PCT.PARAMETER_NONEDIR_BASETYPE_VARBIT = 0; + // TNA does not support headers that are not multiples of 8. + PCT.STRUCTTYPEDECLARATION_BASETYPE_BOOL = 0; + // TNA requires headers to be byte-aligned. + P4Scope::req.byte_align_headers = true; + // TNA requires constant header stack indices. + P4Scope::constraints.const_header_stack_index = true; + // TNA requires that the shift count in IR::SHL must be a constant. + P4Scope::constraints.const_lshift_count = true; + // TNA *currently* only supports single stage actions. + P4Scope::constraints.single_stage_actions = true; + // Saturating arithmetic operators mau not exceed maximum PHV container width. + P4Scope::constraints.max_phv_container_width = 32; +} + +IR::MethodCallStatement *generateDeparserEmitCall() { + auto *call = new IR::PathExpression("pkt.emit"); + auto *args = new IR::Vector(); + + args->push_back(new IR::Argument(new IR::PathExpression("h"))); + + auto *mce = new IR::MethodCallExpression(call, args); + auto *mst = new IR::MethodCallStatement(mce); + return mst; +} + +} // namespace + +IR::P4Parser *TofinoTnaSmithTarget::generateIngressParserBlock() const { + IR::IndexedVector parserLocals; + P4Scope::startLocalScope(); + + // Generate type_parser, note that this is labeled "p". + IR::IndexedVector params; + + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_in"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::Out, "hdr"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::Out, "ig_md"_cs, + "ingress_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::Out, "ig_intr_md"_cs, + "ingress_intrinsic_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + + auto *tpParser = new IR::Type_Parser("SwitchIngressParser", parList); + + // Add params to the parser scope. + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // we only add values that are not read-only, to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + // Generate variable declarations. + for (int i = 0; i < 5; i++) { + auto *varDecl = declarationGenerator().genVariableDeclaration(); + parserLocals.push_back(varDecl); + } + + // Generate reguster actions. + // These are currently disabled because + // they cause a compiler bug, which is currently + // being investigates + for (int i = 0; i < 0; i++) { + // Need to pass in parserLocals because we generate register declarations + // inside genDeclInstance. + // auto *reg = RegisterActionDeclaration::genDeclInstance(&parserLocals); + // parserLocals.push_back(reg); + } + + // Generate Parser states. + IR::IndexedVector states; + auto *startState = parserGenerator().genStartState(); + + // Insert custom parsing statements into the start state. + auto *pktPath = new IR::PathExpression("pkt"); + auto *pktExtract = new IR::Member(pktPath, "extract"); + auto *pktAdvance = new IR::Member(pktPath, "advance"); + auto *igIntrMd = new IR::PathExpression("ig_intr_md"); + auto *extractTofinoMd = parserGenerator().genHdrExtract(pktExtract, igIntrMd); + startState->components.push_back(extractTofinoMd); + auto *portMdSize = new IR::PathExpression("PORT_METADATA_SIZE"); + auto *advanceTofinoMd = parserGenerator().genHdrExtract(pktAdvance, portMdSize); + startState->components.push_back(advanceTofinoMd); + // Done with custom statements. + states.push_back(startState); + states.push_back(parserGenerator().genHdrStates()); + + P4Scope::endLocalScope(); + + // Add the parser to the whole scope. + auto *p4parser = new IR::P4Parser("SwitchIngressParser", tpParser, parserLocals, states); + P4Scope::addToScope(p4parser); + return p4parser; +} + +IR::P4Control *TofinoTnaSmithTarget::generateIngressBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "ig_md"_cs, + "ingress_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "ig_intr_md"_cs, + "ingress_intrinsic_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter( + IR::Direction::In, "ig_prsr_md"_cs, "ingress_intrinsic_metadata_from_parser_t"_cs)); + params.push_back(declarationGenerator().genParameter( + IR::Direction::InOut, "ig_dprsr_md"_cs, "ingress_intrinsic_metadata_for_deparser_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "ig_tm_md"_cs, + "ingress_intrinsic_metadata_for_tm_t"_cs)); + + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("ingress", parList); + + // add to the scope + for (const auto *param : parList->parameters) { + P4Scope::addToScope(param); + // add to the name_2_type + // only add values that are !read-only to the modifiable types + if (param->direction == IR::Direction::In) { + P4Scope::addLval(param->type, param->name.name, true); + } else { + P4Scope::addLval(param->type, param->name.name, false); + } + } + + IR::IndexedVector localDecls = declarationGenerator().genLocalControlDecls(); + // apply body + auto *applyBlock = statementGenerator().genBlockStatement(false); + // hardcode the output port to be zero + auto *outputPort = new IR::PathExpression("ig_tm_md.ucast_egress_port"); + auto *outputPortVal = new IR::Constant(IR::Type_InfInt::get(), 0); + auto *assign = new IR::AssignmentStatement(outputPort, outputPortVal); + // some hack to insert the expression at the beginning + auto it = applyBlock->components.begin(); + applyBlock->components.insert(it, assign); + // end of scope + P4Scope::endLocalScope(); + + // add to the whole scope + auto *p4ctrl = new IR::P4Control("ingress", typeCtrl, localDecls, applyBlock); + P4Scope::addToScope(p4ctrl); + return p4ctrl; +} + +IR::P4Control *TofinoTnaSmithTarget::generateIngressDeparserBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_out"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "ig_md"_cs, + "ingress_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter( + IR::Direction::In, "ig_dprsr_md"_cs, "ingress_intrinsic_metadata_for_deparser_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("SwitchIngressDeparser", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + blkStat->push_back(generateDeparserEmitCall()); + + return new IR::P4Control("SwitchIngressDeparser", typeCtrl, localDecls, blkStat); +} + +IR::P4Parser *TofinoTnaSmithTarget::generateEgressParserBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_in"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::Out, "h"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::Out, "eg_md"_cs, + "egress_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::Out, "eg_intr_md"_cs, + "egress_intrinsic_metadata_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeParser = new IR::Type_Parser("SwitchEgressParser", parList); + IR::IndexedVector localDecls; + // TODO(fruffy): this hacky. FIX + // generate states + IR::IndexedVector states; + IR::IndexedVector components; + IR::Expression *transition = new IR::PathExpression("accept"); + auto *startState = new IR::ParserState("start", components, transition); + // insert custom parsing statements into the start state + auto *pktPath = new IR::PathExpression("pkt"); + auto *pktExtract = new IR::Member(pktPath, "extract"); + auto *egIntrMd = new IR::PathExpression("eg_intr_md"); + auto *extractTofinoMd = parserGenerator().genHdrExtract(pktExtract, egIntrMd); + startState->components.push_back(extractTofinoMd); + states.push_back(startState); + return new IR::P4Parser("SwitchEgressParser", typeParser, localDecls, states); +} + +IR::P4Control *TofinoTnaSmithTarget::generateEgressDeparserBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::None, "pkt"_cs, "packet_out"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "eg_md"_cs, "egress_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter( + IR::Direction::In, "eg_intr_dprs_md"_cs, "egress_intrinsic_metadata_for_deparser_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("SwitchEgressDeparser"_cs, parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + blkStat->push_back(generateDeparserEmitCall()); + + return new IR::P4Control("SwitchEgressDeparser", typeCtrl, localDecls, blkStat); +} + +IR::P4Control *TofinoTnaSmithTarget::generateEgressBlock() const { + // start of new scope + P4Scope::startLocalScope(); + + IR::IndexedVector params; + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "h"_cs, SYS_HDR_NAME)); + params.push_back(declarationGenerator().genParameter(IR::Direction::InOut, "eg_md"_cs, + "egress_metadata_t"_cs)); + params.push_back(declarationGenerator().genParameter(IR::Direction::In, "eg_intr_md"_cs, + "egress_intrinsic_metadata_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::In, "eg_intr_md_from_prsr"_cs, + "egress_intrinsic_metadata_from_parser_t"_cs)); + params.push_back(declarationGenerator().genParameter( + IR::Direction::InOut, "eg_intr_dprs_md"_cs, "egress_intrinsic_metadata_for_deparser_t"_cs)); + params.push_back( + declarationGenerator().genParameter(IR::Direction::InOut, "eg_intr_oport_md"_cs, + "egress_intrinsic_metadata_for_output_port_t"_cs)); + auto *parList = new IR::ParameterList(params); + auto *typeCtrl = new IR::Type_Control("SwitchEgress", parList); + IR::IndexedVector localDecls; + auto *blkStat = new IR::BlockStatement(); + + return new IR::P4Control("SwitchEgress", typeCtrl, localDecls, blkStat); +} + +int TofinoTnaSmithTarget::writeTargetPreamble(std::ostream *ostream) const { + *ostream << "#include \n"; + *ostream << "#define __TARGET_TOFINO__ 1\n"; + *ostream << "#include \n\n"; + return EXIT_SUCCESS; +} + +const IR::P4Program *TofinoTnaSmithTarget::generateP4Program() const { + P4Scope::startLocalScope(); + + // insert banned structures + P4Scope::notInitializedStructs.insert("ingress_intrinsic_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("ingress_intrinsic_metadata_for_tm_t"_cs); + P4Scope::notInitializedStructs.insert("ingress_intrinsic_metadata_from_parser_t"_cs); + P4Scope::notInitializedStructs.insert("ingress_intrinsic_metadata_for_deparser_t"_cs); + P4Scope::notInitializedStructs.insert("egress_intrinsic_metadata_t"_cs); + P4Scope::notInitializedStructs.insert("egress_intrinsic_metadata_from_parser_t"_cs); + P4Scope::notInitializedStructs.insert("egress_intrinsic_metadata_for_deparser_t"_cs); + P4Scope::notInitializedStructs.insert("egress_intrinsic_metadata_for_output_port_t"_cs); + + // set tna-specific probabilities + setTnaProbabilities(); + + // start to assemble the model + auto *objects = new IR::Vector(); + + // insert tofino metadata + generateTnaMetadata(); + + objects->push_back(declarationGenerator().genEthernetHeaderType()); + + // generate some declarations + int typeDecls = Utils::getRandInt(DECL.MIN_TYPE, DECL.MAX_TYPE); + for (int i = 0; i < typeDecls; ++i) { + objects->push_back(declarationGenerator().genTypeDeclaration()); + } + + // generate struct Headers + objects->push_back(declarationGenerator().genHeaderStruct()); + // generate struct ingress_metadata_t + objects->push_back(generateIngressMetadataT()); + // generate struct egress_metadata_t + objects->push_back(generateEgressMetadataT()); + + // generate some callables + int callableDecls = Utils::getRandInt(DECL.MIN_CALLABLES, DECL.MAX_CALLABLES); + for (int i = 0; i < callableDecls; ++i) { + std::vector percent = {80, 15, 0, 5}; + switch (Utils::getRandInt(percent)) { + case 0: { + objects->push_back(declarationGenerator().genFunctionDeclaration()); + break; + } + case 1: { + objects->push_back(declarationGenerator().genActionDeclaration()); + break; + } + case 2: { + objects->push_back(declarationGenerator().genExternDeclaration()); + break; + } + case 3: { + objects->push_back(declarationGenerator().genControlDeclaration()); + break; + } + } + } + + // generate all the necessary pipelines for the package + objects->push_back(generateIngressParserBlock()); + objects->push_back(generateIngressBlock()); + objects->push_back(generateIngressDeparserBlock()); + objects->push_back(generateEgressParserBlock()); + objects->push_back(generateEgressBlock()); + objects->push_back(generateEgressDeparserBlock()); + + // finally assemble the package + objects->push_back(generatePackageDeclaration()); + objects->push_back(generateMainPackage()); + + return new IR::P4Program(*objects); +} +} // namespace P4Tools::P4Smith::Tofino diff --git a/backends/p4tools/modules/smith/targets/tofino/target.h b/backends/p4tools/modules/smith/targets/tofino/target.h new file mode 100644 index 00000000000..c7c488f109b --- /dev/null +++ b/backends/p4tools/modules/smith/targets/tofino/target.h @@ -0,0 +1,67 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_TOFINO_TARGET_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_TOFINO_TARGET_H_ + +#include +#include + +#include "backends/p4tools/modules/smith/common/declarations.h" +#include "backends/p4tools/modules/smith/common/expressions.h" +#include "backends/p4tools/modules/smith/common/parser.h" +#include "backends/p4tools/modules/smith/common/statements.h" +#include "backends/p4tools/modules/smith/common/table.h" +#include "backends/p4tools/modules/smith/core/target.h" +#include "ir/ir.h" + +namespace P4Tools::P4Smith::Tofino { + +class AbstractTofinoSmithTarget : public SmithTarget { + protected: + explicit AbstractTofinoSmithTarget(const std::string &deviceName, const std::string &archName); +}; + +class TofinoTnaSmithTarget : public AbstractTofinoSmithTarget { + private: + DeclarationGenerator *_declarationGenerator = new DeclarationGenerator(*this); + ExpressionGenerator *_expressionGenerator = new ExpressionGenerator(*this); + StatementGenerator *_statementGenerator = new StatementGenerator(*this); + ParserGenerator *_parserGenerator = new ParserGenerator(*this); + TableGenerator *_tableGenerator = new TableGenerator(*this); + + [[nodiscard]] IR::P4Parser *generateIngressParserBlock() const; + [[nodiscard]] IR::P4Control *generateIngressBlock() const; + [[nodiscard]] IR::P4Control *generateIngressDeparserBlock() const; + [[nodiscard]] IR::P4Parser *generateEgressParserBlock() const; + [[nodiscard]] IR::P4Control *generateEgressBlock() const; + [[nodiscard]] IR::P4Control *generateEgressDeparserBlock() const; + + public: + /// Registers this target. + static void make(); + + [[nodiscard]] int writeTargetPreamble(std::ostream *ostream) const override; + + [[nodiscard]] const IR::P4Program *generateP4Program() const override; + + [[nodiscard]] DeclarationGenerator &declarationGenerator() const override { + return *_declarationGenerator; + } + + [[nodiscard]] ExpressionGenerator &expressionGenerator() const override { + return *_expressionGenerator; + } + + [[nodiscard]] StatementGenerator &statementGenerator() const override { + return *_statementGenerator; + } + + [[nodiscard]] ParserGenerator &parserGenerator() const override { return *_parserGenerator; } + + [[nodiscard]] TableGenerator &tableGenerator() const override { return *_tableGenerator; } + + private: + TofinoTnaSmithTarget(); +}; + +} // namespace P4Tools::P4Smith::Tofino + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TARGETS_TOFINO_TARGET_H_ */ diff --git a/backends/p4tools/modules/smith/targets/tofino/test/P4Tests.cmake b/backends/p4tools/modules/smith/targets/tofino/test/P4Tests.cmake new file mode 100644 index 00000000000..46260e1f551 --- /dev/null +++ b/backends/p4tools/modules/smith/targets/tofino/test/P4Tests.cmake @@ -0,0 +1,7 @@ +# This file defines how to execute Smith on P4 programs. +# General test utilities. +include(${P4TOOLS_SOURCE_DIR}/cmake/TestUtils.cmake) + +set(SMITH_BMV2_CMD ${smith_SOURCE_DIR}/scripts/compilation-test.sh) + + diff --git a/backends/p4tools/modules/smith/toolname.h b/backends/p4tools/modules/smith/toolname.h new file mode 100644 index 00000000000..88b7a126bf8 --- /dev/null +++ b/backends/p4tools/modules/smith/toolname.h @@ -0,0 +1,12 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_TOOLNAME_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_TOOLNAME_H_ + +#include + +namespace P4Tools::P4Smith { + +static inline constexpr std::string_view TOOL_NAME = "smith"; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_TOOLNAME_H_ */ diff --git a/backends/p4tools/modules/smith/util/util.cpp b/backends/p4tools/modules/smith/util/util.cpp new file mode 100644 index 00000000000..b035b399ca6 --- /dev/null +++ b/backends/p4tools/modules/smith/util/util.cpp @@ -0,0 +1,61 @@ +#include "backends/p4tools/modules/smith/util/util.h" + +#include +#include +#include +#include +#include +#include + +#include "backends/p4tools/common/lib/util.h" +#include "backends/p4tools/modules/smith/common/scope.h" +#include "backends/p4tools/modules/smith/util/wordlist.h" + +namespace P4Tools::P4Smith { + +std::string getRandomString(size_t len) { + static const std::vector P4_KEYWORDS = {"if", "void", "else", + "key", "actions", "true"}; + static const std::array ALPHANUMERIC_CHARACTERS = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"}; + + std::string ret; + + while (true) { + std::stringstream ss; + // Try to get a name from the wordlist. + ss << Wordlist::getFromWordlist(); + size_t lenFromWordlist = ss.str().length(); + + if (lenFromWordlist == len) { + ret = ss.str(); + } else if (lenFromWordlist > len) { + // We got a bigger word from the wordlist, so we have to truncate it. + ret = ss.str().substr(0, len); + } else if (lenFromWordlist < len) { + // The word was too small so we append it. + // Note: This also covers the case that we ran + // out of the words from the wordlist. + for (size_t i = lenFromWordlist; i < len; i++) { + ss << ALPHANUMERIC_CHARACTERS.at( + Utils::getRandInt(0, sizeof(ALPHANUMERIC_CHARACTERS) - 2)); + } + ret = ss.str(); + } + + if (std::find(P4_KEYWORDS.begin(), P4_KEYWORDS.end(), ret) != P4_KEYWORDS.end()) { + continue; + } + + // The name is usable, break the loop. + if (P4Scope::usedNames.count(ret) == 0) { + break; + } + } + + P4Scope::usedNames.insert(ret); + return ret; +} + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/util/util.h b/backends/p4tools/modules/smith/util/util.h new file mode 100644 index 00000000000..ae8e7ce3586 --- /dev/null +++ b/backends/p4tools/modules/smith/util/util.h @@ -0,0 +1,25 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_UTIL_UTIL_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_UTIL_UTIL_H_ + +#include + +#include "lib/cstring.h" + +namespace P4Tools::P4Smith { + +static constexpr int INTEGER_WIDTH(32); + +/// These are hardcoded initialization names. +static const cstring SYS_HDR_NAME("Headers"); +static const cstring ETH_HEADER_T("ethernet_t"); +static const cstring ETH_HDR("eth_hdr"); + +/// @returns a randomly generated string. +/// If we can, return a word from a 10,000 word wordlist, +/// if not, generate a random string and return it. +/// @param len : Ignored when choosing from the wordlist. +std::string getRandomString(size_t len); + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_UTIL_UTIL_H_ */ diff --git a/backends/p4tools/modules/smith/util/wordlist.cpp b/backends/p4tools/modules/smith/util/wordlist.cpp new file mode 100644 index 00000000000..43728f4233d --- /dev/null +++ b/backends/p4tools/modules/smith/util/wordlist.cpp @@ -0,0 +1,703 @@ +#include "backends/p4tools/modules/smith/util/wordlist.h" + +#include +#include + +namespace P4Tools::P4Smith { + +std::size_t Wordlist::counter = 0; + +const std::array Wordlist::WORDS = { + "about", "search", "other", "which", "their", "there", "contact", "business", + "online", "first", "would", "services", "these", "click", "service", "price", + "people", "state", "email", "health", "world", "products", "music", "should", + "product", "system", "policy", "number", "please", "support", "message", "after", + "software", "video", "where", "rights", "public", "books", "school", "through", + "links", "review", "years", "order", "privacy", "items", "company", "group", + "under", "general", "research", "january", "reviews", "program", "games", "could", + "great", "united", "hotel", "center", "store", "travel", "comments", "report", + "member", "details", "terms", "before", "hotels", "right", "because", "local", + "those", "using", "results", "office", "national", "design", "posted", "internet", + "address", "within", "states", "phone", "shipping", "reserved", "subject", "between", + "forum", "family", "based", "black", "check", "special", "prices", "website", + "index", "being", "women", "today", "south", "project", "pages", "version", + "section", "found", "sports", "house", "related", "security", "county", "american", + "photo", "members", "power", "while", "network", "computer", "systems", "three", + "total", "place", "download", "without", "access", "think", "north", "current", + "posts", "media", "control", "water", "history", "pictures", "personal", "since", + "guide", "board", "location", "change", "white", "small", "rating", "children", + "during", "return", "students", "shopping", "account", "times", "sites", "level", + "digital", "profile", "previous", "events", "hours", "image", "title", "another", + "shall", "property", "class", "still", "money", "quality", "every", "listing", + "content", "country", "private", "little", "visit", "tools", "reply", "customer", + "december", "compare", "movies", "include", "college", "value", "article", "provide", + "source", "author", "press", "learn", "around", "print", "course", "canada", + "process", "stock", "training", "credit", "point", "science", "advanced", "sales", + "english", "estate", "select", "windows", "photos", "thread", "category", "large", + "gallery", "table", "register", "however", "october", "november", "market", "library", + "really", "action", "start", "series", "model", "features", "industry", "human", + "provided", "required", "second", "movie", "forums", "march", "better", "yahoo", + "going", "medical", "friend", "server", "study", "staff", "articles", "feedback", + "again", "looking", "issues", "april", "never", "users", "complete", "street", + "topic", "comment", "things", "working", "against", "standard", "person", "below", + "mobile", "party", "payment", "login", "student", "programs", "offers", "legal", + "above", "recent", "stores", "problem", "memory", "social", "august", "quote", + "language", "story", "options", "rates", "create", "young", "america", "field", + "paper", "single", "example", "girls", "password", "latest", "question", "changes", + "night", "texas", "poker", "status", "browse", "issue", "range", "building", + "seller", "court", "february", "always", "result", "audio", "light", "write", + "offer", "groups", "given", "files", "event", "release", "analysis", "request", + "china", "making", "picture", "needs", "possible", "might", "month", "major", + "areas", "future", "space", "cards", "problems", "london", "meeting", "become", + "interest", "child", "enter", "share", "similar", "garden", "schools", "million", + "added", "listed", "learning", "energy", "delivery", "popular", "stories", "journal", + "reports", "welcome", "central", "images", "notice", "original", "radio", "until", + "color", "council", "includes", "track", "archive", "others", "format", "least", + "society", "months", "safety", "friends", "trade", "edition", "messages", "further", + "updated", "having", "provides", "david", "already", "green", "studies", "close", + "common", "drive", "specific", "several", "living", "called", "short", "display", + "limited", "powered", "means", "director", "daily", "beach", "natural", "whether", + "period", "planning", "database", "official", "weather", "average", "window", "france", + "region", "island", "record", "direct", "records", "district", "calendar", "costs", + "style", "front", "update", "parts", "early", "miles", "sound", "resource", + "present", "either", "document", "works", "material", "written", "federal", "hosting", + "rules", "final", "adult", "tickets", "thing", "centre", "cheap", "finance", + "minutes", "third", "gifts", "europe", "reading", "topics", "cover", "usually", + "together", "videos", "percent", "function", "getting", "global", "economic", "player", + "projects", "lyrics", "often", "submit", "germany", "amount", "watch", "included", + "though", "thanks", "deals", "various", "words", "linux", "james", "weight", + "heart", "received", "choose", "archives", "points", "magazine", "error", "camera", + "clear", "receive", "domain", "methods", "chapter", "makes", "policies", "beauty", + "manager", "india", "position", "taken", "listings", "models", "michael", "known", + "cases", "florida", "simple", "quick", "wireless", "license", "friday", "whole", + "annual", "later", "basic", "shows", "google", "church", "method", "purchase", + "active", "response", "practice", "hardware", "figure", "holiday", "enough", "designed", + "along", "among", "death", "writing", "speed", "brand", "discount", "higher", + "effects", "created", "remember", "yellow", "increase", "kingdom", "thought", "stuff", + "french", "storage", "japan", "doing", "loans", "shoes", "entry", "nature", + "orders", "africa", "summary", "growth", "notes", "agency", "monday", "european", + "activity", "although", "western", "income", "force", "overall", "river", "package", + "contents", "players", "engine", "album", "regional", "supplies", "started", "views", + "plans", "double", "build", "screen", "exchange", "types", "lines", "continue", + "across", "benefits", "needed", "season", "apply", "someone", "anything", "printer", + "believe", "effect", "asked", "sunday", "casino", "volume", "cross", "anyone", + "mortgage", "silver", "inside", "solution", "mature", "rather", "weeks", "addition", + "supply", "nothing", "certain", "running", "lower", "union", "jewelry", "clothing", + "names", "robert", "homepage", "skills", "islands", "advice", "career", "military", + "rental", "decision", "leave", "british", "teens", "woman", "sellers", "middle", + "cable", "taking", "values", "division", "coming", "tuesday", "object", "lesbian", + "machine", "length", "actually", "score", "client", "returns", "capital", "follow", + "sample", "shown", "saturday", "england", "culture", "flash", "george", "choice", + "starting", "thursday", "courses", "consumer", "airport", "foreign", "artist", "outside", + "levels", "channel", "letter", "phones", "ideas", "summer", "allow", "degree", + "contract", "button", "releases", "homes", "super", "matter", "custom", "virginia", + "almost", "located", "multiple", "asian", "editor", "cause", "focus", "featured", + "rooms", "female", "thomas", "primary", "cancer", "numbers", "reason", "browser", + "spring", "answer", "voice", "friendly", "schedule", "purpose", "feature", "comes", + "police", "everyone", "approach", "cameras", "brown", "physical", "medicine", "ratings", + "chicago", "forms", "glass", "happy", "smith", "wanted", "thank", "unique", + "survey", "prior", "sport", "ready", "animal", "sources", "mexico", "regular", + "secure", "simply", "evidence", "station", "round", "paypal", "favorite", "option", + "master", "valley", "recently", "probably", "rentals", "built", "blood", "improve", + "larger", "networks", "earth", "parents", "nokia", "impact", "transfer", "kitchen", + "strong", "carolina", "wedding", "hospital", "ground", "overview", "owners", "disease", + "italy", "perfect", "classic", "basis", "command", "cities", "william", "express", + "award", "distance", "peter", "ensure", "involved", "extra", "partners", "budget", + "rated", "guides", "success", "maximum", "existing", "quite", "selected", "amazon", + "patients", "warning", "horse", "forward", "flowers", "stars", "lists", "owner", + "retail", "animals", "useful", "directly", "housing", "takes", "bring", "catalog", + "searches", "trying", "mother", "traffic", "joined", "input", "strategy", "agent", + "valid", "modern", "senior", "ireland", "teaching", "grand", "testing", "trial", + "charge", "units", "instead", "canadian", "normal", "wrote", "ships", "entire", + "leading", "metal", "positive", "fitness", "chinese", "opinion", "football", "abstract", + "output", "funds", "greater", "likely", "develop", "artists", "guest", "seems", + "trust", "contains", "session", "multi", "republic", "vacation", "century", "academic", + "graphics", "indian", "expected", "grade", "dating", "pacific", "mountain", "filter", + "mailing", "vehicle", "longer", "consider", "northern", "behind", "panel", "floor", + "german", "buying", "match", "proposed", "default", "require", "outdoor", "morning", + "allows", "protein", "plant", "reported", "politics", "partner", "authors", "boards", + "faculty", "parties", "mission", "string", "sense", "modified", "released", "stage", + "internal", "goods", "unless", "richard", "detailed", "japanese", "approved", "target", + "except", "ability", "maybe", "moving", "brands", "places", "pretty", "spain", + "southern", "yourself", "winter", "battery", "youth", "pressure", "boston", "keywords", + "medium", "break", "purposes", "dance", "itself", "defined", "papers", "playing", + "awards", "studio", "reader", "virtual", "device", "answers", "remote", "external", + "apple", "offered", "theory", "enjoy", "remove", "surface", "minimum", "visual", + "variety", "teachers", "martin", "manual", "block", "subjects", "agents", "repair", + "civil", "steel", "songs", "fixed", "wrong", "hands", "finally", "updates", + "desktop", "classes", "paris", "sector", "capacity", "requires", "jersey", "fully", + "father", "electric", "quotes", "officer", "driver", "respect", "unknown", "worth", + "teacher", "workers", "georgia", "peace", "campus", "showing", "creative", "coast", + "benefit", "progress", "funding", "devices", "grant", "agree", "fiction", "watches", + "careers", "beyond", "families", "museum", "blogs", "accepted", "former", "complex", + "agencies", "parent", "spanish", "michigan", "columbia", "setting", "scale", "stand", + "economy", "highest", "helpful", "monthly", "critical", "frame", "musical", "angeles", + "employee", "chief", "gives", "bottom", "packages", "detail", "changed", "heard", + "begin", "colorado", "royal", "clean", "switch", "russian", "largest", "african", + "titles", "relevant", "justice", "connect", "bible", "basket", "applied", "weekly", + "demand", "suite", "vegas", "square", "chris", "advance", "auction", "allowed", + "correct", "charles", "nation", "selling", "piece", "sheet", "seven", "older", + "illinois", "elements", "species", "cells", "module", "resort", "facility", "random", + "pricing", "minister", "motion", "looks", "fashion", "visitors", "monitor", "trading", + "forest", "calls", "whose", "coverage", "couple", "giving", "chance", "vision", + "ending", "clients", "actions", "listen", "discuss", "accept", "naked", "clinical", + "sciences", "markets", "lowest", "highly", "appear", "lives", "currency", "leather", + "patient", "actual", "stone", "commerce", "perhaps", "persons", "tests", "village", + "accounts", "amateur", "factors", "coffee", "settings", "buyer", "cultural", "steve", + "easily", "poster", "closed", "holidays", "zealand", "balance", "graduate", "replies", + "initial", "label", "thinking", "scott", "canon", "league", "waste", "minute", + "provider", "optional", "sections", "chair", "fishing", "effort", "phase", "fields", + "fantasy", "letters", "motor", "context", "install", "shirt", "apparel", "crime", + "count", "breast", "johnson", "quickly", "dollars", "websites", "religion", "claim", + "driving", "surgery", "patch", "measures", "kansas", "chemical", "doctor", "reduce", + "brought", "himself", "enable", "exercise", "santa", "leader", "diamond", "israel", + "servers", "alone", "meetings", "seconds", "jones", "arizona", "keyword", "flight", + "congress", "username", "produced", "italian", "pocket", "saint", "freedom", "argument", + "creating", "drugs", "joint", "premium", "fresh", "attorney", "upgrade", "factor", + "growing", "stream", "hearing", "eastern", "auctions", "therapy", "entries", "dates", + "signed", "upper", "serious", "prime", "samsung", "limit", "began", "louis", + "steps", "errors", "shops", "efforts", "informed", "thoughts", "creek", "worked", + "quantity", "urban", "sorted", "myself", "tours", "platform", "labor", "admin", + "nursing", "defense", "machines", "heavy", "covered", "recovery", "merchant", "expert", + "protect", "solid", "became", "orange", "vehicles", "prevent", "theme", "campaign", + "marine", "guitar", "finding", "examples", "saying", "spirit", "claims", "motorola", + "affairs", "touch", "intended", "towards", "goals", "election", "suggest", "branch", + "charges", "serve", "reasons", "magic", "mount", "smart", "talking", "latin", + "avoid", "manage", "corner", "oregon", "element", "birth", "virus", "abuse", + "requests", "separate", "quarter", "tables", "define", "racing", "facts", "column", + "plants", "faith", "chain", "identify", "avenue", "missing", "domestic", "sitemap", + "moved", "houston", "reach", "mental", "viewed", "moment", "extended", "sequence", + "attack", "sorry", "centers", "opening", "damage", "reserve", "recipes", "gamma", + "plastic", "produce", "placed", "truth", "counter", "failure", "follows", "weekend", + "dollar", "ontario", "films", "bridge", "native", "williams", "movement", "printing", + "baseball", "owned", "approval", "draft", "chart", "played", "contacts", "jesus", + "readers", "clubs", "jackson", "equal", "matching", "offering", "shirts", "profit", + "leaders", "posters", "variable", "expect", "parking", "compared", "workshop", "russia", + "codes", "kinds", "seattle", "golden", "teams", "lighting", "senate", "forces", + "funny", "brother", "turned", "portable", "tried", "returned", "pattern", "named", + "theatre", "laser", "earlier", "sponsor", "warranty", "indiana", "harry", "objects", + "delete", "evening", "assembly", "nuclear", "taxes", "mouse", "signal", "criminal", + "issued", "brain", "sexual", "powerful", "dream", "obtained", "false", "flower", + "passed", "supplied", "falls", "opinions", "promote", "stated", "stats", "hawaii", + "appears", "carry", "decided", "covers", "hello", "designs", "maintain", "tourism", + "priority", "adults", "clips", "savings", "graphic", "payments", "binding", "brief", + "ended", "winning", "eight", "straight", "script", "served", "wants", "prepared", + "dining", "alert", "atlanta", "dakota", "queen", "credits", "clearly", "handle", + "sweet", "criteria", "pubmed", "diego", "truck", "behavior", "enlarge", "revenue", + "measure", "changing", "votes", "looked", "festival", "ocean", "flights", "experts", + "signs", "depth", "whatever", "logged", "laptop", "vintage", "train", "exactly", + "explore", "maryland", "concept", "nearly", "eligible", "checkout", "reality", "forgot", + "handling", "origin", "gaming", "feeds", "billion", "scotland", "faster", "dallas", + "bought", "nations", "route", "followed", "broken", "frank", "alaska", "battle", + "anime", "speak", "protocol", "query", "equity", "speech", "rural", "shared", + "sounds", "judge", "bytes", "forced", "fight", "height", "speaker", "filed", + "obtain", "offices", "designer", "remain", "managed", "failed", "marriage", "korea", + "banks", "secret", "kelly", "leads", "negative", "austin", "toronto", "theater", + "springs", "missouri", "andrew", "perform", "healthy", "assets", "injury", "joseph", + "ministry", "drivers", "lawyer", "figures", "married", "proposal", "sharing", "portal", + "waiting", "birthday", "gratis", "banking", "brian", "toward", "slightly", "assist", + "conduct", "lingerie", "calling", "serving", "profiles", "miami", "comics", "matters", + "houses", "postal", "controls", "breaking", "combined", "ultimate", "wales", "minor", + "finish", "noted", "reduced", "physics", "spent", "extreme", "samples", "davis", + "daniel", "reviewed", "forecast", "removed", "helps", "singles", "cycle", "amounts", + "contain", "accuracy", "sleep", "pharmacy", "brazil", "creation", "static", "scene", + "hunter", "crystal", "famous", "writer", "chairman", "violence", "oklahoma", "speakers", + "drink", "academy", "dynamic", "gender", "cleaning", "concerns", "vendor", "intel", + "officers", "referred", "supports", "regions", "junior", "rings", "meaning", "ladies", + "henry", "ticket", "guess", "agreed", "soccer", "import", "posting", "presence", + "instant", "viewing", "majority", "christ", "aspects", "austria", "ahead", "scheme", + "utility", "preview", "manner", "matrix", "devel", "despite", "strength", "turkey", + "proper", "degrees", "delta", "seeking", "inches", "phoenix", "shares", "daughter", + "standing", "comfort", "colors", "cisco", "ordering", "alpha", "appeal", "cruise", + "bonus", "bookmark", "specials", "disney", "adobe", "smoking", "becomes", "drives", + "alabama", "improved", "trees", "achieve", "dress", "dealer", "nearby", "carried", + "happen", "exposure", "gambling", "refer", "miller", "outdoors", "clothes", "caused", + "luxury", "babes", "frames", "indeed", "circuit", "layer", "printed", "removal", + "easier", "printers", "adding", "kentucky", "mostly", "taylor", "prints", "spend", + "factory", "interior", "revised", "optical", "relative", "amazing", "clock", "identity", + "suites", "feeling", "hidden", "victoria", "serial", "relief", "revision", "ratio", + "planet", "copies", "recipe", "permit", "seeing", "proof", "tennis", "bedroom", + "empty", "instance", "licensed", "orlando", "bureau", "maine", "ideal", "specs", + "recorded", "pieces", "finished", "parks", "dinner", "lawyers", "sydney", "stress", + "cream", "trends", "discover", "patterns", "boxes", "hills", "fourth", "advisor", + "aware", "wilson", "shape", "irish", "stations", "remains", "greatest", "firms", + "operator", "generic", "usage", "charts", "mixed", "census", "exist", "wheel", + "transit", "compact", "poetry", "lights", "tracking", "angel", "keeping", "attempt", + "matches", "width", "noise", "engines", "forget", "array", "accurate", "stephen", + "climate", "alcohol", "greek", "managing", "sister", "walking", "explain", "smaller", + "newest", "happened", "extent", "sharp", "lesbians", "export", "managers", "aircraft", + "modules", "sweden", "conflict", "versions", "employer", "occur", "knows", "describe", + "concern", "backup", "citizens", "heritage", "holding", "trouble", "spread", "coach", + "kevin", "expand", "audience", "assigned", "jordan", "affect", "virgin", "raised", + "directed", "dealers", "sporting", "helping", "affected", "totally", "plate", "expenses", + "indicate", "blonde", "anderson", "organic", "albums", "cheats", "guests", "hosted", + "diseases", "nevada", "thailand", "agenda", "anyway", "tracks", "advisory", "logic", + "template", "prince", "circle", "grants", "anywhere", "atlantic", "edward", "investor", + "leaving", "wildlife", "cooking", "speaking", "sponsors", "respond", "sizes", "plain", + "entered", "launch", "checking", "costa", "belgium", "guidance", "trail", "symbol", + "crafts", "highway", "buddy", "observed", "setup", "booking", "glossary", "fiscal", + "styles", "denver", "filled", "channels", "ericsson", "appendix", "notify", "blues", + "portion", "scope", "supplier", "cables", "cotton", "biology", "dental", "killed", + "border", "ancient", "debate", "starts", "causes", "arkansas", "leisure", "learned", + "notebook", "explorer", "historic", "attached", "opened", "husband", "disabled", "crazy", + "upcoming", "britain", "concert", "scores", "comedy", "adopted", "weblog", "linear", + "bears", "carrier", "edited", "constant", "mouth", "jewish", "meter", "linked", + "portland", "concepts", "reflect", "deliver", "wonder", "lessons", "fruit", "begins", + "reform", "alerts", "treated", "mysql", "relating", "assume", "alliance", "confirm", + "neither", "lewis", "howard", "offline", "leaves", "engineer", "replace", "checks", + "reached", "becoming", "safari", "sugar", "stick", "allen", "relation", "enabled", + "genre", "slide", "montana", "tested", "enhance", "exact", "bound", "adapter", + "formal", "hockey", "storm", "micro", "colleges", "laptops", "showed", "editors", + "threads", "supreme", "brothers", "presents", "dolls", "estimate", "cancel", "limits", + "weapons", "paint", "delay", "pilot", "outlet", "czech", "novel", "ultra", + "winner", "idaho", "episode", "potter", "plays", "bulletin", "modify", "oxford", + "truly", "epinions", "painting", "universe", "patent", "eating", "planned", "watching", + "lodge", "mirror", "sterling", "sessions", "kernel", "stocks", "buyers", "journals", + "jennifer", "antonio", "charged", "broad", "taiwan", "chosen", "greece", "swiss", + "sarah", "clark", "terminal", "nights", "behalf", "liquid", "nebraska", "salary", + "foods", "gourmet", "guard", "properly", "orleans", "saving", "empire", "resume", + "twenty", "newly", "raise", "prepare", "avatar", "illegal", "hundreds", "lincoln", + "helped", "premier", "tomorrow", "decide", "consent", "drama", "visiting", "downtown", + "keyboard", "contest", "bands", "suitable", "millions", "lunch", "audit", "chamber", + "guinea", "findings", "muscle", "clicking", "polls", "typical", "tower", "yours", + "chicken", "attend", "shower", "sending", "jason", "tonight", "holdem", "shell", + "province", "catholic", "governor", "seemed", "swimming", "spyware", "formula", "solar", + "catch", "pakistan", "reliable", "doubt", "finder", "unable", "periods", "tasks", + "attacks", "const", "doors", "symptoms", "resorts", "biggest", "memorial", "visitor", + "forth", "insert", "gateway", "alumni", "drawing", "ordered", "fighting", "happens", + "romance", "bruce", "split", "themes", "powers", "heaven", "pregnant", "twice", + "focused", "egypt", "bargain", "cellular", "norway", "vermont", "asking", "blocks", + "normally", "hunting", "diabetes", "shift", "bodies", "cutting", "simon", "writers", + "marks", "flexible", "loved", "mapping", "numerous", "birds", "indexed", "superior", + "saved", "paying", "cartoon", "shots", "moore", "granted", "choices", "carbon", + "spending", "magnetic", "registry", "crisis", "outlook", "massive", "denmark", "employed", + "bright", "treat", "header", "poverty", "formed", "piano", "sheets", "patrick", + "puerto", "displays", "plasma", "allowing", "earnings", "mystery", "journey", "delaware", + "bidding", "risks", "banner", "charter", "barbara", "counties", "ports", "dreams", + "blogger", "stands", "teach", "occurred", "rapid", "hairy", "reverse", "deposit", + "seminar", "latina", "wheels", "specify", "dutch", "formats", "depends", "boots", + "holds", "router", "concrete", "editing", "poland", "folder", "womens", "upload", + "pulse", "voting", "courts", "notices", "detroit", "metro", "toshiba", "strip", + "pearl", "accident", "resident", "possibly", "airline", "regard", "exists", "smooth", + "strike", "flashing", "narrow", "threat", "surveys", "sitting", "putting", "vietnam", + "trailer", "castle", "gardens", "missed", "malaysia", "antique", "labels", "willing", + "acting", "heads", "stored", "logos", "antiques", "density", "hundred", "strange", + "mention", "parallel", "honda", "amended", "operate", "bills", "bathroom", "stable", + "opera", "doctors", "lesson", "cinema", "asset", "drinking", "reaction", "blank", + "enhanced", "entitled", "severe", "generate", "deluxe", "humor", "monitors", "lived", + "duration", "pursuant", "fabric", "visits", "tight", "domains", "contrast", "flying", + "berlin", "siemens", "adoption", "meant", "capture", "pounds", "buffalo", "plane", + "desire", "camping", "meets", "welfare", "caught", "marked", "driven", "measured", + "medline", "bottle", "marshall", "massage", "rubber", "closing", "tampa", "thousand", + "legend", "grace", "susan", "adams", "python", "monster", "villa", "columns", + "hamilton", "cookies", "inner", "tutorial", "entity", "cruises", "holder", "portugal", + "lawrence", "roman", "duties", "valuable", "ethics", "forever", "dragon", "captain", + "imagine", "brings", "heating", "scripts", "stereo", "taste", "dealing", "commit", + "airlines", "liberal", "livecam", "trips", "sides", "turns", "cache", "jacket", + "oracle", "matthew", "lease", "aviation", "hobbies", "proud", "excess", "disaster", + "console", "commands", "giant", "achieved", "injuries", "shipped", "seats", "alarm", + "voltage", "anthony", "nintendo", "usual", "loading", "stamps", "appeared", "franklin", + "angle", "vinyl", "mining", "ongoing", "worst", "imaging", "betting", "liberty", + "wyoming", "convert", "analyst", "garage", "exciting", "thongs", "ringtone", "finland", + "morgan", "derived", "pleasure", "honor", "oriented", "eagle", "desktops", "pants", + "columbus", "nurse", "prayer", "quiet", "postage", "producer", "cheese", "comic", + "crown", "maker", "crack", "picks", "semester", "fetish", "applies", "casinos", + "smoke", "apache", "filters", "craft", "apart", "fellow", "blind", "lounge", + "coins", "gross", "strongly", "hilton", "proteins", "horror", "familiar", "capable", + "douglas", "debian", "epson", "elected", "carrying", "victory", "madison", "editions", + "mainly", "ethnic", "actor", "finds", "fifth", "citizen", "vertical", "prize", + "occurs", "absolute", "consists", "anytime", "soldiers", "guardian", "lecture", "layout", + "classics", "horses", "dirty", "wayne", "donate", "taught", "worker", "alive", + "temple", "prove", "wings", "breaks", "genetic", "waters", "promise", "prefer", + "ridge", "cabinet", "modem", "harris", "bringing", "evaluate", "tiffany", "tropical", + "collect", "toyota", "streets", "vector", "shaved", "turning", "buffer", "purple", + "larry", "mutual", "pipeline", "syntax", "prison", "skill", "chairs", "everyday", + "moves", "inquiry", "ethernet", "checked", "exhibit", "throw", "trend", "sierra", + "visible", "desert", "oldest", "rhode", "mercury", "steven", "handbook", "navigate", + "worse", "summit", "victims", "spaces", "burning", "escape", "coupons", "somewhat", + "receiver", "cialis", "boats", "glance", "scottish", "arcade", "richmond", "russell", + "tells", "obvious", "fiber", "graph", "covering", "platinum", "judgment", "bedrooms", + "talks", "filing", "foster", "modeling", "passing", "awarded", "trials", "tissue", + "clinton", "masters", "bonds", "alberta", "commons", "fraud", "spectrum", "arrival", + "pottery", "emphasis", "roger", "aspect", "awesome", "mexican", "counts", "priced", + "crash", "desired", "inter", "closer", "assumes", "heights", "shadow", "riding", + "firefox", "expense", "grove", "venture", "clinic", "korean", "healing", "princess", + "entering", "packet", "spray", "studios", "buttons", "funded", "thompson", "winners", + "extend", "roads", "dublin", "rolling", "memories", "nelson", "arrived", "creates", + "faces", "tourist", "mayor", "murder", "adequate", "senator", "yield", "grades", + "cartoons", "digest", "lodging", "hence", "entirely", "replaced", "radar", "rescue", + "losses", "combat", "reducing", "stopped", "lakes", "closely", "diary", "kings", + "shooting", "flags", "baker", "launched", "shock", "walls", "abroad", "ebony", + "drawn", "arthur", "visited", "walker", "suggests", "beast", "operated", "targets", + "overseas", "dodge", "counsel", "pizza", "invited", "yards", "gordon", "farmers", + "queries", "ukraine", "absence", "nearest", "cluster", "vendors", "whereas", "serves", + "woods", "surprise", "partial", "shoppers", "couples", "ranking", "jokes", "simpson", + "twiki", "sublime", "palace", "verify", "globe", "trusted", "copper", "dicke", + "kerry", "receipt", "supposed", "ordinary", "nobody", "ghost", "applying", "pride", + "knowing", "reporter", "keith", "champion", "cloudy", "linda", "chile", "plenty", + "sentence", "throat", "ignore", "maria", "uniform", "wealth", "vacuum", "dancing", + "brass", "writes", "plaza", "outcomes", "survival", "quest", "publish", "trans", + "jonathan", "whenever", "lifetime", "pioneer", "booty", "acrobat", "plates", "acres", + "venue", "athletic", "thermal", "essays", "vital", "telling", "fairly", "coastal", + "config", "charity", "excel", "modes", "campbell", "stupid", "harbor", "hungary", + "traveler", "segment", "realize", "enemy", "puzzle", "rising", "aluminum", "wells", + "wishlist", "opens", "insight", "secrets", "lucky", "latter", "thick", "trailers", + "repeat", "syndrome", "philips", "penalty", "glasses", "enables", "iraqi", "builder", + "vista", "jessica", "chips", "terry", "flood", "arena", "pupils", "stewart", + "outcome", "expanded", "casual", "grown", "polish", "lovely", "extras", "centres", + "jerry", "clause", "smile", "lands", "troops", "indoor", "bulgaria", "armed", + "broker", "charger", "believed", "cooling", "trucks", "divorce", "laura", "shopper", + "tokyo", "partly", "nikon", "candy", "pills", "tiger", "donald", "folks", + "sensor", "exposed", "telecom", "angels", "deputy", "sealed", "loaded", "scenes", + "boost", "spanking", "founded", "chronic", "icons", "moral", "catering", "finger", + "keeps", "pound", "locate", "trained", "roses", "bread", "tobacco", "wooden", + "motors", "tough", "roberts", "incident", "gonna", "dynamics", "decrease", "chest", + "pension", "billy", "revenues", "emerging", "worship", "craig", "herself", "churches", + "damages", "reserves", "solve", "shorts", "minority", "diverse", "johnny", "recorder", + "facing", "nancy", "tones", "passion", "sight", "defence", "patches", "refund", + "towns", "trembl", "divided", "emails", "cyprus", "insider", "seminars", "makers", + "hearts", "worry", "carter", "legacy", "pleased", "danger", "vitamin", "widely", + "phrase", "genuine", "raising", "paradise", "hybrid", "reads", "roles", "glory", + "bigger", "billing", "diesel", "versus", "combine", "exceed", "saudi", "fault", + "babies", "karen", "compiled", "romantic", "revealed", "albert", "examine", "jimmy", + "graham", "bristol", "margaret", "compaq", "slowly", "rugby", "portions", "infant", + "sectors", "samuel", "fluid", "grounds", "regards", "unlike", "equation", "baskets", + "wright", "barry", "proven", "cached", "warren", "studied", "reviewer", "involves", + "profits", "devil", "grass", "comply", "marie", "florist", "cherry", "deutsch", + "kenya", "webcam", "funeral", "nutten", "earrings", "enjoyed", "chapters", "charlie", + "quebec", "dennis", "francis", "sized", "manga", "noticed", "socket", "silent", + "literary", "signals", "theft", "swing", "symbols", "humans", "analog", "facial", + "choosing", "talent", "dated", "seeker", "wisdom", "shoot", "boundary", "packard", + "offset", "payday", "philip", "elite", "holders", "believes", "swedish", "poems", + "deadline", "robot", "witness", "collins", "equipped", "stages", "winds", "powder", + "broadway", "acquired", "assess", "stones", "entrance", "gnome", "roots", "losing", + "attempts", "gadgets", "noble", "glasgow", "impacts", "gospel", "shore", "loves", + "induced", "knight", "loose", "linking", "appeals", "earned", "illness", "islamic", + "pending", "parker", "lebanon", "kennedy", "teenage", "triple", "cooper", "vincent", + "secured", "unusual", "answered", "slots", "disorder", "routine", "toolbar", "rocks", + "titans", "wearing", "sought", "genes", "mounted", "habitat", "firewall", "median", + "scanner", "herein", "animated", "judicial", "integer", "bachelor", "attitude", "engaged", + "falling", "basics", "montreal", "carpet", "struct", "lenses", "binary", "genetics", + "attended", "dropped", "walter", "besides", "hosts", "moments", "atlas", "strings", + "feels", "torture", "deleted", "mitchell", "ralph", "warner", "embedded", "inkjet", + "wizard", "corps", "actors", "liver", "liable", "brochure", "morris", "petition", + "eminem", "recall", "antenna", "picked", "assumed", "belief", "killing", "bikini", + "memphis", "shoulder", "decor", "lookup", "texts", "harvard", "brokers", "diameter", + "ottawa", "podcast", "seasons", "refine", "bidder", "singer", "evans", "herald", + "literacy", "fails", "aging", "plugin", "diving", "invite", "alice", "latinas", + "suppose", "involve", "moderate", "terror", "younger", "thirty", "opposite", "rapidly", + "dealtime", "intro", "mercedes", "clerk", "mills", "outline", "tramadol", "holland", + "receives", "jeans", "fonts", "refers", "favor", "veterans", "sigma", "xhtml", + "occasion", "victim", "demands", "sleeping", "careful", "arrive", "sunset", "tracked", + "moreover", "minimal", "lottery", "framed", "aside", "licence", "michelle", "essay", + "dialogue", "camps", "declared", "aaron", "handheld", "trace", "disposal", "florists", + "packs", "switches", "romania", "consult", "greatly", "blogging", "cycling", "midnight", + "commonly", "inform", "turkish", "pentium", "quantum", "murray", "intent", "largely", + "pleasant", "announce", "spoke", "arrow", "sampling", "rough", "weird", "inspired", + "holes", "weddings", "blade", "suddenly", "oxygen", "cookie", "meals", "canyon", + "meters", "merely", "passes", "pointer", "stretch", "durham", "permits", "muslim", + "sleeve", "netscape", "cleaner", "cricket", "feeding", "stroke", "township", "rankings", + "robin", "robinson", "strap", "sharon", "crowd", "olympic", "remained", "entities", + "customs", "rainbow", "roulette", "decline", "gloves", "israeli", "medicare", "skiing", + "cloud", "valve", "hewlett", "explains", "proceed", "flickr", "feelings", "knife", + "jamaica", "shelf", "timing", "liked", "adopt", "denied", "fotos", "britney", + "freeware", "donation", "outer", "deaths", "rivers", "tales", "katrina", "islam", + "nodes", "thumbs", "seeds", "cited", "targeted", "skype", "realized", "twelve", + "founder", "decade", "gamecube", "dispute", "tired", "titten", "adverse", "excerpt", + "steam", "drinks", "voices", "acute", "climbing", "stood", "perfume", "carol", + "honest", "albany", "restore", "stack", "somebody", "curve", "creator", "amber", + "museums", "coding", "tracker", "passage", "trunk", "hiking", "pierre", "jelsoft", + "headset", "oakland", "colombia", "waves", "camel", "lamps", "suicide", "archived", + "arabia", "juice", "chase", "logical", "sauce", "extract", "panama", "payable", + "courtesy", "athens", "judges", "retired", "remarks", "detected", "decades", "walked", + "arising", "nissan", "bracelet", "juvenile", "afraid", "acoustic", "railway", "cassette", + "pointed", "causing", "mistake", "norton", "locked", "fusion", "mineral", "steering", + "beads", "fortune", "canvas", "parish", "claimed", "screens", "cemetery", "planner", + "croatia", "flows", "stadium", "fewer", "coupon", "nurses", "proxy", "lanka", + "edwards", "contests", "costume", "tagged", "berkeley", "voted", "killer", "bikes", + "gates", "adjusted", "bishop", "pulled", "shaped", "seasonal", "farmer", "counters", + "slave", "cultures", "norfolk", "coaching", "examined", "encoding", "heroes", "painted", + "lycos", "zdnet", "artwork", "cosmetic", "resulted", "portrait", "ethical", "carriers", + "mobility", "floral", "builders", "struggle", "schemes", "neutral", "fisher", "spears", + "bedding", "joining", "heading", "equally", "bearing", "combo", "seniors", "worlds", + "guilty", "haven", "tablet", "charm", "violent", "basin", "ranch", "crossing", + "cottage", "drunk", "crimes", "resolved", "mozilla", "toner", "latex", "branches", + "anymore", "delhi", "holdings", "alien", "locator", "broke", "nepal", "zimbabwe", + "browsing", "resolve", "melissa", "moscow", "thesis", "nylon", "discs", "rocky", + "bargains", "frequent", "nigeria", "ceiling", "pixels", "ensuring", "hispanic", "anybody", + "diamonds", "fleet", "untitled", "bunch", "totals", "marriott", "singing", "afford", + "starring", "referral", "optimal", "distinct", "turner", "sucking", "cents", "reuters", + "spoken", "omega", "stayed", "civic", "manuals", "watched", "saver", "thereof", + "grill", "redeem", "rogers", "grain", "regime", "wanna", "wishes", "depend", + "differ", "ranging", "monica", "repairs", "breath", "candle", "hanging", "colored", + "verified", "formerly", "situated", "seeks", "herbal", "loving", "strictly", "routing", + "stanley", "retailer", "vitamins", "elegant", "gains", "renewal", "opposed", "deemed", + "scoring", "brooklyn", "sisters", "critics", "spots", "hacker", "madrid", "margin", + "solely", "salon", "norman", "turbo", "headed", "voters", "madonna", "murphy", + "thinks", "thats", "soldier", "phillips", "aimed", "justin", "interval", "mirrors", + "tricks", "reset", "brush", "expansys", "panels", "repeated", "assault", "spare", + "kodak", "tongue", "bowling", "danish", "monkey", "filename", "skirt", "florence", + "invest", "honey", "analyzes", "drawings", "scenario", "lovers", "atomic", "approx", + "arabic", "gauge", "junction", "faced", "rachel", "solving", "weekends", "produces", + "chains", "kingston", "sixth", "engage", "deviant", "quoted", "adapters", "farms", + "imports", "cheat", "bronze", "sandy", "suspect", "macro", "sender", "crucial", + "adjacent", "tuition", "spouse", "exotic", "viewer", "signup", "threats", "puzzles", + "reaching", "damaged", "receptor", "laugh", "surgical", "destroy", "citation", "pitch", + "autos", "premises", "perry", "proved", "imperial", "dozen", "benjamin", "teeth", + "cloth", "studying", "stamp", "lotus", "salmon", "olympus", "cargo", "salem", + "starter", "upgrades", "likes", "butter", "pepper", "weapon", "luggage", "burden", + "tapes", "zones", "races", "stylish", "maple", "grocery", "offshore", "depot", + "kenneth", "blend", "harrison", "julie", "emission", "finest", "realty", "janet", + "apparent", "phpbb", "autumn", "probe", "toilet", "ranked", "jackets", "routes", + "packed", "excited", "outreach", "helen", "mounting", "recover", "lopez", "balanced", + "timely", "talked", "debug", "delayed", "chuck", "explicit", "villas", "ebook", + "exclude", "peeing", "brooks", "newton", "anxiety", "bingo", "whilst", "spatial", + "ceramic", "prompt", "precious", "minds", "annually", "scanners", "xanax", "fingers", + "sunny", "ebooks", "delivers", "necklace", "leeds", "cedar", "arranged", "theaters", + "advocacy", "raleigh", "threaded", "qualify", "blair", "hopes", "mason", "diagram", + "burns", "pumps", "footwear", "beijing", "peoples", "victor", "mario", "attach", + "licenses", "utils", "removing", "advised", "spider", "ranges", "pairs", "trails", + "hudson", "isolated", "calgary", "interim", "assisted", "divine", "approve", "chose", + "compound", "abortion", "dialog", "venues", "blast", "wellness", "calcium", "newport", + "indians", "shield", "harvest", "membrane", "prague", "previews", "locally", "pickup", + "mothers", "nascar", "iceland", "candles", "sailing", "sacred", "morocco", "chrome", + "tommy", "refused", "brake", "exterior", "greeting", "ecology", "oliver", "congo", + "botswana", "delays", "olive", "cyber", "verizon", "scored", "clone", "velocity", + "lambda", "relay", "composed", "tears", "oasis", "baseline", "angry", "silicon", + "compete", "lover", "belong", "honolulu", "beatles", "rolls", "thomson", "barnes", + "malta", "daddy", "ferry", "rabbit", "seating", "exports", "omaha", "electron", + "loads", "heather", "passport", "motel", "unions", "treasury", "warrant", "solaris", + "frozen", "occupied", "royalty", "scales", "rally", "observer", "sunshine", "strain", + "ceremony", "somehow", "arrested", "yamaha", "hebrew", "gained", "dying", "laundry", + "stuck", "solomon", "placing", "stops", "homework", "adjust", "assessed", "enabling", + "filling", "imposed", "silence", "focuses", "soviet", "treaty", "vocal", "trainer", + "organ", "stronger", "volumes", "advances", "lemon", "toxic", "darkness", "bizrate", + "vienna", "implied", "stanford", "packing", "statute", "rejected", "satisfy", "shelter", + "chapel", "gamespot", "layers", "guided", "bahamas", "powell", "mixture", "bench", + "rider", "radius", "logging", "hampton", "borders", "butts", "bobby", "sheep", + "railroad", "lectures", "wines", "nursery", "harder", "cheapest", "travesti", "stuart", + "salvador", "salad", "monroe", "tender", "paste", "clouds", "tanzania", "preserve", + "unsigned", "staying", "easter", "theories", "praise", "jeremy", "venice", "estonia", + "veteran", "streams", "landing", "signing", "executed", "katie", "showcase", "integral", + "relax", "namibia", "synopsis", "hardly", "prairie", "reunion", "composer", "sword", + "absent", "sells", "ecuador", "hoping", "accessed", "spirits", "coral", "pixel", + "float", "colin", "imported", "paths", "bubble", "acquire", "contrary", "tribune", + "vessel", "acids", "focusing", "viruses", "cheaper", "admitted", "dairy", "admit", + "fancy", "equality", "samoa", "stickers", "leasing", "lauren", "beliefs", "squad", + "analyze", "ashley", "scroll", "relate", "wages", "suffer", "forests", "invalid", + "concerts", "martial", "males", "retain", "execute", "tunnel", "genres", "cambodia", + "patents", "chaos", "wheat", "beaver", "updating", "readings", "kijiji", "confused", + "compiler", "eagles", "bases", "accused", "unity", "bride", "defines", "airports", + "begun", "brunette", "packets", "anchor", "socks", "parade", "trigger", "gathered", + "essex", "slovenia", "notified", "beaches", "folders", "dramatic", "surfaces", "terrible", + "routers", "pendant", "dresses", "baptist", "hiring", "clocks", "females", "wallace", + "reflects", "taxation", "fever", "cuisine", "surely", "myspace", "theorem", "stylus", + "drums", "arnold", "chicks", "cattle", "radical", "rover", "treasure", "reload", + "flame", "levitra", "tanks", "assuming", "monetary", "elderly", "floating", "bolivia", + "spell", "hottest", "stevens", "kuwait", "emily", "alleged", "compile", "webster", + "struck", "plymouth", "warnings", "bridal", "annex", "tribal", "curious", "freight", + "rebate", "meetup", "eclipse", "sudan", "shuttle", "stunning", "cycles", "affects", + "detect", "actively", "ampland", "fastest", "butler", "injured", "payroll", "cookbook", + "courier", "uploaded", "hints", "collapse", "americas", "unlikely", "techno", "beverage", + "tribute", "wired", "elvis", "immune", "latvia", "forestry", "barriers", "rarely", + "infected", "martha", "genesis", "barrier", "argue", "trains", "metals", "bicycle", + "letting", "arise", "celtic", "thereby", "jamie", "particle", "minerals", "advise", + "humidity", "bottles", "boxing", "bangkok", "hughes", "jeffrey", "chess", "operates", + "brisbane", "survive", "oscar", "menus", "reveal", "canal", "amino", "herbs", + "clinics", "manitoba", "missions", "watson", "lying", "costumes", "strict", "saddam", + "drill", "offense", "bryan", "protest", "hobby", "tries", "nickname", "inline", + "washing", "staffing", "trick", "enquiry", "closure", "timber", "intense", "playlist", + "showers", "ruling", "steady", "statutes", "myers", "drops", "wider", "plugins", + "enrolled", "sensors", "screw", "publicly", "hourly", "blame", "geneva", "freebsd", + "reseller", "handed", "suffered", "intake", "informal", "tucson", "heavily", "swingers", + "fifty", "headers", "mistakes", "uncle", "defining", "counting", "assure", "devoted", + "jacob", "sodium", "randy", "hormone", "timothy", "brick", "naval", "medieval", + "bridges", "captured", "thehun", "decent", "casting", "dayton", "shortly", "cameron", + "carlos", "donna", "andreas", "warrior", "diploma", "cabin", "innocent", "scanning", + "valium", "copying", "cordless", "patricia", "eddie", "uganda", "fired", "trivia", + "adidas", "perth", "grammar", "syria", "disagree", "klein", "harvey", "tires", + "hazard", "retro", "gregory", "episodes", "boolean", "circular", "anger", "mainland", + "suits", "chances", "interact", "bizarre", "glenn", "auckland", "olympics", "fruits", + "ribbon", "startup", "suzuki", "trinidad", "kissing", "handy", "exempt", "crops", + "reduces", "geometry", "slovakia", "guild", "gorgeous", "capitol", "dishes", "barbados", + "chrysler", "nervous", "refuse", "extends", "mcdonald", "replica", "plumbing", "brussels", + "tribe", "trades", "superb", "trinity", "handled", "legends", "floors", "exhaust", + "shanghai", "speaks", "burton", "davidson", "copied", "scotia", "farming", "gibson", + "roller", "batch", "organize", "alter", "nicole", "latino", "ghana", "edges", + "mixing", "handles", "skilled", "fitted", "harmony", "asthma", "twins", "triangle", + "amend", "oriental", "reward", "windsor", "zambia", "hydrogen", "webshots", "sprint", + "chick", "advocate", "inputs", "genome", "escorts", "thong", "medal", "coaches", + "vessels", "walks", "knives", "arrange", "artistic", "honors", "booth", "indie", + "unified", "bones", "breed", "detector", "ignored", "polar", "fallen", "precise", + "sussex", "msgid", "invoice", "gather", "backed", "alfred", "colonial", "carey", + "motels", "forming", "embassy", "danny", "rebecca", "slight", "proceeds", "indirect", + "amongst", "msgstr", "arrest", "adipex", "horizon", "deeply", "toolbox", "marina", + "prizes", "bosnia", "browsers", "patio", "surfing", "lloyd", "optics", "pursue", + "overcome", "attract", "brighton", "beans", "ellis", "disable", "snake", "succeed", + "leonard", "lending", "reminder", "searched", "plains", "raymond", "insights", "sullivan", + "midwest", "karaoke", "lonely", "hereby", "observe", "julia", "berry", "collar", + "racial", "bermuda", "amanda", "mobiles", "kelkoo", "exhibits", "terrace", "bacteria", + "replied", "seafood", "novels", "ought", "safely", "finite", "kidney", "fixes", + "sends", "durable", "mazda", "allied", "throws", "moisture", "roster", "symantec", + "spencer", "wichita", "nasdaq", "uruguay", "timer", "tablets", "tuning", "gotten", + "tyler", "futures", "verse", "highs", "wanting", "custody", "scratch", "launches", + "ellen", "rocket", "bullet", "towers", "racks", "nasty", "latitude", "tumor", + "deposits", "beverly", "mistress", "trustees", "watts", "duncan", "reprints", "bernard", + "forty", "tubes", "midlands", "priest", "floyd", "ronald", "analysts", "queue", + "trance", "locale", "nicholas", "bundle", "hammer", "invasion", "runner", "notion", + "skins", "mailed", "fujitsu", "spelling", "arctic", "exams", "rewards", "beneath", + "defend", "medicaid", "infrared", "seventh", "welsh", "belly", "quarters", "stolen", + "soonest", "haiti", "naturals", "lenders", "fitting", "fixtures", "bloggers", "agrees", + "surplus", "elder", "sonic", "cheers", "belarus", "zoning", "gravity", "thumb", + "guitars", "essence", "flooring", "ethiopia", "mighty", "athletes", "humanity", "holmes", + "scholars", "galaxy", "chester", "snapshot", "caring", "segments", "dominant", "twist", + "itunes", "stomach", "buried", "newbie", "minimize", "darwin", "ranks", "debut", + "bradley", "anatomy", "fraction", "defects", "milton", "marker", "clarity", "sandra", + "adelaide", "monaco", "settled", "folding", "emirates", "airfare", "vaccine", "belize", + "promised", "volvo", "penny", "robust", "bookings", "minolta", "porter", "jungle", + "ivory", "alpine", "andale", "fabulous", "remix", "alias", "newer", "spice", + "implies", "cooler", "maritime", "periodic", "overhead", "ascii", "prospect", "shipment", + "breeding", "donor", "tension", "trash", "shapes", "manor", "envelope", "diane", + "homeland", "excluded", "andrea", "breeds", "rapids", "disco", "bailey", "endif", + "emotions", "incoming", "lexmark", "cleaners", "eternal", "cashiers", "rotation", "eugene", + "metric", "minus", "bennett", "hotmail", "joshua", "armenia", "varied", "grande", + "closest", "actress", "assign", "tigers", "aurora", "slides", "milan", "premiere", + "lender", "villages", "shade", "chorus", "rhythm", "digit", "argued", "dietary", + "symphony", "clarke", "sudden", "marilyn", "lions", "findlaw", "pools", "lyric", + "claire", "speeds", "matched", "carroll", "rational", "fighters", "chambers", "warming", + "vocals", "fountain", "chubby", "grave", "burner", "finnish", "gentle", "deeper", + "muslims", "footage", "howto", "worthy", "reveals", "saints", "carries", "devon", + "helena", "saves", "regarded", "marion", "lobby", "egyptian", "tunisia", "outlined", + "headline", "treating", "punch", "gotta", "cowboy", "bahrain", "enormous", "karma", + "consist", "betty", "queens", "lucas", "tribes", "defeat", "clicks", "honduras", + "naughty", "hazards", "insured", "harper", "mardi", "tenant", "cabinets", "tattoo", + "shake", "algebra", "shadows", "holly", "silly", "mercy", "hartford", "freely", + "marcus", "sunrise", "wrapping", "weblogs", "timeline", "belongs", "readily", "fence", + "nudist", "infinite", "diana", "ensures", "lindsay", "legally", "shame", "civilian", + "fatal", "remedy", "realtors", "briefly", "genius", "fighter", "flesh", "retreat", + "adapted", "barely", "wherever", "estates", "democrat", "borough", "failing", "retained", + "pamela", "andrews", "marble", "jesse", "logitech", "surrey", "briefing", "belkin", + "highland", "modular", "brandon", "giants", "balloon", "winston", "solved", "hawaiian", + "gratuit", "consoles", "qatar", "magnet", "porsche", "cayman", "jaguar", "sheer", + "posing", "hopkins", "urgent", "infants", "gothic", "cylinder", "witch", "cohen", + "puppy", "kathy", "graphs", "surround", "revenge", "expires", "enemies", "finances", + "accepts", "enjoying", "patrol", "smell", "italiano", "carnival", "roughly", "sticker", + "promises", "divide", "cornell", "satin", "deserve", "mailto", "promo", "worried", + "tunes", "garbage", "combines", "bradford", "phrases", "chelsea", "boring", "reynolds", + "speeches", "reaches", "schema", "catalogs", "quizzes", "prefix", "lucia", "savannah", + "barrel", "typing", "nerve", "planets", "deficit", "boulder", "pointing", "renew", + "coupled", "myanmar", "metadata", "harold", "circuits", "floppy", "texture", "handbags", + "somerset", "incurred", "antigua", "thunder", "caution", "locks", "namely", "euros", + "pirates", "aerial", "rebel", "origins", "hired", "makeup", "textile", "nathan", + "tobago", "indexes", "hindu", "licking", "markers", "weights", "albania", "lasting", + "wicked", "kills", "roommate", "webcams", "pushed", "slope", "reggae", "failures", + "surname", "theology", "nails", "evident", "whats", "rides", "rehab", "saturn", + "allergy", "twisted", "merit", "enzyme", "zshops", "planes", "edmonton", "tackle", + "disks", "condo", "pokemon", "ambien", "retrieve", "vernon", "worldcat", "titanium", + "fairy", "builds", "shaft", "leslie", "casio", "deutsche", "postings", "kitty", + "drain", "monte", "fires", "algeria", "blessed", "cardiff", "cornwall", "favors", + "potato", "panic", "sticks", "leone", "excuse", "reforms", "basement", "onion", + "strand", "sandwich", "lawsuit", "cheque", "banners", "reject", "circles", "italic", + "beats", "merry", "scuba", "passive", "valued", "courage", "verde", "gazette", + "hitachi", "batman", "hearings", "coleman", "anaheim", "textbook", "dried", "luther", + "frontier", "settle", "stopping", "refugees", "knights", "palmer", "derby", "peaceful", + "altered", "pontiac", "doctrine", "scenic", "trainers", "sewing", "conclude", "munich", + "celebs", "propose", "lighter", "advisors", "pavilion", "tactics", "trusts", "talented", + "annie", "pillow", "derek", "shorter", "harley", "relying", "finals", "paraguay", + "steal", "parcel", "refined", "fifteen", "fears", "predict", "boutique", "acrylic", + "rolled", "tuner", "peterson", "shannon", "toddler", "flavor", "alike", "homeless", + "horrible", "hungry", "metallic", "blocked", "warriors", "cadillac", "malawi", "sagem", + "curtis", "parental", "strikes", "lesser", "marathon", "pressing", "gasoline", "dressed", + "scout", "belfast", "dealt", "niagara", "warcraft", "charms", "catalyst", "trader", + "bucks", "denial", "thrown", "prepaid", "raises", "electro", "badge", "wrist", + "analyzed", "heath", "ballot", "lexus", "varying", "remedies", "validity", "trustee", + "weighted", "angola", "performs", "plastics", "realm", "jenny", "helmet", "salaries", + "postcard", "elephant", "yemen", "tsunami", "scholar", "nickel", "buses", "expedia", + "geology", "coating", "wallet", "cleared", "smilies", "boating", "drainage", "shakira", + "corners", "broader", "rouge", "yeast", "clearing", "coated", "intend", "louise", + "kenny", "routines", "hitting", "yukon", "beings", "aquatic", "reliance", "habits", + "striking", "podcasts", "singh", "gilbert", "ferrari", "brook", "outputs", "ensemble", + "insulin", "assured", "biblical", "accent", "mysimon", "eleven", "wives", "ambient", + "utilize", "mileage", "prostate", "adaptor", "auburn", "unlock", "hyundai", "pledge", + "vampire", "angela", "relates", "nitrogen", "xerox", "merger", "softball", "firewire", + "nextel", "framing", "musician", "blocking", "rwanda", "sorts", "vsnet", "limiting", + "dispatch", "papua", "restored", "armor", "riders", "chargers", "remark", "dozens", + "varies", "rendered", "picking", "guards", "openings", "councils", "kruger", "pockets", + "granny", "viral", "inquire", "pipes", "laden", "aruba", "cottages", "realtor", + "merge", "edgar", "develops", "chassis", "dubai", "pushing", "fleece", "pierce", + "allan", "dressing", "sperm", "filme", "craps", "frost", "sally", "yacht", + "tracy", "prefers", "drilling", "breach", "whale", "tomatoes", "bedford", "mustang", + "clusters", "antibody", "momentum", "wiring", "pastor", "calvin", "shark", "phases", + "grateful", "emerald", "laughing", "grows", "cliff", "tract", "ballet", "abraham", + "bumper", "webpage", "garlic", "hostels", "shine", "senegal", "banned", "wendy", + "briefs", "diffs", "mumbai", "ozone", "radios", "tariff", "nvidia", "opponent", + "pasta", "muscles", "serum", "wrapped", "swift", "runtime", "inbox", "focal", + "distant", "decimal", "propecia", "samba", "hostel", "employ", "mongolia", "penguin", + "magical", "miracle", "manually", "reprint", "centered", "yearly", "wound", "belle", + "writings", "hamburg", "cindy", "fathers", "charging", "marvel", "lined", "petite", + "terrain", "strips", "gossip", "rangers", "rotary", "discrete", "beginner", "boxed", + "cubic", "sapphire", "kinase", "skirts", "crawford", "labeled", "marking", "serbia", + "sheriff", "griffin", "declined", "guyana", "spies", "neighbor", "elect", "highways", + "thinkpad", "intimate", "preston", "deadly", "bunny", "chevy", "rounds", "longest", + "tions", "dentists", "flyer", "dosage", "variance", "cameroon", "baking", "adaptive", + "computed", "needle", "baths", "brakes", "nirvana", "invision", "sticky", "destiny", + "generous", "madness", "emacs", "climb", "blowing", "heated", "jackie", "sparc", + "cardiac", "dover", "adrian", "vatican", "brutal", "learners", "token", "seekers", + "yields", "suited", "numeric", "skating", "kinda", "aberdeen", "emperor", "dylan", + "belts", "blacks", "educated", "rebates", "burke", "proudly", "inserted", "pulling", + "basename", "obesity", "curves", "suburban", "touring", "clara", "vertex", "tomato", + "andorra", "expired", "travels", "flush", "waiver", "hayes", "delight", "survivor", + "garcia", "cingular", "moses", "counted", "declare", "johns", "valves", "impaired", + "donors", "jewel", "teddy", "teaches", "ventures", "bufing", "stranger", "tragedy", + "julian", "dryer", "painful", "velvet", "tribunal", "ruled", "pensions", "prayers", + "funky", "nowhere", "joins", "wesley", "lately", "scary", "mattress", "mpegs", + "brunei", "likewise", "banana", "slovak", "cakes", "mixer", "remind", "sbjct", + "charming", "tooth", "annoying", "stays", "disclose", "affair", "drove", "washer", + "upset", "restrict", "springer", "beside", "mines", "rebound", "logan", "mentor", + "fought", "baghdad", "metres", "pencil", "freeze", "titled", "sphere", "ratios", + "concord", "endorsed", "walnut", "lance", "ladder", "italia", "liberia", "sherman", + "maximize", "hansen", "senators", "workout", "bleeding", "colon", "lanes", "purse", + "optimize", "stating", "caroline", "align", "bless", "engaging", "crest", "triumph", + "welding", "deferred", "alloy", "condos", "plots", "polished", "gently", "tulsa", + "locking", "casey", "draws", "fridge", "blanket", "bloom", "simpsons", "elliott", + "fraser", "justify", "blades", "loops", "surge", "trauma", "tahoe", "advert", + "possess", "flashers", "subaru", "vanilla", "picnic", "souls", "arrivals", "spank", + "hollow", "vault", "securely", "fioricet", "groove", "pursuit", "wires", "mails", + "backing", "sleeps", "blake", "travis", "endless", "figured", "orbit", "niger", + "bacon", "heater", "colony", "cannon", "circus", "promoted", "forbes", "moldova", + "paxil", "spine", "trout", "enclosed", "cooked", "thriller", "transmit", "apnic", + "fatty", "gerald", "pressed", "scanned", "hunger", "mariah", "joyce", "surgeon", + "cement", "planners", "disputes", "textiles", "missile", "intranet", "closes", "deborah", + "marco", "assists", "gabriel", "auditor", "aquarium", "violin", "prophet", "bracket", + "isaac", "oxide", "naples", "promptly", "modems", "harmful", "prozac", "sexually", + "dividend", "newark", "glucose", "phantom", "playback", "turtle", "warned", "neural", + "fossil", "hometown", "badly", "apollo", "persian", "handmade", "greene", "robots", + "grenada", "scoop", "earning", "mailman", "sanyo", "nested", "somalia", "movers", + "verbal", "blink", "carlo", "workflow", "novelty", "bryant", "tiles", "voyuer", + "switched", "tamil", "garmin", "fuzzy", "grams", "richards", "budgets", "toolkit", + "render", "carmen", "hardwood", "erotica", "temporal", "forge", "dense", "brave", + "awful", "airplane", "istanbul", "impose", "viewers", "asbestos", "meyer", "enters", + "savage", "willow", "resumes", "throwing", "existed", "wagon", "barbie", "knock", + "potatoes", "thorough", "peers", "roland", "optimum", "quilt", "creature", "mounts", + "syracuse", "refresh", "webcast", "michel", "subtle", "notre", "maldives", "stripes", + "firmware", "shepherd", "canberra", "cradle", "mambo", "flour", "sympathy", "choir", + "avoiding", "blond", "expects", "jumping", "fabrics", "polymer", "hygiene", "poultry", + "virtue", "burst", "surgeons", "bouquet", "promotes", "mandate", "wiley", "corpus", + "johnston", "fibre", "shades", "indices", "adware", "zoloft", "prisoner", "daisy", + "halifax", "ultram", "cursor", "earliest", "donated", "stuffed", "insects", "crude", + "morrison", "maiden", "examines", "viking", "myrtle", "bored", "cleanup", "bother", + "budapest", "knitting", "attacked", "bhutan", "mating", "compute", "redhead", "arrives", + "tractor", "allah", "unwrap", "fares", "resist", "hoped", "safer", "wagner", + "touched", "cologne", "wishing", "ranger", "smallest", "newman", "marsh", "ricky", + "scared", "theta", "monsters", "asylum", "lightbox", "robbie", "stake", "cocktail", + "outlets", "arbor", "poison"}; + +const char *P4Tools::P4Smith::Wordlist::getFromWordlist() { + if (counter < WORDLIST_LENGTH) { + counter++; + return WORDS.at(counter - 1); + } + return ""; +} + +} // namespace P4Tools::P4Smith diff --git a/backends/p4tools/modules/smith/util/wordlist.h b/backends/p4tools/modules/smith/util/wordlist.h new file mode 100644 index 00000000000..637cdb35cb1 --- /dev/null +++ b/backends/p4tools/modules/smith/util/wordlist.h @@ -0,0 +1,32 @@ +#ifndef BACKENDS_P4TOOLS_MODULES_SMITH_UTIL_WORDLIST_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_UTIL_WORDLIST_H_ +#include +#include + +#define WORDLIST_LENGTH 10000 + +namespace P4Tools::P4Smith { + +/// This class is a wrapper around an underlying array of words, which is currently being +/// used to aid random name generation. +class Wordlist { + public: + Wordlist() = default; + + ~Wordlist() = default; + + /// Pops and @returns the top-most(closest to the beginning of the array) non-popped + /// element from the array. + static const char *getFromWordlist(); + + private: + /// Stores the address of the next word to be popped of the words array + static std::size_t counter; + + /// The actual array storing the words. + static const std::array WORDS; +}; + +} // namespace P4Tools::P4Smith + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_UTIL_WORDLIST_H_ */ diff --git a/backends/p4tools/modules/smith/version.h.cmake b/backends/p4tools/modules/smith/version.h.cmake new file mode 100644 index 00000000000..3504ff6d49c --- /dev/null +++ b/backends/p4tools/modules/smith/version.h.cmake @@ -0,0 +1,39 @@ +/* +Copyright 2018-present Barefoot Networks, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may !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 BACKENDS_P4TOOLS_MODULES_SMITH_VERSION_H_ +#define BACKENDS_P4TOOLS_MODULES_SMITH_VERSION_H_ + +/** + Set the compiler version at build time. + The build system defines P4C_VERSION as a full string as well as the + following components: P4C_VERSION_MAJOR, P4C_VERSION_MINOR, + P4C_VERSION_PATCH, P4C_VERSION_RC, and P4C_GIT_SHA. + + They can be used to construct a version string as follows: + #define VERSION_STRING "@P4C_VERSION@" + or + #define VERSION_STRING "@P4C_VERSION_MAJOR@.@P4C_VERSION_MINOR@.@P4C_VERSION_PATCH@@P4C_VERSION_RC@" + + Or, since this is backend specific, feel free to define other numbering + scheme. + + */ + +#define SMITH_VERSION_STRING "@P4C_VERSION@" + + +#endif /* BACKENDS_P4TOOLS_MODULES_SMITH_VERSION_H_ */ diff --git a/backends/p4tools/modules/testgen/README.md b/backends/p4tools/modules/testgen/README.md index 3bcb7fa7e6b..6729e5fd456 100644 --- a/backends/p4tools/modules/testgen/README.md +++ b/backends/p4tools/modules/testgen/README.md @@ -34,6 +34,12 @@ P4Testgen depends on the P4Tools framework and is automatically installed with P P4Testgen is available as part of the [official P4C docker image](https://hub.docker.com/r/p4lang/p4c/). On Debian-based systems, it is also possible to install a P4Testgen binary by following [these](https://github.com/p4lang/p4c#installing-packaged-versions-of-p4c) instructions. +### Dependencies +In addition to [P4Tools'](../README.md#dependencies) own dependencies P4Testgen depends on the following external software: + * [inja](https://github.com/pantor/inja) template engine for testcase generation. + +These dependencies are automatically installed via CMakelist's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) module. + ## Extensions P4Testgen extensions are instantiations of a particular combination of P4 architecture and the target that executes the P4 code. For example, the `v1model.p4` architecture can be executed on the behavioral model. P4Testgen extension make use of the core P4Testgen framework to generate tests. Several open-source extensions are available.