[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:
Morten Borup Petersen 2022-01-06 17:00:29 +01:00 committed by GitHub
parent c9c50d2889
commit 898118be38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 28 deletions

View File

@ -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"> {

View File

@ -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;
}

View File

@ -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
}