mirror of https://github.com/llvm/circt.git
[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:
parent
d8b0baade5
commit
d360917dae
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
add_circt_dialect_library(CIRCTMooreTransforms
|
add_circt_dialect_library(CIRCTMooreTransforms
|
||||||
|
LowerConcatRef.cpp
|
||||||
SimplifyProcedures.cpp
|
SimplifyProcedures.cpp
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue