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 ;
926using namespace mlir ;
1027using 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
17295void 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