Skip to content

Commit

Permalink
Implementing functions (p4lang#1355)
Browse files Browse the repository at this point in the history
* Grammar for functions
* Method instance support for functions
* Handle returns with expressions in def-use and RemoveReturns
* Function inlining
  • Loading branch information
Mihai Budiu authored Jun 29, 2018
1 parent c369c28 commit 3fc1202
Show file tree
Hide file tree
Showing 48 changed files with 1,026 additions and 277 deletions.
37 changes: 20 additions & 17 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ p4c
├── backends
│ ├── p4test -- "fake" back-end for testing
│ ├── ebpf -- extended Berkeley Packet Filters back-end
│ ├── graphs -- backend that can draw graphiz graphs of P4 programs
│ └── bmv2 -- behavioral model version 2 (switch simulator) back-end
├── control-plane -- control plane API
├── docs -- documentation
Expand All @@ -19,17 +20,18 @@ p4c
│ └── XXXX -- symlinks to custom back-ends
├── frontends
│ ├── common -- common front-end code
│ ├── parsers -- parser and lexer code for P4_14 and P4_16
│ ├── p4-14 -- P4_14 front-end
│ └── p4 -- P4_16 front-end
├── ir -- core internal representation
├── lib -- common utilities (libp4toolkit.a)
├── m4 -- m4 macros used by autotools
├── midend -- code that may be useful for writing mid-ends
├── p4include -- standard P4 files needed by the compiler (e.g., core.p4)
├── test -- test code
│ └── unittests -- unit test code
│ └── gtest -- unit test code written using gtest
├── tools -- external programs used in the build/test process
│ ├── driver -- p4c compiler driver
│ ├── driver -- p4c compiler driver: a script that invokes various compilers
│ ├── stf -- Python code to parse STF files (used for testing P4 programs)
| └── ir-generator -- code for the IR C++ class hierarchy generator
└── testdata -- test inputs and reference outputs
├── p4_16_samples -- P4_16 input test programs
Expand All @@ -38,6 +40,8 @@ p4c
├── p4_16_errors_outputs -- Expected outputs from P4_16 negative tests
├── p4_16_bmv_errors -- P4_16 negative input tests for the bmv2 backend
├── v1_1_samples -- P4 v1.1 sample programs
├── p4_14_errors -- P4_14 negative input test programs
├── p4_14_errors_outputs -- Expected outputs from P4_14 negative tests
├── p4_14_samples -- P4_14 input test programs
├── p4_14_samples_outputs -- Expected outputs from P4_14 tests
└── p4_14_errors -- P4_14 negative input test programs
Expand All @@ -48,8 +52,8 @@ p4c
* the P4_14 (P4 v1.0.4) language is described in the P4 spec:
https://p4lang.github.io/p4-spec/p4-14/v1.0.4/tex/p4.pdf

* the P4_16 language (v1.0.0) is described in [this pdf
document](https://p4lang.github.io/p4-spec/docs/P4-16-v1.0.0-spec.pdf)
* the P4_16 language (v1.1.0) is described in [this pdf
document](https://p4.org/p4-spec/docs/P4-16-v1.1.0-draft.pdf)

* the core design of the compiler intermediate representation (IR) and
the visitor patterns are briefly described in [IR](IR.md)
Expand All @@ -71,12 +75,14 @@ p4c
* [BMv2](../backends/bmv2/README.md)
* [eBPF](../backends/ebpf/README.md)

* Check out the [IntelliJ P4 plugin](https://github.com/TakeshiTseng/IntelliJ-P4-Plugin)

# How to contribute

* do write unit test code
* code has to be reviewed before it is merged
* make sure all tests pass when you send a pull request (only PASS tests allowed)
* make sure `make cpplint` produces no errors
* make sure all tests pass when you send a pull request
* make sure `make cpplint` produces no errors (`make check` will also run this)
* write documentation

# Writing documentation
Expand Down Expand Up @@ -168,26 +174,23 @@ git push -f
in file `pass_manager.cpp` above level 2, use the following compiler
command-line option: `-Tnode:1,pass_manager:2`

To execute LOG statements in a header file you must supply the complete
name of the header file, e.g.: `-TfunctionsInlining.h:3`.

## Testing

The testing infrastructure is based on autotools. We use several
small python and shell scripts to work around limitations of
autotools.
The testing infrastructure is based on small python and shell scripts.

* To run tests execute `make check -j3`
- There should be no FAIL or XPASS tests.
- XFAIL tests are tolerated only transiently - these indicate known
unfixed bugs in the compiler.
- XFAIL tests are tolerated only transiently.

* To run a subset of tests execute `make check-PATTERN`. E.g., `make
check-p4`.

* To rerun the tests that failed last time run `make recheck -j3`

* Add unit tests in `test/unittests`
* To rerun the tests that failed last time run `make recheck`

* Code for running various compiler back-ends on p4 files is generated
using a simple python script `tools/gen-tests.py`.
* Add unit tests in `test/gtest`

## Coding conventions

Expand Down
3 changes: 3 additions & 0 deletions frontends/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ set (P4_FRONTEND_SRCS
p4/fromv1.0/converters.cpp
p4/fromv1.0/programStructure.cpp
p4/frontend.cpp
p4/functionsInlining.cpp
p4/hierarchicalNames.cpp
p4/inlining.cpp
p4/localizeActions.cpp
Expand Down Expand Up @@ -73,6 +74,7 @@ set (P4_FRONTEND_HDRS
p4/checkConstants.h
p4/checkNamedArgs.h
p4/cloner.h
p4/commonInlining.h
p4/coreLibrary.h
p4/createBuiltins.h
p4/def_use.h
Expand All @@ -87,6 +89,7 @@ set (P4_FRONTEND_HDRS
p4/fromv1.0/programStructure.h
p4/fromv1.0/v1model.h
p4/frontend.h
p4/functionsInlining.h
p4/hierarchicalNames.h
p4/inlining.h
p4/localizeActions.h
Expand Down
78 changes: 5 additions & 73 deletions frontends/p4/actionsInlining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,58 +23,6 @@ limitations under the License.

namespace P4 {

void AInlineWorkList::dbprint(std::ostream& out) const {
for (auto t : sites) {
out << t.first;
for (auto c : t.second) {
out << std::endl << "\t" << c.first << " => " << c.second;
}
}
}

void ActionsInlineList::analyze() {
P4::CallGraph<const IR::P4Action*> cg("Actions call-graph");

for (auto c : toInline)
cg.calls(c->caller, c->callee);

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

std::reverse(inlineOrder.begin(), inlineOrder.end());
}

AInlineWorkList* ActionsInlineList::next() {
if (inlineOrder.size() == 0)
return nullptr;

std::set<const IR::P4Action*> callers;
auto result = new AInlineWorkList();

// Find actions that can be inlined simultaneously.
// This traversal is in topological order starting from leaf callees.
// We stop at the first action which calls one of the actions
// we have already selected.
while (!inlineOrder.empty()) {
auto last = inlineOrder.back();
if (callers.find(last->callee) != callers.end())
break;
inlineOrder.pop_back();
result->add(last);
callers.emplace(last->caller);
}
BUG_CHECK(!result->empty(), "Empty list of methods to inline");
return result;
}

void DiscoverActionsInlining::postorder(const IR::MethodCallStatement* mcs) {
auto mi = P4::MethodInstance::resolve(mcs, refMap, typeMap);
CHECK_NULL(mi);
Expand All @@ -95,34 +43,18 @@ void DiscoverActionsInlining::postorder(const IR::MethodCallStatement* mcs) {
toInline->add(aci);
}

const IR::Node* InlineActionsDriver::preorder(IR::P4Program* program) {
LOG1("Inline actions driver");
const IR::P4Program* prog = program;
toInline->analyze();
while (auto todo = toInline->next()) {
LOG1("Processing " << todo);
inliner->prepare(toInline, todo);
prog = prog->apply(*inliner);
if (::errorCount() > 0)
break;
}

prune();
return prog;
}

Visitor::profile_t ActionsInliner::init_apply(const IR::Node* node) {
P4::ResolveReferences solver(refMap, true);
node->apply(solver);
LOG1("ActionsInliner " << toInline);
LOG2("ActionsInliner " << toInline);
return Transform::init_apply(node);
}

const IR::Node* ActionsInliner::preorder(IR::P4Action* action) {
if (toInline->sites.count(getOriginal<IR::P4Action>()) == 0)
prune();
replMap = &toInline->sites[getOriginal<IR::P4Action>()];
LOG1("Visiting: " << getOriginal());
LOG2("Visiting: " << getOriginal());
return action;
}

Expand All @@ -135,15 +67,15 @@ const IR::Node* ActionsInliner::postorder(IR::P4Action* action) {

const IR::Node* ActionsInliner::preorder(IR::MethodCallStatement* statement) {
auto orig = getOriginal<IR::MethodCallStatement>();
LOG1("Visiting " << orig);
LOG2("Visiting " << orig);
if (replMap == nullptr)
return statement;

auto callee = get(*replMap, orig);
if (callee == nullptr)
return statement;

LOG1("Inlining: " << toInline);
LOG2("Inlining: " << callee);
IR::IndexedVector<IR::StatOrDecl> body;
ParameterSubstitution subst;
TypeVariableSubstitution tvs; // empty
Expand Down Expand Up @@ -200,7 +132,7 @@ const IR::Node* ActionsInliner::preorder(IR::MethodCallStatement* statement) {
auto annotations = callee->annotations->where(
[](const IR::Annotation* a) { return a->name != IR::Annotation::nameAnnotation; });
auto result = new IR::BlockStatement(statement->srcInfo, annotations, body);
LOG1("Replacing " << orig << " with " << result);
LOG2("Replacing " << orig << " with " << result);
return result;
}

Expand Down
84 changes: 7 additions & 77 deletions frontends/p4/actionsInlining.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,54 +20,13 @@ limitations under the License.
#include "ir/ir.h"
#include "frontends/p4/typeChecking/typeChecker.h"
#include "frontends/p4/unusedDeclarations.h"
#include "commonInlining.h"

namespace P4 {

struct ActionCallInfo {
const IR::P4Action* caller;
const IR::P4Action* callee;
const IR::MethodCallStatement* call;

ActionCallInfo(const IR::P4Action* caller, const IR::P4Action* callee,
const IR::MethodCallStatement* call) :
caller(caller), callee(callee), call(call)
{ CHECK_NULL(caller); CHECK_NULL(callee); CHECK_NULL(call); }
void dbprint(std::ostream& out) const
{ out << callee << " into " << caller << " at " << call; }
};

struct AInlineWorkList {
// Map caller -> statement -> callee
std::map<const IR::P4Action*,
std::map<const IR::MethodCallStatement*, const IR::P4Action*>> sites;
void add(ActionCallInfo* info) {
CHECK_NULL(info);
sites[info->caller][info->call] = info->callee;
}
void dbprint(std::ostream& out) const;
bool empty() const
{ return sites.empty(); }
};

class ActionsInlineList {
std::vector<ActionCallInfo*> toInline; // initial data
std::vector<ActionCallInfo*> inlineOrder; // sorted in inlining order
public:
void analyze(); // generate the inlining order
AInlineWorkList* next();
void add(ActionCallInfo* aci)
{ toInline.push_back(aci); }

void replace(const IR::P4Action* container, const IR::P4Action* replacement) {
LOG1("Substituting " << container << " with " << replacement);
for (auto e : inlineOrder) {
if (e->callee == container)
e->callee = replacement;
if (e->caller == container)
e->caller = replacement;
}
}
};
typedef SimpleCallInfo<IR::P4Action, IR::MethodCallStatement> ActionCallInfo;
typedef SimpleInlineWorkList<IR::P4Action, IR::MethodCallStatement, ActionCallInfo> AInlineWorkList;
typedef SimpleInlineList<IR::P4Action, ActionCallInfo, AInlineWorkList> ActionsInlineList;

class DiscoverActionsInlining : public Inspector {
ActionsInlineList* toInline; // output
Expand All @@ -84,27 +43,8 @@ class DiscoverActionsInlining : public Inspector {
void postorder(const IR::MethodCallStatement* mcs) override;
};

// Base class for an inliner that processes actions.
class AbstractActionInliner : public Transform {
protected:
ActionsInlineList* list;
AInlineWorkList* toInline;
AbstractActionInliner() : list(nullptr), toInline(nullptr) {}
public:
void prepare(ActionsInlineList* list, AInlineWorkList* toInline) {
CHECK_NULL(list); CHECK_NULL(toInline);
this->list = list;
this->toInline = toInline;
}
Visitor::profile_t init_apply(const IR::Node* node) {
LOG1("AbstractActionInliner " << toInline);
return Transform::init_apply(node);
}
virtual ~AbstractActionInliner() {}
};

// General-purpose actions inliner.
class ActionsInliner : public AbstractActionInliner {
class ActionsInliner : public AbstractInliner<ActionsInlineList, AInlineWorkList> {
P4::ReferenceMap* refMap;
std::map<const IR::MethodCallStatement*, const IR::P4Action*>* replMap;
public:
Expand All @@ -118,17 +58,7 @@ class ActionsInliner : public AbstractActionInliner {
const IR::Node* preorder(IR::MethodCallStatement* statement) override;
};

class InlineActionsDriver : public Transform {
ActionsInlineList* toInline;
AbstractActionInliner* inliner;
public:
InlineActionsDriver(ActionsInlineList* toInline, AbstractActionInliner *inliner) :
toInline(toInline), inliner(inliner)
{ CHECK_NULL(toInline); CHECK_NULL(inliner); setName("InlineActionsDriver"); }
// 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;
};
typedef InlineDriver<ActionsInlineList, AInlineWorkList> InlineActionsDriver;

class InlineActions : public PassManager {
ActionsInlineList actionsToInline;
Expand All @@ -147,7 +77,7 @@ class InlineActions : public PassManager {

namespace P4_14 {

// Special inliner which works directly on P4 v1.0 representation
/// Special inliner which works directly on P4-14 representation
class InlineActions : public Transform {
const IR::V1Program *global;
class SubstActionArgs : public Transform {
Expand Down
Loading

0 comments on commit 3fc1202

Please sign in to comment.