Skip to content

Commit

Permalink
[P4Testgen] Extend the CompilerTarget runProgram function with data s…
Browse files Browse the repository at this point in the history
…tructures which can pass on more information. (#4323)

* Step 1: Extend the CompilerTarget runProgram function with data structures which can pass on more information.

* Use a typedef for the compiler result.
  • Loading branch information
fruffy authored Jan 12, 2024
1 parent 61969ab commit 313faed
Show file tree
Hide file tree
Showing 15 changed files with 103 additions and 77 deletions.
18 changes: 10 additions & 8 deletions backends/p4tools/common/compiler/compiler_target.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#include "backends/p4tools/common/compiler/compiler_target.h"

#include <functional>
#include <string>
#include <utility>
#include <vector>

#include "backends/p4tools/common/compiler/context.h"
#include "backends/p4tools/common/compiler/convert_hs_index.h"
#include "backends/p4tools/common/compiler/midend.h"
#include "backends/p4tools/common/core/target.h"
#include "frontends/common/applyOptionsPragmas.h"
Expand All @@ -18,13 +18,17 @@

namespace P4Tools {

const IR::P4Program &CompilerResult::getProgram() const { return program; }

CompilerResult::CompilerResult(const IR::P4Program &program) : program(program) {}

ICompileContext *CompilerTarget::makeContext() { return get().makeContextImpl(); }

std::vector<const char *> *CompilerTarget::initCompiler(int argc, char **argv) {
return get().initCompilerImpl(argc, argv);
}

std::optional<const IR::P4Program *> CompilerTarget::runCompiler() {
CompilerResultOrError CompilerTarget::runCompiler() {
const auto *program = P4Tools::CompilerTarget::runParser();
if (program == nullptr) {
return std::nullopt;
Expand All @@ -33,7 +37,7 @@ std::optional<const IR::P4Program *> CompilerTarget::runCompiler() {
return runCompiler(program);
}

std::optional<const IR::P4Program *> CompilerTarget::runCompiler(const std::string &source) {
CompilerResultOrError CompilerTarget::runCompiler(const std::string &source) {
const auto *program = P4::parseP4String(source, P4CContext::get().options().langVersion);
if (program == nullptr) {
return std::nullopt;
Expand All @@ -42,12 +46,11 @@ std::optional<const IR::P4Program *> CompilerTarget::runCompiler(const std::stri
return runCompiler(program);
}

std::optional<const IR::P4Program *> CompilerTarget::runCompiler(const IR::P4Program *program) {
CompilerResultOrError CompilerTarget::runCompiler(const IR::P4Program *program) {
return get().runCompilerImpl(program);
}

std::optional<const IR::P4Program *> CompilerTarget::runCompilerImpl(
const IR::P4Program *program) const {
CompilerResultOrError CompilerTarget::runCompilerImpl(const IR::P4Program *program) const {
const auto &self = get();

program = self.runFrontend(program);
Expand All @@ -60,7 +63,7 @@ std::optional<const IR::P4Program *> CompilerTarget::runCompilerImpl(
return std::nullopt;
}

return program;
return *new CompilerResult(*program);
}

ICompileContext *CompilerTarget::makeContextImpl() const {
Expand Down Expand Up @@ -119,5 +122,4 @@ CompilerTarget::CompilerTarget(std::string deviceName, std::string archName)
: Target("compiler", std::move(deviceName), std::move(archName)) {}

const CompilerTarget &CompilerTarget::get() { return Target::get<CompilerTarget>("compiler"); }

} // namespace P4Tools
36 changes: 29 additions & 7 deletions backends/p4tools/common/compiler/compiler_target.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef BACKENDS_P4TOOLS_COMMON_COMPILER_COMPILER_TARGET_H_
#define BACKENDS_P4TOOLS_COMMON_COMPILER_COMPILER_TARGET_H_

#include <functional>
#include <optional>
#include <string>
#include <vector>
Expand All @@ -10,10 +11,30 @@
#include "frontends/common/options.h"
#include "frontends/p4/frontend.h"
#include "ir/ir.h"
#include "lib/castable.h"
#include "lib/compile_context.h"

namespace P4Tools {

/// An extensible result object which is returned by the CompilerTarget.
/// In its simplest form, this holds the transformed P4 program after the front- and midend passes.
class CompilerResult : public ICastable {
private:
/// The reference to the input P4 program, after it has been transformed by the compiler.
std::reference_wrapper<const IR::P4Program> program;

public:
explicit CompilerResult(const IR::P4Program &program);

/// @returns the reference to the input P4 program, after it has been transformed by the
/// compiler.
[[nodiscard]] const IR::P4Program &getProgram() const;
};

/// P4Tools compilers may return an error instead of a compiler result.
/// This is a convenience definition for the return value.
using CompilerResultOrError = std::optional<std::reference_wrapper<const CompilerResult>>;

/// Encapsulates the details of invoking the P4 compiler for a target device and architecture.
class CompilerTarget : public Target {
public:
Expand All @@ -25,28 +46,29 @@ class CompilerTarget : public Target {
/// @returns any unprocessed arguments, or nullptr if there was an error.
static std::vector<const char *> *initCompiler(int argc, char **argv);

/// Runs the P4 compiler to produce an IR.
/// Runs the P4 compiler to produce an IR and various other kinds of information on the input
/// program.
///
/// @returns std::nullopt if an error occurs during compilation.
static std::optional<const IR::P4Program *> runCompiler();
static CompilerResultOrError runCompiler();

/// Runs the P4 compiler to produce an IR for the given source code.
/// Runs the P4 compiler to produce an IR and other information for the given source code.
///
/// @returns std::nullopt if an error occurs during compilation.
static std::optional<const IR::P4Program *> runCompiler(const std::string &source);
static CompilerResultOrError runCompiler(const std::string &source);

private:
/// Runs the front and mid ends on the given parsed program.
///
/// @returns std::nullopt if an error occurs during compilation.
static std::optional<const IR::P4Program *> runCompiler(const IR::P4Program *);
static CompilerResultOrError runCompiler(const IR::P4Program *);

protected:
/// @see @makeContext.
virtual ICompileContext *makeContextImpl() const;
[[nodiscard]] virtual ICompileContext *makeContextImpl() const;

/// @see runCompiler.
virtual std::optional<const IR::P4Program *> runCompilerImpl(const IR::P4Program *) const;
virtual CompilerResultOrError runCompilerImpl(const IR::P4Program *) const;

/// This implementation just forwards the given arguments to the compiler.
///
Expand Down
13 changes: 6 additions & 7 deletions backends/p4tools/common/p4ctool.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#ifndef BACKENDS_P4TOOLS_COMMON_P4CTOOL_H_
#define BACKENDS_P4TOOLS_COMMON_P4CTOOL_H_

#include <cstdlib>
#include <vector>

#include "backends/p4tools/common/compiler/compiler_target.h"
#include "backends/p4tools/common/core/target.h"
#include "ir/ir.h"

namespace P4Tools {

Expand All @@ -19,7 +18,7 @@ class AbstractP4cTool {
/// Provides the implementation of the tool.
///
/// @param program The P4 program after mid-end processing.
virtual int mainImpl(const IR::P4Program *program) = 0;
virtual int mainImpl(const CompilerResult &compilerResult) = 0;

virtual void registerTarget() = 0;

Expand All @@ -41,11 +40,11 @@ class AbstractP4cTool {
AutoCompileContext autoContext(*compileContext);

// Run the compiler to get an IR and invoke the tool.
const auto program = P4Tools::CompilerTarget::runCompiler();
if (!program) {
return 1;
const auto compilerResult = P4Tools::CompilerTarget::runCompiler();
if (!compilerResult.has_value()) {
return EXIT_FAILURE;
}
return mainImpl(*program);
return mainImpl(compilerResult.value());
}
};

Expand Down
1 change: 0 additions & 1 deletion backends/p4tools/modules/testgen/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <vector>

#include "lib/crash.h"
#include "lib/exceptions.h"

#include "backends/p4tools/modules/testgen/testgen.h"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ TEST_F(SmallStepTest, Binary01) {
const auto test = createSmallStepExprTest("bit<8> f;", "8w42 + hdr.h.f");
ASSERT_TRUE(test);

const auto *opBin = extractExpr<IR::Operation_Binary>(test->program);
const auto *opBin = extractExpr<IR::Operation_Binary>(test->getProgram());
ASSERT_TRUE(opBin);

// Step on the binary operation and examine the resulting continuation
// to include the rebuilt IR::Add node.
stepAndExamineOp(opBin, opBin->right, test->program, [opBin](const IR::PathExpression *expr) {
return new IR::Add(opBin->left, expr);
});
stepAndExamineOp(
opBin, opBin->right, test->getProgram(),
[opBin](const IR::PathExpression *expr) { return new IR::Add(opBin->left, expr); });
}

/// Test the step function for e + e binary operation.
Expand All @@ -39,44 +39,44 @@ TEST_F(SmallStepTest, Binary02) {
"hdr.h.f1 + hdr.h.f2");
ASSERT_TRUE(test);

const auto *opBin = extractExpr<IR::Operation_Binary>(test->program);
const auto *opBin = extractExpr<IR::Operation_Binary>(test->getProgram());
ASSERT_TRUE(opBin);

// Step on the binary operation and examine the resulting continuation
// to include the rebuilt IR::Add node.
stepAndExamineOp(opBin, opBin->left, test->program, [opBin](const IR::PathExpression *expr) {
return new IR::Add(expr, opBin->right);
});
stepAndExamineOp(
opBin, opBin->left, test->getProgram(),
[opBin](const IR::PathExpression *expr) { return new IR::Add(expr, opBin->right); });
}

/// Test the step function for e == v binary operation.
TEST_F(SmallStepTest, Binary03) {
const auto test = createSmallStepExprTest("bit<8> f;", "hdr.h.f == 8w42");
ASSERT_TRUE(test);

const auto *opBin = extractExpr<IR::Operation_Binary>(test->program);
const auto *opBin = extractExpr<IR::Operation_Binary>(test->getProgram());
ASSERT_TRUE(opBin);

// Step on the binary operation and examine the resulting continuation
// to include the rebuilt IR::Equ node.
stepAndExamineOp(opBin, opBin->left, test->program, [opBin](const IR::PathExpression *expr) {
return new IR::Equ(expr, opBin->right);
});
stepAndExamineOp(
opBin, opBin->left, test->getProgram(),
[opBin](const IR::PathExpression *expr) { return new IR::Equ(expr, opBin->right); });
}

/// Test the step function for v ++ e binary operation.
TEST_F(SmallStepTest, Binary04) {
const auto test = createSmallStepExprTest("bit<8> f;", "8w42 ++ hdr.h.f");
ASSERT_TRUE(test);

const auto *opBin = extractExpr<IR::Operation_Binary>(test->program);
const auto *opBin = extractExpr<IR::Operation_Binary>(test->getProgram());
ASSERT_TRUE(opBin);

// Step on the binary operation and examine the resulting continuation
// to include the rebuilt IR::Concat node.
stepAndExamineOp(opBin, opBin->right, test->program, [opBin](const IR::PathExpression *expr) {
return new IR::Concat(opBin->left, expr);
});
stepAndExamineOp(
opBin, opBin->right, test->getProgram(),
[opBin](const IR::PathExpression *expr) { return new IR::Concat(opBin->left, expr); });
}

} // anonymous namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ TEST_F(SmallStepTest, Unary01) {
const auto test = createSmallStepExprTest("bit<8> f;", "-(hdr.h.f)");
ASSERT_TRUE(test);

const auto *opUn = extractExpr<IR::Operation_Unary>(test->program);
const auto *opUn = extractExpr<IR::Operation_Unary>(test->getProgram());
ASSERT_TRUE(opUn);

// Step on the unary operation and examine the resulting continuation
// to include the rebuilt IR::Neg node.
stepAndExamineOp(opUn, opUn->expr, test->program,
stepAndExamineOp(opUn, opUn->expr, test->getProgram(),
[](const IR::PathExpression *expr) { return new IR::Neg(expr); });
}

Expand All @@ -35,12 +35,12 @@ TEST_F(SmallStepTest, Unary02) {
const auto test = createSmallStepExprTest("bool f;", "!(hdr.h.f)");
ASSERT_TRUE(test);

const auto *opUn = extractExpr<IR::Operation_Unary>(test->program);
const auto *opUn = extractExpr<IR::Operation_Unary>(test->getProgram());
ASSERT_TRUE(opUn);

// Step on the unary operation and examine the resulting continuation
// to include the rebuilt IR::LNot node.
stepAndExamineOp(opUn, opUn->expr, test->program, [](const IR::PathExpression *expr) {
stepAndExamineOp(opUn, opUn->expr, test->getProgram(), [](const IR::PathExpression *expr) {
return new IR::LNot(IR::Type_Boolean::get(), expr);
});
}
Expand All @@ -50,12 +50,12 @@ TEST_F(SmallStepTest, Unary03) {
const auto test = createSmallStepExprTest("bit<8> f;", "~(hdr.h.f)");
ASSERT_TRUE(test);

const auto *opUn = extractExpr<IR::Operation_Unary>(test->program);
const auto *opUn = extractExpr<IR::Operation_Unary>(test->getProgram());
ASSERT_TRUE(opUn);

// Step on the unary operation and examine the resulting continuation
// to include the rebuilt IR::Cmpl node.
stepAndExamineOp(opUn, opUn->expr, test->program,
stepAndExamineOp(opUn, opUn->expr, test->getProgram(),
[](const IR::PathExpression *expr) { return new IR::Cmpl(expr); });
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ TEST_F(SmallStepTest, Value01) {
const auto test = createSmallStepExprTest("bit<8> f;", "8w42");
ASSERT_TRUE(test);

const auto *opValue = extractExpr<IR::Constant>(test->program);
const auto *opValue = extractExpr<IR::Constant>(test->getProgram());
ASSERT_TRUE(opValue);

// Step on the value and examine the resulting state.
stepAndExamineValue(opValue, test->program);
stepAndExamineValue(opValue, test->getProgram());
}

/// Test the step function for a bool value.
TEST_F(SmallStepTest, Value02) {
const auto test = createSmallStepExprTest("bit<1> f;", "true");
ASSERT_TRUE(test);

const auto *opValue = extractExpr<IR::BoolLiteral>(test->program);
const auto *opValue = extractExpr<IR::BoolLiteral>(test->getProgram());
ASSERT_TRUE(opValue);

// Step on the value and examine the resulting state.
stepAndExamineValue(opValue, test->program);
stepAndExamineValue(opValue, test->getProgram());
}

} // anonymous namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ class Z3SolverSatTests : public ::testing::Test {
}

// Produce a ProgramInfo, which is needed to create a SmallStepEvaluator.
const auto *progInfo = TestgenTarget::initProgram(test->program);
const auto *progInfo = TestgenTarget::initProgram(&test->getProgram());
if (progInfo == nullptr) {
return;
}

// Extract the binary operation from the P4Program
auto *const declVector = test->program->getDeclsByName("mau")->toVector();
auto *const declVector = test->getProgram().getDeclsByName("mau")->toVector();
const auto *decl = (*declVector)[0];
const auto *control = decl->to<IR::P4Control>();
for (const auto *st : control->body->components) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include <gtest/gtest.h>

#include <algorithm>
#include <optional>
#include <string>
#include <vector>
Expand All @@ -13,13 +12,11 @@
#include "backends/p4tools/common/core/z3_solver.h"
#include "backends/p4tools/common/lib/model.h"
#include "ir/declaration.h"
#include "ir/indexed_vector.h"
#include "ir/ir.h"
#include "lib/big_int_util.h"
#include "lib/cstring.h"
#include "lib/enumerator.h"
#include "lib/exceptions.h"
#include "lib/log.h"
#include "test/gtest/helpers.h"

#include "backends/p4tools/modules/testgen/core/target.h"
Expand Down Expand Up @@ -90,13 +87,13 @@ class Z3SolverTest : public P4ToolsTest {
}

// Produce a ProgramInfo, which is needed to create a SmallStepEvaluator.
const auto *progInfo = TestgenTarget::initProgram(test->program);
const auto *progInfo = TestgenTarget::initProgram(&test->getProgram());
if (progInfo == nullptr) {
return;
}

// Extract the binary operation from the P4Program
auto *const declVector = test->program->getDeclsByName("mau")->toVector();
auto *const declVector = test->getProgram().getDeclsByName("mau")->toVector();
const auto *decl = (*declVector)[0];
const auto *control = decl->to<IR::P4Control>();
SymbolicConverter converter;
Expand Down
Loading

0 comments on commit 313faed

Please sign in to comment.