Skip to content

Commit

Permalink
[Handshake] Add control operand to handshake.instance operation (ll…
Browse files Browse the repository at this point in the history
…vm#2055)

When lowering a `handshake.instance` operation, we need to be able to decide when the instance is activated; or in other words, when the input control signal is asserted. Lowering from a CDFG to handshake, this will be whenever control flow arrived to the basic block of the source `builtin.call` operation, which the `handshake.instance` op originated from. With this new operand, we are able to be explicit about the activation of the `handshake.instance` op, and be able to implement lowering of the operation to FIRRTL.
  • Loading branch information
mortbopet authored Nov 1, 2021
1 parent f390004 commit 10b2bd4
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 6 deletions.
7 changes: 7 additions & 0 deletions include/circt/Dialect/Handshake/HandshakeOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,18 @@ def InstanceOp : Handshake_Op<"instance", [CallOpInterface]> {
CallInterfaceCallable getCallableForCallee() {
return (*this)->getAttrOfType<SymbolRefAttr>("module");
}

/// Get the control operand of this instance op
Value getControl() {
return getOperands().back();
}
}];

let assemblyFormat = [{
$module `(` $operands `)` attr-dict `:` functional-type($operands, results)
}];

let verifier = [{ return ::verify$cppClass(*this); }];
}

// This is almost exactly like a standard FuncOp, except that it has some
Expand Down
13 changes: 10 additions & 3 deletions lib/Conversion/StandardToHandshake/StandardToHandshake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1636,12 +1636,19 @@ struct HandshakeCanonicalizePattern : public ConversionPattern {
LogicalResult replaceCallOps(handshake::FuncOp f,
ConversionPatternRewriter &rewriter) {
for (Block &block : f) {
/// An instance is activated whenever control arrives at the basic block of
/// the source callOp.
Operation *cntrlMg =
block.isEntryBlock() ? getStartOp(&block) : getControlMerge(&block);
assert(cntrlMg);
for (Operation &op : block) {
if (auto callOp = dyn_cast<CallOp>(op)) {
llvm::SmallVector<Value> operands;
llvm::copy(callOp.getOperands(), std::back_inserter(operands));
operands.push_back(cntrlMg->getResult(0));
rewriter.setInsertionPoint(callOp);
rewriter.replaceOpWithNewOp<handshake::InstanceOp>(
callOp, callOp.getCallee(), callOp.getResultTypes(),
callOp.getOperands());
callOp, callOp.getCallee(), callOp.getResultTypes(), operands);
}
}
}
Expand Down Expand Up @@ -1701,10 +1708,10 @@ LogicalResult lowerFuncOp(mlir::FuncOp funcOp, MLIRContext *ctx) {
},
ctx, newFuncOp);

(void)partiallyLowerFuncOp<handshake::FuncOp>(replaceCallOps, ctx, newFuncOp);
(void)partiallyLowerFuncOp<handshake::FuncOp>(setControlOnlyPath, ctx,
newFuncOp);
(void)partiallyLowerFuncOp<handshake::FuncOp>(addMergeOps, ctx, newFuncOp);
(void)partiallyLowerFuncOp<handshake::FuncOp>(replaceCallOps, ctx, newFuncOp);
(void)partiallyLowerFuncOp<handshake::FuncOp>(addBranchOps, ctx, newFuncOp);
(void)partiallyLowerFuncOp<handshake::FuncOp>(addSinkOps, ctx, newFuncOp);
(void)partiallyLowerFuncOp<handshake::FuncOp>(connectConstantsToControl, ctx,
Expand Down
11 changes: 11 additions & 0 deletions lib/Dialect/Handshake/HandshakeOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,17 @@ bool handshake::JoinOp::tryExecute(
return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
}

static LogicalResult verifyInstanceOp(handshake::InstanceOp op) {
if (op->getNumOperands() == 0)
return op.emitOpError() << "must provide at least a control operand.";

if (!op.getControl().getType().dyn_cast<NoneType>())
return op.emitOpError()
<< "last operand must be a control (none-typed) operand.";

return success();
}

//===----------------------------------------------------------------------===//
// TableGen'd op method definitions
//===----------------------------------------------------------------------===//
Expand Down
64 changes: 61 additions & 3 deletions test/Conversion/StandardToHandshake/test_call.mlir
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: circt-opt -lower-std-to-handshake %s | FileCheck %s
// RUN: circt-opt -lower-std-to-handshake -split-input-file %s | FileCheck %s

func @bar(%0 : i32) -> i32 {
// CHECK-LABEL: handshake.func @bar(
// CHECK-SAME: %[[VAL_0:.*]]: i32,
Expand All @@ -15,10 +16,67 @@ func @foo(%0 : i32) -> i32 {
// CHECK-SAME: %[[VAL_0:.*]]: i32,
// CHECK-SAME: %[[VAL_1:.*]]: none, ...) -> (i32, none) {
// CHECK: %[[VAL_2:.*]] = "handshake.merge"(%[[VAL_0]]) : (i32) -> i32
// CHECK: %[[VAL_3:.*]] = handshake.instance @bar(%[[VAL_2]]) : (i32) -> i32
// CHECK: handshake.return %[[VAL_3]], %[[VAL_1]] : i32, none
// CHECK: %[[VAL_4:.*]]:2 = "handshake.fork"(%[[VAL_1]]) {control = true} : (none) -> (none, none)
// CHECK: %[[VAL_3:.*]] = handshake.instance @bar(%[[VAL_2]], %[[VAL_4]]#0) : (i32, none) -> i32
// CHECK: handshake.return %[[VAL_3]], %[[VAL_4]]#1 : i32, none
// CHECK: }

%a1 = call @bar(%0) : (i32) -> i32
return %a1 : i32
}

// -----

// Branching control flow with calls in each branch.

// CHECK-LABEL: handshake.func @add(
func @add(%arg0 : i32, %arg1: i32) -> i32 {
%0 = arith.addi %arg0, %arg1 : i32
return %0 : i32
}

// CHECK-LABEL: handshake.func @sub(
func @sub(%arg0 : i32, %arg1: i32) -> i32 {
%0 = arith.subi %arg0, %arg1 : i32
return %0 : i32
}

// CHECK: handshake.func @main(%[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i1, %[[VAL_3:.*]]: none, ...) -> (i32, none) {
// CHECK: %[[VAL_4:.*]] = "handshake.merge"(%[[VAL_0]]) : (i32) -> i32
// CHECK: %[[VAL_5:.*]] = "handshake.merge"(%[[VAL_1]]) : (i32) -> i32
// CHECK: %[[VAL_6:.*]] = "handshake.merge"(%[[VAL_2]]) : (i1) -> i1
// CHECK: %[[VAL_7:.*]]:3 = "handshake.fork"(%[[VAL_6]]) {control = false} : (i1) -> (i1, i1, i1)
// CHECK: %[[VAL_8:.*]], %[[VAL_9:.*]] = "handshake.conditional_branch"(%[[VAL_7]]#2, %[[VAL_4]]) {control = false} : (i1, i32) -> (i32, i32)
// CHECK: %[[VAL_10:.*]], %[[VAL_11:.*]] = "handshake.conditional_branch"(%[[VAL_7]]#1, %[[VAL_5]]) {control = false} : (i1, i32) -> (i32, i32)
// CHECK: %[[VAL_12:.*]], %[[VAL_13:.*]] = "handshake.conditional_branch"(%[[VAL_7]]#0, %[[VAL_3]]) {control = true} : (i1, none) -> (none, none)
// CHECK: %[[VAL_14:.*]] = "handshake.merge"(%[[VAL_8]]) : (i32) -> i32
// CHECK: %[[VAL_15:.*]] = "handshake.merge"(%[[VAL_10]]) : (i32) -> i32
// CHECK: %[[VAL_16:.*]]:2 = "handshake.control_merge"(%[[VAL_12]]) {control = true} : (none) -> (none, index)
// CHECK: %[[VAL_17:.*]]:2 = "handshake.fork"(%[[VAL_16]]#0) {control = true} : (none) -> (none, none)
// CHECK: "handshake.sink"(%[[VAL_16]]#1) : (index) -> ()
// CHECK: %[[VAL_18:.*]] = handshake.instance @add(%[[VAL_14]], %[[VAL_15]], %[[VAL_17]]#1) : (i32, i32, none) -> i32
// CHECK: %[[VAL_19:.*]] = "handshake.branch"(%[[VAL_17]]#0) {control = true} : (none) -> none
// CHECK: %[[VAL_20:.*]] = "handshake.branch"(%[[VAL_18]]) {control = false} : (i32) -> i32
// CHECK: %[[VAL_21:.*]] = "handshake.merge"(%[[VAL_9]]) : (i32) -> i32
// CHECK: %[[VAL_22:.*]] = "handshake.merge"(%[[VAL_11]]) : (i32) -> i32
// CHECK: %[[VAL_23:.*]]:2 = "handshake.control_merge"(%[[VAL_13]]) {control = true} : (none) -> (none, index)
// CHECK: %[[VAL_24:.*]]:2 = "handshake.fork"(%[[VAL_23]]#0) {control = true} : (none) -> (none, none)
// CHECK: "handshake.sink"(%[[VAL_23]]#1) : (index) -> ()
// CHECK: %[[VAL_25:.*]] = handshake.instance @sub(%[[VAL_21]], %[[VAL_22]], %[[VAL_24]]#1) : (i32, i32, none) -> i32
// CHECK: %[[VAL_26:.*]] = "handshake.branch"(%[[VAL_24]]#0) {control = true} : (none) -> none
// CHECK: %[[VAL_27:.*]] = "handshake.branch"(%[[VAL_25]]) {control = false} : (i32) -> i32
// CHECK: %[[VAL_28:.*]]:2 = "handshake.control_merge"(%[[VAL_26]], %[[VAL_19]]) {control = true} : (none, none) -> (none, index)
// CHECK: %[[VAL_29:.*]] = "handshake.mux"(%[[VAL_28]]#1, %[[VAL_27]], %[[VAL_20]]) : (index, i32, i32) -> i32
// CHECK: handshake.return %[[VAL_29]], %[[VAL_28]]#0 : i32, none
// CHECK: }
func @main(%arg0 : i32, %arg1 : i32, %cond : i1) -> i32 {
cond_br %cond, ^bb1, ^bb2
^bb1:
%0 = call @add(%arg0, %arg1) : (i32, i32) -> i32
br ^bb3(%0 : i32)
^bb2:
%1 = call @sub(%arg0, %arg1) : (i32, i32) -> i32
br ^bb3(%1 : i32)
^bb3(%res : i32):
return %res : i32
}
24 changes: 24 additions & 0 deletions test/Dialect/Handshake/errors.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,27 @@ handshake.func @invalid_mux_narrow_select(%arg0: i1, %arg1: i32, %arg2: i32, %ar
%0 = "handshake.mux"(%arg0, %arg1, %arg2, %arg3) : (i1, i32, i32, i32) -> (i32)
return %0 : i32
}

// -----

handshake.func @foo(%ctrl : none) -> none{
handshake.return %ctrl : none
}

handshake.func @invalid_instance_op(%arg0 : i32, %ctrl : none) -> none {
// expected-error @+1 {{'handshake.instance' op last operand must be a control (none-typed) operand.}}
handshake.instance @foo(%ctrl, %arg0) : (none, i32) -> ()
handshake.return %ctrl : none
}

// -----

handshake.func @foo(%ctrl : none) -> none{
handshake.return %ctrl : none
}

handshake.func @invalid_instance_op(%ctrl : none) -> none {
// expected-error @+1 {{'handshake.instance' op must provide at least a control operand.}}
handshake.instance @foo() : () -> ()
handshake.return %ctrl : none
}
26 changes: 26 additions & 0 deletions test/handshake-runner/call_controlflow.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: handshake-runner %s 3 2 1 | FileCheck %s
// RUN: circt-opt -lower-std-to-handshake %s > handshake.mlir
// RUN handshake-runner handshake.mlir 3 2 1 | FileCheck %s
// CHECK: 5

func @add(%arg0 : i32, %arg1: i32) -> i32 {
%0 = arith.addi %arg0, %arg1 : i32
return %0 : i32
}

func @sub(%arg0 : i32, %arg1: i32) -> i32 {
%0 = arith.subi %arg0, %arg1 : i32
return %0 : i32
}

func @main(%arg0 : i32, %arg1 : i32, %cond : i1) -> i32 {
cond_br %cond, ^bb1, ^bb2
^bb1:
%0 = call @add(%arg0, %arg1) : (i32, i32) -> i32
br ^bb3(%0 : i32)
^bb2:
%1 = call @sub(%arg0, %arg1) : (i32, i32) -> i32
br ^bb3(%1 : i32)
^bb3(%res : i32):
return %res : i32
}

0 comments on commit 10b2bd4

Please sign in to comment.