[Moore] Add SimplifyAssigns pass to handle concat_ref. (#7216)

Co-authored-by: Fabian Schuiki <fschuiki@iis.ee.ethz.ch>
Co-authored-by: Hideto Ueno <hideto.ueno@sifive.com>
This commit is contained in:
Hailong Sun 2024-07-01 17:30:25 +08:00 committed by GitHub
parent d8b0baade5
commit d360917dae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 230 additions and 2 deletions

View File

@ -23,6 +23,7 @@ namespace moore {
#include "circt/Dialect/Moore/MoorePasses.h.inc" #include "circt/Dialect/Moore/MoorePasses.h.inc"
std::unique_ptr<mlir::Pass> createSimplifyProceduresPass(); std::unique_ptr<mlir::Pass> createSimplifyProceduresPass();
std::unique_ptr<mlir::Pass> createLowerConcatRefPass();
/// Generate the code for registering passes. /// Generate the code for registering passes.
#define GEN_PASS_REGISTRATION #define GEN_PASS_REGISTRATION

View File

@ -29,4 +29,14 @@ def SimplifyProcedures : Pass<"moore-simplify-procedures", "moore::SVModuleOp">
let constructor = "circt::moore::createSimplifyProceduresPass()"; let constructor = "circt::moore::createSimplifyProceduresPass()";
} }
def LowerConcatRef : Pass<"moore-lower-concatref", "moore::SVModuleOp"> {
let summary = "Lower moore.concat_ref ops";
let description = [{
It's used to disassemble the LHS of assignments that have a form like
"{a, b} = c" onto "a = c[9001:42];" and "b = c[41:0]". Aimed at
conveniently lowering this kind of assignment.
}];
let constructor = "circt::moore::createLowerConcatRefPass()";
}
#endif // CIRCT_DIALECT_MOORE_MOOREPASSES_TD #endif // CIRCT_DIALECT_MOORE_MOOREPASSES_TD

View File

@ -1,4 +1,5 @@
add_circt_dialect_library(CIRCTMooreTransforms add_circt_dialect_library(CIRCTMooreTransforms
LowerConcatRef.cpp
SimplifyProcedures.cpp SimplifyProcedures.cpp

View File

@ -0,0 +1,113 @@
//===- LowerConcatRef.cpp - moore.concat_ref lowering ---------------------===//
//
// 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 LowerConcatRef pass.
// It's used to disassemble the moore.concat_ref. Which is tricky to lower
// directly. For example, disassemble "{a, b} = c" onto "a = c[7:3]"
// and "b = c[2:0]".
//
//===----------------------------------------------------------------------===//
#include "circt/Dialect/Moore/MooreOps.h"
#include "circt/Dialect/Moore/MoorePasses.h"
#include "mlir/Transforms/DialectConversion.h"
namespace circt {
namespace moore {
#define GEN_PASS_DEF_LOWERCONCATREF
#include "circt/Dialect/Moore/MoorePasses.h.inc"
} // namespace moore
} // namespace circt
using namespace circt;
using namespace moore;
using namespace mlir;
namespace {
// A helper function for collecting the non-concatRef operands of concatRef.
static void collectOperands(Value operand, SmallVectorImpl<Value> &operands) {
if (auto concatRefOp = operand.getDefiningOp<ConcatRefOp>())
for (auto nestedOperand : concatRefOp.getValues())
collectOperands(nestedOperand, operands);
else
operands.push_back(operand);
}
template <typename OpTy>
struct ConcatRefLowering : public OpConversionPattern<OpTy> {
using OpConversionPattern<OpTy>::OpConversionPattern;
using OpAdaptor = typename OpTy::Adaptor;
LogicalResult
matchAndRewrite(OpTy op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
// Use to collect the operands of concatRef.
SmallVector<Value, 4> operands;
collectOperands(op.getDst(), operands);
auto srcWidth =
cast<UnpackedType>(op.getSrc().getType()).getBitSize().value();
// Disassemble assignments with the LHS is concatRef. And create new
// corresponding assignments using non-concatRef LHS.
for (auto operand : operands) {
auto type = cast<RefType>(operand.getType()).getNestedType();
auto width = type.getBitSize().value();
rewriter.setInsertionPoint(op);
// FIXME: Need to estimate whether the bits range is from large to
// small or vice versa. Like "logic [7:0] or [0:7]".
// Only able to correctly handle the situation like "[7:0]" now.
auto i32 = moore::IntType::getInt(op.getContext(), 32);
auto lowBit =
rewriter.create<ConstantOp>(op.getLoc(), i32, srcWidth - width);
auto extract =
rewriter.create<ExtractOp>(op.getLoc(), type, op.getSrc(), lowBit);
// Update the real bit width of RHS of assignment. Like "c" the above
// description mentioned.
srcWidth = srcWidth - width;
rewriter.create<OpTy>(op.getLoc(), operand, extract);
}
rewriter.eraseOp(op);
return success();
}
};
struct LowerConcatRefPass
: public circt::moore::impl::LowerConcatRefBase<LowerConcatRefPass> {
void runOnOperation() override;
};
} // namespace
std::unique_ptr<mlir::Pass> circt::moore::createLowerConcatRefPass() {
return std::make_unique<LowerConcatRefPass>();
}
void LowerConcatRefPass::runOnOperation() {
MLIRContext &context = getContext();
ConversionTarget target(context);
target.addDynamicallyLegalOp<ContinuousAssignOp, BlockingAssignOp,
NonBlockingAssignOp>([](auto op) {
return !op->getOperand(0).template getDefiningOp<ConcatRefOp>();
});
target.addLegalDialect<MooreDialect>();
RewritePatternSet patterns(&context);
patterns.add<ConcatRefLowering<ContinuousAssignOp>,
ConcatRefLowering<BlockingAssignOp>,
ConcatRefLowering<NonBlockingAssignOp>>(&context);
if (failed(
applyPartialConversion(getOperation(), target, std::move(patterns))))
signalPassFailure();
}

View File

@ -53,7 +53,8 @@ void SimplifyProceduresPass::runOnOperation() {
// Collect the users of the global variable that is mentioned above. // Collect the users of the global variable that is mentioned above.
DenseSet<Operation *> users; DenseSet<Operation *> users;
for (auto *user : nestedOp.getOperand(0).getUsers()) for (auto *user : nestedOp.getOperand(0).getUsers())
if (!users.contains(user)) // Ensuring don't handle the users existing in another procedure body.
if (user->getBlock() == procedureOp.getBody())
users.insert(user); users.insert(user);
// Because the operand of moore.event_wait is net. // Because the operand of moore.event_wait is net.
@ -98,6 +99,5 @@ void SimplifyProceduresPass::runOnOperation() {
assignOps.erase(assignOp); assignOps.erase(assignOp);
} }
} }
return WalkResult::advance();
}); });
} }

View File

@ -0,0 +1,87 @@
// RUN: circt-opt --moore-lower-concatref %s | FileCheck %s
// CHECK-LABEL: moore.module @Foo()
moore.module @Foo() {
%a = moore.variable : <i8960>
%b = moore.variable : <i42>
%c = moore.variable : <i9002>
%u = moore.variable : <l8960>
%v = moore.variable : <l42>
%w = moore.variable : <l9002>
// CHECK: moore.concat_ref %a, %b
%0 = moore.concat_ref %a, %b : (!moore.ref<i8960>, !moore.ref<i42>) -> <i9002>
// CHECK: %[[C_READ:.+]] = moore.read %c : i9002
%1 = moore.read %c : i9002
// CHECK: %[[CONST_42:.+]] = moore.constant 42 : i32
// CHECK: %[[TMP1:.+]] = moore.extract %[[C_READ]] from %[[CONST_42]] : i9002, i32 -> i8960
// CHECK: moore.assign %a, %[[TMP1]] : i8960
// CHECK: %[[CONST_0:.+]] = moore.constant 0 : i32
// CHECK: %[[TMP2:.+]] = moore.extract %[[C_READ]] from %[[CONST_0]] : i9002, i32 -> i42
// CHECK: moore.assign %b, %[[TMP2]] : i42
moore.assign %0, %1 : i9002
moore.procedure always {
// CHECK: moore.concat_ref %u, %v
%2 = moore.concat_ref %u, %v : (!moore.ref<l8960>, !moore.ref<l42>) -> <l9002>
// CHECK: %[[W_READ:.+]] = moore.read %w : l9002
// CHECK: %[[CONST_42:.+]] = moore.constant 42 : i32
// CHECK: %[[TMP1:.+]] = moore.extract %[[W_READ]] from %[[CONST_42]] : l9002, i32 -> l8960
// CHECK: moore.blocking_assign %u, %[[TMP1]] : l8960
// CHECK: %[[CONST_0:.+]] = moore.constant 0 : i32
// CHECK: %[[TMP2:.+]] = moore.extract %[[W_READ]] from %[[CONST_0]] : l9002, i32 -> l42
// CHECK: moore.blocking_assign %v, %[[TMP2]] : l42
%3 = moore.read %w : l9002
moore.blocking_assign %2, %3 : l9002
%4 = moore.constant 1 : i32
%5 = moore.bool_cast %4 : i32 -> i1
%6 = moore.conversion %5 : !moore.i1 -> i1
scf.if %6 {
// CHECK: moore.concat_ref %u, %v
%7 = moore.concat_ref %u, %v : (!moore.ref<l8960>, !moore.ref<l42>) -> <l9002>
// CHECK: %[[W_READ:.+]] = moore.read %w : l9002
%8 = moore.read %w : l9002
// CHECK: %[[CONST_42:.+]] = moore.constant 42 : i32
// CHECK: %[[TMP1:.+]] = moore.extract %[[W_READ]] from %[[CONST_42]] : l9002, i32 -> l8960
// CHECK: moore.nonblocking_assign %u, %[[TMP1]] : l8960
// CHECK: %[[CONST_0:.+]] = moore.constant 0 : i32
// CHECK: %[[TMP2:.+]] = moore.extract %[[W_READ]] from %[[CONST_0]] : l9002, i32 -> l42
// CHECK: moore.nonblocking_assign %v, %[[TMP2]] : l42
moore.nonblocking_assign %7, %8 : l9002
}
}
moore.output
}
// CHECK-LABEL: moore.module @Nested()
moore.module @Nested() {
%x = moore.variable : <i32>
%y = moore.variable : <i32>
%z = moore.variable : <i32>
moore.procedure always {
// CHECK: %[[Z_READ:.+]] = moore.read %z : i32
%4 = moore.read %z : i32
// CHECK: %[[TMP1:.+]] = moore.conversion %[[Z_READ]] : !moore.i32 -> !moore.i96
%5 = moore.conversion %4 : !moore.i32 -> !moore.i96
// CHECK: %[[TMP2:.+]] = moore.conversion %[[TMP1]] : !moore.i96 -> !moore.i96
%6 = moore.conversion %5 : !moore.i96 -> !moore.i96
// CHECK: moore.concat_ref %x, %x
%0 = moore.concat_ref %x, %x : (!moore.ref<i32>, !moore.ref<i32>) -> <i64>
%1 = moore.concat_ref %0 : (!moore.ref<i64>) -> <i64>
%2 = moore.concat_ref %y : (!moore.ref<i32>) -> <i32>
%3 = moore.concat_ref %1, %2 : (!moore.ref<i64>, !moore.ref<i32>) -> <i96>
// CHECK: %[[CONST_64:.+]] = moore.constant 64 : i32
// CHECK: %[[TMP3:.+]] = moore.extract %[[TMP2]] from %[[CONST_64]] : i96, i32 -> i32
// CHECK: moore.blocking_assign %x, %[[TMP3]] : i32
// CHECK: %[[CONST_32:.+]] = moore.constant 32 : i32
// CHECK: %[[TMP4:.+]] = moore.extract %[[TMP2]] from %[[CONST_32]] : i96, i32 -> i32
// CHECK: moore.blocking_assign %x, %[[TMP4]] : i32
// CHECK: %[[CONST_0:.+]] = moore.constant 0 : i32
// CHECK: %[[TMP5:.+]] = moore.extract %[[TMP2]] from %[[CONST_0]] : i96, i32 -> i32
// CHECK: moore.blocking_assign %y, %[[TMP5]] : i32
moore.blocking_assign %3, %6 : i96
}
moore.output
}

View File

@ -3,6 +3,7 @@
// CHECK-LABEL: moore.module @Foo() // CHECK-LABEL: moore.module @Foo()
moore.module @Foo() { moore.module @Foo() {
%a = moore.variable : <i32> %a = moore.variable : <i32>
%u = moore.variable : <i32>
%x = moore.variable : <i32> %x = moore.variable : <i32>
%y = moore.variable : <i32> %y = moore.variable : <i32>
%z = moore.variable : <i32> %z = moore.variable : <i32>
@ -48,6 +49,16 @@ moore.module @Foo() {
// CHECK: moore.blocking_assign %z, %13 : i32 // CHECK: moore.blocking_assign %z, %13 : i32
moore.blocking_assign %z, %9 : i32 moore.blocking_assign %z, %9 : i32
} }
moore.procedure always_comb {
//CHECK: %0 = moore.read %a : i32
%0 = moore.read %a : i32
//CHECK: %local_a = moore.variable %0 : <i32>
//CHECK: %1 = moore.read %local_a : i32
//CHECK: moore.blocking_assign %u, %1 : i32
moore.blocking_assign %u, %0 : i32
}
// CHECK: moore.output // CHECK: moore.output
moore.output moore.output
} }

View File

@ -7,6 +7,7 @@ llvm_update_compile_flags(circt-verilog)
target_link_libraries(circt-verilog PRIVATE target_link_libraries(circt-verilog PRIVATE
CIRCTImportVerilog CIRCTImportVerilog
CIRCTMooreToCore CIRCTMooreToCore
CIRCTMooreTransforms
CIRCTSupport CIRCTSupport
MLIRIR MLIRIR
) )

View File

@ -14,6 +14,7 @@
#include "circt/Conversion/ImportVerilog.h" #include "circt/Conversion/ImportVerilog.h"
#include "circt/Conversion/MooreToCore.h" #include "circt/Conversion/MooreToCore.h"
#include "circt/Dialect/Moore/MoorePasses.h"
#include "circt/Support/Version.h" #include "circt/Support/Version.h"
#include "mlir/IR/BuiltinOps.h" #include "mlir/IR/BuiltinOps.h"
#include "mlir/Pass/PassManager.h" #include "mlir/Pass/PassManager.h"
@ -219,6 +220,9 @@ static CLOptions opts;
/// that simplify these files like deleting local variables, and then emit the /// that simplify these files like deleting local variables, and then emit the
/// resulting Moore dialect IR . /// resulting Moore dialect IR .
static LogicalResult populateMooreTransforms(mlir::PassManager &pm) { static LogicalResult populateMooreTransforms(mlir::PassManager &pm) {
auto &modulePM = pm.nest<moore::SVModuleOp>();
modulePM.addPass(moore::createLowerConcatRefPass());
modulePM.addPass(moore::createSimplifyProceduresPass());
pm.addPass(mlir::createMem2Reg()); pm.addPass(mlir::createMem2Reg());
// TODO: like dedup pass. // TODO: like dedup pass.