-
Notifications
You must be signed in to change notification settings - Fork 299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FIRRTL] Add OMIR emission pass #2010
Conversation
5fef5d2
to
ac0ea9e
Compare
545b124
to
45e2c83
Compare
I think this is ready for a first round of reviews. The At this point it uses symbols to interpolate module names in the final JSON, but hardcodes wire/reg/regreset/inst names until #2015 lands. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems fine. I'll let @seldridge comment on any OMIR specifics.
reading the OMIR, and serializes the resulting data into a JSON file. | ||
}]; | ||
let constructor = "circt::firrtl::createEmitOMIRPass()"; | ||
let options = [Option<"outputFilename", "file", "std::string", "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pass only makes sense with an output file, yes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now there's still a OMIRFileAnnotation
, which we probably want to get rid of in favor of command line args.
orderedFields.push_back({index, nameAndField.first, fieldDict}); | ||
} | ||
llvm::sort(orderedFields, | ||
[](auto a, auto b) { return std::get<0>(a) < std::get<0>(b); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::less would work too (though a bit more constrained)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was my first reflex as well, but gcc complained about not being able to compare Identifier
and DictionaryAttr
properly.
tools/firtool/firtool.cpp
Outdated
@@ -111,6 +111,10 @@ static cl::opt<bool> | |||
cl::desc("emit metadata for metadata annotations"), | |||
cl::init(true)); | |||
|
|||
static cl::opt<bool> | |||
emitOMIR("emit-omir", cl::desc("emit OMIR to JSON metadata annotations"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bike shedding: maybe reserve "emit" for things which produce files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does, like emit-metadata
. I've messed up the cl::desc
here which makes it sound like it's producing yet another annotation, when in fact it's producing an sv.verbatim
blob that goes into a JSON file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One approach would be to always turn this on if the user passes in an OMIR file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's now on-by-default, and if there's no OMIRAnnotation
, nothing happens. I think that should cover what you have in mind?
firrtl.nla @nla_0 [@NonLocalTrackers, @B, @A] ["b", "a", "A"] | ||
firrtl.module @A() attributes {annotations = [{circt.nonlocal = @nla_0, class = "freechips.rocketchip.objectmodel.OMIRTracker", id = 0}]} {} | ||
firrtl.module @B() { | ||
firrtl.instance a {annotations = [{circt.nonlocal = @nla_0, class = "circt.nonlocal"}]} @A() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would this need a don't touch in the output so that it keeps the name "a"? There may be some basic sv.verbatim lowering that should happen in lowertohw so that nlas can be converted to symbols in a general way and sv.verbatim nodes earlier can pass an nla in to name something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't want to break it, then it would need the weaker don't touch variant of "keep me around, but don't block constant propagation". This doesn't exist yet, though. 😢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question about a
and b
below. Is the intent that this will be fixed once Prithayan's stuff lands?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah good point. There probably have to be a bunch of "don't touch"-annos coming out of EmitOMIR
. Ideally they would only be "don't inline" since renaming of the instances is actually okay (as long as the NLAs are in place).
Even "more ideally", you would also be able to change the hierarchy around. But that would require us to have a way of controlling how NLAs get interpolated into sv.verbatim
. Because for an SV hierarchical name you might want $root.a.b.c
, but for an OMIR output you'd want a Chisel-style ~Circuit|Module/a:Foo/b:Bar/c:Baz
.
Maybe something like a dedicated NLA-as-XMR formatting op could work?
%0 = sv.nla_path @nla_0 {format = "sv"} : !sv.path
%1 = sv.nla_path @nla_0 {format = "chisel"} : !sv.path
sv.verbatim "sv {{0}}; chisel {{1}}"(%0, %1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@seldridge My current plan is to create an NLA for every element in the path, and then use the work @prithayan is doing to assemble the full path. That should at least allow for renaming of a
and b
.
45e2c83
to
60b318d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks awesome, Fabian. Thank you!
Nitty comments throughout. I did not dig into the logic serializing the OMIR too deeply, but this is relatively straightforward. I am annoyed that we have all this duplicated logic for verifying the OMIR during parsing and then here. Not sure what to do about that, though, other than eventually moving to actual custom attributes.
fields.append("omir.tracker", UnitAttr::get(ctx)); | ||
fields.append("type", StringAttr::get(ctx, tpe)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is adding this singleton strictly necessary?
Note: I like omir.tracker
better than the "freechips.rocketchip.objectmodel.OMIRTracker"
which I made up for the scattered targets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that when you see {type = "asdf", id = 42}
in the value
field (or nested within it), it's not clear if this is a OMMap
with fields "type" and "id", or if this used to be a OM.+Target
. Adding the omir.tracker
unit is an unambiguous marker that the dictionary is actually a OM.+Target
, because there's nothing in the original OMIR input JSON that can generate such a unit attr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, for the OMIRTracker
annotations it's important that we still have a "class" field, since that's what we implicitly require for the annotations
array on operations.
test/Dialect/FIRRTL/emit-omir.mlir
Outdated
// CHECK-SAME: \22name\22:\22OMBoolean\22,\22value\22:true | ||
// CHECK-SAME: \22name\22:\22OMInt1\22,\22value\22:9001 | ||
// CHECK-SAME: \22name\22:\22OMInt2\22,\22value\22:-42 | ||
// CHECK-SAME: \22name\22:\22OMDouble\22,\22value\22:3.14 | ||
// CHECK-SAME: \22name\22:\22OMID\22,\22value\22:\22OMID:1337\22 | ||
// CHECK-SAME: \22name\22:\22OMReference\22,\22value\22:\22OMReference:0\22 | ||
// CHECK-SAME: \22name\22:\22OMBigInt\22,\22value\22:\22OMBigInt:42\22 | ||
// CHECK-SAME: \22name\22:\22OMLong\22,\22value\22:\22OMLong:ff\22 | ||
// CHECK-SAME: \22name\22:\22OMString\22,\22value\22:\22OMString:hello\22 | ||
// CHECK-SAME: \22name\22:\22OMBigDecimal\22,\22value\22:\22OMBigDecimal:10.5\22 | ||
// CHECK-SAME: \22name\22:\22OMDeleted\22,\22value\22:\22OMDeleted:\22 | ||
// CHECK-SAME: \22name\22:\22OMConstant\22,\22value\22:\22OMConstant:UInt<2>(\\\22h1\\\22)\22 | ||
// CHECK-SAME: \22name\22:\22OMArray\22,\22value\22:[true,9001,\22OMString:bar\22] | ||
// CHECK-SAME: \22name\22:\22OMMap\22,\22value\22:{\22bar\22:9001,\22foo\22:true} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's annoying that we get all these \22
codes instead of being able to look at quotes here. One idea would be to pipe the output through sed
or something (though that introduces a dependency on sed
being installed) to make the test more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this is very painful to look at 😢. It's much better in the integration test, because that can look at the ExportVerilog
result, but we'll want some form of single-pass unit test around as well.
firrtl.nla @nla_0 [@NonLocalTrackers, @B, @A] ["b", "a", "A"] | ||
firrtl.module @A() attributes {annotations = [{circt.nonlocal = @nla_0, class = "freechips.rocketchip.objectmodel.OMIRTracker", id = 0}]} {} | ||
firrtl.module @B() { | ||
firrtl.instance a {annotations = [{circt.nonlocal = @nla_0, class = "circt.nonlocal"}]} @A() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't want to break it, then it would need the weaker don't touch variant of "keep me around, but don't block constant propagation". This doesn't exist yet, though. 😢
test/Dialect/FIRRTL/emit-omir.mlir
Outdated
// CHECK-LABEL: firrtl.circuit "NonLocalTrackers" { | ||
// CHECK: sv.verbatim | ||
// CHECK-SAME: \22name\22:\22OMReferenceTarget1\22,\22value\22:\22OMReferenceTarget:~NonLocalTrackers|{{[{][{]0[}][}]}}/b:{{[{][{]1[}][}]}}/a:{{[{][{]2[}][}]}}\22 | ||
// CHECK-SAME: #hw.output_file<"omir.json", excludeFromFileList> | ||
// CHECK-SAME: symbols = [@NonLocalTrackers, @B, @A] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test output here looks great!
firrtl.nla @nla_0 [@NonLocalTrackers, @B, @A] ["b", "a", "A"] | ||
firrtl.module @A() attributes {annotations = [{circt.nonlocal = @nla_0, class = "freechips.rocketchip.objectmodel.OMIRTracker", id = 0}]} {} | ||
firrtl.module @B() { | ||
firrtl.instance a {annotations = [{circt.nonlocal = @nla_0, class = "circt.nonlocal"}]} @A() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question about a
and b
below. Is the intent that this will be fixed once Prithayan's stuff lands?
return; | ||
} | ||
|
||
// Extract and order the fields of this node. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch about remembering to order this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would have been a shame if your index
field went to waste 😄
jsonStream.attributeBegin("value"); | ||
emitValue(field.get("value"), jsonStream); | ||
jsonStream.attributeEnd(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hadn't noticed these attributeBegin
/attributeEnd
member functions. Nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the JSON API is very pleasant to work with, I'm genuinely impressed 😄
60b318d
to
bbc3de6
Compare
Add a pass to the FIRRTL dialect that consumes `OMIRAnnotation`s on the circuit, gathers the tracker nodes back up, and serializes the data back into a JSON blob.
bbc3de6
to
d1069c8
Compare
Thanks for the reviews @darthscsi and @seldridge. Going to merge this now to unblock follow-up work. |
Add a pass to the FIRRTL dialect that consumes
OMIRAnnotation
s on the circuit, gathers the tracker nodes back up, and serializes the data back into a JSON blob.Todo
Replace wire/reg/inst names with NLA-based symbols ([LowertoHW] Lower NonLocalAnchor symbols with the VerbatimOp #2015)(will be done in a follow-up PR)