Skip to content

Commit

Permalink
[Emit] Group file header ops into emit.fragment (#6789)
Browse files Browse the repository at this point in the history
Instead of using the replicated op mechanism, fragments of SV code
which must precede certain modules are grouped into fragment ops.
Modules which rely on them reference them through a `emit.fragment`
attribute. The fragments are printed through ExportVerilog.

Subsequent PRs will fully replace replaced ops using fragments.
  • Loading branch information
nandor committed Mar 14, 2024
1 parent 1080acb commit 9c4f721
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 111 deletions.
15 changes: 11 additions & 4 deletions include/circt/Conversion/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,17 @@ def LowerFIRRTLToHW : Pass<"lower-firrtl-to-hw", "mlir::ModuleOp"> {
Lower a module of FIRRTL dialect to the HW dialect family.
}];
let constructor = "circt::createLowerFIRRTLToHWPass()";
let dependentDialects = ["comb::CombDialect", "hw::HWDialect",
"seq::SeqDialect", "sv::SVDialect",
"ltl::LTLDialect", "verif::VerifDialect",
"sim::SimDialect"];

let dependentDialects = [
"comb::CombDialect",
"emit::EmitDialect",
"hw::HWDialect",
"ltl::LTLDialect",
"seq::SeqDialect",
"sim::SimDialect",
"sv::SVDialect",
"verif::VerifDialect",
];
let options = [
Option<"enableAnnotationWarning", "warn-on-unprocessed-annotations",
"bool", "false",
Expand Down
37 changes: 37 additions & 0 deletions include/circt/Dialect/Emit/EmitOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,43 @@ def FileOp : EmitOp<"file", [
}];
}

def FragmentOp : EmitOp<"fragment", [
Symbol,
SingleBlock,
NoTerminator,
NoRegionArguments,
IsolatedFromAbove,
HasParent<"mlir::ModuleOp">
]> {
let summary = "Emittable fragment which can be replicated before modules";
let description = [{
The fragment operation is a container for other operations that can
be emitted before other operations. It carries a symbol that can be
referenced by an `emit.fragments` attribute placed on operations before
which the fragments should be inserted.

In single-file mode, each fragment is emitted once. In split file emission
mode, fragments precede all operations that reference them, but are still
emitted at most once per file.
}];

let regions = (region SizedRegion<1>:$bodyRegion);
let arguments = (ins SymbolNameAttr:$sym_name);

let assemblyFormat = "$sym_name $bodyRegion attr-dict";

let skipDefaultBuilders = 1;
let builders = [
OpBuilder<(ins "StringRef":$symName,
CArg<"llvm::function_ref<void()>", "{}">:$bodyCtor), [{
return build(
$_builder, $_state, $_builder.getStringAttr(symName), bodyCtor);
}]>,
OpBuilder<(ins "StringAttr":$symName,
CArg<"llvm::function_ref<void()>", "{}">:$bodyCtor)>,
];
}

def VerbatimOp : EmitOp<"verbatim", [HasParent<"circt::emit::FileOp">]> {
let summary = "Verbatim opaque text emitted inline.";
let description = [{
Expand Down
43 changes: 34 additions & 9 deletions lib/Conversion/ExportVerilog/ExportVerilog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6039,16 +6039,22 @@ class FileEmitter : public EmitterBase {
public:
explicit FileEmitter(VerilogEmitterState &state) : EmitterBase(state) {}

void emit(emit::FileOp op);
void emit(emit::FileOp op) {
emit(op.getBody());
ps.eof();
}
void emit(emit::FragmentOp op) { emit(op.getBody()); }
void emit(emit::FileListOp op);

private:
void emit(Block *block);

void emitOp(emit::RefOp op);
void emitOp(emit::VerbatimOp op);
};

void FileEmitter::emit(emit::FileOp op) {
for (Operation &op : *op.getBody()) {
void FileEmitter::emit(Block *block) {
for (Operation &op : *block) {
TypeSwitch<Operation *>(&op)
.Case<emit::VerbatimOp, emit::RefOp>([&](auto op) { emitOp(op); })
.Case<VerbatimOp, IfDefOp, MacroDefOp>(
Expand All @@ -6057,7 +6063,6 @@ void FileEmitter::emit(emit::FileOp op) {
.Default(
[&](auto op) { emitOpError(op, "cannot be emitted to a file"); });
}
ps.eof();
}

void FileEmitter::emit(emit::FileListOp op) {
Expand Down Expand Up @@ -6218,6 +6223,9 @@ void SharedEmitterState::gatherFiles(bool separateModules) {
fileMapping.try_emplace(file.getSymNameAttr(), file);
separateFile(file, file.getFileName());
})
.Case<emit::FragmentOp>([&](auto fragment) {
fragmentMapping.try_emplace(fragment.getSymNameAttr(), fragment);
})
.Case<HWModuleOp>([&](auto mod) {
// Build the IR cache.
auto sym = mod.getNameAttr();
Expand Down Expand Up @@ -6338,20 +6346,37 @@ void SharedEmitterState::collectOpsForFile(const FileInfo &file,
size_t numReplicatedOps =
file.emitReplicatedOps && !emitHeaderInclude ? replicatedOps.size() : 0;

thingsToEmit.reserve(thingsToEmit.size() + numReplicatedOps +
file.ops.size());

// Emit each operation in the file preceded by the replicated ops not yet
// printed.
DenseSet<emit::FragmentOp> includedFragments;
for (const auto &opInfo : file.ops) {
Operation *op = opInfo.op;

// Emit the replicated per-file operations before the main operation's
// position (if enabled).
for (; lastReplicatedOp < std::min(opInfo.position, numReplicatedOps);
++lastReplicatedOp)
thingsToEmit.emplace_back(replicatedOps[lastReplicatedOp]);

// Pull in the fragments that the op references. In one file, each
// fragment is emitted only once.
if (auto fragments = op->getAttrOfType<ArrayAttr>("emit.fragments")) {
for (auto sym : fragments.getAsRange<FlatSymbolRefAttr>()) {
auto it = fragmentMapping.find(sym.getAttr());
if (it == fragmentMapping.end()) {
encounteredError = true;
op->emitError("cannot find referenced fragment ") << sym;
continue;
}
emit::FragmentOp fragment = it->second;
if (includedFragments.insert(fragment).second) {
thingsToEmit.emplace_back(it->second);
}
}
}

// Emit the operation itself.
thingsToEmit.emplace_back(opInfo.op);
thingsToEmit.emplace_back(op);
}

// Emit the replicated per-file operations after the last operation (if
Expand All @@ -6376,7 +6401,7 @@ static void emitOperation(VerilogEmitterState &state, Operation *op) {
.Case<TypeScopeOp>([&](auto typedecls) {
ModuleEmitter(state).emitStatement(typedecls);
})
.Case<emit::FileOp, emit::FileListOp>(
.Case<emit::FileOp, emit::FileListOp, emit::FragmentOp>(
[&](auto op) { FileEmitter(state).emit(op); })
.Case<MacroDefOp>(
[&](auto op) { ModuleEmitter(state).emitStatement(op); })
Expand Down
7 changes: 7 additions & 0 deletions lib/Conversion/ExportVerilog/ExportVerilogInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define CONVERSION_EXPORTVERILOG_EXPORTVERILOGINTERNAL_H

#include "circt/Dialect/Comb/CombVisitors.h"
#include "circt/Dialect/Emit/EmitOps.h"
#include "circt/Dialect/HW/HWOps.h"
#include "circt/Dialect/HW/HWSymCache.h"
#include "circt/Dialect/HW/HWVisitors.h"
Expand Down Expand Up @@ -328,6 +329,9 @@ class StringOrOpToEmit {
/// Mapping from symbols to file operations.
using FileMapping = DenseMap<StringAttr, Operation *>;

/// Mapping from symbols to file operations.
using FragmentMapping = DenseMap<StringAttr, emit::FragmentOp>;

/// This class tracks the top-level state for the emitters, which is built and
/// then shared across all per-file emissions that happen in parallel.
struct SharedEmitterState {
Expand Down Expand Up @@ -369,6 +373,9 @@ struct SharedEmitterState {
/// Tracks the referenceable files through their symbol.
FileMapping fileMapping;

/// Tracks referenceable files through their symbol.
FragmentMapping fragmentMapping;

explicit SharedEmitterState(ModuleOp designOp, const LoweringOptions &options,
GlobalNameTable globalNames)
: designOp(designOp), options(options),
Expand Down
1 change: 1 addition & 0 deletions lib/Conversion/FIRRTLToHW/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ add_circt_conversion_library(CIRCTFIRRTLToHW

LINK_LIBS PUBLIC
CIRCTComb
CIRCTEmit
CIRCTFIRRTL
CIRCTHW
CIRCTLTL
Expand Down
Loading

0 comments on commit 9c4f721

Please sign in to comment.