Skip to content

Commit

Permalink
refactored general inlining code into a separate set of classes
Browse files Browse the repository at this point in the history
  • Loading branch information
mbudiu-bfn committed Apr 14, 2016
1 parent fb346aa commit ab145a9
Show file tree
Hide file tree
Showing 12 changed files with 343 additions and 276 deletions.
173 changes: 12 additions & 161 deletions backends/bmv2/controlInlining.cpp
Original file line number Diff line number Diff line change
@@ -1,57 +1,16 @@
#include "controlInlining.h"
#include "frontends/p4/callGraph.h"
#include "frontends/p4/evaluator/substituteParameters.h"
#include "frontends/p4/methodInstance.h"
#include "frontends/p4/parameterSubstitution.h"
#include "frontends/common/resolveReferences/resolveReferences.h"

namespace BMV2 {

CInlineWorkList* ControlsInlineList::next() {
if (toInline.size() == 0)
return nullptr;
auto result = new CInlineWorkList();
std::set<const IR::P4Control*> processing;
while (!toInline.empty()) {
auto toadd = toInline.back();
if (processing.find(toadd->callee) != processing.end())
break;
toInline.pop_back();
result->add(toadd);
processing.emplace(toadd->caller);
}
return result;
}

void CInlineWorkList::add(ControlsCallInfo* cci) {
callerToWork[cci->caller].declToCallee[cci->instantiation] = cci->callee;
for (auto mcs : cci->invocations) {
callerToWork[cci->caller].callToinstance[mcs] = cci->instantiation;
}
Visitor::profile_t SimpleControlsInliner::init_apply(const IR::Node* node) {
P4::ResolveReferences solver(refMap, true);
node->apply(solver);
return AbstractInliner::init_apply(node);
}

///////////////////////////////////////////////////////////////////////////////////////

class SimpleControlsInliner : public Transform {
P4::ReferenceMap* refMap;
ControlsInlineList* list;
CInlineWorkList* toInline;
CInlineWorkList::PerCaller* workToDo;
public:
explicit SimpleControlsInliner(P4::ReferenceMap* refMap, ControlsInlineList* list,
CInlineWorkList* toInline) :
refMap(refMap), list(list), toInline(toInline), workToDo(nullptr)
{ CHECK_NULL(refMap); CHECK_NULL(list); CHECK_NULL(toInline); }

Visitor::profile_t init_apply(const IR::Node* node) {
LOG1("SimpleControlsInliner " << toInline);
return Transform::init_apply(node);
}

const IR::Node* preorder(IR::MethodCallStatement* statement) override;
const IR::Node* preorder(IR::P4Control* caller) override;
};

const IR::Node* SimpleControlsInliner::preorder(IR::P4Control* caller) {
prune();
auto orig = getOriginal<IR::P4Control>();
Expand All @@ -77,28 +36,28 @@ const IR::Node* SimpleControlsInliner::preorder(IR::P4Control* caller) {
// TODO: this is correct only if the arguments have no side-effects.
// There should be a prior pass to ensure this fact. This is
// true for programs that come out of the P4 v1.0 front-end.
subst.populate(callee->constructorParams, inst->arguments);
subst.populate(callee->getConstructorParameters(), inst->arguments);
IR::TypeVariableSubstitution tvs;
if (inst->type->is<IR::Type_Specialized>()) {
auto spec = inst->type->to<IR::Type_Specialized>();
tvs.setBindings(callee, callee->getTypeParameters(), spec->arguments);
tvs.setBindings(callee->getNode(), callee->getTypeParameters(), spec->arguments);
}
P4::SubstituteParameters sp(refMap, &subst, &tvs);
auto clone = callee->apply(sp);
auto clone = callee->getNode()->apply(sp);
if (clone == nullptr)
return caller;
for (auto i : *clone->statefulEnumerator()) {
for (auto i : *clone->to<IR::P4Control>()->statefulEnumerator()) {
if (stateful->getUnique(i->name) == nullptr)
stateful->addUnique(i->name, i);
}
workToDo->declToCallee[inst] = clone;
workToDo->declToCallee[inst] = clone->to<IR::IContainer>();
}
}

visit(caller->body);
auto result = new IR::P4Control(caller->srcInfo, caller->name, caller->type,
caller->constructorParams, std::move(*stateful),
caller->body);
caller->constructorParams, std::move(*stateful),
caller->body);
list->replace(orig, result);
workToDo = nullptr;
return result;
Expand All @@ -114,115 +73,7 @@ const IR::Node* SimpleControlsInliner::preorder(IR::MethodCallStatement* stateme
prune();
auto decl = workToDo->callToinstance[orig];
CHECK_NULL(decl);
return workToDo->declToCallee[decl]->body;
}

/////////////////////////////////////////////////////////////////////////////////////////////

void DiscoverControlsInlining::postorder(const IR::MethodCallStatement* statement) {
LOG2("Visiting " << statement);
auto mi = P4::MethodInstance::resolve(statement, blockMap->refMap, blockMap->typeMap);
if (!mi->isApply())
return;
auto am = mi->to<P4::ApplyMethod>();
CHECK_NULL(am);
if (!am->type->is<IR::Type_Control>())
return;
auto instantiation = am->object->to<IR::Declaration_Instance>();
BUG_CHECK(instantiation != nullptr, "%1% expected an instance declaration", am->object);
inlineList->addInvocation(instantiation, statement);
}

void DiscoverControlsInlining::visit_all(const IR::Block* block) {
for (auto it : block->constantValue) {
if (it.second->is<IR::Block>()) {
visit(it.second->getNode());
}
}
}

bool DiscoverControlsInlining::preorder(const IR::ControlBlock* block) {
LOG2("Visiting " << block);
if (getContext()->node->is<IR::ParserBlock>()) {
::error("%1%: This target does not support invocation of a control from a parser",
block->node);
} else if (getContext()->node->is<IR::ControlBlock>()) {
auto parent = getContext()->node->to<IR::ControlBlock>();
LOG1("Will inline " << block << "@" << block->node << " into " << parent);
auto instance = block->node->to<IR::Declaration_Instance>();
auto callee = block->container;
inlineList->addInstantiation(parent->container, callee, instance);
}

visit_all(block);
visit(block->container->body);
return false;
}

bool DiscoverControlsInlining::preorder(const IR::ParserBlock* block) {
LOG2("Visiting " << block);
if (getContext()->node->is<IR::ControlBlock>()) {
::error("%1%: This target does not support invocation of a parser from a control",
block->node);
} else if (getContext()->node->is<IR::ParserBlock>()) {
BUG("%1%: Parser inlining not yet implmented", block->node);
}
visit_all(block);
return false;
}

const IR::Node* InlineControlsDriver::preorder(IR::P4Program* program) {
LOG1("Inline controls driver");
const IR::P4Program* prog = program;
P4::ResolveReferences solver(refMap, true);

toInline->analyze();
while (auto todo = toInline->next()) {
LOG1("Processing " << todo);
SimpleControlsInliner si(refMap, toInline, todo);
prog = prog->apply(si);
if (::errorCount() > 0)
return prog;
// Must re-resolve references
// TODO: this is too slow; should update references incrementally
prog->apply(solver);
}

prune();
return prog;
}

void ControlsInlineList::analyze() {
P4::CallGraph<const IR::P4Control*> cg("Control call-graph");

for (auto m : inlineMap) {
auto inl = m.second;
if (inl->invocations.size() == 0) continue;
auto it = inl->invocations.begin();
auto first = *it;
if (inl->invocations.size() > 1) {
++it;
auto second = *it;
::error("Multiple invocations of control block not supported on this target: %1%, %2%",
first, second);
continue;
}
cg.add(inl->caller, inl->callee);
}

// must inline from leaves up
std::vector<const IR::P4Control*> order;
cg.sort(order);
for (auto c : order) {
// This is quadratic, but hopefully the call graph is not too large
for (auto m : inlineMap) {
auto inl = m.second;
if (inl->caller == c)
toInline.push_back(inl);
}
}

std::reverse(toInline.begin(), toInline.end());
return workToDo->declToCallee[decl]->to<IR::P4Control>()->body;
}

} // namespace BMV2
101 changes: 7 additions & 94 deletions backends/bmv2/controlInlining.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,106 +2,19 @@
#define _BACKENDS_BMV2_CONTROLINLINING_H_

#include "ir/ir.h"
#include "frontends/p4/evaluator/blockMap.h"
#include "midend/inlining.h"

namespace BMV2 {

// An inliner that only works for programs imported from P4 v1.0

struct ControlsCallInfo {
const IR::P4Control* caller;
const IR::P4Control* callee;
const IR::Declaration_Instance* instantiation;
// each instantiation may be invoked multiple times
std::set<const IR::MethodCallStatement*> invocations;

ControlsCallInfo(const IR::P4Control* caller, const IR::P4Control* callee,
const IR::Declaration_Instance* instantiation) :
caller(caller), callee(callee), instantiation(instantiation)
{ CHECK_NULL(caller); CHECK_NULL(callee); CHECK_NULL(instantiation); }
void addInvocation(const IR::MethodCallStatement* statement)
{ invocations.emplace(statement); }
void dbprint(std::ostream& out) const
{ out << "Inline " << callee << " into " << caller; }
};

struct CInlineWorkList {
struct PerCaller {
std::map<const IR::Declaration_Instance*, const IR::P4Control*> declToCallee;
std::map<const IR::MethodCallStatement*, const IR::Declaration_Instance*> callToinstance;
};
std::map<const IR::P4Control*, PerCaller> callerToWork;

void add(ControlsCallInfo* cci);
};

class ControlsInlineList {
std::map<const IR::Declaration_Instance*, ControlsCallInfo*> inlineMap;
std::vector<ControlsCallInfo*> toInline; // sorted in order of inlining

public:
CInlineWorkList* next();
void addInstantiation(const IR::P4Control* caller, const IR::P4Control* callee,
const IR::Declaration_Instance* instantiation) {
CHECK_NULL(caller); CHECK_NULL(callee); CHECK_NULL(instantiation);
LOG1("Inline instantiation " << instantiation);
auto inst = new ControlsCallInfo(caller, callee, instantiation);
inlineMap.emplace(instantiation, inst);
}

void addInvocation(const IR::Declaration_Instance* instance,
const IR::MethodCallStatement* statement) {
CHECK_NULL(instance); CHECK_NULL(statement);
LOG1("Inline invocation " << instance);
auto info = ::get(inlineMap, instance);
BUG_CHECK(info, "Could not locate instance %1% invoked by %2%", instance, statement);
info->addInvocation(statement);
}

void replace(const IR::P4Control* container, const IR::P4Control* replacement) {
CHECK_NULL(container); CHECK_NULL(replacement);
LOG1("Replacing " << container << " with " << replacement);
for (auto e : toInline) {
if (e->callee == container)
e->callee = replacement;
if (e->caller == container)
e->caller = replacement;
}
}

void analyze();
};

class DiscoverControlsInlining : public Inspector {
ControlsInlineList* inlineList;
P4::BlockMap* blockMap;
public:
DiscoverControlsInlining(ControlsInlineList* inlineList, P4::BlockMap* blockMap) :
inlineList(inlineList), blockMap(blockMap)
{ CHECK_NULL(inlineList); CHECK_NULL(blockMap); }
void visit_all(const IR::Block* block);
bool preorder(const IR::Block* block) override
{ visit_all(block); return false; }
bool preorder(const IR::ControlBlock* block) override;
bool preorder(const IR::ParserBlock* block) override;
void postorder(const IR::MethodCallStatement* statement) override;
// We don't care to visit the program, we just visit the blocks.
bool preorder(const IR::P4Program*) override
{ visit_all(blockMap->toplevelBlock); return false; }
};

class InlineControlsDriver : public Transform {
class SimpleControlsInliner : public P4::AbstractInliner {
P4::ReferenceMap* refMap;
ControlsInlineList* toInline;

P4::InlineSummary::PerCaller* workToDo;
public:
explicit InlineControlsDriver(P4::ReferenceMap* refMap, ControlsInlineList* toInline) :
refMap(refMap), toInline(toInline)
{ CHECK_NULL(refMap); CHECK_NULL(toInline); }

// Not really a visitor, but we want to embed it into a PassManager,
// so we make it look like a visitor.
const IR::Node* preorder(IR::P4Program* program) override;
SimpleControlsInliner(P4::ReferenceMap* refMap) : refMap(refMap), workToDo(nullptr) {}
const IR::Node* preorder(IR::MethodCallStatement* statement) override;
const IR::Node* preorder(IR::P4Control* caller) override;
Visitor::profile_t init_apply(const IR::Node* node) override;
};

} // namespace BMV2
Expand Down
16 changes: 10 additions & 6 deletions backends/bmv2/midend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,32 @@ P4::BlockMap* MidEnd::process(CompilerOptions& options, const IR::P4Program* pro
if (::errorCount() > 0)
return nullptr;

P4::ActionsInlineList actionsToInline;
ControlsInlineList controlsToInline;
P4::ReferenceMap refMap;
P4::TypeMap typeMap;

if (isv1) {
PassManager inliner = {
new DiscoverControlsInlining(&controlsToInline, evaluator0->getBlockMap()),
new InlineControlsDriver(evaluator0->getBlockMap()->refMap, &controlsToInline),
P4::InlineWorkList controlsToInline;
auto inliner = new SimpleControlsInliner(&refMap);
auto find = new P4::DiscoverInlining(&controlsToInline, evaluator0->getBlockMap());
find->allowParsers = false; // this should not be necessary

PassManager inlinerPasses = {
find,
new P4::InlineDriver(&controlsToInline, inliner),
new PassRepeated {
// remove useless callees
new P4::ResolveReferences(&refMap, isv1),
new P4::RemoveUnusedDeclarations(&refMap),
},
};
program = program->apply(inliner);
program = program->apply(inlinerPasses);
if (::errorCount() > 0)
return nullptr;
}

// TODO: add separate inlining passes for v1.2 programs

P4::ActionsInlineList actionsToInline;
auto evaluator1 = new P4::EvaluatorPass(isv1);
PassManager midEnd = {
new P4::ResolveReferences(&refMap, isv1),
Expand Down
3 changes: 2 additions & 1 deletion ir/base.def
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/* -*-C++-*-
This file contains the base classes needed for the compiler IR:
- all interfaces
- most basic abstract classes
Expand Down Expand Up @@ -70,6 +70,7 @@ interface IContainer : IMayBeGenericType {
#emit
// The type of the constructor as a method
virtual const Type_Method* getConstructorMethodType() const = 0;
virtual const ParameterList* getConstructorParameters() const = 0;
#end
}

Expand Down
Loading

0 comments on commit ab145a9

Please sign in to comment.