Skip to content

Commit

Permalink
Implement operator< for IR nodes.
Browse files Browse the repository at this point in the history
  • Loading branch information
fruffy committed Dec 20, 2023
1 parent 9bad3cb commit cdd44a6
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 47 deletions.
4 changes: 2 additions & 2 deletions backends/p4tools/modules/testgen/lib/concolic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ ConcolicMethodImpls::ConcolicMethodImpls(const ImplList &implList) { add(implLis
bool ConcolicResolver::preorder(const IR::ConcolicVariable *var) {
auto concolicMethodName = var->concolicMethodName;
// Convert the concolic member variable to a state variable.
auto concolicReplacment = resolvedConcolicVariables.find(*var);
if (concolicReplacment == resolvedConcolicVariables.end()) {
auto concolicReplacement = resolvedConcolicVariables.find(*var);
if (concolicReplacement == resolvedConcolicVariables.end()) {
bool found = concolicMethodImpls.exec(concolicMethodName, var, state, evaluatedModel,
&resolvedConcolicVariables);
BUG_CHECK(found, "Unknown or unimplemented concolic method: %1%", concolicMethodName);
Expand Down
4 changes: 2 additions & 2 deletions backends/p4tools/modules/testgen/lib/concolic.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ namespace P4Tools::P4Testgen {
/// unique keys. Using this map, you can look up particular state variables and check whether
/// they actually are present, but not expressions. The reason expressions need to be keys is
/// that sometimes entire expressions are mapped to a particular constant.
using ConcolicVariableMap =
ordered_map<std::variant<IR::ConcolicVariable, const IR::Expression *>, const IR::Expression *>;
using ConcolicVariableMap = ordered_map<std::reference_wrapper<const IR::Expression>,
const IR::Expression *, std::less<const IR::Expression>>;

/// Encapsulates a set of concolic method implementations.
class ConcolicMethodImpls {
Expand Down
10 changes: 1 addition & 9 deletions backends/p4tools/modules/testgen/lib/final_state.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#include "backends/p4tools/modules/testgen/lib/final_state.h"

#include <list>
#include <utility>
#include <variant>
#include <vector>

#include <boost/container/vector.hpp>
Expand Down Expand Up @@ -78,13 +76,7 @@ std::optional<std::reference_wrapper<const FinalState>> FinalState::computeConco
const auto *concolicAssignment = resolvedConcolicVariable.second;
const IR::Expression *pathConstraint = nullptr;
// We need to differentiate between state variables and expressions here.
if (std::holds_alternative<IR::ConcolicVariable>(concolicVariable)) {
pathConstraint = new IR::Equ(std::get<IR::ConcolicVariable>(concolicVariable).clone(),
concolicAssignment);
} else if (std::holds_alternative<const IR::Expression *>(concolicVariable)) {
pathConstraint =
new IR::Equ(std::get<const IR::Expression *>(concolicVariable), concolicAssignment);
}
pathConstraint = new IR::Equ(&concolicVariable.get(), concolicAssignment);
CHECK_NULL(pathConstraint);
pathConstraint = state.get().getSymbolicEnv().subst(pathConstraint);
pathConstraint = P4::optimizeExpression(pathConstraint);
Expand Down
7 changes: 3 additions & 4 deletions backends/p4tools/modules/testgen/targets/bmv2/concolic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ const ConcolicMethodImpls::ImplList Bmv2Concolic::BMV2_CONCOLIC_METHOD_IMPLS{
if (const auto *checksumVarType = checksumVar->type->to<IR::Type_Bits>()) {
// Overwrite any previous assignment or result.
(*resolvedConcolicVariables)[*var] = IR::getConstant(checksumVarType, computedResult);

} else {
TESTGEN_UNIMPLEMENTED("Checksum output %1% of type %2% not supported", checksumVar,
checksumVar->type);
Expand All @@ -180,7 +179,7 @@ const ConcolicMethodImpls::ImplList Bmv2Concolic::BMV2_CONCOLIC_METHOD_IMPLS{
for (const auto &variable : resolvedExpressions) {
const auto *varName = variable.first;
const auto *varExpr = variable.second;
(*resolvedConcolicVariables)[varName] = varExpr;
(*resolvedConcolicVariables)[*varName] = varExpr;
}
}},
/* ======================================================================================
Expand Down Expand Up @@ -232,7 +231,7 @@ const ConcolicMethodImpls::ImplList Bmv2Concolic::BMV2_CONCOLIC_METHOD_IMPLS{
for (const auto &variable : resolvedExpressions) {
const auto *varName = variable.first;
const auto *varExpr = variable.second;
(*resolvedConcolicVariables)[varName] = varExpr;
(*resolvedConcolicVariables)[*varName] = varExpr;
}
}},

Expand Down Expand Up @@ -286,7 +285,7 @@ const ConcolicMethodImpls::ImplList Bmv2Concolic::BMV2_CONCOLIC_METHOD_IMPLS{
for (const auto &variable : resolvedExpressions) {
const auto *varName = variable.first;
const auto *varExpr = variable.second;
(*resolvedConcolicVariables)[varName] = varExpr;
(*resolvedConcolicVariables)[*varName] = varExpr;
}
}},
};
Expand Down
20 changes: 11 additions & 9 deletions backends/p4tools/p4tools.def
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ class StateVariable : Expression {
return *ref == *other.ref;
}

/// Implements comparisons so that StateVariables can be used as map keys.
bool operator<(const StateVariable &other) const {
// We use a custom compare function.
// TODO: Is there a faster way to implement this comparison?
bool operator<(const StateVariable &other) const override {
return compare(ref, other.ref) < 0;
}

Expand Down Expand Up @@ -135,17 +132,17 @@ class TaintExpression : Expression {
class SymbolicVariable : Expression {
#noconstructor

/// Implements comparisons so that SymbolicVariables can be used as map keys.
bool operator<(const SymbolicVariable &other) const override {
return label < other.label;
}

/// The label of the symbolic variable.
cstring label;

/// A symbolic variable always has a type and no source info.
SymbolicVariable(Type type, cstring label) : Expression(type), label(label) {}

/// Implements comparisons so that SymbolicVariables can be used as map keys.
bool operator<(const SymbolicVariable &other) const {
return label < other.label;
}

toString { return "|" + label +"(" + type->toString() + ")|"; }

dbprint { out << "|" + label +"(" << type << ")|"; }
Expand Down Expand Up @@ -223,6 +220,11 @@ public:

visit_children { v.visit(type, "type"); }

/// Implements comparisons so that ConcolicVariables can be used as map keys.
bool operator<(const ConcolicVariable &other) const override {
return label < other.label;
}

ConcolicVariable(const Type *type, cstring methodName,
const Vector<Argument> *arguments, int srcIdentifier, int concolicId,
const IndexedVector<Node> &associatedNodes)
Expand Down
1 change: 1 addition & 0 deletions ir/id.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct ID : Util::IHasSourceInfo {
}
bool operator==(const ID &a) const { return name == a.name; }
bool operator!=(const ID &a) const { return name != a.name; }
bool operator<(const ID &a) const { return name < a.name; }
explicit operator bool() const { return name; }
operator cstring() const { return name; }
bool isDontCare() const { return name == "_"; }
Expand Down
8 changes: 6 additions & 2 deletions ir/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,12 @@ class Node : public virtual INode {
/* 'equiv' does a deep-equals comparison, comparing all non-pointer fields and recursing
* though all Node subclass pointers to compare them with 'equiv' as well. */
virtual bool equiv(const Node &a) const { return typeid(*this) == typeid(a); }
#define DEFINE_OPEQ_FUNC(CLASS, BASE) \
virtual bool operator==(const CLASS &) const { return false; }
virtual bool operator<(const Node &a) const {
return typeid(*this).hash_code() < typeid(a).hash_code();
}
#define DEFINE_OPEQ_FUNC(CLASS, BASE) \
virtual bool operator==(const CLASS &) const { return false; } \
virtual bool operator<(const CLASS &) const { return false; }
IRNODE_ALL_SUBCLASSES(DEFINE_OPEQ_FUNC)
#undef DEFINE_OPEQ_FUNC

Expand Down
14 changes: 14 additions & 0 deletions lib/ordered_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,20 @@ class ordered_map {
size_type max_size() const noexcept { return data_map.max_size(); }
bool operator==(const ordered_map &a) const { return data == a.data; }
bool operator!=(const ordered_map &a) const { return data != a.data; }
bool operator<(const ordered_map &a) const {
// we define this to work INDEPENDENT of the order -- so it is possible to have
// two ordered_maps where !(a < b) && !(b < a) && !(a == b) -- such sets have the
// same elements but in a different order. This is generally what you want if you
// have a set of ordered_maps (or use ordered_map as a map key).
auto it = a.data_map.begin();
for (auto &el : data_map) {
if (it == a.data_map.end()) return false;
if (mapcmp()(el.first, it->first)) return true;
if (mapcmp()(it->first, el.first)) return false;
++it;
}
return it != a.data_map.end();
}
void clear() {
data.clear();
data_map.clear();
Expand Down
1 change: 1 addition & 0 deletions tools/ir-generator/ir-generator-lex.l
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ static std::string comment_block;
"#apply" { return APPLY; }
"#no"[a-zA-Z_]* { yylval.str = yytext+3; return NO; }
"#nooperator==" { yylval.str = yytext+3; return NO; }
"#nooperator<" { yylval.str = yytext+3; return NO; }
"0" { yylval.str = yytext; return ZERO; }
-?[0-9]+ { yylval.str = yytext; return INTEGER; }

Expand Down
27 changes: 17 additions & 10 deletions tools/ir-generator/irclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,18 +458,25 @@ Util::Enumerator<IrMethod *> *IrClass::getUserMethods() const {
->map<IrMethod *>([](IrElement *e) -> IrMethod * { return e->to<IrMethod>(); });
}

bool IrClass::shouldSkip(cstring feature) const {
// skip if there is a 'no' directive
bool IrClass::hasNoDirective(cstring feature) const {
auto *e = Util::Enumerator<IrElement *>::createEnumerator(elements);
bool explicitNo =
e->where([](IrElement *el) { return el->is<IrNo>(); })
->where([feature](IrElement *el) { return el->to<IrNo>()->text == feature; })
->any();
if (explicitNo) return true;
// also, skip if the user provided an implementation manually
// (except for validate)
if (feature == "validate") return false;
return e->where([](IrElement *el) { return el->is<IrNo>(); })
->where([feature](IrElement *el) { return el->to<IrNo>()->text == feature; })
->any();
}

bool IrClass::shouldSkip(cstring feature) const {
// Validate is special, it is never skipped.
if (feature == "validate") {
return false;
}

// Skip if there is a '#no' directive.
if (hasNoDirective(feature)) {
return true;
}
// Also skip if the user provided an implementation manually
auto *e = Util::Enumerator<IrElement *>::createEnumerator(elements);
e = Util::Enumerator<IrElement *>::createEnumerator(elements);
bool provided =
e->where([](IrElement *e) { return e->is<IrMethod>(); })
Expand Down
1 change: 1 addition & 0 deletions tools/ir-generator/irclass.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ class IrClass : public IrElement {
int generateConstructor(const ctor_args_t &args, const IrMethod *user, unsigned skip_opt);
void generateMethods();
bool shouldSkip(cstring feature) const;
bool hasNoDirective(cstring feature) const;

public:
const IrClass *getParent() const {
Expand Down
74 changes: 65 additions & 9 deletions tools/ir-generator/methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,42 @@ const ordered_map<cstring, IrMethod::info_t> IrMethod::Generate = {
buf << cl->indent << "}";
return buf.str();
}}},
{"operator<",
{&NamedType::Bool(),
{},
CONST + IN_IMPL + INCL_NESTED + OVERRIDE + CLASSREF,
[](IrClass *cl, Util::SourceInfo, cstring) -> cstring {
std::stringstream buf;
buf << "{" << std::endl << cl->indent << cl->indent << "return ";
bool first = true;
if (auto parent = cl->getParent()) {
if (parent->name == "Node")
buf << "typeid(*this).hash_code() < typeid(a).hash_code()";
else
buf << parent->qualified_name(cl->containedIn) << "::operator<(static_cast<const "
<< parent->qualified_name(cl->containedIn) << " &>(a))";
first = false;
}
for (auto f : *cl->getFields()) {
if (*f->type == NamedType::SourceInfo()) continue; // FIXME -- deal with SourcInfo
if (!first) buf << std::endl << cl->indent << cl->indent << "|| ";
first = false;
if (auto *arr = dynamic_cast<const ArrayType *>(f->type)) {
for (int i = 0; i < arr->size; ++i) {
if (i != 0) buf << std::endl << cl->indent << cl->indent << "|| ";
buf << f->name << "[" << i << "] < a." << f->name << "[" << i << "]";
}
} else {
buf << f->name << " < a." << f->name;
}
}
if (first) { // a nested class with no fields?
buf << "false";
}
buf << ";" << std::endl;
buf << cl->indent << "}";
return buf.str();
}}},
{"operator<<",
{&ReferenceType::OstreamRef,
{new IrField(&ReferenceType::OstreamRef, "out")},
Expand Down Expand Up @@ -320,15 +356,35 @@ void IrClass::generateMethods() {
}
}
}
for (auto *parent = getParent(); parent; parent = parent->getParent()) {
auto eq_overload = new IrMethod("operator==", "{ return a == *this; }");
eq_overload->clss = this;
eq_overload->isOverride = true;
eq_overload->rtype = &NamedType::Bool();
eq_overload->args.push_back(
new IrField(new ReferenceType(new NamedType(parent), true), "a"));
eq_overload->isConst = true;
elements.push_back(eq_overload);
for (const auto *parent = getParent(); parent != nullptr; parent = parent->getParent()) {
if (!hasNoDirective("operator==")) {
std::stringstream buf;
buf << "{ return a.operator==(*this); }";
auto *eq_overload = new IrMethod("operator==", buf.str());
eq_overload->clss = this;
eq_overload->isOverride = true;
eq_overload->inImpl = true;
eq_overload->rtype = &NamedType::Bool();
eq_overload->args.push_back(
new IrField(new ReferenceType(new NamedType(parent), true), "a"));
eq_overload->isConst = true;
elements.push_back(eq_overload);
}
if (!hasNoDirective("operator<")) {
std::stringstream buf;
buf << "{return typeid(" << fullName() << ") == typeid(a) ?";
buf << " this->operator<(static_cast<const " << name << " &>(a)) :";
buf << " typeid(" << fullName() << ").hash_code() < typeid(a).hash_code(); }";
auto *lss_overload = new IrMethod("operator<", buf.str());
lss_overload->clss = this;
lss_overload->isOverride = true;
lss_overload->inImpl = true;
lss_overload->rtype = &NamedType::Bool();
lss_overload->args.push_back(
new IrField(new ReferenceType(new NamedType(parent), true), "a"));
lss_overload->isConst = true;
elements.push_back(lss_overload);
}
}
}
IrMethod *ctor = nullptr;
Expand Down

0 comments on commit cdd44a6

Please sign in to comment.