mirror of https://github.com/llvm/circt.git
[StandardToHandshake] Optionally connect constants to Source ops (#2422)
Control-network connected constants may help debugability, but may result in slightly larger circuits. This commit provides an option to connect constants to Source ops instead.
This commit is contained in:
parent
c9c50d2889
commit
898118be38
|
@ -189,6 +189,10 @@ def StandardToHandshake : Pass<"lower-std-to-handshake", "mlir::ModuleOp"> {
|
|||
let summary = "Lower Standard MLIR into Handshake IR";
|
||||
let constructor = "circt::createStandardToHandshakePass()";
|
||||
let dependentDialects = ["handshake::HandshakeDialect"];
|
||||
let options =
|
||||
[Option<"sourceConstants", "source-constants", "bool", "false",
|
||||
"If true, will connect constants to source operations instead of "
|
||||
"to the control network. May reduce the size of the final circuit.">];
|
||||
}
|
||||
|
||||
def HandshakeRemoveBlock : Pass<"handshake-remove-block-structure", "handshake::FuncOp"> {
|
||||
|
|
|
@ -536,33 +536,32 @@ LogicalResult addBranchOps(handshake::FuncOp f,
|
|||
}
|
||||
|
||||
LogicalResult connectConstantsToControl(handshake::FuncOp f,
|
||||
ConversionPatternRewriter &rewriter) {
|
||||
// Create new constants which have a control-only input to trigger them
|
||||
// Connect input to ControlMerge (trigger const when its block is entered)
|
||||
ConversionPatternRewriter &rewriter,
|
||||
bool sourceConstants) {
|
||||
// Create new constants which have a control-only input to trigger them. These
|
||||
// are conneted to the control network or optionally to a Source operation
|
||||
// (always triggering). Control-network connected constants may help
|
||||
// debugability, but result in a slightly larger circuit.
|
||||
|
||||
for (Block &block : f) {
|
||||
Operation *cntrlMg =
|
||||
block.isEntryBlock() ? getStartOp(&block) : getControlMerge(&block);
|
||||
assert(cntrlMg != nullptr);
|
||||
std::vector<Operation *> cstOps;
|
||||
for (Operation &op : block) {
|
||||
if (auto constantOp = dyn_cast<arith::ConstantOp>(op)) {
|
||||
rewriter.setInsertionPointAfter(&op);
|
||||
Operation *newOp = rewriter.create<handshake::ConstantOp>(
|
||||
op.getLoc(), constantOp.value(), cntrlMg->getResult(0));
|
||||
|
||||
op.getResult(0).replaceAllUsesWith(newOp->getResult(0));
|
||||
cstOps.push_back(&op);
|
||||
}
|
||||
if (sourceConstants) {
|
||||
for (auto constantOp :
|
||||
llvm::make_early_inc_range(f.getOps<arith::ConstantOp>())) {
|
||||
rewriter.setInsertionPointAfter(constantOp);
|
||||
rewriter.replaceOpWithNewOp<handshake::ConstantOp>(
|
||||
constantOp, constantOp.value(),
|
||||
rewriter.create<handshake::SourceOp>(constantOp.getLoc()));
|
||||
}
|
||||
|
||||
// Erase StandardOp constants
|
||||
for (unsigned i = 0, e = cstOps.size(); i != e; ++i) {
|
||||
auto *op = cstOps[i];
|
||||
for (int j = 0, e = op->getNumOperands(); j < e; ++j)
|
||||
op->eraseOperand(0);
|
||||
assert(op->getNumOperands() == 0);
|
||||
rewriter.eraseOp(op);
|
||||
} else {
|
||||
for (Block &block : f) {
|
||||
Operation *cntrlMg =
|
||||
block.isEntryBlock() ? getStartOp(&block) : getControlMerge(&block);
|
||||
assert(cntrlMg != nullptr && "No control operation found in block");
|
||||
for (auto constantOp :
|
||||
llvm::make_early_inc_range(block.getOps<arith::ConstantOp>())) {
|
||||
rewriter.setInsertionPointAfter(constantOp);
|
||||
rewriter.replaceOpWithNewOp<handshake::ConstantOp>(
|
||||
constantOp, constantOp.value(), cntrlMg->getResult(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
return success();
|
||||
|
@ -1298,7 +1297,8 @@ LogicalResult replaceCallOps(handshake::FuncOp f,
|
|||
if (failed(logicalResult)) \
|
||||
return logicalResult;
|
||||
|
||||
LogicalResult lowerFuncOp(mlir::FuncOp funcOp, MLIRContext *ctx) {
|
||||
LogicalResult lowerFuncOp(mlir::FuncOp funcOp, MLIRContext *ctx,
|
||||
bool sourceConstants) {
|
||||
// Only retain those attributes that are not constructed by build.
|
||||
SmallVector<NamedAttribute, 4> attributes;
|
||||
for (const auto &attr : funcOp->getAttrs()) {
|
||||
|
@ -1357,7 +1357,10 @@ LogicalResult lowerFuncOp(mlir::FuncOp funcOp, MLIRContext *ctx) {
|
|||
returnOnError(
|
||||
partiallyLowerFuncOp<handshake::FuncOp>(addSinkOps, ctx, newFuncOp));
|
||||
returnOnError(partiallyLowerFuncOp<handshake::FuncOp>(
|
||||
connectConstantsToControl, ctx, newFuncOp));
|
||||
[&](handshake::FuncOp f, ConversionPatternRewriter &rewriter) {
|
||||
return connectConstantsToControl(f, rewriter, sourceConstants);
|
||||
},
|
||||
ctx, newFuncOp));
|
||||
returnOnError(
|
||||
partiallyLowerFuncOp<handshake::FuncOp>(addForkOps, ctx, newFuncOp));
|
||||
returnOnError(checkDataflowConversion(newFuncOp));
|
||||
|
@ -1420,7 +1423,7 @@ struct StandardToHandshakePass
|
|||
ModuleOp m = getOperation();
|
||||
|
||||
for (auto funcOp : llvm::make_early_inc_range(m.getOps<mlir::FuncOp>())) {
|
||||
if (failed(lowerFuncOp(funcOp, &getContext()))) {
|
||||
if (failed(lowerFuncOp(funcOp, &getContext(), sourceConstants))) {
|
||||
signalPassFailure();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// RUN: circt-opt -lower-std-to-handshake="source-constants" %s --canonicalize | FileCheck %s
|
||||
|
||||
// CHECK-LABEL: handshake.func @foo(
|
||||
// CHECK-SAME: %[[VAL_0:.*]]: i32,
|
||||
// CHECK-SAME: %[[VAL_1:.*]]: none, ...) -> (i32, none) attributes {argNames = ["in0", "inCtrl"], resNames = ["out0", "outCtrl"]} {
|
||||
// CHECK: %[[VAL_2:.*]] = source
|
||||
// CHECK: %[[VAL_3:.*]] = constant %[[VAL_2]] {value = 1 : i32} : i32
|
||||
// CHECK: %[[VAL_4:.*]] = arith.addi %[[VAL_0]], %[[VAL_3]] : i32
|
||||
// CHECK: return %[[VAL_4]], %[[VAL_1]] : i32, none
|
||||
// CHECK: }
|
||||
|
||||
func @foo(%arg0 : i32) -> i32 {
|
||||
%c1_i32 = arith.constant 1 : i32
|
||||
%0 = arith.addi %arg0, %c1_i32 : i32
|
||||
return %0 : i32
|
||||
}
|
Loading…
Reference in New Issue