Skip to content

Commit 560683a

Browse files
committed
JSON printing for headers and parsers
Signed-off-by: Pietro Ghiglio <pghiglio@accesssoftek.com>
1 parent 11f5a96 commit 560683a

File tree

3 files changed

+756
-7
lines changed

3 files changed

+756
-7
lines changed

include/p4mlir/Dialect/BMv2IR/BMv2IR_Ops.td

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ def BMv2IR_ParserStateOp : BMv2IR_Op<"state",
106106
}];
107107

108108
let regions = (region
109-
SizedRegion<1>:$parser_ops,
110-
SizedRegion<1>:$transitions,
111-
SizedRegion<1>:$transition_keys);
109+
MaxSizedRegion<1>:$parser_ops,
110+
MaxSizedRegion<1>:$transitions,
111+
MaxSizedRegion<1>:$transition_keys);
112112

113113
let assemblyFormat = [{
114114
$sym_name

lib/Targets/BMv2/Target.cpp

Lines changed: 282 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,295 @@
11
#include "p4mlir/Targets/BMv2/Target.h"
22

3+
#include <string>
4+
#include <vector>
5+
6+
#include "llvm/ADT/APSInt.h"
7+
#include "llvm/ADT/SmallPtrSet.h"
8+
#include "llvm/ADT/SmallVector.h"
9+
#include "llvm/ADT/TypeSwitch.h"
10+
#include "llvm/Support/Casting.h"
11+
#include "llvm/Support/ErrorHandling.h"
12+
#include "llvm/Support/JSON.h"
13+
#include "llvm/Support/LogicalResult.h"
14+
#include "mlir/IR/BuiltinAttributeInterfaces.h"
15+
#include "mlir/IR/BuiltinAttributes.h"
316
#include "mlir/IR/BuiltinOps.h"
417
#include "mlir/Interfaces/DataLayoutInterfaces.h"
18+
#include "mlir/Support/LLVM.h"
519
#include "mlir/Tools/mlir-translate/Translation.h"
620
#include "p4mlir/Common/Registration.h"
721
#include "p4mlir/Dialect/BMv2IR/BMv2IR_Dialect.h"
22+
#include "p4mlir/Dialect/BMv2IR/BMv2IR_Ops.h"
23+
#include "p4mlir/Dialect/P4HIR/P4HIR_Attrs.h"
824

25+
using namespace llvm;
926
using namespace mlir;
1027
using namespace P4::P4MLIR;
1128

12-
mlir::FailureOr<llvm::json::Value> P4::P4MLIR::bmv2irToJson(ModuleOp moduleOp) {
13-
moduleOp->emitError("MLIR to BMv2 NYI");
14-
return failure();
29+
namespace {
30+
constexpr StringRef id_attrname = "bmv2_id";
31+
32+
template <typename OpTy>
33+
void setID(Operation *rootOp) {
34+
int64_t id = 0;
35+
rootOp->walk([&](OpTy op) {
36+
op->setAttr(id_attrname, IntegerAttr::get(op.getContext(), APSInt::get(id)));
37+
id++;
38+
});
39+
}
40+
41+
int64_t getId(Operation *op) {
42+
auto attr = dyn_cast_or_null<IntegerAttr>(op->getAttr(id_attrname));
43+
assert(attr && "getId called on op without id");
44+
return attr.getSInt();
45+
}
46+
47+
void setUniqueIDS(ModuleOp moduleOp) {
48+
setID<BMv2IR::HeaderInstanceOp>(moduleOp);
49+
setID<BMv2IR::ParserOp>(moduleOp);
50+
setID<BMv2IR::ParserStateOp>(moduleOp);
51+
}
52+
53+
json::Value to_JSON(Operation *op);
54+
55+
json::Value to_JSON(BMv2IR::HeaderType headerTy, int64_t id) {
56+
json::Object res;
57+
58+
json::Array fields;
59+
bool hasVarLenField = false;
60+
for (auto &field : headerTy.getFields()) {
61+
json::Array fieldDesc =
62+
llvm::TypeSwitch<Type, json::Array>(field.type)
63+
.Case([&](P4HIR::BitsType bitTy) -> json::Array {
64+
return json::Array{field.name.str(), bitTy.getWidth(), bitTy.isSigned()};
65+
})
66+
.Case([&](P4HIR::VarBitsType varBitTy) -> json::Array {
67+
hasVarLenField = true;
68+
return json::Array{field.name.str(), "*", varBitTy.isSignedInteger()};
69+
});
70+
71+
fields.push_back(std::move(fieldDesc));
72+
}
73+
74+
res["fields"] = std::move(fields);
75+
res["name"] = headerTy.getName();
76+
res["id"] = id;
77+
if (hasVarLenField) res["max_length"] = headerTy.getMaxLength();
78+
79+
return res;
80+
}
81+
82+
json::Value to_JSON(BMv2IR::HeaderInstanceOp headerInstance) {
83+
json::Object res;
84+
auto name = cast<BMv2IR::HeaderType>(headerInstance.getHeaderType()).getName();
85+
res["name"] = headerInstance.getSymName().str();
86+
res["id"] = getId(headerInstance);
87+
res["header_type"] = name.str();
88+
res["metadata"] = headerInstance.getMetadata();
89+
90+
return res;
91+
}
92+
93+
json::Value to_JSON(BMv2IR::TransitionOp transitionOp) {
94+
json::Object res;
95+
auto type = transitionOp.getType();
96+
auto typeStr = BMv2IR::stringifyTransitionKind(type);
97+
res["type"] = typeStr;
98+
auto maybeNextState = transitionOp.getNextState();
99+
if (maybeNextState.has_value()) {
100+
auto nextStateName = maybeNextState.value().getLeafReference().getValue();
101+
res["next_state"] = nextStateName;
102+
} else {
103+
res["next_state"] = json::Value(nullptr);
104+
}
105+
106+
switch (type) {
107+
case BMv2IR::TransitionKind::Hexstr: {
108+
auto valueAttr = cast<P4HIR::IntAttr>(transitionOp.getValueAttr());
109+
// FIXME: print value as hexadecimal
110+
auto value = std::to_string(valueAttr.getValue().getSExtValue());
111+
res["value"] = value;
112+
auto mask = transitionOp.getMask();
113+
if (mask.has_value()) {
114+
auto maskAttr = cast<P4HIR::IntAttr>(mask.value());
115+
auto mask = std::to_string(maskAttr.getValue().getSExtValue());
116+
res["mask"] = mask;
117+
} else {
118+
res["mask"] = json::Value(nullptr);
119+
}
120+
break;
121+
}
122+
case BMv2IR::TransitionKind::Default: {
123+
res["value"] = json::Value(nullptr);
124+
res["mask"] = json::Value(nullptr);
125+
break;
126+
}
127+
case BMv2IR::TransitionKind::Parse_vset: {
128+
llvm_unreachable("JSON translation for parse_vsets NYE");
129+
}
130+
}
131+
return res;
132+
}
133+
134+
json::Value to_JSON(BMv2IR::LookaheadOp lookAheadOp) {
135+
json::Object res;
136+
res["type"] = "lookahead";
137+
json::Array val{lookAheadOp.getBitOffset(), lookAheadOp.getBitwidth()};
138+
res["value"] = std::move(val);
139+
return res;
140+
}
141+
142+
json::Value to_JSON(BMv2IR::FieldOp fieldOp) {
143+
json::Object res;
144+
res["type"] = "field";
145+
res["value"] = json::Array{fieldOp.getHeaderInstance().getLeafReference().getValue(),
146+
fieldOp.getFieldMember().str()};
147+
148+
return res;
149+
}
150+
151+
json::Value to_JSON(BMv2IR::AssignHeaderOp assignOp) {
152+
// TODO: wrap this in a primitive node
153+
json::Object res;
154+
res["op"] = "assign_header";
155+
json::Object srcNode;
156+
srcNode["type"] = "header";
157+
srcNode["value"] = assignOp.getSrc().getLeafReference().getValue();
158+
json::Object dstNode;
159+
dstNode["type"] = "header";
160+
dstNode["value"] = assignOp.getDst().getLeafReference().getValue();
161+
162+
json::Array parameters;
163+
parameters.push_back(std::move(dstNode));
164+
parameters.push_back(std::move(srcNode));
165+
res["parameters"] = std::move(parameters);
166+
return res;
167+
}
168+
169+
json::Value to_JSON(BMv2IR::AssignOp assignOp) {
170+
json::Object res;
171+
res["op"] = "assign";
172+
json::Array params;
173+
params.push_back(to_JSON(assignOp.getDst().getDefiningOp()));
174+
params.push_back(to_JSON(assignOp.getSrc().getDefiningOp()));
175+
res["parameters"] = std::move(params);
176+
return res;
177+
}
178+
179+
json::Value to_JSON(BMv2IR::ExtractOp extractOp) {
180+
json::Object res;
181+
res["op"] = "extract";
182+
// TODO: add support for non-regular extracts
183+
json::Array parameters;
184+
auto type = extractOp.getExtractType();
185+
if (type == BMv2IR::ExtractKind::Regular) {
186+
json::Object desc;
187+
desc["type"] = "regular";
188+
desc["value"] = extractOp.getHeaderInstance().getLeafReference().getValue();
189+
parameters.push_back(std::move(desc));
190+
} else {
191+
llvm_unreachable("Non-regular extracts not yet supported");
192+
}
193+
res["parameters"] = std::move(parameters);
194+
return res;
195+
}
196+
197+
json::Value to_JSON(BMv2IR::ParserStateOp stateOp) {
198+
json::Object res;
199+
res["name"] = stateOp.getSymName();
200+
res["id"] = getId(stateOp);
201+
202+
json::Array transitions;
203+
if (!stateOp.getTransitions().empty()) {
204+
for (auto &op : stateOp.getTransitions().front()) {
205+
transitions.push_back(to_JSON(cast<BMv2IR::TransitionOp>(&op)));
206+
}
207+
}
208+
res["transitions"] = std::move(transitions);
209+
210+
json::Array keys;
211+
if (!stateOp.getTransitionKeys().empty()) {
212+
for (auto &op : stateOp.getTransitionKeys().front()) {
213+
keys.push_back(to_JSON(&op));
214+
}
215+
}
216+
res["transition_key"] = std::move(keys);
217+
218+
json::Array ops;
219+
if (!stateOp.getParserOps().empty()) {
220+
for (auto &op : stateOp.getParserOps().front()) {
221+
if (!isa<BMv2IR::FieldOp>(op)) ops.push_back(to_JSON(&op));
222+
}
223+
}
224+
res["parser_ops"] = std::move(ops);
225+
226+
return res;
227+
}
228+
229+
json::Value to_JSON(BMv2IR::ParserOp parserOp) {
230+
json::Object res;
231+
res["name"] = parserOp.getSymName();
232+
res["init_state"] = parserOp.getInitState().getLeafReference().getValue();
233+
res["id"] = getId(parserOp);
234+
235+
json::Array states;
236+
parserOp.walk([&states](BMv2IR::ParserStateOp stateOp) { states.push_back(to_JSON(stateOp)); });
237+
238+
res["parse_states"] = std::move(states);
239+
240+
return res;
241+
}
242+
243+
json::Value to_JSON(Operation *op) {
244+
return llvm::TypeSwitch<Operation *, json::Value>(op)
245+
.Case([](BMv2IR::AssignHeaderOp assignOp) { return to_JSON(assignOp); })
246+
.Case([](BMv2IR::LookaheadOp lookAheadOp) { return to_JSON(lookAheadOp); })
247+
.Case([](BMv2IR::AssignOp assignOp) { return to_JSON(assignOp); })
248+
.Case([](BMv2IR::ExtractOp extractOp) { return to_JSON(extractOp); })
249+
.Case([](BMv2IR::FieldOp fieldOp) { return to_JSON(fieldOp); })
250+
.Default([](Operation *op) -> json::Value {
251+
llvm::errs() << "Unsupported op: " << op->getName().getIdentifier() << "\n";
252+
llvm_unreachable("Unsupported op");
253+
});
254+
}
255+
} // anonymous namespace
256+
257+
mlir::FailureOr<json::Value> P4::P4MLIR::bmv2irToJson(ModuleOp moduleOp) {
258+
// P4HIR/BMv2IR use Symbols/Values to reference other IR constructs, while some
259+
// BMv2 nodes require unique IDs. We add IDs as attributes right before converting
260+
// to ensure that they are unique and to avoid tracking them while manipulating IR earlier
261+
// in the pipeline.
262+
setUniqueIDS(moduleOp);
263+
json::Object root;
264+
265+
// Emit header types and header instances
266+
SmallVector<BMv2IR::HeaderInstanceOp> headerInstances;
267+
moduleOp.walk([&](BMv2IR::HeaderInstanceOp instance) { headerInstances.push_back(instance); });
268+
llvm::SetVector<Type> headersTy;
269+
json::Array headerTyNodes;
270+
json::Array headerInstanceNodes;
271+
int64_t headerTypeID = 0;
272+
for (auto instance : headerInstances) {
273+
auto headerTy = cast<BMv2IR::HeaderType>(instance.getHeaderType());
274+
if (!headerTy) return instance.emitError("Unexpected type");
275+
bool inserted = headersTy.insert(headerTy);
276+
if (inserted) {
277+
headerTyNodes.push_back(to_JSON(headerTy, headerTypeID));
278+
headerTypeID++;
279+
}
280+
headerInstanceNodes.push_back(to_JSON(instance));
281+
}
282+
root["header_types"] = std::move(headerTyNodes);
283+
root["headers"] = std::move(headerInstanceNodes);
284+
285+
// Emit parsers
286+
json::Array parsers;
287+
moduleOp.walk([&parsers](BMv2IR::ParserOp parserOp) { parsers.push_back(to_JSON(parserOp)); });
288+
root["parsers"] = std::move(parsers);
289+
290+
json::Value res(std::move(root));
291+
292+
return res;
15293
}
16294

17295
void P4::P4MLIR::registerToBMv2JSONTranslation() {
@@ -30,6 +308,6 @@ LogicalResult P4::P4MLIR::bmv2irToJson(ModuleOp moduleOp, raw_ostream &output) {
30308
auto maybeJsonModule = bmv2irToJson(moduleOp);
31309
if (failed(maybeJsonModule)) return failure();
32310

33-
output << *maybeJsonModule;
311+
output << llvm::formatv("{0:2}", *maybeJsonModule) << "\n";
34312
return success();
35313
}

0 commit comments

Comments
 (0)