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

[Moore] Introduce Mem2Reg to eliminate local variables #7082

Merged
merged 1 commit into from
Jun 13, 2024
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
1 change: 1 addition & 0 deletions include/circt/Dialect/Moore/MooreOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "circt/Dialect/Moore/MooreTypes.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/InferTypeOpInterface.h"
#include "mlir/Interfaces/MemorySlotInterfaces.h"

#define GET_OP_CLASSES
#include "circt/Dialect/Moore/MooreEnums.h.inc"
Expand Down
6 changes: 5 additions & 1 deletion include/circt/Dialect/Moore/MooreOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ def ProcedureOp : MooreOp<"procedure", [
//===----------------------------------------------------------------------===//

def VariableOp : MooreOp<"variable", [
DeclareOpInterfaceMethods<PromotableAllocationOpInterface>,
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
OptionalTypesMatchWith<"initial value and variable types match",
"result", "initial", "cast<RefType>($_self).getNestedType()">
Expand Down Expand Up @@ -251,6 +252,7 @@ def NetOp : MooreOp<"net", [
}

def ReadOp : MooreOp<"read", [
DeclareOpInterfaceMethods<PromotableMemOpInterface>,
TypesMatchWith<"input and result types match",
"result", "input", "RefType::get(cast<UnpackedType>($_self))">
]> {
Expand Down Expand Up @@ -291,7 +293,9 @@ def ContinuousAssignOp : AssignOpBase<"assign", [HasParent<"SVModuleOp">]> {
}];
}

def BlockingAssignOp : AssignOpBase<"blocking_assign"> {
def BlockingAssignOp : AssignOpBase<"blocking_assign", [
DeclareOpInterfaceMethods<PromotableMemOpInterface>
]> {
let summary = "Blocking procedural assignment";
let description = [{
A blocking procedural assignment in a sequential block, such as `x = y`. The
Expand Down
1 change: 1 addition & 0 deletions lib/Dialect/Moore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ add_circt_dialect_library(CIRCTMoore
CIRCTSupport
MLIRIR
MLIRInferTypeOpInterface
MLIRMemorySlotInterfaces
)

add_dependencies(circt-headers MLIRMooreIncGen)
100 changes: 100 additions & 0 deletions lib/Dialect/Moore/MooreOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

using namespace circt;
using namespace circt::moore;
using namespace mlir;

//===----------------------------------------------------------------------===//
// SVModuleOp
Expand Down Expand Up @@ -237,6 +238,33 @@ void VariableOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
setNameFn(getResult(), getName());
}

llvm::SmallVector<MemorySlot> VariableOp::getPromotableSlots() {
if (isa<SVModuleOp>(getOperation()->getParentOp()))
return {};
return {MemorySlot{getResult(), getType()}};
}

Value VariableOp::getDefaultValue(const MemorySlot &slot, OpBuilder &builder) {
if (auto value = getInitial())
return value;
return builder.create<ConstantOp>(
getLoc(),
cast<moore::IntType>(cast<RefType>(slot.elemType).getNestedType()), 0);
}

void VariableOp::handleBlockArgument(const MemorySlot &slot,
BlockArgument argument,
OpBuilder &builder) {}

::std::optional<::mlir::PromotableAllocationOpInterface>
VariableOp::handlePromotionComplete(const MemorySlot &slot, Value defaultValue,
OpBuilder &builder) {
if (defaultValue.use_empty())
defaultValue.getDefiningOp()->erase();
this->erase();
return std::nullopt;
}

//===----------------------------------------------------------------------===//
// NetOp
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -419,6 +447,78 @@ OpFoldResult BoolCastOp::fold(FoldAdaptor adaptor) {
return {};
}

//===----------------------------------------------------------------------===//
// BlockingAssignOp
//===----------------------------------------------------------------------===//

bool BlockingAssignOp::loadsFrom(const MemorySlot &slot) { return false; }

bool BlockingAssignOp::storesTo(const MemorySlot &slot) {
return getDst() == slot.ptr;
}

Value BlockingAssignOp::getStored(const MemorySlot &slot, OpBuilder &builder,
Value reachingDef,
const DataLayout &dataLayout) {
return getSrc();
}

bool BlockingAssignOp::canUsesBeRemoved(
const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
SmallVectorImpl<OpOperand *> &newBlockingUses,
const DataLayout &dataLayout) {

if (blockingUses.size() != 1)
return false;
Value blockingUse = (*blockingUses.begin())->get();
return blockingUse == slot.ptr && getDst() == slot.ptr &&
getSrc() != slot.ptr &&
getSrc().getType() == cast<RefType>(slot.elemType).getNestedType();
}

DeletionKind BlockingAssignOp::removeBlockingUses(
const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
OpBuilder &builder, Value reachingDefinition,
const DataLayout &dataLayout) {
return DeletionKind::Delete;
}

//===----------------------------------------------------------------------===//
// ReadOp
//===----------------------------------------------------------------------===//

bool ReadOp::loadsFrom(const MemorySlot &slot) {
return getOperand() == slot.ptr;
}

bool ReadOp::storesTo(const MemorySlot &slot) { return false; }

Value ReadOp::getStored(const MemorySlot &slot, OpBuilder &builder,
Value reachingDef, const DataLayout &dataLayout) {
llvm_unreachable("getStored should not be called on ReadOp");
}

bool ReadOp::canUsesBeRemoved(const MemorySlot &slot,
const SmallPtrSetImpl<OpOperand *> &blockingUses,
SmallVectorImpl<OpOperand *> &newBlockingUses,
const DataLayout &dataLayout) {

if (blockingUses.size() != 1)
return false;
Value blockingUse = (*blockingUses.begin())->get();
return blockingUse == slot.ptr && getOperand() == slot.ptr &&
getResult().getType() == cast<RefType>(slot.elemType).getNestedType();
}

DeletionKind
ReadOp::removeBlockingUses(const MemorySlot &slot,
const SmallPtrSetImpl<OpOperand *> &blockingUses,
OpBuilder &builder, Value reachingDefinition,
const DataLayout &dataLayout) {
getResult().replaceAllUsesWith(reachingDefinition);
return DeletionKind::Delete;
}

//===----------------------------------------------------------------------===//
// TableGen generated logic.
//===----------------------------------------------------------------------===//
Expand Down
35 changes: 35 additions & 0 deletions test/Dialect/Moore/mem2reg.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// RUN: circt-opt --mem2reg %s | FileCheck %s

// CHECK-LABEL: moore.module @LocalVar() {
moore.module @LocalVar() {
// CHECK: %x = moore.variable : <i32>
// CHECK: %y = moore.variable : <i32>
// CHECK: %z = moore.variable : <i32>
%x = moore.variable : <i32>
%y = moore.variable : <i32>
%z = moore.variable : <i32>
moore.procedure always_comb {
// CHECK: %0 = moore.read %x : i32
// CHECK: %1 = moore.constant 1 : i32
// CHECK: %2 = moore.add %0, %1 : i32
// CHECK: moore.blocking_assign %z, %2 : i32
// CHECK: %3 = moore.constant 1 : i32
// CHECK: %4 = moore.add %2, %3 : i32
// CHECK: moore.blocking_assign %y, %4 : i32
%a = moore.variable : <i32>
%0 = moore.read %x : i32
%1 = moore.constant 1 : i32
%2 = moore.add %0, %1 : i32
moore.blocking_assign %a, %2 : i32
%3 = moore.read %a : i32
moore.blocking_assign %z, %3 : i32
%4 = moore.read %a : i32
%5 = moore.constant 1 : i32
%6 = moore.add %4, %5 : i32
moore.blocking_assign %a, %6 : i32
%7 = moore.read %a : i32
moore.blocking_assign %y, %7 : i32
}
// CHECK: moore.output
moore.output
}
1 change: 1 addition & 0 deletions tools/circt-opt/circt-opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ int main(int argc, char **argv) {

// Register test passes
circt::test::registerAnalysisTestPasses();
mlir::registerMem2RegPass();

return mlir::failed(mlir::MlirOptMain(
argc, argv, "CIRCT modular optimizer driver", registry));
Expand Down
1 change: 1 addition & 0 deletions tools/circt-verilog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ llvm_update_compile_flags(circt-verilog)

target_link_libraries(circt-verilog PRIVATE
CIRCTImportVerilog
CIRCTMooreToCore
CIRCTSupport
MLIRIR
)
53 changes: 51 additions & 2 deletions tools/circt-verilog/circt-verilog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
//===----------------------------------------------------------------------===//

#include "circt/Conversion/ImportVerilog.h"
#include "circt/Conversion/MooreToCore.h"
#include "circt/Support/Version.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Support/FileUtilities.h"
#include "mlir/Transforms/Passes.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/SourceMgr.h"
Expand All @@ -31,7 +33,14 @@ using namespace circt;
//===----------------------------------------------------------------------===//

namespace {
enum class LoweringMode { OnlyPreprocess, OnlyLint, OnlyParse, Full };
enum class LoweringMode {
OnlyPreprocess,
OnlyLint,
OnlyParse,
OutputIRMoore,
OutputIRHW,
Full
};

struct CLOptions {
cl::OptionCategory cat{"Verilog Frontend Options"};
Expand Down Expand Up @@ -60,7 +69,13 @@ struct CLOptions {
"CIRCT IR"),
clEnumValN(LoweringMode::OnlyParse, "parse-only",
"Only parse and elaborate the input, without mapping to "
"CIRCT IR")),
"CIRCT IR"),
clEnumValN(LoweringMode::OutputIRMoore, "ir-moore",
"Run the entire pass manager to just before MooreToCore "
"conversion, and emit the resulting Moore dialect IR"),
clEnumValN(LoweringMode::OutputIRHW, "ir-hw",
"Run the MooreToCore conversion and emit the resulting "
"core dialect IR")),
cl::init(LoweringMode::Full), cl::cat(cat)};

//===--------------------------------------------------------------------===//
Expand Down Expand Up @@ -200,6 +215,23 @@ struct CLOptions {

static CLOptions opts;

/// Parse specified files that had been translated into Moore dialect IR. After
/// that simplify these files like deleting local variables, and then emit the
/// resulting Moore dialect IR .
static LogicalResult populateMooreTransforms(mlir::PassManager &pm) {
pm.addPass(mlir::createMem2Reg());
// TODO: like dedup pass.

return success();
}

/// Convert Moore dialect IR into core dialect IR
static LogicalResult populateMooreToCoreLowering(mlir::PassManager &pm) {
pm.addPass(createConvertMooreToCorePass());

return success();
}

//===----------------------------------------------------------------------===//
// Implementation
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -269,6 +301,23 @@ static LogicalResult executeWithSources(MLIRContext *context,
if (failed(importVerilog(sourceMgr, context, ts, module.get(), &options)))
return failure();

PassManager pm(context);
if (opts.loweringMode == LoweringMode::OutputIRMoore ||
opts.loweringMode == LoweringMode::OutputIRHW) {

// Simplify the Moore dialect IR.
if (failed(populateMooreTransforms(pm)))
return failure();

if (opts.loweringMode == LoweringMode::OutputIRHW)
// Convert Moore IR into core IR.
if (failed(populateMooreToCoreLowering(pm)))
return failure();
}

if (failed(pm.run(module.get())))
return failure();

// Print the final MLIR.
module->print(outputFile->os());
outputFile->keep();
Expand Down
Loading