Skip to content
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] Output directory control for layers and modules #6971

Open
wants to merge 30 commits into
base: main
Choose a base branch
from

Conversation

rwy7
Copy link
Contributor

@rwy7 rwy7 commented Apr 30, 2024

Add output directory control for layers and firrtl.

For specifying the output directories:

  • Add a new annotation called "circt.OutputDirAnnotation", for specifying the output directory of modules
  • For layers, add syntax for optionally specifying the output directory

In the Lower-Layers Pass:

  • Place layer collateral (the bindfile, any layerblocks->modules) under the output directory of the layer, if there is any.
  • Stop outputting layer collateral under the testbench/views

Add a new pass, AssignOutputDirs, which will sink modules into the output directories they are instantiated from. This pass runs after lower-layers. In conjunction with the changes to the lower-layers pass, this means that modules which are only used under a particular layer will be sunk into that layer's output directory.

@uenoku
Copy link
Member

uenoku commented May 1, 2024

What is an expected behavior when modules with different output dir annotations are deduplicated?

@rwy7 rwy7 marked this pull request as ready for review May 6, 2024 18:53
@rwy7
Copy link
Contributor Author

rwy7 commented May 6, 2024

What is an expected behavior when modules with different output dir annotations are deduplicated?

Hey, thanks for taking a look! Two modules will dedup iff they have the same output directory annotation (modulo dirname canonicalization when constructing the the hw::OutputFileAttr). I've added a test for this here.

}
```

### DeclareOutputDirAnnotation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider renaming this -- this is not for declaring anything, it's for specifying the parent.

Besides clarity, specifically since you /can't/ emit one of these for all directories (those without a parent?), a different name might be a good idea.

Copy link
Contributor Author

@rwy7 rwy7 May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like the name! Please, suggest something :)

OutputDirPrecedenceAnnotation?

Although just as a note, you actually can declare a directory whose parent is "no output directory", by using an empty string as the parent name. It's just that there is no need.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interpreting empty string as default output directory (and a valid entry) is unexpected to me (if this is important, consider mentioning it? Maybe omit the parent entry instead? 🤷 )! But that makes sense.

Names ARE hard, haha, I'll circle back 👍.

Your suggestion is an improvement IMO 😄 .

Copy link
Member

@seldridge seldridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LCA algorithm comes across as more complicated than I think it should be. It may be easier to adopt a naive prefix LCA (as that is very understandable) or to go the direction of naive Euler Tour + RMQ (which should be recognizable and has room for performance improvements later if needed). This does appear to be doing a lot of LCA queries hence the RMQ formulation may make sense

Comments throughout.

docs/Dialects/FIRRTL/FIRRTLAnnotations.md Outdated Show resolved Hide resolved
docs/Dialects/FIRRTL/FIRRTLAnnotations.md Outdated Show resolved Hide resolved
"name": "verification_extras",
"parent": "verification"
}
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this include documentation of how to specify that the parent is the root? Is this an empty string, optional JSON field, or null?

include/circt/Dialect/FIRRTL/Passes.td Outdated Show resolved Hide resolved
include/circt/Dialect/FIRRTL/Passes.td Outdated Show resolved Hide resolved
Comment on lines +809 to +819
hw::OutputFileAttr bindFile;
if (auto outputFile =
layerOp->getAttrOfType<hw::OutputFileAttr>("output_file")) {
auto dir = outputFile.getDirectoryAttr().getValue();
bindFile = hw::OutputFileAttr::getFromDirectoryAndFilename(
&getContext(), dir, prefix + ".sv",
/*excludeFromFileList=*/true);
} else {
bindFile = hw::OutputFileAttr::getFromFilename(
&getContext(), prefix + ".sv", /*excludeFromFileList=*/true);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having an empty auto dir might make this cleaner as both code paths can use getFromDirectoryAndFilename.

rawAnnotations = [
{
class = "circt.OutputDirAnnotation",
dirname = "foobarbaz",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two more cases may be interesting to test:

  1. foobarbaz/
  2. foobarbaz/qux

]
} {

firrtl.module private @AssignOutputDirs() {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised CIRCT accepts this as I thought the main module had to be public.

Comment on lines 78 to 89
// when a module with an output directory is used, that
// directory is respected

// when a module doesnt have an output directory, it's dragged

// when a module is used in both a child and parent, it is dragged

// when a module is used by two children, it is dragged to parent.

// circular parent/child relationship
// parent is null / root
// child name is empty???
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: strip comments and add file trailing newline.

@@ -0,0 +1,179 @@
; RUN: firtool --split-input-file %s | FileCheck %s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test would be tighter as MLIR and running only on dedup. This allows for violations of initialization checking, i.e., the test can have modules that are basically empty. Alternatively, firtool Foo.fir -parse-only | circt-opt -firrtl-dedup gives you FIRRTL syntax for the test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea is this is testing that firtool -- whatever its pipeline is -- has the expected (or at least consistent, as it's under test) behavior from a user's experience regarding interaction of dedup + output_file (in terms exposed to user, not when/how/where we set what attributes internally).

For example, as-is, moving the output-dir-assignment pass before dedup -- while not changing dedup's behavior -- would cause test failures (that a test narrowed to dedup would not catch). Specifically regarding the last test in this file (and lower-layers.fir test too, apparently).

And is this indeed the intended behavior / semantics for "output dir" information end-to-end?

Would be good to pin down what is really expected/promised by using these knobs/annotations -- to inform the behavior of dedup or anything else that alters instance hierarchy in a way that changes what-goes-where (inlining, for example -- what if you inline a module with an output_file attribute on it (especially modules that it was the only parent of)?).

How'd y'all frame the intent here -- best-effort output directives, providing users ability to get their desired output even if not specifically promised in a particular way re:pipeline happenstance (but they could use more annotations / etc/ to change what happens if they don't like it?).

Comment on lines 418 to 421
if (auto moduleOp = dyn_cast<FModuleOp>(op)) {
moduleOp->setAttr("output_file", outputFile);
return success();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This silently drops attributes if (mistakenly) multiple output dir annotations are attached to the module. Can we check and emit an error?

rwy7 added 18 commits May 22, 2024 14:09
This helper gets the directory component of an output file name, or returns
nullptr if there is none.
This pass "drags" modules into directories where they are instantiated.
Layers can't be targeted by annotations, so we add this as an attribute to
layers directly, and parse the output directory directly too.
Use the output directory of a layer as the output directory for it's assets,
including:

- extracted layerblock modules
- the bindfile

The assign-output-dirs pass will sink additional modules into layer-specific
output directories, if possible.
Had some bad copy-pasting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants