Skip to content

Commit 4ce2320

Browse files
xl4624asl
andauthored
Add SimplifyParsers pass (#166)
This pass removes parser states that are unreachable from the 'start' state and collapses linear chains of states into a single state. Linear chains are any two states where s1 has exactly one outgoing edge to s2 and s2 has exactly one incoming edge from s1 with no annotations in any state in the chain. Example: Before: start --> s0 ---> s1 ---> s3 ---> s4 ---> accept | ^ v | s2--------------+ After: start ---> s1 ---> s3 (with accept terminator) | ^ v | s2--------------+ where: start = start + s0 s3 = s3 + s4 + accept Implements #98 --------- Signed-off-by: Xiaomin Liu <xl4624@nyu.edu> Signed-off-by: Anton Korobeynikov <anton@korobeynikov.info> Co-authored-by: Anton Korobeynikov <anton@korobeynikov.info>
1 parent e4770ca commit 4ce2320

File tree

9 files changed

+428
-8
lines changed

9 files changed

+428
-8
lines changed

include/p4mlir/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
add_subdirectory(Dialect)
2-
add_subdirectory(Transforms)
2+
add_subdirectory(Transforms)

include/p4mlir/Dialect/P4HIR/P4HIR_ParserOps.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
def ParserOp : P4HIR_Op<"parser",
55
[Symbol, SymbolTable,
6-
FunctionOpInterface, AutomaticAllocationScope]> {
6+
FunctionOpInterface, AutomaticAllocationScope,
7+
SingleBlockImplicitTerminator<"ParserTransitionOp">]> {
78
let arguments = (ins SymbolNameAttr:$sym_name,
89
TypeAttrOf<FuncType>:$applyType,
910
TypeAttrOf<CtorType>:$ctorType,

include/p4mlir/Transforms/Passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ namespace P4::P4MLIR {
2727
#include "p4mlir/Transforms/Passes.h.inc"
2828

2929
std::unique_ptr<mlir::Pass> createPrintParsersGraphPass();
30+
std::unique_ptr<mlir::Pass> createSimplifyParsersPass();
3031

3132
#define GEN_PASS_REGISTRATION
3233
#include "p4mlir/Transforms/Passes.h.inc"

include/p4mlir/Transforms/Passes.td

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,40 @@ def PrintParsersGraph : Pass<"p4hir-print-parsers-graph"> {
1616
let dependentDialects = ["P4MLIR::P4HIR::P4HIRDialect"];
1717
}
1818

19+
//===----------------------------------------------------------------------===//
20+
// SimplifyParsers
21+
//===----------------------------------------------------------------------===//
22+
23+
def SimplifyParsers : Pass<"p4hir-simplify-parsers"> {
24+
let summary = "Simplifies parser control flow";
25+
let description = [{
26+
This pass removes parser states that are unreachable from the 'start' state
27+
and collapses linear chains of states into a single state.
28+
29+
Linear chains are any two states where s1 has exactly one outgoing edge
30+
to s2 and s2 has exactly one incoming edge from s1 with no annotations
31+
in any state in the chain.
32+
33+
Example:
34+
35+
Before:
36+
start --> s0 ---> s1 ---> s3 ---> s4 ---> accept
37+
| ^
38+
v |
39+
s2--------------+
40+
41+
After:
42+
start ---> s1 ---> s3 (with accept terminator)
43+
| ^
44+
v |
45+
s2--------------+
46+
47+
where:
48+
start = start + s0
49+
s3 = s3 + s4 + accept
50+
}];
51+
let constructor = "P4MLIR::createSimplifyParsersPass()";
52+
let dependentDialects = ["P4MLIR::P4HIR::P4HIRDialect"];
53+
}
54+
1955
#endif // P4MLIR_TRANSFORMS_PASSES_TD

lib/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
add_subdirectory(Dialect)
2-
add_subdirectory(Transforms)
2+
add_subdirectory(Transforms)

lib/Dialect/P4HIR/P4HIR_Ops.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,8 +1542,10 @@ LogicalResult P4HIR::AssignSliceOp::verify() {
15421542

15431543
// Parser states use fully-qualified names so we lookup from the top-level moduleOp
15441544
static P4HIR::ParserStateOp lookupParserState(Operation *op, mlir::SymbolRefAttr stateName) {
1545-
return mlir::SymbolTable::lookupNearestSymbolFrom<P4HIR::ParserStateOp>(getParentModule(op),
1546-
stateName);
1545+
auto res = mlir::SymbolTable::lookupNearestSymbolFrom<P4HIR::ParserStateOp>(getParentModule(op),
1546+
stateName);
1547+
assert(res && "expected valid parser state lookup");
1548+
return res;
15471549
}
15481550

15491551
void P4HIR::ParserOp::build(mlir::OpBuilder &builder, mlir::OperationState &result,
@@ -1919,7 +1921,7 @@ LogicalResult P4HIR::RangeOp::verify() {
19191921

19201922
// However, ranges can also be used as collections in ForInOp, which means
19211923
// their results can only be used once and their user must be a ForInOp.
1922-
mlir::Value result = getOperation()->getResult(0);
1924+
mlir::Value result = getResult();
19231925
if (!result.hasOneUse()) {
19241926
return emitOpError("when not nested in p4hir.select_case, ")
19251927
<< "expected single use by p4hir.foreach but found "
@@ -2829,13 +2831,13 @@ llvm::SmallVector<Region *> P4HIR::ForInOp::getLoopRegions() { return {&getBodyR
28292831
// ConditionOp
28302832
//===----------------------------------------------------------------------===//
28312833

2832-
MutableOperandRange P4HIR::ConditionOp::getMutableSuccessorOperands(RegionBranchPoint point) {
2834+
mlir::MutableOperandRange P4HIR::ConditionOp::getMutableSuccessorOperands(RegionBranchPoint point) {
28332835
auto parent = mlir::cast<P4HIR::ForOp>(getOperation()->getParentOp());
28342836
assert((point.isParent() || point.getRegionOrNull() == &parent.getBodyRegion()) &&
28352837
"condition op can only exit the loop or branch to the body region");
28362838

28372839
// No values are yielded to the successor region
2838-
return MutableOperandRange(getOperation(), 0, 0);
2840+
return mlir::MutableOperandRange(getOperation(), 0, 0);
28392841
}
28402842

28412843
//===----------------------------------------------------------------------===//

lib/Transforms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_mlir_dialect_library(P4MLIRTransforms
22
PrintParsersGraph.cpp
3+
SimplifyParsers.cpp
34

45
ADDITIONAL_HEADER_DIRS
56
${PROJECT_SOURCE_DIR}/include/p4mlir/Transforms

lib/Transforms/SimplifyParsers.cpp

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#include <llvm/Support/ErrorHandling.h>
2+
3+
#include "llvm/ADT/DepthFirstIterator.h"
4+
#include "llvm/ADT/STLExtras.h"
5+
#include "llvm/ADT/TypeSwitch.h"
6+
#include "p4mlir/Dialect/P4HIR/ParserGraph.h"
7+
#include "p4mlir/Transforms/Passes.h"
8+
9+
#define DEBUG_TYPE "p4hir-simplify-parsers"
10+
11+
using namespace mlir;
12+
13+
namespace P4::P4MLIR {
14+
#define GEN_PASS_DEF_SIMPLIFYPARSERS
15+
#include "p4mlir/Transforms/Passes.cpp.inc"
16+
17+
namespace {
18+
struct SimplifyParsers : public impl::SimplifyParsersBase<SimplifyParsers> {
19+
void runOnOperation() override;
20+
21+
private:
22+
/// Collapses linear sequences of states without branches or annotations.
23+
void collapseChains(P4HIR::ParserOp parser);
24+
};
25+
} // end anonymous namespace
26+
27+
void SimplifyParsers::collapseChains(P4HIR::ParserOp parser) {
28+
// TODO: Revisit this to use ParserCallGraph instead
29+
mlir::DenseMap<P4HIR::ParserStateOp, unsigned> indegree;
30+
// Initialize indegree[start] to 1 to account for the implicit parser entry edge.
31+
indegree[parser.getStartState()] = 1;
32+
for (auto state : parser.states()) {
33+
for (auto next : state.getNextStates()) {
34+
++indegree[next];
35+
}
36+
}
37+
38+
// succ[s1] = s2 if there is exactly one outgoing edge from s1 to s2
39+
// and s2 has exactly one incoming edge from s1.
40+
mlir::DenseMap<P4HIR::ParserStateOp, P4HIR::ParserStateOp> succ;
41+
mlir::DenseMap<P4HIR::ParserStateOp, P4HIR::ParserStateOp> pred;
42+
43+
// We diconnect any annotated states since they can't be collapsed
44+
// and if we kept them they'd poison downstream merging.
45+
for (auto state : parser.states()) {
46+
if (!llvm::hasNItems(state.getNextStates(), 1)) continue;
47+
// Name annotations are special, if they are the only annotation
48+
// then they can be merged into as heads.
49+
auto ann = state.getAnnotations();
50+
if (ann && !(ann->size() == 1 && ann->contains("name"))) continue;
51+
P4HIR::ParserStateOp successor = *state.getNextStates().begin();
52+
if (indegree[successor] != 1 || successor.getAnnotations()) continue;
53+
54+
succ[state] = successor;
55+
pred[successor] = state;
56+
}
57+
58+
// Process each chain head, collapsing states whenever possible.
59+
for (auto [head, _] : succ) {
60+
if (pred.contains(head)) continue;
61+
LLVM_DEBUG(llvm::dbgs() << "Chaining states into '" << head.getName() << "'\n");
62+
63+
// Walk forward through the chain using successor map
64+
for (auto it = succ.find(head); it != succ.end(); it = succ.find(it->second)) {
65+
P4HIR::ParserStateOp next = it->second;
66+
LLVM_DEBUG(llvm::dbgs() << "\tAdding '" << next.getName() << "' to chain\n");
67+
auto &headOps = head.getBody().front().getOperations();
68+
69+
// Remove the terminator (transition to 'next') from the current head body.
70+
head.getNextTransition()->erase();
71+
72+
// Splice all operations from 'next' into the head body.
73+
headOps.splice(headOps.end(), next.getBody().front().getOperations());
74+
75+
// Remove the now-empty 'next' state.
76+
next.erase();
77+
}
78+
}
79+
}
80+
81+
void SimplifyParsers::runOnOperation() {
82+
getOperation()->walk([&](P4HIR::ParserOp parser) {
83+
LLVM_DEBUG(llvm::dbgs() << "\n--- Simplifying parser '" << parser.getName() << "' ---\n");
84+
llvm::df_iterator_default_set<llvm::GraphTraits<P4HIR::ParserOp>::NodeRef> reachable;
85+
bool acceptReachable = false, rejectReachable = false;
86+
87+
/// Finds all states reachable from the start state and deletes unreachable states.
88+
for (auto stateOp : llvm::depth_first_ext(parser, reachable)) {
89+
auto state = mlir::cast<P4HIR::ParserStateOp>(stateOp);
90+
LLVM_DEBUG(llvm::dbgs() << "DFS visiting " << state.getName() << "\n");
91+
if (state.isAccept()) acceptReachable = true;
92+
if (state.isReject()) rejectReachable = true;
93+
}
94+
if (!acceptReachable && !rejectReachable)
95+
parser.emitError("Parser never reaches the 'accept' or 'reject' state.");
96+
LLVM_DEBUG(llvm::dbgs() << "Parser '" << parser.getName() << "' has " << reachable.size()
97+
<< " reachable states\n");
98+
99+
for (auto state : llvm::make_early_inc_range(parser.states())) {
100+
if (!reachable.contains(state)) {
101+
if (state.isAccept()) {
102+
parser.emitWarning()
103+
<< "Parser has unreachable accept state " << state.getName();
104+
} else {
105+
LLVM_DEBUG(llvm::dbgs()
106+
<< "Removing unreachable state '" << state.getName() << "'\n");
107+
state.erase();
108+
}
109+
}
110+
}
111+
112+
collapseChains(parser);
113+
114+
return WalkResult::advance();
115+
});
116+
}
117+
118+
std::unique_ptr<Pass> createSimplifyParsersPass() { return std::make_unique<SimplifyParsers>(); }
119+
} // namespace P4::P4MLIR

0 commit comments

Comments
 (0)