From ef4531315922f807b3f03a8edfc0404fef927c45 Mon Sep 17 00:00:00 2001 From: Morten Borup Petersen Date: Thu, 23 Sep 2021 18:11:31 +0100 Subject: [PATCH] [SCFToCalyx] Add combinational group inlining [11/12] (#1862) This commit introduces combinational group inlining. The conversion strategy of SCFToCalyx is to create a distinct CombGroupOp for each combinational operator in the source program. This implies that the created CombGroupOp is analogous to the SSA value which the original operation represented. In Calyx, a source operand for a `calyx.assign` must be fully defined within the group that performs the assignment. This means that the source operand is either the output of a sequential component, or a fully defined combinational assignment chain. This commit achieves the latter by recursively inlining use-def chains op combinational logic, by backtracking through the CombGroupOps which generated the combinational assignments of the original program. --- lib/Conversion/SCFToCalyx/SCFToCalyx.cpp | 83 ++++++ .../SCFToCalyx/convert_controlflow.mlir | 238 +++++++++++++++--- .../Conversion/SCFToCalyx/convert_simple.mlir | 22 +- 3 files changed, 299 insertions(+), 44 deletions(-) diff --git a/lib/Conversion/SCFToCalyx/SCFToCalyx.cpp b/lib/Conversion/SCFToCalyx/SCFToCalyx.cpp index 27457067bdbe..503bb26e73cc 100644 --- a/lib/Conversion/SCFToCalyx/SCFToCalyx.cpp +++ b/lib/Conversion/SCFToCalyx/SCFToCalyx.cpp @@ -1234,6 +1234,85 @@ class BuildControl : public FuncOpPartialLoweringPattern { } }; +/// This pass recursively inlines use-def chains of combinational logic (from +/// non-stateful groups) into groups referenced in the control schedule. +class InlineCombGroups + : public PartialLoweringPattern { +public: + InlineCombGroups(MLIRContext *context, LogicalResult &resRef, + ProgramLoweringState &pls) + : PartialLoweringPattern(context, resRef), pls(pls) {} + + LogicalResult partiallyLower(calyx::GroupInterface originGroup, + PatternRewriter &rewriter) const override { + auto &state = pls.compLoweringState( + originGroup->getParentOfType()); + + /// Filter groups which are not part of the control schedule. + if (SymbolTable::symbolKnownUseEmpty(originGroup.symName(), + state.getComponentOp().getControlOp())) + return success(); + + /// Maintain a set of the groups which we've inlined so far. The group + /// itself is implicitly inlined. + llvm::SmallSetVector inlinedGroups; + inlinedGroups.insert(originGroup); + + /// Starting from the matched originGroup, we traverse use-def chains of + /// combinational logic, and inline assignments from the defining + /// combinational groups. + recurseInlineCombGroups( + rewriter, state, inlinedGroups, originGroup, originGroup, + /*disable inlining of the originGroup itself*/ false); + return success(); + } + +private: + void + recurseInlineCombGroups(PatternRewriter &rewriter, + ComponentLoweringState &state, + llvm::SmallSetVector &inlinedGroups, + calyx::GroupInterface originGroup, + calyx::GroupInterface recGroup, bool doInline) const { + inlinedGroups.insert(recGroup); + for (auto assignOp : recGroup.getBody()->getOps()) { + if (doInline) { + /// Inline the assignment into the originGroup. + auto clonedAssignOp = rewriter.clone(*assignOp.getOperation()); + clonedAssignOp->moveBefore(originGroup.getBody(), + originGroup.getBody()->end()); + } + auto src = assignOp.src(); + auto srcDefOp = src.getDefiningOp(); + + /// Things which stop recursive inlining (or in other words, what + /// breaks combinational paths). + /// - Component inputs + /// - Register and memory reads + /// - Constant ops (constant ops are not evaluated by any group) + /// - 'While' return values (these are registers, however, 'while' + /// return values have at the current point of conversion not yet + /// been rewritten to their register outputs, see comment in + /// LateSSAReplacement) + if (src.isa() || + isa(srcDefOp)) + continue; + + auto srcCombGroup = state.getEvaluatingGroup(src); + assert(srcCombGroup && "expected combinational group"); + if (inlinedGroups.count(srcCombGroup)) + continue; + + recurseInlineCombGroups(rewriter, state, inlinedGroups, originGroup, + srcCombGroup, true); + } + } + + ProgramLoweringState &pls; +}; + /// LateSSAReplacement contains various functions for replacing SSA values that /// were not replaced during op construction. class LateSSAReplacement : public FuncOpPartialLoweringPattern { @@ -1665,6 +1744,10 @@ void SCFToCalyxPass::runOnOperation() { /// basic block in the source function. addOncePattern(loweringPatterns, funcMap, *loweringState); + /// This pass recursively inlines use-def chains of combinational logic (from + /// non-stateful groups) into groups referenced in the control schedule. + addOncePattern(loweringPatterns, *loweringState); + /// This pattern performs various SSA replacements that must be done /// after control generation. addOncePattern(loweringPatterns, funcMap, *loweringState); diff --git a/test/Conversion/SCFToCalyx/convert_controlflow.mlir b/test/Conversion/SCFToCalyx/convert_controlflow.mlir index 924a6a96c14c..4751908030d9 100644 --- a/test/Conversion/SCFToCalyx/convert_controlflow.mlir +++ b/test/Conversion/SCFToCalyx/convert_controlflow.mlir @@ -61,31 +61,80 @@ module { // ----- +// Test a while op where the loop body contains basic blocks that may be simplified. + // CHECK: module { // CHECK-NEXT: calyx.program "main" { // CHECK-NEXT: calyx.component @main(%in0: i32, %in1: i32, %in2: i32, %clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%out0: i32, %done: i1 {done}) { // CHECK-NEXT: %true = hw.constant true +// CHECK-NEXT: %c0_i32 = hw.constant 0 : i32 +// CHECK-NEXT: %std_add_2.left, %std_add_2.right, %std_add_2.out = calyx.std_add "std_add_2" : i32, i32, i32 +// CHECK-NEXT: %std_add_1.left, %std_add_1.right, %std_add_1.out = calyx.std_add "std_add_1" : i32, i32, i32 +// CHECK-NEXT: %std_add_0.left, %std_add_0.right, %std_add_0.out = calyx.std_add "std_add_0" : i32, i32, i32 +// CHECK-NEXT: %std_slt_1.left, %std_slt_1.right, %std_slt_1.out = calyx.std_slt "std_slt_1" : i32, i32, i1 // CHECK-NEXT: %std_slt_0.left, %std_slt_0.right, %std_slt_0.out = calyx.std_slt "std_slt_0" : i32, i32, i1 +// CHECK-NEXT: %while_0_arg2_reg.in, %while_0_arg2_reg.write_en, %while_0_arg2_reg.clk, %while_0_arg2_reg.reset, %while_0_arg2_reg.out, %while_0_arg2_reg.done = calyx.register "while_0_arg2_reg" : i32, i1, i1, i1, i32, i1 +// CHECK-NEXT: %while_0_arg1_reg.in, %while_0_arg1_reg.write_en, %while_0_arg1_reg.clk, %while_0_arg1_reg.reset, %while_0_arg1_reg.out, %while_0_arg1_reg.done = calyx.register "while_0_arg1_reg" : i32, i1, i1, i1, i32, i1 // CHECK-NEXT: %while_0_arg0_reg.in, %while_0_arg0_reg.write_en, %while_0_arg0_reg.clk, %while_0_arg0_reg.reset, %while_0_arg0_reg.out, %while_0_arg0_reg.done = calyx.register "while_0_arg0_reg" : i32, i1, i1, i1, i32, i1 // CHECK-NEXT: %ret_arg0_reg.in, %ret_arg0_reg.write_en, %ret_arg0_reg.clk, %ret_arg0_reg.reset, %ret_arg0_reg.out, %ret_arg0_reg.done = calyx.register "ret_arg0_reg" : i32, i1, i1, i1, i32, i1 +// CHECK-NEXT: %bb3_arg1_reg.in, %bb3_arg1_reg.write_en, %bb3_arg1_reg.clk, %bb3_arg1_reg.reset, %bb3_arg1_reg.out, %bb3_arg1_reg.done = calyx.register "bb3_arg1_reg" : i32, i1, i1, i1, i32, i1 +// CHECK-NEXT: %bb3_arg0_reg.in, %bb3_arg0_reg.write_en, %bb3_arg0_reg.clk, %bb3_arg0_reg.reset, %bb3_arg0_reg.out, %bb3_arg0_reg.done = calyx.register "bb3_arg0_reg" : i32, i1, i1, i1, i32, i1 // CHECK-NEXT: calyx.wires { // CHECK-NEXT: calyx.assign %out0 = %ret_arg0_reg.out : i32 // CHECK-NEXT: calyx.group @assign_while_0_init { // CHECK-NEXT: calyx.assign %while_0_arg0_reg.in = %in0 : i32 // CHECK-NEXT: calyx.assign %while_0_arg0_reg.write_en = %true : i1 -// CHECK-NEXT: calyx.group_done %while_0_arg0_reg.done : i1 +// CHECK-NEXT: calyx.assign %while_0_arg1_reg.in = %c0_i32 : i32 +// CHECK-NEXT: calyx.assign %while_0_arg1_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %while_0_arg2_reg.in = %c0_i32 : i32 +// CHECK-NEXT: calyx.assign %while_0_arg2_reg.write_en = %true : i1 +// CHECK-NEXT: %0 = comb.and %while_0_arg0_reg.done, %while_0_arg1_reg.done : i1 +// CHECK-NEXT: %1 = comb.and %0, %while_0_arg2_reg.done : i1 +// CHECK-NEXT: calyx.group_done %true, %1 ? : i1 // CHECK-NEXT: } // CHECK-NEXT: calyx.comb_group @bb0_0 { // CHECK-NEXT: calyx.assign %std_slt_0.left = %while_0_arg0_reg.out : i32 // CHECK-NEXT: calyx.assign %std_slt_0.right = %in1 : i32 // CHECK-NEXT: } +// CHECK-NEXT: calyx.comb_group @bb0_1 { +// CHECK-NEXT: calyx.assign %std_slt_1.left = %while_0_arg1_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_slt_1.right = %while_0_arg2_reg.out : i32 +// CHECK-NEXT: } +// CHECK-NEXT: calyx.group @bb1_to_bb3 { +// CHECK-NEXT: calyx.assign %bb3_arg0_reg.in = %std_add_0.out : i32 +// CHECK-NEXT: calyx.assign %bb3_arg0_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %bb3_arg1_reg.in = %std_add_0.out : i32 +// CHECK-NEXT: calyx.assign %bb3_arg1_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %std_add_0.left = %while_0_arg1_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_add_0.right = %while_0_arg2_reg.out : i32 +// CHECK-NEXT: %0 = comb.and %bb3_arg0_reg.done, %bb3_arg1_reg.done : i1 +// CHECK-NEXT: calyx.group_done %true, %0 ? : i1 +// CHECK-NEXT: } +// CHECK-NEXT: calyx.group @bb2_to_bb3 { +// CHECK-NEXT: calyx.assign %bb3_arg0_reg.in = %std_add_1.out : i32 +// CHECK-NEXT: calyx.assign %bb3_arg0_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %bb3_arg1_reg.in = %std_add_1.out : i32 +// CHECK-NEXT: calyx.assign %bb3_arg1_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %std_add_1.left = %while_0_arg1_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_add_1.right = %while_0_arg2_reg.out : i32 +// CHECK-NEXT: %0 = comb.and %bb3_arg0_reg.done, %bb3_arg1_reg.done : i1 +// CHECK-NEXT: calyx.group_done %true, %0 ? : i1 +// CHECK-NEXT: } // CHECK-NEXT: calyx.group @assign_while_0_latch { -// CHECK-NEXT: calyx.assign %while_0_arg0_reg.in = %while_0_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %while_0_arg0_reg.in = %std_add_2.out : i32 // CHECK-NEXT: calyx.assign %while_0_arg0_reg.write_en = %true : i1 -// CHECK-NEXT: calyx.group_done %while_0_arg0_reg.done : i1 +// CHECK-NEXT: calyx.assign %while_0_arg1_reg.in = %bb3_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %while_0_arg1_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %while_0_arg2_reg.in = %bb3_arg1_reg.out : i32 +// CHECK-NEXT: calyx.assign %while_0_arg2_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %std_add_2.left = %while_0_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_add_2.right = %in2 : i32 +// CHECK-NEXT: %0 = comb.and %while_0_arg0_reg.done, %while_0_arg1_reg.done : i1 +// CHECK-NEXT: %1 = comb.and %0, %while_0_arg2_reg.done : i1 +// CHECK-NEXT: calyx.group_done %true, %1 ? : i1 // CHECK-NEXT: } // CHECK-NEXT: calyx.group @ret_assign_0 { -// CHECK-NEXT: calyx.assign %ret_arg0_reg.in = %while_0_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %ret_arg0_reg.in = %while_0_arg2_reg.out : i32 // CHECK-NEXT: calyx.assign %ret_arg0_reg.write_en = %true : i1 // CHECK-NEXT: calyx.group_done %ret_arg0_reg.done : i1 // CHECK-NEXT: } @@ -95,6 +144,15 @@ module { // CHECK-NEXT: calyx.enable @assign_while_0_init // CHECK-NEXT: calyx.while %std_slt_0.out with @bb0_0 { // CHECK-NEXT: calyx.seq { +// CHECK-NEXT: calyx.if %std_slt_1.out with @bb0_1 { +// CHECK-NEXT: calyx.seq { +// CHECK-NEXT: calyx.enable @bb1_to_bb3 +// CHECK-NEXT: } +// CHECK-NEXT: } else { +// CHECK-NEXT: calyx.seq { +// CHECK-NEXT: calyx.enable @bb2_to_bb3 +// CHECK-NEXT: } +// CHECK-NEXT: } // CHECK-NEXT: calyx.enable @assign_while_0_latch // CHECK-NEXT: } // CHECK-NEXT: } @@ -107,66 +165,104 @@ module { module { func @main(%arg0: i32, %arg1: i32, %arg2: i32) -> i32 { %cst = constant 0 : i32 - %0 = scf.while (%arg3 = %arg0) : (i32) -> (i32) { + %0:3 = scf.while (%arg3 = %arg0, %arg4 = %cst, %arg5 = %cst) : (i32, i32, i32) -> (i32, i32, i32) { %1 = cmpi slt, %arg3, %arg1 : i32 - scf.condition(%1) %arg3 : i32 + scf.condition(%1) %arg3, %arg4, %arg5 : i32, i32, i32 } do { - ^bb0(%arg3: i32): // no predecessors - scf.yield %arg3 : i32 + ^bb0(%arg3: i32, %arg4: i32, %arg5: i32): // no predecessors + %1:2 = scf.execute_region -> (i32, i32) { + %4 = cmpi slt, %arg4, %arg5 : i32 + cond_br %4, ^bb0, ^bb1 + ^bb0: + %3 = addi %arg4, %arg5 : i32 + scf.yield %3, %3 : i32, i32 + ^bb1: + %5 = addi %arg4, %arg5 : i32 + scf.yield %5, %5 : i32, i32 + } + %2 = addi %arg3, %arg2 : i32 + scf.yield %2, %1#0, %1#1 : i32, i32, i32 } - return %0 : i32 + return %0#2 : i32 } } // ----- +// Test an executeRegion with multiple yields. + // CHECK: module { // CHECK-NEXT: calyx.program "main" { // CHECK-NEXT: calyx.component @main(%in0: i32, %in1: i32, %in2: i32, %clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%out0: i32, %done: i1 {done}) { // CHECK-NEXT: %true = hw.constant true +// CHECK-NEXT: %c0_i32 = hw.constant 0 : i32 +// CHECK-NEXT: %std_add_1.left, %std_add_1.right, %std_add_1.out = calyx.std_add "std_add_1" : i32, i32, i32 +// CHECK-NEXT: %std_sub_0.left, %std_sub_0.right, %std_sub_0.out = calyx.std_sub "std_sub_0" : i32, i32, i32 +// CHECK-NEXT: %std_add_0.left, %std_add_0.right, %std_add_0.out = calyx.std_add "std_add_0" : i32, i32, i32 // CHECK-NEXT: %std_slt_1.left, %std_slt_1.right, %std_slt_1.out = calyx.std_slt "std_slt_1" : i32, i32, i1 // CHECK-NEXT: %std_slt_0.left, %std_slt_0.right, %std_slt_0.out = calyx.std_slt "std_slt_0" : i32, i32, i1 +// CHECK-NEXT: %while_0_arg2_reg.in, %while_0_arg2_reg.write_en, %while_0_arg2_reg.clk, %while_0_arg2_reg.reset, %while_0_arg2_reg.out, %while_0_arg2_reg.done = calyx.register "while_0_arg2_reg" : i32, i1, i1, i1, i32, i1 +// CHECK-NEXT: %while_0_arg1_reg.in, %while_0_arg1_reg.write_en, %while_0_arg1_reg.clk, %while_0_arg1_reg.reset, %while_0_arg1_reg.out, %while_0_arg1_reg.done = calyx.register "while_0_arg1_reg" : i32, i1, i1, i1, i32, i1 // CHECK-NEXT: %while_0_arg0_reg.in, %while_0_arg0_reg.write_en, %while_0_arg0_reg.clk, %while_0_arg0_reg.reset, %while_0_arg0_reg.out, %while_0_arg0_reg.done = calyx.register "while_0_arg0_reg" : i32, i1, i1, i1, i32, i1 // CHECK-NEXT: %ret_arg0_reg.in, %ret_arg0_reg.write_en, %ret_arg0_reg.clk, %ret_arg0_reg.reset, %ret_arg0_reg.out, %ret_arg0_reg.done = calyx.register "ret_arg0_reg" : i32, i1, i1, i1, i32, i1 -// CHECK-NEXT: %bb4_arg0_reg.in, %bb4_arg0_reg.write_en, %bb4_arg0_reg.clk, %bb4_arg0_reg.reset, %bb4_arg0_reg.out, %bb4_arg0_reg.done = calyx.register "bb4_arg0_reg" : i32, i1, i1, i1, i32, i1 +// CHECK-NEXT: %bb3_arg1_reg.in, %bb3_arg1_reg.write_en, %bb3_arg1_reg.clk, %bb3_arg1_reg.reset, %bb3_arg1_reg.out, %bb3_arg1_reg.done = calyx.register "bb3_arg1_reg" : i32, i1, i1, i1, i32, i1 // CHECK-NEXT: %bb3_arg0_reg.in, %bb3_arg0_reg.write_en, %bb3_arg0_reg.clk, %bb3_arg0_reg.reset, %bb3_arg0_reg.out, %bb3_arg0_reg.done = calyx.register "bb3_arg0_reg" : i32, i1, i1, i1, i32, i1 // CHECK-NEXT: calyx.wires { // CHECK-NEXT: calyx.assign %out0 = %ret_arg0_reg.out : i32 // CHECK-NEXT: calyx.group @assign_while_0_init { // CHECK-NEXT: calyx.assign %while_0_arg0_reg.in = %in0 : i32 // CHECK-NEXT: calyx.assign %while_0_arg0_reg.write_en = %true : i1 -// CHECK-NEXT: calyx.group_done %while_0_arg0_reg.done : i1 +// CHECK-NEXT: calyx.assign %while_0_arg1_reg.in = %c0_i32 : i32 +// CHECK-NEXT: calyx.assign %while_0_arg1_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %while_0_arg2_reg.in = %c0_i32 : i32 +// CHECK-NEXT: calyx.assign %while_0_arg2_reg.write_en = %true : i1 +// CHECK-NEXT: %0 = comb.and %while_0_arg0_reg.done, %while_0_arg1_reg.done : i1 +// CHECK-NEXT: %1 = comb.and %0, %while_0_arg2_reg.done : i1 +// CHECK-NEXT: calyx.group_done %true, %1 ? : i1 // CHECK-NEXT: } // CHECK-NEXT: calyx.comb_group @bb0_0 { // CHECK-NEXT: calyx.assign %std_slt_0.left = %while_0_arg0_reg.out : i32 // CHECK-NEXT: calyx.assign %std_slt_0.right = %in1 : i32 // CHECK-NEXT: } // CHECK-NEXT: calyx.comb_group @bb0_1 { -// CHECK-NEXT: calyx.assign %std_slt_1.left = %while_0_arg0_reg.out : i32 -// CHECK-NEXT: calyx.assign %std_slt_1.right = %in0 : i32 +// CHECK-NEXT: calyx.assign %std_slt_1.left = %while_0_arg1_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_slt_1.right = %while_0_arg2_reg.out : i32 // CHECK-NEXT: } // CHECK-NEXT: calyx.group @bb1_to_bb3 { -// CHECK-NEXT: calyx.assign %bb3_arg0_reg.in = %in0 : i32 +// CHECK-NEXT: calyx.assign %bb3_arg0_reg.in = %std_add_0.out : i32 // CHECK-NEXT: calyx.assign %bb3_arg0_reg.write_en = %true : i1 -// CHECK-NEXT: calyx.group_done %bb3_arg0_reg.done : i1 +// CHECK-NEXT: calyx.assign %bb3_arg1_reg.in = %std_add_0.out : i32 +// CHECK-NEXT: calyx.assign %bb3_arg1_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %std_add_0.left = %while_0_arg1_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_add_0.right = %while_0_arg2_reg.out : i32 +// CHECK-NEXT: %0 = comb.and %bb3_arg0_reg.done, %bb3_arg1_reg.done : i1 +// CHECK-NEXT: calyx.group_done %true, %0 ? : i1 // CHECK-NEXT: } // CHECK-NEXT: calyx.group @bb2_to_bb3 { -// CHECK-NEXT: calyx.assign %bb3_arg0_reg.in = %in1 : i32 +// CHECK-NEXT: calyx.assign %bb3_arg0_reg.in = %std_sub_0.out : i32 // CHECK-NEXT: calyx.assign %bb3_arg0_reg.write_en = %true : i1 -// CHECK-NEXT: calyx.group_done %bb3_arg0_reg.done : i1 -// CHECK-NEXT: } -// CHECK-NEXT: calyx.group @bb3_to_bb4 { -// CHECK-NEXT: calyx.assign %bb4_arg0_reg.in = %bb3_arg0_reg.out : i32 -// CHECK-NEXT: calyx.assign %bb4_arg0_reg.write_en = %true : i1 -// CHECK-NEXT: calyx.group_done %bb4_arg0_reg.done : i1 +// CHECK-NEXT: calyx.assign %bb3_arg1_reg.in = %std_sub_0.out : i32 +// CHECK-NEXT: calyx.assign %bb3_arg1_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %std_sub_0.left = %while_0_arg1_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_sub_0.right = %while_0_arg2_reg.out : i32 +// CHECK-NEXT: %0 = comb.and %bb3_arg0_reg.done, %bb3_arg1_reg.done : i1 +// CHECK-NEXT: calyx.group_done %true, %0 ? : i1 // CHECK-NEXT: } // CHECK-NEXT: calyx.group @assign_while_0_latch { -// CHECK-NEXT: calyx.assign %while_0_arg0_reg.in = %bb4_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %while_0_arg0_reg.in = %std_add_1.out : i32 // CHECK-NEXT: calyx.assign %while_0_arg0_reg.write_en = %true : i1 -// CHECK-NEXT: calyx.group_done %while_0_arg0_reg.done : i1 +// CHECK-NEXT: calyx.assign %while_0_arg1_reg.in = %bb3_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %while_0_arg1_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %while_0_arg2_reg.in = %bb3_arg1_reg.out : i32 +// CHECK-NEXT: calyx.assign %while_0_arg2_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %std_add_1.left = %while_0_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_add_1.right = %in2 : i32 +// CHECK-NEXT: %0 = comb.and %while_0_arg0_reg.done, %while_0_arg1_reg.done : i1 +// CHECK-NEXT: %1 = comb.and %0, %while_0_arg2_reg.done : i1 +// CHECK-NEXT: calyx.group_done %true, %1 ? : i1 // CHECK-NEXT: } // CHECK-NEXT: calyx.group @ret_assign_0 { -// CHECK-NEXT: calyx.assign %ret_arg0_reg.in = %while_0_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %ret_arg0_reg.in = %while_0_arg2_reg.out : i32 // CHECK-NEXT: calyx.assign %ret_arg0_reg.write_en = %true : i1 // CHECK-NEXT: calyx.group_done %ret_arg0_reg.done : i1 // CHECK-NEXT: } @@ -185,7 +281,6 @@ module { // CHECK-NEXT: calyx.enable @bb2_to_bb3 // CHECK-NEXT: } // CHECK-NEXT: } -// CHECK-NEXT: calyx.enable @bb3_to_bb4 // CHECK-NEXT: calyx.enable @assign_while_0_latch // CHECK-NEXT: } // CHECK-NEXT: } @@ -198,23 +293,88 @@ module { module { func @main(%arg0: i32, %arg1: i32, %arg2: i32) -> i32 { %cst = constant 0 : i32 - %0 = scf.while (%arg3 = %arg0) : (i32) -> (i32) { + %0:3 = scf.while (%arg3 = %arg0, %arg4 = %cst, %arg5 = %cst) : (i32, i32, i32) -> (i32, i32, i32) { %1 = cmpi slt, %arg3, %arg1 : i32 - scf.condition(%1) %arg3 : i32 + scf.condition(%1) %arg3, %arg4, %arg5 : i32, i32, i32 } do { - ^bb0(%arg3: i32): // no predecessors - %2 = scf.execute_region -> i32 { - %3 = cmpi slt, %arg3, %arg0 : i32 - cond_br %3, ^bb1, ^bb2 + ^bb0(%arg3: i32, %arg4: i32, %arg5: i32): // no predecessors + %1:2 = scf.execute_region -> (i32, i32) { + %4 = cmpi slt, %arg4, %arg5 : i32 + cond_br %4, ^bb0, ^bb1 + ^bb0: + %3 = addi %arg4, %arg5 : i32 + scf.yield %3, %3 : i32, i32 ^bb1: - br ^bb3(%arg0 : i32) - ^bb2: - br ^bb3(%arg1 : i32) - ^bb3(%4 : i32): - scf.yield %4 : i32 + %5 = subi %arg4, %arg5 : i32 + scf.yield %5, %5 : i32, i32 } - scf.yield %2 : i32 + %2 = addi %arg3, %arg2 : i32 + scf.yield %2, %1#0, %1#1 : i32, i32, i32 } - return %0 : i32 + return %0#2 : i32 + } +} + +// ----- + +// Test control flow where the conditional is computed as a sequence of +// combinational operations. We expect all combinational logic in this sequence +// to be inlined into the combinational group. This also tests multiple returns. + +// CHECK: module { +// CHECK-NEXT: calyx.program "main" { +// CHECK-NEXT: calyx.component @main(%in0: i32, %in1: i32, %in2: i32, %clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%out0: i32, %done: i1 {done}) { +// CHECK-NEXT: %true = hw.constant true +// CHECK-NEXT: %std_ge_0.left, %std_ge_0.right, %std_ge_0.out = calyx.std_ge "std_ge_0" : i32, i32, i1 +// CHECK-NEXT: %std_add_1.left, %std_add_1.right, %std_add_1.out = calyx.std_add "std_add_1" : i32, i32, i32 +// CHECK-NEXT: %std_add_0.left, %std_add_0.right, %std_add_0.out = calyx.std_add "std_add_0" : i32, i32, i32 +// CHECK-NEXT: %ret_arg0_reg.in, %ret_arg0_reg.write_en, %ret_arg0_reg.clk, %ret_arg0_reg.reset, %ret_arg0_reg.out, %ret_arg0_reg.done = calyx.register "ret_arg0_reg" : i32, i1, i1, i1, i32, i1 +// CHECK-NEXT: calyx.wires { +// CHECK-NEXT: calyx.assign %out0 = %ret_arg0_reg.out : i32 +// CHECK-NEXT: calyx.comb_group @bb0_2 { +// CHECK-NEXT: calyx.assign %std_ge_0.left = %std_add_1.out : i32 +// CHECK-NEXT: calyx.assign %std_ge_0.right = %in2 : i32 +// CHECK-NEXT: calyx.assign %std_add_1.left = %std_add_0.out : i32 +// CHECK-NEXT: calyx.assign %std_add_0.left = %in0 : i32 +// CHECK-NEXT: calyx.assign %std_add_0.right = %in1 : i32 +// CHECK-NEXT: calyx.assign %std_add_1.right = %in1 : i32 +// CHECK-NEXT: } +// CHECK-NEXT: calyx.group @ret_assign_0 { +// CHECK-NEXT: calyx.assign %ret_arg0_reg.in = %in1 : i32 +// CHECK-NEXT: calyx.assign %ret_arg0_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.group_done %ret_arg0_reg.done : i1 +// CHECK-NEXT: } +// CHECK-NEXT: calyx.group @ret_assign_1 { +// CHECK-NEXT: calyx.assign %ret_arg0_reg.in = %in2 : i32 +// CHECK-NEXT: calyx.assign %ret_arg0_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.group_done %ret_arg0_reg.done : i1 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: calyx.control { +// CHECK-NEXT: calyx.seq { +// CHECK-NEXT: calyx.if %std_ge_0.out with @bb0_2 { +// CHECK-NEXT: calyx.seq { +// CHECK-NEXT: calyx.enable @ret_assign_0 +// CHECK-NEXT: } +// CHECK-NEXT: } else { +// CHECK-NEXT: calyx.seq { +// CHECK-NEXT: calyx.enable @ret_assign_1 +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +module { + func @main(%a0 : i32, %a1 : i32, %a2 : i32) -> i32 { + %0 = addi %a0, %a1 : i32 + %1 = addi %0, %a1 : i32 + %b = cmpi uge, %1, %a2 : i32 + cond_br %b, ^bb1, ^bb2 + ^bb1: + return %a1 : i32 + ^bb2: + return %a2 : i32 } } diff --git a/test/Conversion/SCFToCalyx/convert_simple.mlir b/test/Conversion/SCFToCalyx/convert_simple.mlir index e138d4c1e5b7..fe8c8d5788c0 100644 --- a/test/Conversion/SCFToCalyx/convert_simple.mlir +++ b/test/Conversion/SCFToCalyx/convert_simple.mlir @@ -1,15 +1,24 @@ // RUN: circt-opt %s --lower-scf-to-calyx -split-input-file | FileCheck %s // CHECK: module { -// CHECK-NEXT: calyx.program "main" { -// CHECK-NEXT: calyx.component @main(%in0: i32, %clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%out0: i32, %done: i1 {done}) { +// CHECK-NEXT: calyx.program "main" { +// CHECK-NEXT: calyx.component @main(%in0: i32, %in1: i32, %clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%out0: i32, %done: i1 {done}) { // CHECK-NEXT: %true = hw.constant true +// CHECK-NEXT: %std_sub_0.left, %std_sub_0.right, %std_sub_0.out = calyx.std_sub "std_sub_0" : i32, i32, i32 +// CHECK-NEXT: %std_lsh_0.left, %std_lsh_0.right, %std_lsh_0.out = calyx.std_lsh "std_lsh_0" : i32, i32, i32 +// CHECK-NEXT: %std_add_0.left, %std_add_0.right, %std_add_0.out = calyx.std_add "std_add_0" : i32, i32, i32 // CHECK-NEXT: %ret_arg0_reg.in, %ret_arg0_reg.write_en, %ret_arg0_reg.clk, %ret_arg0_reg.reset, %ret_arg0_reg.out, %ret_arg0_reg.done = calyx.register "ret_arg0_reg" : i32, i1, i1, i1, i32, i1 // CHECK-NEXT: calyx.wires { // CHECK-NEXT: calyx.assign %out0 = %ret_arg0_reg.out : i32 // CHECK-NEXT: calyx.group @ret_assign_0 { -// CHECK-NEXT: calyx.assign %ret_arg0_reg.in = %in0 : i32 +// CHECK-NEXT: calyx.assign %ret_arg0_reg.in = %std_sub_0.out : i32 // CHECK-NEXT: calyx.assign %ret_arg0_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.assign %std_sub_0.left = %std_lsh_0.out : i32 +// CHECK-NEXT: calyx.assign %std_lsh_0.left = %std_add_0.out : i32 +// CHECK-NEXT: calyx.assign %std_add_0.left = %in0 : i32 +// CHECK-NEXT: calyx.assign %std_add_0.right = %in1 : i32 +// CHECK-NEXT: calyx.assign %std_lsh_0.right = %in0 : i32 +// CHECK-NEXT: calyx.assign %std_sub_0.right = %std_add_0.out : i32 // CHECK-NEXT: calyx.group_done %ret_arg0_reg.done : i1 // CHECK-NEXT: } // CHECK-NEXT: } @@ -22,7 +31,10 @@ // CHECK-NEXT: } // CHECK-NEXT: } module { - func @main(%arg0 : i32) -> i32 { - return %arg0 : i32 + func @main(%a0 : i32, %a1 : i32) -> i32 { + %0 = addi %a0, %a1 : i32 + %1 = shift_left %0, %a0 : i32 + %2 = subi %1, %0 : i32 + return %2 : i32 } }