[HW] Add an attribute to make file lists (#1986)

Add an attribute to indicate the file containing the op should be put in a specific filelist at emission time.  This attribute allows files to exist in multiple filelists.
This commit is contained in:
Andrew Lenharth 2021-10-13 05:48:17 -07:00 committed by GitHub
parent 33e1eb393a
commit bcfaf7843e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 131 additions and 12 deletions

View File

@ -81,6 +81,44 @@ def OutputFileAttr : AttrDef<HWDialect, "OutputFile"> {
}];
}
// An attribute to indicate which filelist an operation's file should be
// included in.
def FileListAttr : AttrDef<HWDialect, "FileList"> {
let summary = "Ouput filelist attribute";
let description = [{
This attribute represents an output filelist for something which will be
printed. The `filename` string is the file which the filename of the
operation to be output to.
When ExportVerilog runs, some of the files produced are lists of other files
which are produced. Each filelist exported contains entities' output file
with `FileListAttr` marked.
Examples:
```mlir
#hw.ouput_filelist<"/home/tester/t.F">
#hw.ouput_filelist<"t.f">
```
}];
let mnemonic = "output_filelist";
let parameters = (ins "::mlir::StringAttr":$filename);
let builders = [
AttrBuilderWithInferredContext<(ins
"::mlir::StringAttr":$filename), [{
return get(filename.getContext(), filename);
}]>,
];
let extraClassDeclaration = [{
/// Get an OutputFileAttr from a string filename, canonicalizing the
/// filename.
static FileListAttr getFromFilename(::mlir::MLIRContext *context,
const ::mlir::Twine &filename);
}];
}
/// An attribute describing a module parameter, or instance parameter
/// specification.
def ParamDeclAttr : AttrDef<HWDialect, "ParamDecl"> {

View File

@ -3655,6 +3655,9 @@ struct SharedEmitterState {
/// the map.
llvm::MapVector<Identifier, FileInfo> files;
/// The various file lists and their contents to emit
llvm::StringMap<SmallVector<Identifier>> fileLists;
/// A list of operations replicated in each output file (e.g., `sv.verbatim`
/// or `sv.ifdef` without dedicated output file).
SmallVector<Operation *, 0> replicatedOps;
@ -3733,6 +3736,14 @@ void SharedEmitterState::gatherFiles(bool separateModules) {
addToFilelist = !attr.getExcludeFromFilelist().getValue();
}
// Collect extra file lists to output the file to.
SmallVector<StringAttr> opFileList;
if (auto fl = op.getAttrOfType<hw::FileListAttr>("output_filelist"))
opFileList.push_back(fl.getFilename());
if (auto fla = op.getAttrOfType<ArrayAttr>("output_filelist"))
for (auto fl : fla)
opFileList.push_back(fl.cast<hw::FileListAttr>().getFilename());
auto separateFile = [&](Operation *op, Twine defaultFileName = "") {
// If we're emitting to a separate file and the output_file attribute
// didn't specify a filename, take the default one if present or emit an
@ -3747,10 +3758,13 @@ void SharedEmitterState::gatherFiles(bool separateModules) {
}
}
auto &file = files[Identifier::get(outputPath, op->getContext())];
auto destFile = Identifier::get(outputPath, op->getContext());
auto &file = files[destFile];
file.ops.push_back(info);
file.emitReplicatedOps = emitReplicatedOps;
file.addToFilelist = addToFilelist;
for (auto fl : opFileList)
fileLists[fl.getValue()].push_back(destFile);
};
// Separate the operation into dedicated output file, or emit into the
@ -3960,6 +3974,15 @@ LogicalResult circt::exportVerilog(ModuleOp module, llvm::raw_ostream &os) {
emitter.collectOpsForFile(it.second, list);
}
// Emit the filelists.
for (auto &it : emitter.fileLists) {
std::string contents("\n// ----- 8< ----- FILE \"" + it.first().str() +
"\" ----- 8< -----\n\n");
for (auto &name : it.second)
contents += name.str() + "\n";
list.emplace_back(contents);
}
// Finally, emit all the ops we collected.
emitter.emitOps(list, os, /*parallelize=*/true);
return failure(emitter.encounteredError);
@ -3997,32 +4020,40 @@ std::unique_ptr<mlir::Pass> circt::createExportVerilogPass() {
// Split Emitter
//===----------------------------------------------------------------------===//
static void createSplitOutputFile(Identifier fileName, FileInfo &file,
StringRef dirname,
SharedEmitterState &emitter) {
static std::unique_ptr<llvm::ToolOutputFile>
createOutputFile(StringRef fileName, StringRef dirname,
SharedEmitterState &emitter) {
// Determine the output path from the output directory and filename.
SmallString<128> outputFilename(dirname);
appendPossiblyAbsolutePath(outputFilename, fileName.strref());
appendPossiblyAbsolutePath(outputFilename, fileName);
auto outputDir = llvm::sys::path::parent_path(outputFilename);
// Create the output directory if needed.
std::error_code error = llvm::sys::fs::create_directories(outputDir);
if (error) {
mlir::emitError(file.ops[0].op->getLoc(),
mlir::emitError(emitter.rootOp.getLoc(),
"cannot create output directory \"" + outputDir +
"\": " + error.message());
emitter.encounteredError = true;
return;
return {};
}
// Open the output file.
std::string errorMessage;
auto output = mlir::openOutputFile(outputFilename, &errorMessage);
if (!output) {
llvm::errs() << errorMessage << "\n";
mlir::emitError(emitter.rootOp.getLoc(), errorMessage);
emitter.encounteredError = true;
return;
}
return output;
}
static void createSplitOutputFile(Identifier fileName, FileInfo &file,
StringRef dirname,
SharedEmitterState &emitter) {
auto output = createOutputFile(fileName, dirname, emitter);
if (!output)
return;
SharedEmitterState::EmissionList list;
emitter.collectOpsForFile(file, list);
@ -4067,6 +4098,16 @@ LogicalResult circt::exportSplitVerilog(ModuleOp module, StringRef dirname) {
}
output->keep();
// Emit the filelists.
for (auto &it : emitter.fileLists) {
auto output = createOutputFile(it.first(), dirname, emitter);
if (!output)
continue;
for (auto &name : it.second)
output->os() << name.str() << "\n";
output->keep();
}
return failure(emitter.encounteredError);
}

View File

@ -156,6 +156,31 @@ void OutputFileAttr::print(DialectAsmPrinter &p) const {
p << ">";
}
//===----------------------------------------------------------------------===//
// FileListAttr
//===----------------------------------------------------------------------===//
/// Option ::= 'includeReplicatedOp'
/// OutputFileAttr ::= 'output_file<' name (',' Option)* '>'
Attribute FileListAttr::parse(DialectAsmParser &p, Type type) {
StringAttr filename;
if (p.parseLess() || p.parseAttribute<StringAttr>(filename) ||
p.parseGreater())
return Attribute();
auto *context = p.getContext();
return FileListAttr::get(context, filename);
}
void FileListAttr::print(DialectAsmPrinter &p) const {
p << "output_filelist<" << getFilename() << ">";
}
FileListAttr FileListAttr::getFromFilename(MLIRContext *context,
const Twine &filename) {
auto canonicalized = canonicalizeFilename("", filename);
return FileListAttr::get(StringAttr::get(context, canonicalized));
}
//===----------------------------------------------------------------------===//
// ParamDeclAttr
//===----------------------------------------------------------------------===//

View File

@ -146,3 +146,5 @@ hw.module @signed_arrays(%arg0: si8) -> (out: !hw.array<2xsi8>) {
hw.module @argRenames(%arg1: i32) attributes {argNames = [""]} {
}
hw.module @fileListTest(%arg1: i32) attributes {output_filelist = #hw.output_filelist<"foo.f">} {
}

View File

@ -3,7 +3,8 @@
// CHECK-LABEL: module B
#file1 = #hw.output_file<"dir1/file1.sv", includeReplicatedOps>
hw.module @foo1(%a: i1) -> (b: i1) attributes {output_file = #file1} {
#filelist1 = #hw.output_filelist<"dir5/foo.f">
hw.module @foo1(%a: i1) -> (b: i1) attributes {output_file = #file1, output_filelist = #filelist1} {
hw.output %a : i1
}
// CHECK-LABEL: FILE "dir1{{.}}file1.sv"
@ -11,14 +12,16 @@ hw.module @foo1(%a: i1) -> (b: i1) attributes {output_file = #file1} {
// CHECK-LABEL: module B
#file2 = #hw.output_file<"dir1/file2.sv">
hw.module @foo2(%a: i1) -> (b: i1) attributes {output_file = #file2} {
#filelist2 = [#hw.output_filelist<"dir5/foo.f"> ,#hw.output_filelist<"dir5/bar.f">]
hw.module @foo2(%a: i1) -> (b: i1) attributes {output_file = #file2, output_filelist = #filelist2} {
hw.output %a : i1
}
// CHECK-LABEL: FILE "dir1{{.}}file2.sv"
// CHECK-LABEL: module foo2
#file3 = #hw.output_file<"dir2/", includeReplicatedOps>
hw.module @foo3(%a: i1) -> (b: i1) attributes {output_file = #file3} {
#filelist3 = [#hw.output_filelist<"dir5/bar.f">]
hw.module @foo3(%a: i1) -> (b: i1) attributes {output_file = #file3, output_filelist = #filelist3} {
hw.output %a : i1
}
// CHECK-LABEL: FILE "dir2{{.}}foo3.sv"
@ -37,3 +40,13 @@ sv.verbatim "module C; endmodule" {output_file = #hw.output_file<"/tmp/dummy.sv"
// CHECK-LABEL: FILE "/tmp/dummy.sv"
// CHECK-LABEL: module C
// Filelists
// CHECK-LABEL: FILE "dir5/foo.f"
// CHECK-EMPTY:
// CHECK-NEXT: dir1/file1.sv
// CHECK-NEXT: dir1/file2.sv
// CHECK-LABEL: FILE "dir5/bar.f"
// CHECK-EMPTY:
// CHECK-NEXT: dir1/file2.sv
// CHECK-NEXT: dir2/foo3.sv