diff --git a/include/circt/Dialect/FIRRTL/FIRRTLDeclarations.td b/include/circt/Dialect/FIRRTL/FIRRTLDeclarations.td index db3c6d472672..c7b8841a9f08 100644 --- a/include/circt/Dialect/FIRRTL/FIRRTLDeclarations.td +++ b/include/circt/Dialect/FIRRTL/FIRRTLDeclarations.td @@ -777,4 +777,21 @@ def ObjectOp : FIRRTLOp<"object", [ }]; } +def DomainCreateAnonOp : FIRRTLOp<"domain.anon", [ + ParentOneOf<[ + "firrtl::ContractOp", + "firrtl::FModuleOp", + "firrtl::LayerBlockOp", + "firrtl::MatchOp", + "firrtl::WhenOp", + "sv::IfDefOp" + ]>, + DeclareOpInterfaceMethods + ]> { + let summary = "Create an anonymous domain, without parameters set"; + let arguments = (ins FlatSymbolRefAttr:$domain); + let results = (outs DomainType:$result); + let hasCustomAssemblyFormat = 1; +} + #endif // CIRCT_DIALECT_FIRRTL_FIRRTLDECLARATIONS_TD diff --git a/lib/Dialect/FIRRTL/Export/FIREmitter.cpp b/lib/Dialect/FIRRTL/Export/FIREmitter.cpp index 324f00e5eb2a..7b3fbdfa4bfd 100644 --- a/lib/Dialect/FIRRTL/Export/FIREmitter.cpp +++ b/lib/Dialect/FIRRTL/Export/FIREmitter.cpp @@ -110,6 +110,7 @@ struct Emitter { void emitStatement(RefReleaseInitialOp op); void emitStatement(LayerBlockOp op); void emitStatement(GenericIntrinsicOp op); + void emitStatement(DomainCreateAnonOp op); template void emitVerifStatement(T op, StringRef mnemonic); @@ -755,7 +756,8 @@ void Emitter::emitStatementsInBlock(Block &block) { CombMemOp, MemoryPortOp, MemoryDebugPortOp, MemoryPortAccessOp, DomainDefineOp, RefDefineOp, RefForceOp, RefForceInitialOp, RefReleaseOp, RefReleaseInitialOp, LayerBlockOp, - GenericIntrinsicOp>([&](auto op) { emitStatement(op); }) + GenericIntrinsicOp, DomainCreateAnonOp>( + [&](auto op) { emitStatement(op); }) .Default([&](auto op) { startStatement(); ps << "// operation " << PPExtString(op->getName().getStringRef()); @@ -1285,6 +1287,10 @@ void Emitter::emitStatement(MemoryPortAccessOp op) { } void Emitter::emitStatement(DomainDefineOp op) { + // If the source is an anonymous domain, then we can skip emitting this op. + if (isa_and_nonnull(op.getSrc().getDefiningOp())) + return; + startStatement(); emitAssignLike([&]() { emitExpression(op.getDest()); }, [&]() { emitExpression(op.getSrc()); }, PPExtString("="), @@ -1392,6 +1398,10 @@ void Emitter::emitStatement(GenericIntrinsicOp op) { emitLocationAndNewLine(op); } +void Emitter::emitStatement(DomainCreateAnonOp op) { + // These ops are not emitted. +} + void Emitter::emitExpression(Value value) { // Handle the trivial case where we already have a name for this value which // we can use. diff --git a/lib/Dialect/FIRRTL/FIRRTLOps.cpp b/lib/Dialect/FIRRTL/FIRRTLOps.cpp index f770563e948a..b790816f7293 100644 --- a/lib/Dialect/FIRRTL/FIRRTLOps.cpp +++ b/lib/Dialect/FIRRTL/FIRRTLOps.cpp @@ -4250,6 +4250,8 @@ static FlatSymbolRefAttr getDomainTypeName(Value value) { return getDomainTypeNameOfResult(instance, result.getResultNumber()); if (auto instance = dyn_cast(op)) return getDomainTypeNameOfResult(instance, result.getResultNumber()); + if (auto anonDomain = dyn_cast(op)) + return anonDomain.getDomainAttr(); return {}; } @@ -7024,6 +7026,44 @@ LogicalResult BindOp::verifyInnerRefs(hw::InnerRefNamespace &ns) { return success(); } +//===----------------------------------------------------------------------===// +// Domain operations +//===----------------------------------------------------------------------===// + +void DomainCreateAnonOp::print(OpAsmPrinter &p) { + p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"domain"}); + p << " : "; + p.printType(getType()); + p << " of "; + p.printAttributeWithoutType(getDomainAttr()); +} + +ParseResult DomainCreateAnonOp::parse(OpAsmParser &parser, + OperationState &result) { + Type type; + StringAttr domainKind; + if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() || + parser.parseType(type) || parser.parseKeyword("of") || + parser.parseSymbolName(domainKind)) + return failure(); + + auto domain = FlatSymbolRefAttr::get(result.getContext(), domainKind); + result.getOrAddProperties().setDomain(domain); + result.addTypes(type); + + return success(); +} + +LogicalResult +DomainCreateAnonOp::verifySymbolUses(SymbolTableCollection &symbolTable) { + auto circuitOp = getOperation()->getParentOfType(); + auto domain = getDomainAttr(); + if (!symbolTable.lookupSymbolIn(circuitOp, domain)) + return emitOpError() << "references undefined domain '" << domain << "'"; + + return success(); +} + //===----------------------------------------------------------------------===// // TblGen Generated Logic. //===----------------------------------------------------------------------===// diff --git a/test/Dialect/FIRRTL/emit-basic.mlir b/test/Dialect/FIRRTL/emit-basic.mlir index 70aedfb1feac..2e1be3efa660 100644 --- a/test/Dialect/FIRRTL/emit-basic.mlir +++ b/test/Dialect/FIRRTL/emit-basic.mlir @@ -1009,4 +1009,20 @@ firrtl.circuit "Foo" { firrtl.domain.define %O, %ext_O } + // Test that anonymous domain create ops and their defines are elided. + firrtl.extmodule @AnonymousDomains_Foo( + in A: !firrtl.domain of @ClockDomain + ) + // CHECK-LABEL: module AnonymousDomains : + firrtl.module @AnonymousDomains() { + // CHECK-NEXT: inst foo of AnonymousDomains_Foo + %0 = firrtl.domain.anon : !firrtl.domain of @ClockDomain + %foo_A = firrtl.instance foo @AnonymousDomains_Foo( + in A: !firrtl.domain of @ClockDomain + ) + firrtl.domain.define %foo_A, %0 + // CHECK-NEXT: wire end : UInt<1> + %end = firrtl.wire : !firrtl.uint<1> + } + } diff --git a/test/Dialect/FIRRTL/errors.mlir b/test/Dialect/FIRRTL/errors.mlir index 5e6e40eb27bc..f0b390cd4d9e 100644 --- a/test/Dialect/FIRRTL/errors.mlir +++ b/test/Dialect/FIRRTL/errors.mlir @@ -3198,3 +3198,22 @@ firrtl.circuit "WrongInstanceChoiceDomainInfo" { ) } } + +// ----- + +firrtl.circuit "AnonDomainPointingAtNonDomain" { + firrtl.extmodule @Foo() + firrtl.module @UndefinedDomainInAnonDomain() { + // expected-error @below {{references undefined domain '@Foo'}} + %0 = firrtl.domain.anon : !firrtl.domain of @Foo + } +} + +// ----- + +firrtl.circuit "UndefinedDomainInAnonDomain" { + firrtl.module @UndefinedDomainInAnonDomain() { + // expected-error @below {{references undefined domain '@Foo'}} + %0 = firrtl.domain.anon : !firrtl.domain of @Foo + } +} diff --git a/test/Dialect/FIRRTL/round-trip.mlir b/test/Dialect/FIRRTL/round-trip.mlir index f76e9b9198d8..a9c35ec0af73 100644 --- a/test/Dialect/FIRRTL/round-trip.mlir +++ b/test/Dialect/FIRRTL/round-trip.mlir @@ -276,4 +276,16 @@ firrtl.module @InstanceChoiceWithDomain(in %A: !firrtl.domain of @ClockDomain, i firrtl.domain.define %inst_A, %A } +// CHECK-LABEL: firrtl.module @CreateAnonDomain +firrtl.extmodule @CreateAnonDomainChild(in A: !firrtl.domain of @ClockDomain) +firrtl.module @CreateAnonDomain() { + // CHECK-NEXT: %0 = firrtl.domain.anon : !firrtl.domain of @ClockDomain + %0 = firrtl.domain.anon : !firrtl.domain of @ClockDomain + %child_A = firrtl.instance child @CreateAnonDomainChild( + in A: !firrtl.domain of @ClockDomain + ) + // CHECK: firrtl.domain.define %child_A, %0 + firrtl.domain.define %child_A, %0 +} + }