diff --git a/include/circt/Dialect/Moore/MoorePasses.h b/include/circt/Dialect/Moore/MoorePasses.h index aaff7b6a302f..ad9d07a4d2bd 100644 --- a/include/circt/Dialect/Moore/MoorePasses.h +++ b/include/circt/Dialect/Moore/MoorePasses.h @@ -23,6 +23,7 @@ namespace moore { #include "circt/Dialect/Moore/MoorePasses.h.inc" std::unique_ptr createSimplifyProceduresPass(); +std::unique_ptr createDedupPass(); /// Generate the code for registering passes. #define GEN_PASS_REGISTRATION diff --git a/include/circt/Dialect/Moore/MoorePasses.td b/include/circt/Dialect/Moore/MoorePasses.td index c3031ae5b683..9ed2734e594f 100644 --- a/include/circt/Dialect/Moore/MoorePasses.td +++ b/include/circt/Dialect/Moore/MoorePasses.td @@ -29,4 +29,40 @@ def SimplifyProcedures : Pass<"moore-simplify-procedures", "moore::SVModuleOp"> let constructor = "circt::moore::createSimplifyProceduresPass()"; } +def Dedup : Pass<"moore-dedup", "mlir::ModuleOp"> { + let summary = "Deduplicate modules which are generated by same instance in SV"; + let description = [{ + This pass detects modules which are structurally equivalent and removes the + duplicate module by replacing all instances of one with the other. + This pass is only suitable for software simulation. Different symbolName + modules relate to different hardware units in hardware simulation. So this pass + is not recommended in hardware simulation. + For example, + ``` + module { + moore.module @top() { + %a = moore.net wire : + %0 = moore.read %a : l4 + moore.instance "insA" @NestedA(a: %0: !moore.l4) -> () + %1 = moore.read %a : l4 + --- moore.instance "insB" @NestedA_0(a: %1: !moore.l4) -> () + +++ moore.instance "insB" @NestedA(a: %1: !moore.l4) -> () + moore.output + } + --- moore.module @NestedA_0(in %a : !moore.l4) { + --- %a_0 = moore.net name "a" wire : + --- moore.assign %a_0, %a : l4 + --- moore.output + --- } + moore.module @NestedA(in %a : !moore.l4) { + %a_0 = moore.net name "a" wire : + moore.assign %a_0, %a : l4 + moore.output + } + } + ``` + }]; + let constructor = "circt::moore::createDedupPass()"; +} + #endif // CIRCT_DIALECT_MOORE_MOOREPASSES_TD diff --git a/lib/Dialect/Moore/Transforms/CMakeLists.txt b/lib/Dialect/Moore/Transforms/CMakeLists.txt index b974c169a746..b2c4ebe026cd 100644 --- a/lib/Dialect/Moore/Transforms/CMakeLists.txt +++ b/lib/Dialect/Moore/Transforms/CMakeLists.txt @@ -1,5 +1,6 @@ add_circt_dialect_library(CIRCTMooreTransforms SimplifyProcedures.cpp +Dedup.cpp DEPENDS diff --git a/lib/Dialect/Moore/Transforms/Dedup.cpp b/lib/Dialect/Moore/Transforms/Dedup.cpp new file mode 100644 index 000000000000..b04d081d9504 --- /dev/null +++ b/lib/Dialect/Moore/Transforms/Dedup.cpp @@ -0,0 +1,87 @@ +//===- Dedup.cpp - Moore module deduping --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements moore module deduplication. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/Moore/MooreOps.h" +#include "circt/Dialect/Moore/MoorePasses.h" + +namespace circt { +namespace moore { +#define GEN_PASS_DEF_DEDUP +#include "circt/Dialect/Moore/MoorePasses.h.inc" +} // namespace moore +} // namespace circt + +using namespace circt; +using namespace moore; + +namespace { +class DedupPass : public circt::moore::impl::DedupBase { + // This table contains information to determine module module uniqueness. + using ModuleInfo = struct ModuleStruct { + SmallVector portTypes; + SmallVector inputTypes; + SmallVector outputTypes; + }; + friend bool operator==(const ModuleInfo &lhs, const ModuleInfo &rhs) { + return lhs.portTypes == rhs.portTypes && lhs.inputTypes == rhs.inputTypes && + lhs.outputTypes == rhs.outputTypes; + } + + // This table records Op name and Op info + using ModuleInfoTable = DenseMap; + ModuleInfoTable moduleInfoTable; + + // This table records old module name and equiplance module name to update + using Symbol2Symbol = DenseMap; + Symbol2Symbol replaceTable; + + void runOnOperation() override; +}; +} // namespace + +std::unique_ptr circt::moore::createDedupPass() { + return std::make_unique(); +} + +void DedupPass::runOnOperation() { + // Do equiplance and record in replacTable + // Dedup already exist module op + getOperation()->walk([&](SVModuleOp moduleOp) { + // Define uniqueness + auto moduleName = moduleOp.getSymNameAttr(); + auto moduleType = moduleOp.getModuleType(); + ModuleInfo moduleInfo = {moduleType.getPortTypes(), + moduleType.getInputTypes(), + moduleType.getOutputTypes()}; + + // Compare and record to replacetable + // erase this op if there is a equiplance + for (const auto &existModuleInfo : moduleInfoTable) { + if (existModuleInfo.second == moduleInfo) { + moduleOp->erase(); + replaceTable.insert({moduleName, existModuleInfo.first}); + return WalkResult::advance(); + } + } + moduleInfoTable[moduleName] = moduleInfo; + return WalkResult::advance(); + }); + + // Referring to replacetable, replace instance's module name + getOperation()->walk([&](InstanceOp instanceOp) { + auto instanceName = instanceOp.getModuleNameAttr().getAttr(); + if (replaceTable.lookup(instanceName)) { + instanceOp.setModuleName(replaceTable[instanceName]); + } + return WalkResult::advance(); + }); +} diff --git a/test/Dialect/Moore/dedup.mlir b/test/Dialect/Moore/dedup.mlir new file mode 100644 index 000000000000..dfcc66edf902 --- /dev/null +++ b/test/Dialect/Moore/dedup.mlir @@ -0,0 +1,31 @@ +// RUN: circt-opt --moore-dedup %s | FileCheck %s + +// CHECK-LABEL: moore.module @Foo() +moore.module @Foo() { + %a = moore.net wire : + %0 = moore.read %a : l4 + // CHECK: moore.instance "insA" @NestedA + moore.instance "insA" @NestedA(a: %0: !moore.l4) -> () + %1 = moore.read %a : l4 + // CHECK: moore.instance "insB" @NestedA + moore.instance "insB" @NestedA_0(a: %1: !moore.l4) -> () + moore.output +} +// CHECK: moore.module @NestedA +moore.module @NestedA(in %a : !moore.l4) { + %a_0 = moore.net name "a" wire : + moore.assign %a_0, %a : l4 + moore.output +} +// CHECK-NOT: moore.module @NestedA_0 +moore.module @NestedA_0(in %a : !moore.l4) { + %a_0 = moore.net name "a" wire : + moore.assign %a_0, %a : l4 + moore.output +} +// CHECK-NOT: moore.module @NestedA_1 +moore.module @NestedA_1(in %a : !moore.l4) { + %a_0 = moore.net name "a" wire : + moore.assign %a_0, %a : l4 + moore.output +}