Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DPDK backend: Add support for tuple expressions in transition select statement #2836

Merged
merged 33 commits into from
Jul 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a805208
Fix dpdk regression failure: Duplicates declaration
usha1830 Apr 21, 2021
611261b
Fix cpplint errors
usha1830 Apr 22, 2021
9c8e6d2
Added checks to ensure parameters exist before accessing them
usha1830 Apr 22, 2021
b184e0b
Merge branch 'master' into ushag/fix_duplicates_declaration
usha1830 Apr 22, 2021
c03e07d
Merge remote-tracking branch 'upstream/master'
usha1830 Apr 23, 2021
c99d261
Merging with p4lang/p4c repo main branch
usha1830 May 6, 2021
c6a5c12
Merge branch 'p4lang:main' into main
usha1830 May 7, 2021
4817ea2
Fix PSA extern Register's implementation
usha1830 May 11, 2021
bfb6f02
Updated spec.cpp as per review comments
usha1830 May 12, 2021
f5cfb35
Emit a warning if Register instantiation is found outside control block
usha1830 May 12, 2021
5ca47b1
Fixed a typo
usha1830 May 12, 2021
f0c7f9c
Remove restriction for instantiation and invocation of register exter…
usha1830 May 13, 2021
f3ab6ad
Merge branch 'p4lang:main' into ushag_conditional_operator
usha1830 May 28, 2021
778cd74
Conditional operator support for P4C-DPDK.
usha1830 May 28, 2021
2b992a4
Merge branch 'p4lang:main' into ushag_conditional_operator
usha1830 May 31, 2021
61567fd
Changing ternary match to exact in test program, updating the referen…
usha1830 May 31, 2021
009fd24
Merge branch 'ushag_conditional_operator' of https://github.com/usha1…
usha1830 May 31, 2021
afa8764
Merge branch 'p4lang:main' into ushag_conditional_operator
usha1830 Jun 4, 2021
05a68ba
Added support for range and mask operations in transition select stat…
usha1830 Jun 4, 2021
d43963c
Merge branch 'p4lang:main' into origin/ushag/mask_range
usha1830 Jun 9, 2021
8b9927f
Support mask operations in dpdk backend (#1)
usha1830 Jun 9, 2021
3bde5de
Remove pass for inserting temporary mask variables
usha1830 Jun 10, 2021
f7cb04d
Merge branch 'p4lang:main' into origin/ushag/mask_range
usha1830 Jun 10, 2021
a3e6ef6
update reference output
usha1830 Jun 10, 2021
6b963ce
Merge branch 'p4lang:main' into origin/ushag/mask_range
usha1830 Jun 16, 2021
8cf5882
Address review comments
usha1830 Jun 16, 2021
00ce76f
Merge branch 'p4lang:main' into origin/ushag/mask_range
usha1830 Jun 20, 2021
32d2abe
Merge branch 'p4lang:main' into origin/ushag/mask_range
usha1830 Jun 22, 2021
d89ed0c
Merge branch 'p4lang:main' into origin/ushag/mask_range
usha1830 Jul 2, 2021
8301a85
DPDK backend: Add support for tuple expressions in transition select …
usha1830 Jul 3, 2021
a849326
Address review comments for tuple expression support
usha1830 Jul 7, 2021
ebac98c
Use refmap to create label names, use ERR_UNSUPPORTED_ON_TARGET for a…
usha1830 Jul 11, 2021
b117ec9
Update test files to include core.p4
usha1830 Jul 11, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 119 additions & 29 deletions backends/dpdk/dpdkProgram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,79 @@ IR::Declaration_Variable *ConvertToDpdkParser::addNewTmpVarToMetadata(cstring na
return newTmpVar;
}

/* This is a helper function for handling the transition select statement. It populates the left
and right operands of comparison, to be used in conditional jump instructions. When the keyset
of select case is simple expressions, it populates the left and rigt operands with the input
expressions. When the keyset is Mask expression "a &&& b", it inserts temporary variables in
Metadata structure and populates the left and right operands of comparison with
"input & b" and "a & b" */
void ConvertToDpdkParser::getCondVars(const IR::Expression *sv, const IR::Expression *ce,
IR::Expression **leftExpr, IR::Expression **rightExpr) {
if (sv->is<IR::Constant>() && sv->type->width_bits() > 32) {
::error(ErrorType::ERR_UNSUPPORTED_ON_TARGET,
"%1%, Constant expression wider than 32-bit is not permitted", sv);
return;
}
if (sv->type->width_bits() > 64) {
::error(ErrorType::ERR_UNSUPPORTED_ON_TARGET,
"%1%, Select expression wider than 64-bit is not permitted", sv);
return;
}
if (auto maskexpr = ce->to<IR::Mask>()) {
auto left = maskexpr->left;
auto right = maskexpr->right;
unsigned value = right->to<IR::Constant>()->asUnsigned() &
left->to<IR::Constant>()->asUnsigned();
auto tmpDecl = addNewTmpVarToMetadata("tmpMask", sv->type);
auto tmpMask = new IR::PathExpression(
IR::ID("m." + tmpDecl->name.name));
collector->push_variable(new IR::DpdkDeclaration(tmpDecl));
add_instr(new IR::DpdkMovStatement(tmpMask, sv));
add_instr(new IR::DpdkAndStatement(tmpMask, tmpMask, right));
*leftExpr = tmpMask;
*rightExpr = new IR::Constant(value);
} else {
*leftExpr = sv->clone();
*rightExpr = ce->clone();
}
}

/* This is a helper function for handling tuple expressions in select statement in parser block.
The tuple expressions are evaluated as exact match of each element in the tuple and is
translated to dpdk assembly by short-circuiting the conditions where beginning of each keyset
starts with a unique case label.

transition select(a,b) { Equivalent dpdk assembly:
(a1, b1) : state1;
(a2, b2) : state2; jmpneq case1 a, a1
(a3, b3) : state3; jmpneq case1 b, b1
} ==> jmp state1
is evaluated as case1: jmpneq case2 a, a2
if (a == a1 && b == b1) jmpneq case2 b, b2
goto state1 jmp state2
else (a == a2 && b == b2) case2: jmpneq case a, a3
goto state2 jmpneq case2 b, b3
..... jmp state3
....
This function emits a series of jmps for each keyset.
*/
void ConvertToDpdkParser::handleTupleExpression(const IR::ListExpression *cl,
const IR::ListExpression *input, int inputSize,
cstring trueLabel, cstring falseLabel) {
IR::Expression *left;
IR::Expression *right;
/* Compare each element in the input tuple with the keyset tuple */
for (auto i = 0; i < inputSize; i++) {
auto switch_var = input->components.at(i);
auto caseExpr = cl->components.at(i);
if (!caseExpr->is<IR::DefaultExpression>()) {
mihaibudiu marked this conversation as resolved.
Show resolved Hide resolved
getCondVars(switch_var, caseExpr, &left, &right);
add_instr(new IR::DpdkJmpNotEqualStatement(falseLabel, left, right));
}
}
add_instr(new IR::DpdkJmpLabelStatement(trueLabel));
}

bool ConvertToDpdkParser::preorder(const IR::P4Parser *p) {
for (auto l : p->parserLocals) {
collector->push_variable(new IR::DpdkDeclaration(l));
Expand Down Expand Up @@ -240,41 +313,58 @@ bool ConvertToDpdkParser::preorder(const IR::P4Parser *p) {
for (auto i : h.get_instr())
add_instr(i);
}

// Handle transition select statement
if (state->selectExpression) {
if (auto e = state->selectExpression->to<IR::SelectExpression>()) {
auto caseList = e->selectCases;
const IR::Expression *switch_var;
BUG_CHECK(e->select->components.size() == 1,
"Unsupported select expression %1%", e->select);
switch_var = e->select->components[0];
for (auto v : e->selectCases) {
if (!v->keyset->is<IR::DefaultExpression>()) {
if (v->keyset->is<IR::Mask>()) {
auto maskexpr = v->keyset->to<IR::Mask>();
auto left = maskexpr->left;
auto right = maskexpr->right;
/* Dpdk architecture requires that both operands of &&& be constants */
if (!left->is<IR::Constant>() || !left->is<IR::Constant>())
::error(ErrorType::ERR_UNSUPPORTED,
"Non constant values are not supported in Mask operation");
unsigned value = right->to<IR::Constant>()->asUnsigned() &
left->to<IR::Constant>()->asUnsigned();
auto tmpDecl = addNewTmpVarToMetadata("tmpMask", switch_var->type);
auto tmpMask = new IR::PathExpression(
IR::ID("m." + tmpDecl->name.name));
collector->push_variable(new IR::DpdkDeclaration(tmpDecl));
add_instr(new IR::DpdkMovStatement(tmpMask, switch_var));
add_instr(new IR::DpdkAndStatement(tmpMask, tmpMask, right));
const IR::Expression *caseExpr;
IR::Expression *left;
IR::Expression *right;

/* Handling single expression in keyset as special case because :
- for tuple expression we emit a series of jmpneq followed by an unconditional
jump, handling single expression separately by emitting a jmpeq saves us one
unconditional jmp
- the keyset for single expression is not a ListExpression, so handling it with
tuple expressions would anyways require additional and conversion to
ListExpression.
*/
if (e->select->components.size() == 1) {
switch_var = e->select->components.at(0);
for (auto sc : caseList) {
caseExpr = sc->keyset;
if (!sc->keyset->is<IR::DefaultExpression>()){
getCondVars(switch_var, caseExpr, &left, &right);
add_instr(new IR::DpdkJmpEqualStatement(
append_parser_name(p, v->state->path->name),
tmpMask, new IR::Constant(value)));
append_parser_name(p, sc->state->path->name), left, right));
} else {
add_instr(new IR::DpdkJmpEqualStatement(
append_parser_name(p, v->state->path->name), switch_var, v->keyset));
add_instr(new IR::DpdkJmpLabelStatement(
append_parser_name(p, sc->state->path->name)));
}
}
} else {
auto tupleInputExpr = e->select;
auto inputSize = tupleInputExpr->components.size();
cstring trueLabel, falseLabel;
/* For each select case, emit the block start label and then a series of
jmp instructions. */
for (auto sc : caseList) {
if (!sc->keyset->is<IR::DefaultExpression>()) {
/* Create label names, falseLabel for next keyset comparison and
trueLabel for the state to jump on match */
falseLabel = refmap->newName(state->name);
trueLabel = sc->state->path->name;
handleTupleExpression(sc->keyset->to<IR::ListExpression>(),
tupleInputExpr, inputSize, append_parser_name(p,
trueLabel), append_parser_name(p, falseLabel));
add_instr(new IR::DpdkLabelStatement(
append_parser_name(p, falseLabel)));
} else {
add_instr(new IR::DpdkJmpLabelStatement(
append_parser_name(p, sc->state->path->name)));
}
} else {
auto i = new IR::DpdkJmpLabelStatement(
append_parser_name(p, v->state->path->name));
add_instr(i);
}
}
} else if (auto path =
Expand Down
7 changes: 6 additions & 1 deletion backends/dpdk/dpdkProgram.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class ConvertToDpdkProgram : public Transform {
std::map<const cstring, IR::IndexedVector<IR::Parameter> *>
*args_struct_map;
std::map<const IR::Declaration_Instance *, cstring> *csum_map;
std::vector<const IR::Declaration_Instance *> *externDecls;
std::vector<const IR::Declaration_Instance *> *externDecls;
public:
ConvertToDpdkProgram(BMV2::PsaProgramStructure &structure,
P4::ReferenceMap *refmap, P4::TypeMap *typemap,
Expand Down Expand Up @@ -102,11 +102,16 @@ class ConvertToDpdkParser : public Inspector {
IR::IndexedVector<IR::DpdkAsmStatement> getInstructions() {
return instructions;
}

bool preorder(const IR::P4Parser *a) override;
bool preorder(const IR::ParserState *s) override;
void add_instr(const IR::DpdkAsmStatement *s) { instructions.push_back(s); }
cstring append_parser_name(const IR::P4Parser* p, cstring);
IR::Declaration_Variable *addNewTmpVarToMetadata (cstring name, const IR::Type* type);
void handleTupleExpression(const IR::ListExpression *cl, const IR::ListExpression *input,
int inputSize, cstring trueLabel, cstring falseLabel);
void getCondVars(const IR::Expression *sv, const IR::Expression *ce,
IR::Expression **leftExpr, IR::Expression **rightExpr);
};

class ConvertToDpdkControl : public Inspector {
Expand Down
166 changes: 166 additions & 0 deletions testdata/p4_16_samples/psa-example-select_tuple-1.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include <core.p4>
#include <psa.p4>


typedef bit<48> EthernetAddress;

header ethernet_t {
EthernetAddress dstAddr;
EthernetAddress srcAddr;
bit<16> etherType;
}

header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
bit<32> srcAddr;
bit<32> dstAddr;
}

header tcp_t {
bit<16> srcPort;
bit<16> dstPort;
bit<32> seqNo;
bit<32> ackNo;
bit<4> dataOffset;
bit<3> res;
bit<3> ecn;
bit<6> ctrl;
bit<16> window;
bit<16> checksum;
bit<16> urgentPtr;
}

struct empty_metadata_t {
}


struct metadata {
bit<16> data;
}

struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
tcp_t tcp;
}


parser IngressParserImpl(packet_in buffer,
out headers hdr,
inout metadata user_meta,
in psa_ingress_parser_input_metadata_t istd,
in empty_metadata_t resubmit_meta,
in empty_metadata_t recirculate_meta)
{
state start {
buffer.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType, hdr.ethernet.srcAddr) {
(16w0x0800, 48w0xF00) : parse_ipv4;
(0x800 &&& 0xF00, 48w0x3883) : parse_tcp;
(_, 0x678): parse_ipv4;
default : accept;
}
}
state parse_ipv4 {
buffer.extract(hdr.ipv4);
transition select(hdr.ipv4.protocol) {
_ : parse_tcp;
}
}
state parse_tcp {
buffer.extract(hdr.tcp);
transition accept;
}
}

control ingress(inout headers hdr,
inout metadata user_meta,
in psa_ingress_input_metadata_t istd,
inout psa_ingress_output_metadata_t ostd)
{
action execute() {
user_meta.data = 1;
}
table tbl {
key = {
hdr.ethernet.srcAddr : ternary;
}
actions = { NoAction; execute; }
}
apply {
tbl.apply();
}
}
// END:Parse_Error_Example

parser EgressParserImpl(packet_in buffer,
out headers hdr,
inout metadata user_meta,
in psa_egress_parser_input_metadata_t istd,
in empty_metadata_t normal_meta,
in empty_metadata_t clone_i2e_meta,
in empty_metadata_t clone_e2e_meta)
{
state start {
transition accept;
}
}

control egress(inout headers hdr,
inout metadata user_meta,
in psa_egress_input_metadata_t istd,
inout psa_egress_output_metadata_t ostd)
{
apply { }
}

control IngressDeparserImpl(packet_out packet,
out empty_metadata_t clone_i2e_meta,
out empty_metadata_t resubmit_meta,
out empty_metadata_t normal_meta,
inout headers hdr,
in metadata meta,
in psa_ingress_output_metadata_t istd)
{
apply {
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
packet.emit(hdr.tcp);
}
}

// BEGIN:Compute_New_IPv4_Checksum_Example
control EgressDeparserImpl(packet_out packet,
out empty_metadata_t clone_e2e_meta,
out empty_metadata_t recirculate_meta,
inout headers hdr,
in metadata meta,
in psa_egress_output_metadata_t istd,
in psa_egress_deparser_input_metadata_t edstd)
{
apply {
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
packet.emit(hdr.tcp);
}
}
// END:Compute_New_IPv4_Checksum_Example

IngressPipeline(IngressParserImpl(),
ingress(),
IngressDeparserImpl()) ip;

EgressPipeline(EgressParserImpl(),
egress(),
EgressDeparserImpl()) ep;

PSA_Switch(ip, PacketReplicationEngine(), ep, BufferingQueueingEngine()) main;
Loading