diff --git a/include/circt/Dialect/Moore/CMakeLists.txt b/include/circt/Dialect/Moore/CMakeLists.txt index 6e8d5c2d0935..0fdc97c38bf8 100644 --- a/include/circt/Dialect/Moore/CMakeLists.txt +++ b/include/circt/Dialect/Moore/CMakeLists.txt @@ -15,3 +15,7 @@ mlir_tablegen(MooreAttributes.cpp.inc -gen-attrdef-defs -attrdefs-dialect MooreDialect) add_public_tablegen_target(CIRCTMooreAttributesIncGen) add_dependencies(circt-headers CIRCTMooreAttributesIncGen) + +set(LLVM_TARGET_DEFINITIONS MoorePasses.td) +mlir_tablegen(MoorePasses.h.inc -gen-pass-decls) +add_public_tablegen_target(CIRCTMooreTransformsIncGen) diff --git a/include/circt/Dialect/Moore/MoorePasses.h b/include/circt/Dialect/Moore/MoorePasses.h new file mode 100644 index 000000000000..7ec63836b17b --- /dev/null +++ b/include/circt/Dialect/Moore/MoorePasses.h @@ -0,0 +1,31 @@ +//===- Passes.h - Moore pass entry points -----------------------*- C++ -*-===// +// +// 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 header file defines prototypes that expose pass constructors. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_MOORE_MOOREPASSES_H +#define CIRCT_DIALECT_MOORE_MOOREPASSES_H + +#include "circt/Dialect/Moore/MooreOps.h" +#include "mlir/Pass/Pass.h" + +namespace circt { +namespace moore { + +std::unique_ptr createSimplifyProceduresPass(); + +/// Generate the code for registering passes. +#define GEN_PASS_REGISTRATION +#include "circt/Dialect/Moore/MoorePasses.h.inc" + +} // namespace moore +} // namespace circt + +#endif // CIRCT_DIALECT_MOORE_MOOREPASSES_H diff --git a/include/circt/Dialect/Moore/MoorePasses.td b/include/circt/Dialect/Moore/MoorePasses.td new file mode 100644 index 000000000000..977c477b2317 --- /dev/null +++ b/include/circt/Dialect/Moore/MoorePasses.td @@ -0,0 +1,32 @@ +//===--- Passes.td - Moore pass definition file ------------*- tablegen -*-===// +// +// 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 defines the passes that work on the Moore dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_DIALECT_MOORE_MOOREPASSES_TD +#define CIRCT_DIALECT_MOORE_MOOREPASSES_TD + +include "mlir/Pass/PassBase.td" + +def SimplifyProcedures : Pass<"simplify-procedures", "moore::SVModuleOp"> { + let summary = "Simplify procedures"; + let description = [{ + Cause we want to introduce mem2reg in the moore dialect to eliminate the + local temporary variables, if the local variabels exist in the procedure + body, it can be promoted by mem2reg. But global/module-level variables + don't be promoted. So this pass is aimed at inserting a local "shadow" + variable in always blocks for every module-level variable that the + process modifies. Then perform mem2reg to eliminate the local "shadow" + variable. + }]; + let constructor = "circt::moore::createSimplifyProceduresPass()"; +} + +#endif // CIRCT_DIALECT_MOORE_MOOREPASSES_TD diff --git a/include/circt/InitAllPasses.h b/include/circt/InitAllPasses.h index 3e65635c5e0c..66ca3f930cbd 100644 --- a/include/circt/InitAllPasses.h +++ b/include/circt/InitAllPasses.h @@ -28,6 +28,7 @@ #include "circt/Dialect/Ibis/IbisPasses.h" #include "circt/Dialect/LLHD/Transforms/Passes.h" #include "circt/Dialect/MSFT/MSFTPasses.h" +#include "circt/Dialect/Moore/MoorePasses.h" #include "circt/Dialect/OM/OMPasses.h" #include "circt/Dialect/Pipeline/PipelinePasses.h" #include "circt/Dialect/SSP/SSPPasses.h" @@ -70,6 +71,7 @@ inline void registerAllPasses() { ssp::registerPasses(); systemc::registerPasses(); verif::registerPasses(); + moore::registerPasses(); } } // namespace circt diff --git a/lib/Dialect/Moore/CMakeLists.txt b/lib/Dialect/Moore/CMakeLists.txt index aab709566108..bda0ea7713cc 100644 --- a/lib/Dialect/Moore/CMakeLists.txt +++ b/lib/Dialect/Moore/CMakeLists.txt @@ -23,3 +23,5 @@ add_circt_dialect_library(CIRCTMoore ) add_dependencies(circt-headers MLIRMooreIncGen) + +add_subdirectory(Transforms) diff --git a/lib/Dialect/Moore/Transforms/CMakeLists.txt b/lib/Dialect/Moore/Transforms/CMakeLists.txt new file mode 100644 index 000000000000..b974c169a746 --- /dev/null +++ b/lib/Dialect/Moore/Transforms/CMakeLists.txt @@ -0,0 +1,14 @@ +add_circt_dialect_library(CIRCTMooreTransforms +SimplifyProcedures.cpp + + + DEPENDS + CIRCTMooreTransformsIncGen + + LINK_LIBS PUBLIC + CIRCTMoore + CIRCTSupport + MLIRIR + MLIRPass + MLIRTransformUtils +) diff --git a/lib/Dialect/Moore/Transforms/PassDetail.h b/lib/Dialect/Moore/Transforms/PassDetail.h new file mode 100644 index 000000000000..6d585d0e1589 --- /dev/null +++ b/lib/Dialect/Moore/Transforms/PassDetail.h @@ -0,0 +1,31 @@ +//===- PassDetail.h - Morre pass class details ------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Stuff shared between the different Moore passes. +// +//===----------------------------------------------------------------------===// + +// clang-tidy seems to expect the absolute path in the header guard on some +// systems, so just disable it. +// NOLINTNEXTLINE(llvm-header-guard) +#ifndef DIALECT_MOORE_TRANSFORMS_PASSDETAIL_H +#define DIALECT_MOORE_TRANSFORMS_PASSDETAIL_H + +#include "circt/Dialect/Moore/MoorePasses.h" +#include "mlir/Pass/Pass.h" + +namespace circt { +namespace moore { + +#define GEN_PASS_CLASSES +#include "circt/Dialect/Moore/MoorePasses.h.inc" + +} // namespace moore +} // namespace circt + +#endif // DIALECT_MOORE_TRANSFORMS_PASSDETAIL_H diff --git a/lib/Dialect/Moore/Transforms/SimplifyProcedures.cpp b/lib/Dialect/Moore/Transforms/SimplifyProcedures.cpp new file mode 100644 index 000000000000..3c7b5716f75a --- /dev/null +++ b/lib/Dialect/Moore/Transforms/SimplifyProcedures.cpp @@ -0,0 +1,95 @@ +//===- DeleteLocalVar.cpp - Delete local temporary variables --------------===// +// +// 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 defines the SimplifyProcedures pass. +// Use to insert a local "shadow" variable in always blocks for every +// module-level variable that the process modifies. +// +//===----------------------------------------------------------------------===// + +#include "PassDetail.h" +#include "circt/Dialect/Moore/MooreOps.h" +#include "circt/Dialect/Moore/MooreTypes.h" +#include "mlir/IR/Builders.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" + +using namespace circt; +using namespace moore; + +namespace { +struct SimplifyProceduresPass + : public SimplifyProceduresBase { + void runOnOperation() override; +}; +} // namespace + +std::unique_ptr circt::moore::createSimplifyProceduresPass() { + return std::make_unique(); +} + +void SimplifyProceduresPass::runOnOperation() { + getOperation()->walk([&](ProcedureOp procedureOp) { + mlir::OpBuilder builder(&getContext()); + + // Use to collect blocking assignments that have been replaced by a "shadow" + // variable. + DenseSet assignOps; + for (auto &nestedOp : procedureOp) { + // Only create a "shadow" varaible for the global variable used by other + // operations in the procedure body. + if (isa(nestedOp) && + isa( + nestedOp.getOperand(0).getDefiningOp()->getParentOp())) { + // Collect the users of the global variable that is mentioned above. + DenseSet users; + for (auto *user : nestedOp.getOperand(0).getUsers()) + if (!users.contains(user)) + users.insert(user); + + auto varOp = cast(nestedOp.getOperand(0).getDefiningOp()); + auto varName = builder.getStringAttr("local_" + varOp.getName()); + auto resultType = varOp.getResult().getType(); + builder.setInsertionPointToStart(procedureOp.getBody()); + auto readOp = builder.create( + nestedOp.getLoc(), cast(resultType).getNestedType(), + varOp.getResult()); + auto newVarOp = builder.create(nestedOp.getLoc(), + resultType, varName, readOp); + builder.clearInsertionPoint(); + + // Replace the users of the global variable with a corresponding + // "shadow" variable. + for (auto *user : users) { + user->replaceUsesOfWith(user->getOperand(0), newVarOp); + if (isa(user)) + assignOps.insert(user); + } + } + + // Ensure the global variable has the correct value. So needing to create + // a blocking assign for the global variable when the "shadow" variable + // has a new value. + for (auto *assignOp : assignOps) + if (auto localVarOp = llvm::dyn_cast_or_null( + assignOp->getOperand(0).getDefiningOp())) { + auto resultType = localVarOp.getResult().getType(); + builder.setInsertionPointAfter(assignOp); + auto readOp = builder.create( + localVarOp.getLoc(), cast(resultType).getNestedType(), + localVarOp.getResult()); + builder.create( + nestedOp.getLoc(), + localVarOp.getInitial().getDefiningOp()->getOperand(0), readOp); + builder.clearInsertionPoint(); + assignOps.erase(assignOp); + } + } + return WalkResult::advance(); + }); +} diff --git a/test/Dialect/Moore/simplify-procedures.mlir b/test/Dialect/Moore/simplify-procedures.mlir new file mode 100644 index 000000000000..217bea07a460 --- /dev/null +++ b/test/Dialect/Moore/simplify-procedures.mlir @@ -0,0 +1,53 @@ +// RUN: circt-opt --simplify-procedures %s | FileCheck %s + +// CHECK-LABEL: moore.module @Foo() +moore.module @Foo() { + %a = moore.variable : + %x = moore.variable : + %y = moore.variable : + %z = moore.variable : + moore.procedure always_comb { + // CHECK: %0 = moore.read %a : i32 + // CHECK: %local_a = moore.variable %0 : + // CHECK: %1 = moore.constant 1 : i32 + %0 = moore.constant 1 : i32 + // CHECK: moore.blocking_assign %local_a, %1 : i32 + // CHECK: %2 = moore.read %local_a : i32 + // CHECK: moore.blocking_assign %a, %2 : i32 + moore.blocking_assign %a, %0 : i32 + // CHECK: %3 = moore.read %local_a : i32 + %1 = moore.read %a : i32 + // CHECK: moore.blocking_assign %x, %3 : i32 + moore.blocking_assign %x, %1 : i32 + // CHECK: %4 = moore.read %local_a : i32 + %2 = moore.read %a : i32 + // CHECK: %5 = moore.constant 1 : i32 + %3 = moore.constant 1 : i32 + // CHECK: %6 = moore.add %4, %5 : i32 + %4 = moore.add %2, %3 : i32 + // CHECK: moore.blocking_assign %local_a, %6 : i32 + // CHECK: %7 = moore.read %local_a : i32 + // CHECK: moore.blocking_assign %a, %7 : i32 + moore.blocking_assign %a, %4 : i32 + // CHECK: %8 = moore.read %local_a : i32 + %5 = moore.read %a : i32 + // CHECK: moore.blocking_assign %y, %8 : i32 + moore.blocking_assign %y, %5 : i32 + // CHECK: %9 = moore.read %local_a : i32 + %6 = moore.read %a : i32 + // CHECK: %10 = moore.constant 1 : i32 + %7 = moore.constant 1 : i32 + // CHECK: %11 = moore.add %9, %10 : i32 + %8 = moore.add %6, %7 : i32 + // CHECK: moore.blocking_assign %local_a, %11 : i32 + // CHECK: %12 = moore.read %local_a : i32 + // CHECK: moore.blocking_assign %a, %12 : i32 + moore.blocking_assign %a, %8 : i32 + // CHECK: %13 = moore.read %local_a : i32 + %9 = moore.read %a : i32 + // CHECK: moore.blocking_assign %z, %13 : i32 + moore.blocking_assign %z, %9 : i32 + } + // CHECK: moore.output + moore.output +} diff --git a/tools/circt-opt/CMakeLists.txt b/tools/circt-opt/CMakeLists.txt index 1dbf079ee29b..b72efcf11536 100644 --- a/tools/circt-opt/CMakeLists.txt +++ b/tools/circt-opt/CMakeLists.txt @@ -51,6 +51,7 @@ target_link_libraries(circt-opt CIRCTCombToLLVM CIRCTLLHDTransforms CIRCTMoore + CIRCTMooreTransforms CIRCTMooreToCore CIRCTMSFT CIRCTMSFTTransforms