mirror of https://github.com/llvm/circt.git
[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:
parent
33e1eb393a
commit
bcfaf7843e
|
@ -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"> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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">} {
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue