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

Add struct field pruning support #14

Merged
merged 14 commits into from
Feb 22, 2022
10 changes: 5 additions & 5 deletions pruner/src/compiler_pruner.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <vector>

#include "frontends/common/constantFolding.h"
#include "frontends/common/resolveReferences/resolveReferences.h"
#include "frontends/p4/createBuiltins.h"
Expand Down Expand Up @@ -47,10 +49,9 @@ const IR::P4Program *apply_unused_decls(const IR::P4Program *program,
P4::ReferenceMap refMap;
P4::TypeMap typeMap;
const IR::P4Program *temp = nullptr;

PassManager pass_manager({new ExtendedUnusedDeclarations(&refMap)});

INFO("Applying custom RemoveAllUnusedDeclarations...");
std::vector<struct_obj *> used_structs;
PassManager pass_manager(
{new ExtendedUnusedDeclarations(&refMap, &used_structs)});
temp = program->apply(pass_manager);
check_pruned_program(&program, temp, pruner_conf);

Expand All @@ -64,7 +65,6 @@ const IR::P4Program *apply_compiler_passes(const IR::P4Program *program,
auto action = DiagnosticAction::Ignore;
P4CContext::get().setDefaultWarningDiagnosticAction(action);
INFO("\nPruning with compiler passes")

bool genericPassesApplied = false;
// apply the compiler passes
program = apply_generic_passes(program, pruner_conf, &genericPassesApplied);
Expand Down
2 changes: 1 addition & 1 deletion pruner/src/expression_pruner.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class ExpressionPruner : public Transform {
// const IR::Node *postorder(const IR::Slice *s);
// const IR::Node *postorder(const IR::Mux *m);
// const IR::Node *postorder(const IR::Member *m);
// const IR::Node *postorder(const IR::PathExpression *p);
// const IR::Node *postorder(IR::Member *p) override;
// const IR::Node *postorder(const IR::SerEnumMember *m);
// const IR::Node *postorder(const IR::DefaultExpression *de);
// const IR::Node *postorder(const IR::ListExpression *le);
Expand Down
94 changes: 93 additions & 1 deletion pruner/src/extended_unused.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,43 @@
#include "extended_unused.h"

#include "frontends/p4/sideEffects.h"
#include "ir/ir-generated.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not include this file. Instead include ir/ir.h

#include "pruner_util.h"

namespace P4PRUNER {

bool PruneUnused::check_if_field_used(cstring name_of_struct,
cstring name_of_field) {
for (struct_obj *s : *used_structs) {
if (s->name == name_of_struct) {
for (cstring f : *(s->fields)) {
if (f == name_of_field) {
return true;
}
}
}
}
return false;
}

const IR::Node *PruneUnused::preorder(IR::Type_StructLike *ts) {
prune(); // do not remove individual struct members yet
if (!unused_refMap->isUsed(getOriginal<IR::IDeclaration>())) {
return nullptr;
}
return ts;
}

const IR::Node *PruneUnused::preorder(IR::StructField *sf) {
const auto *p = getParent<IR::Type_StructLike>();
cstring p_name = p->getP4Type()->toString();
cstring f_name = sf->toString();
bool res = check_if_field_used(p_name, f_name);
if (res) {
return sf;
}
return nullptr;
}

const IR::Node *PruneUnused::preorder(IR::Type_Extern *te) {
if (!unused_refMap->isUsed(getOriginal<IR::IDeclaration>())) {
return nullptr;
Expand All @@ -33,4 +59,70 @@ const IR::Node *PruneUnused::preorder(IR::Function *f) {
return f;
}

// List unused struct declarations

bool ListStructs::preorder(const IR::Member *p) {
const auto *pexpr = p->expr->to<IR::PathExpression>();
if (pexpr == nullptr) {
return true; // i.e, expr was not a path expression
}
const IR::IDeclaration *decl =
unused_refMap->getDeclaration(pexpr->path, false);
if (decl == nullptr) {
return true;
}
const auto *v = decl->to<IR::Parameter>();
if (v == nullptr) {
return true; // Only handle parameters for now
robotrobo marked this conversation as resolved.
Show resolved Hide resolved
}
cstring mem = p->member.name;
cstring parent = v->type->getP4Type()->toString();
insertField(parent, mem);
return true;
}

Visitor::profile_t ListStructs::init_apply(const IR::Node *node) {
robotrobo marked this conversation as resolved.
Show resolved Hide resolved
return Inspector::init_apply(node);
}

void ListStructs::insertField(cstring name_of_struct, cstring name_of_field) {
bool foundStruct = false;
bool foundField = false;

for (auto *s : *used_structs) {
if (name_of_struct == s->name) {
foundStruct = true;
for (auto f : *s->fields) {
foundField = name_of_field == f;
}
if (!foundField) {
s->fields->push_back(name_of_field);
}
}
}
if (!foundStruct) {
// Lets have this on the heap
auto *f = new std::vector<cstring>();
f->push_back(name_of_field);
auto *s = new struct_obj;
s->name = name_of_struct;
s->fields = f;
used_structs->push_back(s);
}
}

void PruneUnused::show_used_structs() {
LOG2("Printing Used Structs");
if (used_structs->empty()) {
LOG1("used_structs is empty");
return;
}
for (struct_obj *s : *used_structs) {
LOG2("Struct: " << s->name);
for (cstring f : *(s->fields)) {
LOG2("\tField: " << f);
}
}
}

} // namespace P4PRUNER
44 changes: 38 additions & 6 deletions pruner/src/extended_unused.h
Original file line number Diff line number Diff line change
@@ -1,36 +1,68 @@
#ifndef _PRUNE_UNUSED_H_
#define _PRUNE_UNUSED_H_

#include <vector>
// P4C headers
#include "frontends/common/resolveReferences/resolveReferences.h"
#include "frontends/p4/unusedDeclarations.h"
#include "ir/ir.h"

namespace P4PRUNER {

struct struct_obj {
cstring name;
std::vector<cstring> *fields{};
};

class PruneUnused : public P4::RemoveUnusedDeclarations {
// the refmap of the parent class is private so we have to use our own
// a little bit unfortunate but the pointer is the same
// we also do not modify the map so this is fairly safe
const P4::ReferenceMap *unused_refMap;
const std::vector<struct_obj *> *used_structs;

public:
explicit PruneUnused(const P4::ReferenceMap *refMap)
: P4::RemoveUnusedDeclarations(refMap), unused_refMap(refMap) {
explicit PruneUnused(const P4::ReferenceMap *refMap,
std::vector<struct_obj *> *_used_structs)
: P4::RemoveUnusedDeclarations(refMap), unused_refMap(refMap),
used_structs(_used_structs) {
CHECK_NULL(refMap);
setName("PruneUnused");
}
const IR::Node *preorder(IR::Type_StructLike *ts) override;
const IR::Node *preorder(IR::StructField *sf) override;
const IR::Node *preorder(IR::Type_Extern *te) override;
const IR::Node *preorder(IR::Method *m) override;
const IR::Node *preorder(IR::Function *f) override;
void show_used_structs();
bool check_if_field_used(cstring name_of_struct, cstring name_of_field);
};

class ListStructs : public Inspector {
const P4::ReferenceMap *unused_refMap;
std::vector<struct_obj *> *used_structs;

public:
explicit ListStructs(const P4::ReferenceMap *refMap,
std::vector<struct_obj *> *_used_structs)
: unused_refMap(refMap), used_structs(_used_structs) {
CHECK_NULL(refMap);
robotrobo marked this conversation as resolved.
Show resolved Hide resolved
CHECK_NULL(_used_structs);
setName("ListStructs");
}
Visitor::profile_t init_apply(const IR::Node *node) override;
bool preorder(const IR::Member *p) override;
void insertField(cstring name_of_struct, cstring name_of_field);
};

class ExtendedUnusedDeclarations : public PassManager {
public:
explicit ExtendedUnusedDeclarations(P4::ReferenceMap *refMap) {
explicit ExtendedUnusedDeclarations(
P4::ReferenceMap *refMap, std::vector<struct_obj *> *used_structs) {
CHECK_NULL(refMap);
passes.emplace_back(new PassRepeated{new P4::ResolveReferences(refMap),
new PruneUnused(refMap)});
passes.emplace_back(
new PassRepeated{new P4::ResolveReferences(refMap),
new ListStructs(refMap, used_structs),
new PruneUnused(refMap, used_structs)});
setName("ExtendedUnusedDeclarations");
}
};
Expand Down
7 changes: 5 additions & 2 deletions pruner/src/statement_pruner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ const IR::Node *Pruner::preorder(IR::BlockStatement *s) {
return s;
}

const IR::Node *Pruner::preorder(IR::EmptyStatement * /*e*/) {
// Always remove empty statements.
const IR::Node *Pruner::preorder(IR::EmptyStatement *e) {
// Don't remove empty statements if they are children of an if block.
if (this->getParent<IR::IfStatement>() != nullptr) {
return e;
}
return nullptr;
}

Expand Down
118 changes: 118 additions & 0 deletions pruner/tests/references/crash_bugs/crash_one_reference.p4
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,52 @@ header ethernet_t {
bit<16> eth_type;
}

struct zRDuhm {
bit<128> LEQY;
bit<8> KOcO;
ethernet_t EWpj;
bit<32> BUWO;
bit<32> NnjD;
}

header PguzcJ {
bit<4> YRcl;
bit<32> vGcj;
bit<16> CazW;
}

header yLNoil {
bit<128> ILBe;
bit<64> MXzB;
}

header vuOVMH {
bit<8> GPVl;
bit<4> hrds;
bit<128> HoON;
bit<32> Wsxt;
}

struct avXIXF {
bit<64> Vpbo;
bit<32> CeRJ;
zRDuhm tkyq;
}

header gPTIia {
bit<32> pvdW;
bit<8> HQZo;
}

struct OXxQuH {
bit<8> xBZh;
}

header jUHTpn {
bit<4> yrVX;
bit<32> bZKC;
}

struct Headers {
ethernet_t eth_hdr;
ethernet_t[6] gvFM;
Expand All @@ -30,9 +65,32 @@ struct Headers {
}

bit<16> SaTkzAX(PguzcJ Oofi) {
bool cmdluC = true;
bit<32> cqckxY = 32w10;
bit<128> RfSkhs = 128w10;
bit<64> nZqXev = 64w10;
bit<128> xYhUqh = RfSkhs;
return 16w10;
}
yLNoil wClaYko(jUHTpn DoHl) {
bit<4> wgIKEQ = 4w14;
const zRDuhm zTQmam = (zRDuhm){LEQY = 128w232569008864530135157127505601818385808,KOcO = 8w10,EWpj = (ethernet_t){dst_addr = 48w211703505640505,src_addr = 48w10,eth_type = 16w10},BUWO = 32w10,NnjD = 32w2274055191};
return (yLNoil){ILBe = 128w10,MXzB = 64w10};
}
bit<4> rjsgGkN(bit<16> SOZw) {
const bit<128> cMIrQM = 128w10;
bit<16> YVHHin = (bit<16>)16w10;
return 4w10;
bit<8> XmGxDm = (bit<8>)8w143;
bit<16> aneguV = 16w46085;
return 4w12;
}
parser p(packet_in pkt, out Headers hdr) {
bit<4> WCyPOo = 145w10[106:58][12:9];
bit<4> OpKEBC = 4w14;
yLNoil LnJSsf = (yLNoil){ILBe = 128w10,MXzB = 64w8739799105651448972};
bool MHFZYM = false;
bit<8> AfRsJs = 8w38;
state start {
transition parse_hdrs;
}
Expand All @@ -42,8 +100,68 @@ parser p(packet_in pkt, out Headers hdr) {
}

control ingress(inout Headers h) {
bit<128> WbiJDx = 128w230764102599282411499082228184235706426;
action AWhhg(inout bit<16> HpDQ, OXxQuH GSUK) {
}
action TyIkj(bit<8> cFUf, bit<64> UYcs) {
const bool naGUDB = false;
return;
const bit<32> kFhgzn = 32w10;
return;
}
action isfLJ(inout bit<4> zayb, bit<32> ezim, bit<16> pxcw) {
{
}
{
avXIXF paUCEG = (avXIXF){Vpbo = 64w10,CeRJ = 32w10,tkyq = (zRDuhm){LEQY = 128w10,KOcO = (bit<8>)h.uAdg.HQZo,EWpj = (ethernet_t){dst_addr = 48w10,src_addr = 48w10,eth_type = pxcw},BUWO = 32w502793114,NnjD = 32w10}};
}
const gPTIia khmurm = (gPTIia){pvdW = 32w10,HQZo = 8w10};
}
table xLdpWu {
key = {
16w19744: exact @name("wvtOpl") ;
}
actions = {
isfLJ(h.IDZn.YRcl);
@defaultonly NoAction();
}
default_action = NoAction();
}
table uiTHkJ {
key = {
}
actions = {
@defaultonly NoAction();
}
default_action = NoAction();
}
table YfGdXm {
key = {
}
actions = {
isfLJ(h.uAdg.pvdW[20:17]);
@defaultonly NoAction();
}
default_action = NoAction();
}
table ObUSpl {
key = {
4w9 : exact @name("LzviDL") ;
16w19115 : exact @name("SBnBQt") ;
48w145114785635731: exact @name("JEgqHq") ;
}
actions = {
isfLJ(h.IDZn.YRcl);
@defaultonly NoAction();
}
default_action = NoAction();
}
apply {
const int XsZqcA = 181433741;
bit<128> CYNOag = 128w334026077963046502987531789306364882607;
const bit<4> lgWdgD = 4w10;
h.gvFM[max((bit<3>)SaTkzAX({ 4w6, 32w10, 16w10 }), 3w5)].src_addr = 48w10;
bit<64> GTZTgt = 64w10;
}
}

Expand Down
Loading