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] Generic intrinsic parsing/emitter support #6897

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 88 additions & 23 deletions lib/Dialect/FIRRTL/Export/FIREmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ struct Emitter {
void emitDeclaration(OptionOp op);
void emitEnabledLayers(ArrayRef<Attribute> layers);

void emitParamAssign(ParamDeclAttr param, Operation *op,
std::optional<PPExtString> wordBeforeLHS = std::nullopt);
void emitGenericIntrinsic(GenericIntrinsicOp op);

// Statement emission
void emitStatementsInBlock(Block &block);
void emitStatement(WhenOp op);
Expand Down Expand Up @@ -91,6 +95,7 @@ struct Emitter {
void emitStatement(RefReleaseOp op);
void emitStatement(RefReleaseInitialOp op);
void emitStatement(LayerBlockOp op);
void emitStatement(GenericIntrinsicOp op);

template <class T>
void emitVerifStatement(T op, StringRef mnemonic);
Expand Down Expand Up @@ -120,6 +125,7 @@ struct Emitter {
void emitExpression(DoubleConstantOp op);
void emitExpression(ListCreateOp op);
void emitExpression(UnresolvedPathOp op);
void emitExpression(GenericIntrinsicOp op);

void emitPrimExpr(StringRef mnemonic, Operation *op,
ArrayRef<uint32_t> attrs = {});
Expand Down Expand Up @@ -419,6 +425,57 @@ void Emitter::emitEnabledLayers(ArrayRef<Attribute> layers) {
}
}

void Emitter::emitParamAssign(ParamDeclAttr param, Operation *op,
std::optional<PPExtString> wordBeforeLHS) {
if (wordBeforeLHS) {
ps << *wordBeforeLHS << PP::nbsp;
}
ps << PPExtString(param.getName().strref()) << PP::nbsp << "=" << PP::nbsp;
TypeSwitch<Attribute>(param.getValue())
.Case<IntegerAttr>([&](auto attr) { ps.addAsString(attr.getValue()); })
.Case<FloatAttr>([&](auto attr) {
SmallString<16> str;
attr.getValue().toString(str);
ps << str;
})
.Case<StringAttr>(
[&](auto attr) { ps.writeQuotedEscaped(attr.getValue()); })
.Default([&](auto attr) {
emitOpError(op, "with unsupported parameter attribute: ") << attr;
ps << "<unsupported-attr ";
ps.addAsString(attr);
ps << ">";
});
}

void Emitter::emitGenericIntrinsic(GenericIntrinsicOp op) {
ps << "intrinsic(";
ps.scopedBox(PP::cbox0, [&]() {
ps.scopedBox(PP::ibox2, [&]() {
ps << op.getIntrinsic();
ps.scopedBox(PP::ibox0, [&]() {
auto params = op.getParameters();
if (!params.empty()) {
ps << "<";
ps.scopedBox(PP::ibox0, [&]() {
interleaveComma(
params.getAsRange<ParamDeclAttr>(),
[&](ParamDeclAttr param) { emitParamAssign(param, op); });
});
ps << ">";
}
});
if (op.getNumResults() != 0)
emitTypeWithColon(op.getResult().getType());
});
if (op.getNumOperands() != 0) {
ps << "," << PP::space;
ps.scopedBox(PP::ibox0, [&]() { interleaveComma(op->getOperands()); });
}
ps << ")";
});
}

/// Emit an entire module.
void Emitter::emitModule(FModuleOp op) {
startStatement();
Expand Down Expand Up @@ -519,27 +576,9 @@ void Emitter::emitModulePorts(ArrayRef<PortInfo> ports,
}

void Emitter::emitModuleParameters(Operation *op, ArrayAttr parameters) {
for (auto param : llvm::map_range(parameters, [](Attribute attr) {
return cast<ParamDeclAttr>(attr);
})) {
for (auto param : parameters.getAsRange<ParamDeclAttr>()) {
startStatement();
// TODO: AssignLike ?
ps << "parameter " << PPExtString(param.getName().getValue()) << " = ";
TypeSwitch<Attribute>(param.getValue())
.Case<IntegerAttr>([&](auto attr) { ps.addAsString(attr.getValue()); })
.Case<FloatAttr>([&](auto attr) {
SmallString<16> str;
attr.getValue().toString(str);
ps << str;
})
.Case<StringAttr>(
[&](auto attr) { ps.writeQuotedEscaped(attr.getValue()); })
.Default([&](auto attr) {
emitOpError(op, "with unsupported parameter attribute: ") << attr;
ps << "<unsupported-attr ";
ps.addAsString(attr);
ps << ">";
});
emitParamAssign(param, op, PPExtString("parameter"));
setPendingNewline();
}
}
Expand Down Expand Up @@ -580,7 +619,14 @@ void Emitter::emitDeclaration(OptionOp op) {
/// Check if an operation is inlined into the emission of their users. For
/// example, subfields are always inlined.
static bool isEmittedInline(Operation *op) {
return isExpression(op) && !isa<InvalidValueOp>(op);
// FIRRTL expressions are statically classified as always inlineable.
// InvalidValueOp never is inlined, and is handled specially.
// GenericIntrinsicOp is inlined if has exactly one use (only emit once)
// that is not emitted inline. This is to ensure it is emitted inline
// in common cases, but only inspect one level deep.
return (isExpression(op) && !isa<InvalidValueOp>(op)) ||
(isa<GenericIntrinsicOp>(op) && op->hasOneUse() &&
!isEmittedInline(*op->getUsers().begin()));
}

void Emitter::emitStatementsInBlock(Block &block) {
Expand All @@ -596,7 +642,8 @@ void Emitter::emitStatementsInBlock(Block &block) {
InvalidValueOp, SeqMemOp, CombMemOp, MemoryPortOp,
MemoryDebugPortOp, MemoryPortAccessOp, RefDefineOp, RefForceOp,
RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp,
LayerBlockOp>([&](auto op) { emitStatement(op); })
LayerBlockOp, GenericIntrinsicOp>(
[&](auto op) { emitStatement(op); })
.Default([&](auto op) {
startStatement();
ps << "// operation " << PPExtString(op->getName().getStringRef());
Expand Down Expand Up @@ -1087,6 +1134,20 @@ void Emitter::emitStatement(InvalidValueOp op) {
emitLocationAndNewLine(op);
}

void Emitter::emitStatement(GenericIntrinsicOp op) {
startStatement();
if (op.use_empty())
emitGenericIntrinsic(op);
else {
dtzSiFive marked this conversation as resolved.
Show resolved Hide resolved
assert(!isEmittedInline(op));
auto name = circuitNamespace.newName("_gen_int");
addValueName(op.getResult(), name);
emitAssignLike([&]() { ps << "node " << PPExtString(name); },
[&]() { emitGenericIntrinsic(op); });
}
emitLocationAndNewLine(op);
}

void Emitter::emitExpression(Value value) {
// Handle the trivial case where we already have a name for this value which
// we can use.
Expand Down Expand Up @@ -1114,7 +1175,7 @@ void Emitter::emitExpression(Value value) {
BitsPrimOp, HeadPrimOp, TailPrimOp, PadPrimOp, MuxPrimOp, ShlPrimOp,
ShrPrimOp, UninferredResetCastOp, ConstCastOp, StringConstantOp,
FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
UnresolvedPathOp,
UnresolvedPathOp, GenericIntrinsicOp,
// Reference expressions
RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp>(
[&](auto op) {
Expand Down Expand Up @@ -1302,6 +1363,10 @@ void Emitter::emitExpression(UnresolvedPathOp op) {
ps << ")";
}

void Emitter::emitExpression(GenericIntrinsicOp op) {
emitGenericIntrinsic(op);
}

void Emitter::emitExpression(ConstCastOp op) { emitExpression(op.getInput()); }

void Emitter::emitPrimExpr(StringRef mnemonic, Operation *op,
Expand Down
Loading
Loading