diff --git a/backends/p4tools/common/lib/symbolic_env.cpp b/backends/p4tools/common/lib/symbolic_env.cpp index e6c1fcae016..95d9d6888ce 100644 --- a/backends/p4tools/common/lib/symbolic_env.cpp +++ b/backends/p4tools/common/lib/symbolic_env.cpp @@ -105,11 +105,14 @@ bool SymbolicEnv::isSymbolicValue(const IR::Node *node) { if (expr->is()) { return true; } - // DefaultExpresssions are symbolic values. + // DefaultExpressions are symbolic values. if (expr->is()) { return true; } - + // InOut references are symbolic when the resolved input argument is symbolic. + if (const auto *inout = expr->to()) { + return isSymbolicValue(inout->resolvedRef); + } // Symbolic values can be composed using several IR nodes. if (const auto *unary = expr->to()) { return (unary->is() || unary->is() || unary->is() || diff --git a/backends/p4tools/common/lib/trace_event_types.cpp b/backends/p4tools/common/lib/trace_event_types.cpp index da3a3b9867a..3438c1deccd 100644 --- a/backends/p4tools/common/lib/trace_event_types.cpp +++ b/backends/p4tools/common/lib/trace_event_types.cpp @@ -266,49 +266,31 @@ void ExtractFailure::print(std::ostream &os) const { * Emit * ============================================================================================= */ -Emit::Emit(const IR::Expression *emitHeader, - std::vector> fields) - : emitHeader(emitHeader), fields(std::move(fields)) {} +Emit::Emit(const IR::HeaderExpression *emitHeader) : emitHeader(emitHeader) {} const Emit *Emit::subst(const SymbolicEnv &env) const { std::vector> applyFields; - applyFields.reserve(fields.size()); - for (const auto &field : fields) { - applyFields.emplace_back(field.first, env.subst(field.second)); - } - return new Emit(emitHeader, applyFields); + return new Emit(env.subst(emitHeader)->checkedTo()); } const Emit *Emit::apply(Transform &visitor) const { - std::vector> applyFields; - applyFields.reserve(fields.size()); - for (const auto &field : fields) { - applyFields.emplace_back(field.first, field.second->apply(visitor)); - } - return new Emit(emitHeader, applyFields); + return new Emit(emitHeader->apply(visitor)->checkedTo()); } const Emit *Emit::evaluate(const Model &model, bool doComplete) const { - std::vector> applyFields; - applyFields.reserve(fields.size()); - for (const auto &field : fields) { - if (Taint::hasTaint(field.second)) { - applyFields.emplace_back(field.first, &Taint::TAINTED_STRING_LITERAL); - } else { - applyFields.emplace_back(field.first, model.evaluate(field.second, doComplete)); - } - } - return new Emit(emitHeader, applyFields); + return new Emit( + model.evaluateStructExpr(emitHeader, doComplete)->checkedTo()); } void Emit::print(std::ostream &os) const { - os << "[Emit] " << emitHeader->toString() << " -> "; - for (const auto &field : fields) { - os << field.first->toString() << " = " << formatHexExpr(field.second, true); - if (field != fields.back()) { - os << " | "; - } - } + // Convert the header expression to a string and strip any new lines. + // TODO: Maybe there is a better way to format newlines? + std::stringstream assignStream; + emitHeader->dbprint(assignStream); + auto headerString = assignStream.str(); + headerString.erase(std::remove(headerString.begin(), headerString.end(), '\n'), + headerString.cend()); + os << "[Emit]: " << headerString; } /* ============================================================================================= diff --git a/backends/p4tools/common/lib/trace_event_types.h b/backends/p4tools/common/lib/trace_event_types.h index 8dffacbba02..dd2426e0085 100644 --- a/backends/p4tools/common/lib/trace_event_types.h +++ b/backends/p4tools/common/lib/trace_event_types.h @@ -228,19 +228,15 @@ class ExtractFailure : public TraceEvent { /// A field being emitted by a deparser. class Emit : public TraceEvent { private: - /// The label of the emitted header. Either a PathExpression or a member. - const IR::Expression *emitHeader; - - /// The list of fields and their values of the emitted header. - std::vector> fields; + /// The emitted header structure. + const IR::HeaderExpression *emitHeader; public: [[nodiscard]] const Emit *subst(const SymbolicEnv &env) const override; const Emit *apply(Transform &visitor) const override; [[nodiscard]] const Emit *evaluate(const Model &model, bool doComplete) const override; - Emit(const IR::Expression *emitHeader, - std::vector> fields); + explicit Emit(const IR::HeaderExpression *emitHeader); ~Emit() override = default; Emit(const Emit &) = default; Emit(Emit &&) = default; diff --git a/backends/p4tools/common/lib/util.cpp b/backends/p4tools/common/lib/util.cpp index a507d26e1fb..63bbaa0b683 100644 --- a/backends/p4tools/common/lib/util.cpp +++ b/backends/p4tools/common/lib/util.cpp @@ -91,14 +91,14 @@ const IR::Constant *Utils::getRandConstantForType(const IR::Type_Bits *type) { const IR::MethodCallExpression *Utils::generateInternalMethodCall( cstring methodName, const std::vector &argVector, - const IR::Type *returnType) { + const IR::Type *returnType, const IR::ParameterList *paramList) { auto *args = new IR::Vector(); for (const auto *expr : argVector) { args->push_back(new IR::Argument(expr)); } return new IR::MethodCallExpression( returnType, - new IR::Member(new IR::Type_Method(new IR::ParameterList(), methodName), + new IR::Member(new IR::Type_Method(paramList, methodName), new IR::PathExpression(new IR::Type_Extern("*"), new IR::Path("*")), methodName), args); diff --git a/backends/p4tools/common/lib/util.h b/backends/p4tools/common/lib/util.h index 1105f11ed19..8a109c21646 100644 --- a/backends/p4tools/common/lib/util.h +++ b/backends/p4tools/common/lib/util.h @@ -89,7 +89,8 @@ class Utils { /// is typically Type_Void. static const IR::MethodCallExpression *generateInternalMethodCall( cstring methodName, const std::vector &argVector, - const IR::Type *returnType = IR::Type_Void::get()); + const IR::Type *returnType = IR::Type_Void::get(), + const IR::ParameterList *paramList = new IR::ParameterList()); /// Shuffles the given iterable @param inp template diff --git a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp index c9e71d4cd9b..9c6df46fa93 100644 --- a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp +++ b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.cpp @@ -121,10 +121,54 @@ void ExprStepper::evalActionCall(const IR::P4Action *action, const IR::MethodCal result->emplace_back(state); } +bool ExprStepper::resolveMethodCallArguments(const IR::MethodCallExpression *call) { + IR::Vector resolvedArgs; + const auto *method = call->method->type->checkedTo(); + const auto &methodParams = method->parameters->parameters; + const auto *callArguments = call->arguments; + for (size_t idx = 0; idx < callArguments->size(); ++idx) { + const auto *arg = callArguments->at(idx); + const auto *param = methodParams.at(idx); + const auto *argExpr = arg->expression; + if (param->direction == IR::Direction::Out || SymbolicEnv::isSymbolicValue(argExpr)) { + continue; + } + // If the parameter is not an out parameter (meaning we do not care about its content) and + // the argument is not yet symbolic, try to resolve it. + return stepToSubexpr( + argExpr, result, state, [call, idx, param](const Continuation::Parameter *v) { + // TODO: It seems expensive to copy the function every time we resolve an argument. + // We should do this all at once. But how? + // This is the same problem as in stepToListSubexpr + // Thankfully, most method calls have less than 10 arguments. + auto *clonedCall = call->clone(); + auto *arguments = clonedCall->arguments->clone(); + auto *arg = arguments->at(idx)->clone(); + const IR::Expression *computedExpr = v->param; + // A parameter with direction InOut might be read and also written to. + // We capture this ambiguity with an InOutReference. + if (param->direction == IR::Direction::InOut) { + auto stateVar = ToolsVariables::convertReference(arg->expression); + computedExpr = new IR::InOutReference(stateVar, computedExpr); + } + arg->expression = computedExpr; + (*arguments)[idx] = arg; + clonedCall->arguments = arguments; + return Continuation::Return(clonedCall); + }); + } + return true; +} + bool ExprStepper::preorder(const IR::MethodCallExpression *call) { logStep(call); // A method call expression represents an invocation of an action, a table, an extern, or // setValid/setInvalid. + + if (!resolveMethodCallArguments(call)) { + return false; + } + // Handle method calls. These are either table invocations or extern calls. if (call->method->type->is()) { if (const auto *path = call->method->to()) { @@ -255,6 +299,13 @@ bool ExprStepper::preorder(const IR::Mux *mux) { bool ExprStepper::preorder(const IR::PathExpression *pathExpression) { logStep(pathExpression); + // If the path expression is a Type_MatchKind, convert it to a StringLiteral. + if (pathExpression->type->is()) { + state.replaceTopBody(Continuation::Return( + new IR::StringLiteral(IR::Type_MatchKind::get(), pathExpression->path->name))); + result->emplace_back(state); + return false; + } // Otherwise convert the path expression into a qualified member and return it. state.replaceTopBody(Continuation::Return(state.get(pathExpression))); result->emplace_back(state); diff --git a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.h b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.h index 30c3ac07cde..0c197bf78e7 100644 --- a/backends/p4tools/modules/testgen/core/small_step/expr_stepper.h +++ b/backends/p4tools/modules/testgen/core/small_step/expr_stepper.h @@ -67,6 +67,11 @@ class ExprStepper : public AbstractStepper { /// values for hit, miss and action_run after that. void handleHitMissActionRun(const IR::Member *member); + /// Resolve all arguments to the method call by stepping into each argument that is not yet + /// symbolic or a pure reference (represented as Out direction). + /// @returns false when an argument needs to be resolved, true otherwise. + bool resolveMethodCallArguments(const IR::MethodCallExpression *call); + /// Evaluates a call to an extern method. Upon return, the given result will be augmented with /// the successor states resulting from evaluating the call. /// diff --git a/backends/p4tools/modules/testgen/core/small_step/extern_stepper.cpp b/backends/p4tools/modules/testgen/core/small_step/extern_stepper.cpp index eb42d7d1293..6d5626dcb59 100644 --- a/backends/p4tools/modules/testgen/core/small_step/extern_stepper.cpp +++ b/backends/p4tools/modules/testgen/core/small_step/extern_stepper.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -15,13 +14,11 @@ #include "backends/p4tools/common/lib/trace_event_types.h" #include "backends/p4tools/common/lib/variables.h" #include "ir/id.h" -#include "ir/indexed_vector.h" #include "ir/ir.h" #include "ir/irutils.h" #include "ir/vector.h" #include "lib/cstring.h" #include "lib/exceptions.h" -#include "lib/log.h" #include "backends/p4tools/modules/testgen//lib/exceptions.h" #include "backends/p4tools/modules/testgen/core/externs.h" @@ -293,8 +290,8 @@ void ExprStepper::evalInternalExternMethodCall(const IR::MethodCallExpression *c [this](const IR::MethodCallExpression * /*call*/, const IR::Expression * /*receiver*/, IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { - const auto *blockRef = args->at(0)->expression->checkedTo(); - const auto *block = state.findDecl(blockRef); + const auto *blockRef = args->at(0)->expression->checkedTo(); + const auto *block = state.findDecl(new IR::Path(blockRef->value)); const auto *archSpec = TestgenTarget::getArchSpec(); auto blockName = block->getName().name; auto &nextState = state.clone(); @@ -328,8 +325,8 @@ void ExprStepper::evalInternalExternMethodCall(const IR::MethodCallExpression *c [this](const IR::MethodCallExpression * /*call*/, const IR::Expression * /*receiver*/, IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { - const auto *blockRef = args->at(0)->expression->checkedTo(); - const auto *block = state.findDecl(blockRef); + const auto *blockRef = args->at(0)->expression->checkedTo(); + const auto *block = state.findDecl(new IR::Path(blockRef->value)); const auto *archSpec = TestgenTarget::getArchSpec(); auto blockName = block->getName().name; auto &nextState = state.clone(); @@ -722,13 +719,8 @@ void ExprStepper::evalExternMethodCall(const IR::MethodCallExpression *call, [](const IR::MethodCallExpression * /*call*/, const IR::Expression * /*receiver*/, IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { - const auto *emitOutput = args->at(0)->expression; - const auto *emitType = emitOutput->type->checkedTo(); - if (!(emitOutput->is() || emitOutput->is())) { - TESTGEN_UNIMPLEMENTED("Emit input %1% of type %2% not supported", emitOutput, - emitType); - } - const auto *validVar = state.get(ToolsVariables::getHeaderValidity(emitOutput)); + const auto *emitHeader = args->at(0)->expression->checkedTo(); + const auto *validVar = emitHeader->validity; // Check whether the validity bit of the header is tainted. If it is, the entire // emit is tainted. There is not much we can do here, so throw an error. @@ -738,25 +730,21 @@ void ExprStepper::evalExternMethodCall(const IR::MethodCallExpression *call, "The validity bit of %1% is tainted. Tainted emit calls can not be " "mitigated " "because it is unclear whether the header will be emitted. Abort.", - emitOutput); + emitHeader); } // This call assumes that the "expandEmit" midend pass is being used. expandEmit // unravels emit calls on structs into emit calls on the header members. { auto &nextState = state.clone(); - std::vector> fields; - for (const auto *field : emitType->fields) { - const auto *fieldType = field->type; - if (fieldType->is()) { - BUG("Unexpected emit field %1% of type %2%", field, fieldType); - } - const auto *fieldRef = new IR::Member(fieldType, emitOutput, field->name); - const IR::Expression *fieldExpr = nextState.get(fieldRef); - fieldType = fieldExpr->type; + // Append to the emit buffer. + auto flatFields = IR::flattenStructExpression(emitHeader); + for (const auto *fieldExpr : flatFields) { + const auto *fieldType = fieldExpr->type; + BUG_CHECK(!fieldType->is(), + "Unexpected emit field %1% of type %2%", fieldExpr, fieldType); if (const auto *varbits = fieldType->to()) { fieldType = IR::getBitType(varbits->assignedSize); } - fields.emplace_back(fieldRef, fieldExpr); auto fieldWidth = fieldType->width_bits(); // If the width is zero, do not bother with emitting. if (fieldWidth == 0) { @@ -780,7 +768,7 @@ void ExprStepper::evalExternMethodCall(const IR::MethodCallExpression *call, // Append to the emit buffer. nextState.appendToEmitBuffer(fieldExpr); } - nextState.add(*new TraceEvents::Emit(emitOutput, fields)); + nextState.add(*new TraceEvents::Emit(emitHeader)); nextState.popBody(); // Only when the header is valid, the members are emitted and the packet // delta is adjusted. @@ -789,7 +777,7 @@ void ExprStepper::evalExternMethodCall(const IR::MethodCallExpression *call, { auto &invalidState = state.clone(); std::stringstream traceString; - traceString << "Invalid emit: " << emitOutput->toString(); + traceString << "Invalid emit: " << emitHeader->toString(); invalidState.add(*new TraceEvents::Generic(traceString)); invalidState.popBody(); result->emplace_back(new IR::LNot(IR::Type::Boolean::get(), validVar), state, diff --git a/backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.cpp b/backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.cpp index c2265ce4a73..62b1656e5b3 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/expr_stepper.cpp @@ -25,7 +25,6 @@ #include "lib/cstring.h" #include "lib/error.h" #include "lib/exceptions.h" -#include "lib/log.h" #include "lib/ordered_map.h" #include "backends/p4tools/modules/testgen/core/externs.h" @@ -37,11 +36,9 @@ #include "backends/p4tools/modules/testgen/lib/exceptions.h" #include "backends/p4tools/modules/testgen/lib/execution_state.h" #include "backends/p4tools/modules/testgen/lib/packet_vars.h" -#include "backends/p4tools/modules/testgen/lib/test_spec.h" #include "backends/p4tools/modules/testgen/targets/bmv2/constants.h" #include "backends/p4tools/modules/testgen/targets/bmv2/program_info.h" #include "backends/p4tools/modules/testgen/targets/bmv2/table_stepper.h" -#include "backends/p4tools/modules/testgen/targets/bmv2/target.h" #include "backends/p4tools/modules/testgen/targets/bmv2/test_spec.h" namespace P4Tools::P4Testgen::Bmv2 { @@ -298,23 +295,11 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression const IR::Vector *args, ExecutionState &state) { const ExternMethodImpls::MethodImpl assertAssumeExecute = - [](const IR::MethodCallExpression *call, const IR::Expression * /*receiver*/, + [](const IR::MethodCallExpression * /*call*/, const IR::Expression * /*receiver*/, IR::ID &methodName, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { const auto *cond = args->at(0)->expression; - if (!SymbolicEnv::isSymbolicValue(cond)) { - // Evaluate the condition. - stepToSubexpr(cond, result, state, [call](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = new IR::Vector(); - arguments->push_back(new IR::Argument(v->param)); - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } - // If the assert/assume condition is tainted, we do not know whether we abort. if (Taint::hasTaint(cond)) { TESTGEN_UNIMPLEMENTED( @@ -359,14 +344,10 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression const ExecutionState &state, SmallStepEvaluator::Result &result) { auto &nextState = state.clone(); const auto *nineBitType = IR::getBitType(BMv2Constants::PORT_BIT_WIDTH); - const auto *metadataLabel = args->at(0)->expression; - if (!(metadataLabel->is() || metadataLabel->is())) { - TESTGEN_UNIMPLEMENTED("Drop input %1% of type %2% not supported", metadataLabel, - metadataLabel->type); - } + const auto *metadataLabel = args->at(0)->expression->checkedTo(); // Use an assignment to set egress_spec to true. // This variable will be processed in the deparser. - const auto *portVar = new IR::Member(nineBitType, metadataLabel, "egress_spec"); + const auto *portVar = new IR::Member(nineBitType, metadataLabel->ref, "egress_spec"); nextState.set(portVar, IR::getConstant(nineBitType, BMv2Constants::DROP_PORT)); nextState.add(*new TraceEvents::Generic("mark_to_drop executed.")); nextState.popBody(); @@ -479,25 +460,18 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { auto msg = args->at(0)->expression->checkedTo()->value; - std::stringstream totalStream; - if (const auto *structExpr = args->at(1)->expression->to()) { - int exprNumber = 0; - for (size_t i = 0; i < msg.size(); i++) { - if (i + 1 < msg.size() && msg.get(i) == '{' && msg.get(i + 1) == '}') { - structExpr->components.at(exprNumber)->expression->dbprint(totalStream); - exprNumber += 1; - i += 1; - } else { - totalStream << msg.get(i); - } - } - } else { - msg = msg.replace("{}", args->at(1)->toString()); - totalStream << msg; - } + auto value = args->at(1)->expression; + std::stringstream assignStream; + assignStream << msg << ": "; + + // Strip any newlines in the value we want to record. + value->dbprint(assignStream); + auto assignString = assignStream.str(); + assignString.erase(std::remove(assignString.begin(), assignString.end(), '\n'), + assignString.cend()); auto &nextState = state.clone(); - nextState.add(*new TraceEvents::Generic(totalStream.str())); + nextState.add(*new TraceEvents::Generic(assignString.c_str())); nextState.popBody(); result->emplace_back(nextState); }}, @@ -537,23 +511,6 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression // If any of the input arguments is tainted, the entire extern is unreliable. for (size_t idx = 1; idx < args->size(); ++idx) { const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); } const auto *hashOutput = args->at(0)->expression; @@ -604,29 +561,9 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression */ {"register.read", {"result", "index"}, - [this](const IR::MethodCallExpression *call, const IR::Expression *receiver, + [this](const IR::MethodCallExpression * /*call*/, const IR::Expression *receiver, IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { - for (size_t idx = 1; idx < args->size(); ++idx) { - const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } - } const auto *readOutput = args->at(0)->expression; const auto *index = args->at(1)->expression; auto &nextState = state.clone(); @@ -699,7 +636,7 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression */ {"register.write", {"index", "value"}, - [this](const IR::MethodCallExpression *call, const IR::Expression *receiver, + [this](const IR::MethodCallExpression * /*call*/, const IR::Expression *receiver, IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { const auto *index = args->at(0)->expression; @@ -710,26 +647,7 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression "Only registers with bit or int types are currently supported for " "v1model."); } - for (size_t idx = 0; idx < args->size(); ++idx) { - const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } - } + const auto *receiverPath = receiver->checkedTo(); const auto &externInstance = state.findDecl(receiverPath); auto &nextState = state.clone(); @@ -888,24 +806,10 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression return; } - // TODO: Frontload this in the expression stepper for method call expressions. - const auto *index = args->at(0)->expression; - if (!SymbolicEnv::isSymbolicValue(index)) { - // Evaluate the condition. - stepToSubexpr(index, result, state, [call](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(0)->clone(); - arg->expression = v->param; - (*arguments)[0] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } auto &nextState = state.clone(); std::vector replacements; + const auto *index = args->at(0)->expression; const auto *receiverPath = receiver->checkedTo(); const auto &externInstance = nextState.findDecl(receiverPath); @@ -1123,7 +1027,7 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression */ {"*method.clone_preserving_field_list", {"type", "session", "data"}, - [](const IR::MethodCallExpression *call, const IR::Expression * /*receiver*/, + [](const IR::MethodCallExpression * /*call*/, const IR::Expression * /*receiver*/, IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { // Grab the recirculate count. Stop after more than 1 circulation loop to avoid @@ -1143,23 +1047,6 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression bool argsAreTainted = false; for (size_t idx = 0; idx < args->size(); ++idx) { const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); } // If any of the input arguments is tainted, the entire extern is unreliable. @@ -1330,7 +1217,7 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression */ {"*method.clone", {"type", "session"}, - [](const IR::MethodCallExpression *call, const IR::Expression * /*receiver*/, + [](const IR::MethodCallExpression * /*call*/, const IR::Expression * /*receiver*/, IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { // Grab the recirculate count. Stop after more than 1 circulation loop to avoid @@ -1350,23 +1237,6 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression bool argsAreTainted = false; for (size_t idx = 0; idx < args->size(); ++idx) { const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); } // If any of the input arguments is tainted, the entire extern is unreliable. @@ -1465,23 +1335,6 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression // If any of the input arguments is tainted, the entire extern is unreliable. for (size_t idx = 0; idx < args->size(); ++idx) { const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); } @@ -1590,27 +1443,11 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression // If any of the input arguments is tainted, the entire extern is unreliable. for (size_t idx = 0; idx < args->size() - 2; ++idx) { const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); } - const auto &checksumVar = ToolsVariables::convertReference(args->at(2)->expression); + const auto &checksumVar = + args->at(2)->expression->checkedTo()->ref; const auto *updateCond = args->at(0)->expression; const auto *checksumVarType = checksumVar->type; const auto *data = args->at(1)->expression; @@ -1682,27 +1519,11 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression // If any of the input arguments is tainted, the entire extern is unreliable. for (size_t idx = 0; idx < args->size() - 2; ++idx) { const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); } - const auto &checksumVar = ToolsVariables::convertReference(args->at(2)->expression); + const auto &checksumVar = + args->at(2)->expression->checkedTo()->ref; const auto *updateCond = args->at(0)->expression; const auto *checksumVarType = checksumVar->type; const auto *data = args->at(1)->expression; @@ -1761,23 +1582,6 @@ void Bmv2V1ModelExprStepper::evalExternMethodCall(const IR::MethodCallExpression // If any of the input arguments is tainted, the entire extern is unreliable. for (size_t idx = 0; idx < args->size(); ++idx) { const auto *arg = args->at(idx); - const auto *argExpr = arg->expression; - - // TODO: Frontload this in the expression stepper for method call expressions. - if (!SymbolicEnv::isSymbolicValue(argExpr)) { - // Evaluate the condition. - stepToSubexpr(argExpr, result, state, - [call, idx](const Continuation::Parameter *v) { - auto *clonedCall = call->clone(); - auto *arguments = clonedCall->arguments->clone(); - auto *arg = arguments->at(idx)->clone(); - arg->expression = v->param; - (*arguments)[idx] = arg; - clonedCall->arguments = arguments; - return Continuation::Return(clonedCall); - }); - return; - } argsAreTainted = argsAreTainted || Taint::hasTaint(arg->expression); } diff --git a/backends/p4tools/modules/testgen/targets/bmv2/program_info.cpp b/backends/p4tools/modules/testgen/targets/bmv2/program_info.cpp index 4760df2a0fa..ec8358fdfd5 100644 --- a/backends/p4tools/modules/testgen/targets/bmv2/program_info.cpp +++ b/backends/p4tools/modules/testgen/targets/bmv2/program_info.cpp @@ -137,14 +137,18 @@ std::vector Bmv2V1ModelProgramInfo::processDeclaration( std::vector cmds; // Copy-in. - const auto *copyInCall = new IR::MethodCallStatement( - Utils::generateInternalMethodCall("copy_in", {new IR::PathExpression(typeDecl->name)})); + const auto *copyInCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_in", {new IR::StringLiteral(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); cmds.emplace_back(copyInCall); // Insert the actual pipeline. cmds.emplace_back(typeDecl); // Copy-out. - const auto *copyOutCall = new IR::MethodCallStatement( - Utils::generateInternalMethodCall("copy_out", {new IR::PathExpression(typeDecl->name)})); + const auto *copyOutCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_out", {new IR::StringLiteral(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); cmds.emplace_back(copyOutCall); auto *dropStmt = new IR::MethodCallStatement(Utils::generateInternalMethodCall("drop_and_exit", {})); diff --git a/backends/p4tools/modules/testgen/targets/ebpf/expr_stepper.cpp b/backends/p4tools/modules/testgen/targets/ebpf/expr_stepper.cpp index 63a15eea776..969d3522051 100644 --- a/backends/p4tools/modules/testgen/targets/ebpf/expr_stepper.cpp +++ b/backends/p4tools/modules/testgen/targets/ebpf/expr_stepper.cpp @@ -167,24 +167,25 @@ void EBPFExprStepper::evalExternMethodCall(const IR::MethodCallExpression *call, [](const IR::MethodCallExpression * /*call*/, const IR::Expression * /*receiver*/, IR::ID & /*methodName*/, const IR::Vector *args, const ExecutionState &state, SmallStepEvaluator::Result &result) { - const auto *headers = args->at(0)->expression; - if (!(headers->is() || headers->is())) { - TESTGEN_UNIMPLEMENTED("IP header input %1% of type %2% not supported", headers, - headers->type); - } // Input must be the headers struct. - headers->type->checkedTo(); - const auto *oneBitType = IR::getBitType(1); - const auto *tcpRef = new IR::Member(headers, "tcp"); - const auto *syn = state.get(new IR::Member(oneBitType, tcpRef, "syn")); - const auto *ack = state.get(new IR::Member(oneBitType, tcpRef, "ack")); + const auto *headers = args->at(0)->expression->checkedTo(); + const auto *tcpRef = headers->getField("tcp"); + CHECK_NULL(tcpRef); + const auto *tcpHeader = tcpRef->expression->checkedTo(); + const auto *syn = tcpHeader->getField("syn"); + CHECK_NULL(syn); + const auto *ack = tcpHeader->getField("ack"); + CHECK_NULL(ack); + const auto *synExpr = syn->expression; + const auto *ackExpr = ack->expression; // Implement the simple conntrack case since we do not support multiple packets here // yet. // TODO: We need custom test objects to implement richer, stateful testing here. auto &nextState = state.clone(); - const auto *cond = new IR::LAnd(new IR::Equ(syn, IR::getConstant(syn->type, 1)), - new IR::Equ(ack, IR::getConstant(ack->type, 0))); + const auto *cond = + new IR::LAnd(new IR::Equ(synExpr, IR::getConstant(synExpr->type, 1)), + new IR::Equ(ackExpr, IR::getConstant(ackExpr->type, 0))); nextState.replaceTopBody(Continuation::Return(cond)); result->emplace_back(nextState); }}, diff --git a/backends/p4tools/modules/testgen/targets/ebpf/program_info.cpp b/backends/p4tools/modules/testgen/targets/ebpf/program_info.cpp index 69171bc01a0..aa537bbef7a 100644 --- a/backends/p4tools/modules/testgen/targets/ebpf/program_info.cpp +++ b/backends/p4tools/modules/testgen/targets/ebpf/program_info.cpp @@ -84,14 +84,18 @@ std::vector EBPFProgramInfo::processDeclaration( std::vector cmds; // Copy-in. - const auto *copyInCall = new IR::MethodCallStatement( - Utils::generateInternalMethodCall("copy_in", {new IR::PathExpression(typeDecl->name)})); + const auto *copyInCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_in", {new IR::StringLiteral(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); cmds.emplace_back(copyInCall); // Insert the actual pipeline. cmds.emplace_back(typeDecl); // Copy-out. - const auto *copyOutCall = new IR::MethodCallStatement( - Utils::generateInternalMethodCall("copy_out", {new IR::PathExpression(typeDecl->name)})); + const auto *copyOutCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_out", {new IR::StringLiteral(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); cmds.emplace_back(copyOutCall); // After some specific pipelines (filter), we check whether the packet has been dropped. diff --git a/backends/p4tools/modules/testgen/targets/pna/dpdk/program_info.cpp b/backends/p4tools/modules/testgen/targets/pna/dpdk/program_info.cpp index 4b8036dbe66..b1b8399652d 100644 --- a/backends/p4tools/modules/testgen/targets/pna/dpdk/program_info.cpp +++ b/backends/p4tools/modules/testgen/targets/pna/dpdk/program_info.cpp @@ -67,14 +67,18 @@ std::vector PnaDpdkProgramInfo::processDeclaration( std::vector cmds; // Copy-in. - const auto *copyInCall = new IR::MethodCallStatement( - Utils::generateInternalMethodCall("copy_in", {new IR::PathExpression(typeDecl->name)})); + const auto *copyInCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_in", {new IR::StringLiteral(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); cmds.emplace_back(copyInCall); // Insert the actual pipeline. cmds.emplace_back(typeDecl); // Copy-out. - const auto *copyOutCall = new IR::MethodCallStatement( - Utils::generateInternalMethodCall("copy_out", {new IR::PathExpression(typeDecl->name)})); + const auto *copyOutCall = new IR::MethodCallStatement(Utils::generateInternalMethodCall( + "copy_out", {new IR::StringLiteral(typeDecl->name)}, IR::Type_Void::get(), + new IR::ParameterList( + {new IR::Parameter("blockRef", IR::Direction::In, IR::Type_Unknown::get())}))); cmds.emplace_back(copyOutCall); auto *dropStmt = diff --git a/backends/p4tools/p4tools.def b/backends/p4tools/p4tools.def index 4ad888970c6..dc4ba077282 100644 --- a/backends/p4tools/p4tools.def +++ b/backends/p4tools/p4tools.def @@ -261,3 +261,32 @@ class HeaderExpression : StructExpression { BUG_CHECK(structType == nullptr || structType->is(), "%1%: unexpected header type", structType->node_type_name()); } } + +/// An wrapper which models an InOut argument. +/// Contains both a copy of the original reference as well as the resolved value of that reference. +class InOutReference : Expression { +/// Because we do not use a reference for "ref" and also inline the member the irgenerator produces +/// a constructor that does not compile. We need to write the constructor manually instead. +#noconstructor +#nomethod_constructor + /// The original reference. + inline StateVariable ref; + /// The value of the reference after it was resolved. + Expression resolvedRef; + + InOutReference(JSONLoader & json) : Expression(json), ref(json) { + json.load("resolvedRef", resolvedRef); + } + + InOutReference(Util::SourceInfo srcInfo, IR::StateVariable &ref, const Expression* resolvedRef) : + Expression(srcInfo, ref.type), ref(ref), resolvedRef(resolvedRef) + { validate(); } + + InOutReference(IR::StateVariable &ref, const Expression* resolvedRef) : + Expression(ref.type), ref(ref), resolvedRef(resolvedRef) + { validate(); } + + toString { return ref->toString() + "(" + resolvedRef->toString() + ")"; } + + dbprint { out << ref << "(" << resolvedRef << ")"; } +}