Skip to content

Commit

Permalink
Add a hashing function for state and symbolic variables.
Browse files Browse the repository at this point in the history
Signed-off-by: fruffy <[email protected]>
  • Loading branch information
fruffy committed Aug 15, 2024
1 parent 39d9d44 commit ff5b0e9
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 89 deletions.
2 changes: 2 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,11 @@ cc_library(
"frontends/parsers/p4/p4lexer.cc",
"frontends/parsers/v1/v1lexer.cc",
"ir/ir-generated.cpp",
# TODO: These should be compiled in their respective folder.
"backends/dpdk/spec.cpp",
"backends/dpdk/dbprint-dpdk.cpp",
"backends/dpdk/printUtils.cpp",
"//backends/p4tools:ir_extension_sources",
],
copts = [
# Where p4c should look for p4include at runtime.
Expand Down
9 changes: 8 additions & 1 deletion backends/p4tools/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ TESTGEN_TARGETS = ["bmv2"]

filegroup(
name = "ir_extension",
srcs = ["p4tools.def"],
srcs = ["common/ir.def"],
visibility = ["//visibility:public"], # So p4c can compile these.
)

# Sources for IR definitions in P4Tools. Need to be compiled in the top-level.
filegroup(
name = "ir_extension_sources",
srcs = ["common/ir.cpp"],
visibility = ["//visibility:public"], # So p4c can compile these.
)

Expand Down
4 changes: 1 addition & 3 deletions backends/p4tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ set(ENABLE_TESTING ON)
# Include sub projects.
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/common)

# Custom IR constructs for P4Tools.
set(IR_DEF_FILES ${IR_DEF_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/p4tools.def)

file(GLOB tools_modules RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/modules ${CMAKE_CURRENT_SOURCE_DIR}/modules/*)
foreach(ext ${tools_modules})
set(tools_dir ${CMAKE_CURRENT_SOURCE_DIR}/modules/${ext})
Expand All @@ -79,3 +76,4 @@ endforeach(ext)

# Propagate new def files upwards after all modules have been included.
set(IR_DEF_FILES ${IR_DEF_FILES} PARENT_SCOPE)
set(EXTENSION_IR_SOURCES ${EXTENSION_IR_SOURCES} PARENT_SCOPE)
4 changes: 4 additions & 0 deletions backends/p4tools/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ set(
lib/variables.cpp
)

# Custom IR constructs for P4Tools.
set(IR_DEF_FILES ${IR_DEF_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/ir.def PARENT_SCOPE)
set(EXTENSION_IR_SOURCES ${EXTENSION_IR_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/ir.cpp PARENT_SCOPE)

add_library(p4tools-common OBJECT ${P4C_TOOLS_COMMON_SOURCES})

target_link_libraries(
Expand Down
147 changes: 147 additions & 0 deletions backends/p4tools/common/ir.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#include "ir/ir.h"

namespace IR {

namespace {

int compare(const Expression *e1, const Expression *e2);

int compare(const Member *m1, const Member *m2) {
auto result = compare(m1->expr, m2->expr);
if (result != 0) {
return result;
}
if (m1->member.name < m2->member.name) {
return -1;
}
if (m1->member.name > m2->member.name) {
return 1;
}
return 0;
}

int compare(const PathExpression *p1, const PathExpression *p2) {
if (p1->path->name.name < p2->path->name.name) {
return -1;
}
if (p1->path->name.name > p2->path->name.name) {
return 1;
}
return 0;
}

int compare(const ArrayIndex *a1, const ArrayIndex *a2) {
auto result = compare(a1->left, a2->left);
if (result != 0) {
return result;
}
const auto *a1Val = a1->right->to<Constant>();
BUG_CHECK(
a1Val != nullptr,
"Value %1% is not a constant. Only constants are supported as part of a state variable.",
a1->right);
const auto *a2Val = a2->right->to<Constant>();
BUG_CHECK(
a2Val != nullptr,
"Value %1% is not a constant. Only constants are supported as part of a state variable.",
a2->right);
if (a1Val->value < a2Val->value) {
return -1;
}
if (a1Val->value > a2Val->value) {
return 1;
}
return 0;
}

int compare(const Expression *e1, const Expression *e2) {
// e1 is a Member.
if (const auto *m1 = e1->to<Member>()) {
if (const auto *m2 = e2->to<Member>()) {
return IR::compare(m1, m2);
}
if (e2->is<PathExpression>()) {
return 1;
}
if (e2->is<ArrayIndex>()) {
return 1;
}
}
// e1 is a PathExpression.
if (const auto *p1 = e1->to<PathExpression>()) {
if (const auto *p2 = e2->to<PathExpression>()) {
return IR::compare(p1, p2);
}
if (e2->is<Member>()) {
return -1;
}
if (e2->is<ArrayIndex>()) {
return 1;
}
}
// e1 is a ArrayIndex.
if (const auto *a1 = e1->to<ArrayIndex>()) {
if (const auto *a2 = e2->to<ArrayIndex>()) {
return IR::compare(a1, a2);
}
if (e2->is<Member>()) {
return -1;
}
if (e2->is<PathExpression>()) {
return -1;
}
}
BUG("Either %1% of type %2% or %3% of type %4% is not a valid StateVariable", e1,
e1->node_type_name(), e2, e2->node_type_name());
}

} // namespace

int StateVariable::compare(const Expression *e1, const Expression *e2) {
return IR::compare(e1, e2);
}

namespace {

uint64_t hash(uint64_t seed, const Expression *expression);

uint64_t hash(uint64_t seed, const PathExpression *pathExpression) {
return Util::hash_combine(seed, std::hash<cstring>()(pathExpression->path->name));
}

uint64_t hash(uint64_t seed, const ArrayIndex *arrayIndex) {
const auto *arrayIndexVal = arrayIndex->right->to<Constant>();
BUG_CHECK(
arrayIndexVal != nullptr,
"Value %1% is not a constant. Only constants are supported as part of a state variable.",
arrayIndex->right);
return IR::hash(Util::hash_combine(seed, std::hash<big_int>()(arrayIndexVal->value)),
arrayIndex->left);
}

uint64_t hash(uint64_t seed, const Member *member) {
return IR::hash(Util::hash_combine(seed, std::hash<cstring>()(member->member)), member->expr);
}

uint64_t hash(uint64_t seed, const Expression *expression) {
// expression is a Member.
if (const auto *member = expression->to<Member>()) {
return hash(seed, member);
}
// expression is a PathExpression.
if (const auto *pathExpression = expression->to<PathExpression>()) {
return hash(seed, pathExpression);
}
// expression is a ArrayIndex.
if (const auto *arrayIndex = expression->to<ArrayIndex>()) {
return hash(seed, arrayIndex);
}
BUG("Either %1% of type %2% is not a valid StateVariable", expression,
expression->node_type_name());
}

} // namespace

uint64_t StateVariable::hash() const { return IR::hash(0, ref); }

} // namespace IR
84 changes: 4 additions & 80 deletions backends/p4tools/p4tools.def → backends/p4tools/common/ir.def
Original file line number Diff line number Diff line change
Expand Up @@ -44,87 +44,11 @@ class StateVariable : Expression {
return compare(ref, other.ref) < 0;
}

int compare(const Expression *e1, const Expression *e2) const {
// e1 is a Member.
if (const auto *m1 = e1->to<Member>()) {
if (const auto *m2 = e2->to<Member>()) {
return compare(m1, m2);
}
if (e2->is<PathExpression>()) {
return 1;
}
if (e2->is<ArrayIndex>()) {
return 1;
}
}
// e1 is a PathExpression.
if (const auto *p1 = e1->to<PathExpression>()) {
if (const auto *p2 = e2->to<PathExpression>()) {
return compare(p1, p2);
}
if (e2->is<Member>()) {
return -1;
}
if (e2->is<ArrayIndex>()) {
return 1;
}
}
// e1 is a ArrayIndex.
if (const auto *a1 = e1->to<ArrayIndex>()) {
if (const auto *a2 = e2->to<ArrayIndex>()) {
return compare(a1, a2);
}
if (e2->is<Member>()) {
return -1;
}
if (e2->is<PathExpression>()) {
return -1;
}
}
BUG("Either %1% of type %2% or %3% of type %4% is not a valid StateVariable", e1, e1->node_type_name(), e2, e2->node_type_name());
}

int compare(const Member *m1, const Member *m2) const {
auto result = compare(m1->expr, m2->expr);
if (result != 0) {
return result;
}
if (m1->member.name < m2->member.name) {
return -1;
}
if (m1->member.name > m2->member.name) {
return 1;
}
return 0;
}
/// Compares two expressions. Returns -1 if e1 < e2, 0 if e1 == e2, and 1 if e1 > e2.
static int compare(const Expression *e1, const Expression *e2);

int compare(const PathExpression *p1, const PathExpression *p2) const {
if (p1->path->name.name < p2->path->name.name) {
return -1;
}
if (p1->path->name.name > p2->path->name.name) {
return 1;
}
return 0;
}

int compare(const ArrayIndex *a1, const ArrayIndex *a2) const {
auto result = compare(a1->left, a2->left);
if (result != 0) {
return result;
}
auto a1Val = a1->right->to<Constant>();
BUG_CHECK(a1Val != nullptr, "Value %1% is not a constant. Only constants are supported as part of a state variable.", a1->right);
auto a2Val = a2->right->to<Constant>();
BUG_CHECK(a2Val != nullptr, "Value %1% is not a constant. Only constants are supported as part of a state variable.", a2->right);
if (a1Val->value < a2Val->value) {
return -1;
}
if (a1Val->value > a2Val->value) {
return 1;
}
return 0;
}
/// Returns the hash of the state variable expression.
uint64_t hash() const;

toString { return ref->toString(); }

Expand Down
11 changes: 11 additions & 0 deletions backends/p4tools/common/lib/ir_compare.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ struct StateVariableLess {
}
};

/// Hash for StateVariable pointers. We only hash the label.
struct StateVariableHash {
size_t operator()(const IR::StateVariable *s1) const {
return Util::Hasher<uint64_t>()(s1->hash());
}

size_t operator()(const IR::StateVariable &s1) const {
return Util::Hasher<uint64_t>()(s1.hash());
}
};

} // namespace P4::IR

#endif /* BACKENDS_P4TOOLS_COMMON_LIB_IR_COMPARE_H_ */
5 changes: 3 additions & 2 deletions backends/p4tools/common/lib/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
#include <map>
#include <utility>

#include "absl/container/btree_map.h"
#include "backends/p4tools/common/lib/ir_compare.h"
#include "ir/ir.h"
#include "ir/solver.h"
#include "ir/visitor.h"

namespace P4::P4Tools {

/// Symbolic maps map a state variable to a IR::Expression.
using SymbolicMapType = absl::btree_map<IR::StateVariable, const IR::Expression *>;
using SymbolicMapType = absl::flat_hash_map<IR::StateVariable, const IR::Expression *,
IR::StateVariableHash, IR::StateVariableEqual>;

/// Represents a solution found by the solver. A model is a concretized form of a symbolic
/// environment. All the expressions in a Model must be of type IR::Literal.
Expand Down
11 changes: 11 additions & 0 deletions ir/compare.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ struct SymbolicVariableLess {
}
};

/// Hash for SymbolicVariable pointers. We only hash the label.
struct SymbolicVariableHash {
size_t operator()(const IR::SymbolicVariable *s1) const {
return std::hash<cstring>()(s1->label);
}

size_t operator()(const IR::SymbolicVariable &s1) const {
return std::hash<cstring>()(s1.label);
}
};

} // namespace P4::IR

#endif /* IR_COMPARE_H_ */
10 changes: 7 additions & 3 deletions ir/solver.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#ifndef IR_SOLVER_H_
#define IR_SOLVER_H_

#include <functional>
#include <optional>
#include <vector>

#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "ir/compare.h"
#include "ir/ir.h"
#include "lib/castable.h"
Expand All @@ -17,8 +19,10 @@ namespace P4 {
using Constraint = IR::Expression;

/// This type maps symbolic variables to their value assigned by the solver.
using SymbolicMapping =
absl::btree_map<const IR::SymbolicVariable *, const IR::Expression *, IR::SymbolicVariableLess>;
using SymbolicMapping = absl::flat_hash_map<const IR::SymbolicVariable *, const IR::Expression *,
IR::SymbolicVariableHash, IR::SymbolicVariableEqual>;
using SymbolicSet = absl::flat_hash_set<const IR::SymbolicVariable *, IR::SymbolicVariableHash,
IR::SymbolicVariableEqual>;

/// Provides a higher-level interface for an SMT solver.
class AbstractSolver : public ICastable {
Expand Down

0 comments on commit ff5b0e9

Please sign in to comment.