From 959cffc622ff805bebe9d5eda37567df9ba92446 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Wed, 8 Jan 2025 22:15:27 -0800 Subject: [PATCH 01/12] initial commit. --- .../circt/Dialect/Calyx/CalyxLoweringUtils.h | 19 +++-- .../LoopScheduleToCalyx.cpp | 72 +++++++++++++------ .../LoopScheduleToCalyx/convert_pipeline.mlir | 37 ++++++++++ 3 files changed, 101 insertions(+), 27 deletions(-) diff --git a/include/circt/Dialect/Calyx/CalyxLoweringUtils.h b/include/circt/Dialect/Calyx/CalyxLoweringUtils.h index 04cbf32e3f32..8cb05f9c115f 100644 --- a/include/circt/Dialect/Calyx/CalyxLoweringUtils.h +++ b/include/circt/Dialect/Calyx/CalyxLoweringUtils.h @@ -29,6 +29,7 @@ #include "llvm/ADT/TypeSwitch.h" #include "llvm/Support/JSON.h" +#include #include namespace circt { @@ -404,12 +405,13 @@ class ComponentLoweringStateInterface { /// Put the name of the callee and the instance of the call into map. void addInstance(StringRef calleeName, InstanceOp instanceOp); - /// Return the group which evaluates the value v. Optionally, caller may - /// specify the expected type of the group. + /// Returns the evaluating group or None if not found. template - TGroupOp getEvaluatingGroup(Value v) { + std::optional findEvaluatingGroup(Value v) { auto it = valueGroupAssigns.find(v); - assert(it != valueGroupAssigns.end() && "No group evaluating value!"); + if (it == valueGroupAssigns.end()) + return std::nullopt; + if constexpr (std::is_same_v) return it->second; else { @@ -419,6 +421,15 @@ class ComponentLoweringStateInterface { } } + /// Return the group which evaluates the value v. Optionally, caller may + /// specify the expected type of the group. + template + TGroupOp getEvaluatingGroup(Value v) { + std::optional group = findEvaluatingGroup(v); + assert(group.has_value() && "No group evaluating value!"); + return *group; + } + template struct IsFloatingPoint : std::false_type {}; diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index 06d3357a3903..21939e852a46 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -126,6 +126,20 @@ class PipelineScheduler : public calyx::SchedulerInterface { return pipelineRegs[stage]; } + /// Returns whether this value is a pipeline register. + bool isPipelineReg(Value value) { + auto regOp = value.getDefiningOp(); + if (regOp == nullptr) + return false; + for (const auto &[_, registers] : pipelineRegs) { + for (const auto &[_, r] : registers) { + if (r == regOp) + return true; + } + } + return false; + } + /// Add a stage's groups to the pipeline prologue. void addPipelinePrologue(Operation *op, SmallVector groupNames) { pipelinePrologue[op].push_back(groupNames); @@ -1019,11 +1033,11 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { // Collect group names for the prologue or epilogue. SmallVector prologueGroups, epilogueGroups; + auto &state = getState(); auto updatePrologueAndEpilogue = [&](calyx::GroupOp group) { // Mark the group for scheduling in the pipeline's block. - getState().addBlockScheduleable(stage->getBlock(), - group); + state.addBlockScheduleable(stage->getBlock(), group); // Add the group to the prologue or epilogue for this stage as // necessary. The goal is to fill the pipeline so it will be in steady @@ -1046,8 +1060,7 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { // Covers the case where there are no values that need to be passed // through to the next stage, e.g., some intermediary store. for (auto &op : stage.getBodyBlock()) - if (auto group = getState() - .getNonPipelinedGroupFrom(&op)) + if (auto group = state.getNonPipelinedGroupFrom(&op)) updatePrologueAndEpilogue(*group); } @@ -1058,26 +1071,39 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { // Get the pipeline register for that result. auto pipelineRegister = pipelineRegisters[i]; - // Get the evaluating group for that value. - calyx::GroupInterface evaluatingGroup = - getState().getEvaluatingGroup(value); - - // Remember the final group for this stage result. calyx::GroupOp group; - - // Stitch the register in, depending on whether the group was - // combinational or sequential. - if (auto combGroup = - dyn_cast(evaluatingGroup.getOperation())) - group = - convertCombToSeqGroup(combGroup, pipelineRegister, value, rewriter); - else - group = - replaceGroupRegister(evaluatingGroup, pipelineRegister, rewriter); - - // Replace the stage result uses with the register out. - stage.getResult(i).replaceAllUsesWith(pipelineRegister.getOut()); - + // Get the evaluating group for that value. + std::optional evaluatingGroup = + state.findEvaluatingGroup(value); + if (!evaluatingGroup.has_value()) { + if (!state.isPipelineReg(value)) { + llvm::errs() << "unexpected: this is not a pipeline register and was " + "not previously evaluated. Please open an issue.\n"; + return LogicalResult::failure(); + } + // This is a pipeline register being written to another pipeline + // register. We create a new group to build this assignment. + std::string groupName = state.getUniqueName( + loweringState().blockName(pipelineRegister->getBlock())); + group = calyx::createGroup( + rewriter, state.getComponentOp(), pipelineRegister->getLoc(), + groupName); + calyx::buildAssignmentsForRegisterWrite( + rewriter, group, state.getComponentOp(), pipelineRegister, value); + } else { + // Stitch the register in, depending on whether the group was + // combinational or sequential. + auto combGroup = + dyn_cast(evaluatingGroup->getOperation()); + group = combGroup == nullptr + ? replaceGroupRegister(*evaluatingGroup, pipelineRegister, + rewriter) + : convertCombToSeqGroup(combGroup, pipelineRegister, value, + rewriter); + + // Replace the stage result uses with the register out. + stage.getResult(i).replaceAllUsesWith(pipelineRegister.getOut()); + } updatePrologueAndEpilogue(group); } diff --git a/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir b/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir index 17cc7390c90a..a1471463354a 100644 --- a/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir +++ b/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir @@ -267,3 +267,40 @@ module { } } +// ----- + +// Pipeline register to pipeline register writes are valid. + +//CHECK: calyx.group @bb0_3 { +//CHECK-NEXT: calyx.assign %stage_1_register_0_reg.in = %stage_0_register_0_reg.out : i32 +//CHECK-NEXT: calyx.assign %stage_1_register_0_reg.write_en = %true : i1 +//CHECK-NEXT: calyx.group_done %stage_1_register_0_reg.done : i1 +//CHECK-NEXT: } +//CHECK-NEXT: } +module { + func.func @gemm(%arg0: i32, %arg1: memref<30x30xi32>) attributes {llvm.linkage = #llvm.linkage} { + %alloca = memref.alloca() : memref<30x30xi32> + cf.br ^bb4 + ^bb4: + %0 = arith.constant 0 : index + %c1_5 = arith.constant 1 : index + loopschedule.pipeline II = 2 trip_count = 20 iter_args(%arg5 = %0) : (index) -> () { + %6 = arith.cmpi ult, %arg5, %0 : index + loopschedule.register %6 : i1 + } do { + %6:2 = loopschedule.pipeline.stage start = 0 { + %10 = memref.load %arg1[%0, %0] : memref<30x30xi32> + %11 = arith.addi %arg5, %c1_5 : index + loopschedule.register %10, %11 : i32, index + } : i32, index + %7 = loopschedule.pipeline.stage start = 1 { + loopschedule.register %6#0 : i32 + } : i32 + loopschedule.terminator iter_args(%6#1), results() : (index) -> () + } + cf.br ^bb6 + ^bb6: + return + } +} + From 2716528434c31e199373b3ca06a0e0847ed1ae66 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Wed, 8 Jan 2025 22:26:32 -0800 Subject: [PATCH 02/12] update error message. --- .../LoopScheduleToCalyx/LoopScheduleToCalyx.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index 21939e852a46..d6a7266ca97f 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -1077,8 +1077,11 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { state.findEvaluatingGroup(value); if (!evaluatingGroup.has_value()) { if (!state.isPipelineReg(value)) { - llvm::errs() << "unexpected: this is not a pipeline register and was " - "not previously evaluated. Please open an issue.\n"; + // We add this for any unhandled cases. + llvm::errs() << "unexpected: input value: " << value << ", in stage " + << stage.getStageNumber() << " register " << i + << " is not a pipeline register and was not previously " + "evaluated in a Calyx group. Please open an issue.\n"; return LogicalResult::failure(); } // This is a pipeline register being written to another pipeline @@ -1091,8 +1094,8 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { calyx::buildAssignmentsForRegisterWrite( rewriter, group, state.getComponentOp(), pipelineRegister, value); } else { - // Stitch the register in, depending on whether the group was - // combinational or sequential. + // This was previously evaluated. Stitch the register in, depending on + // whether the group was combinational or sequential. auto combGroup = dyn_cast(evaluatingGroup->getOperation()); group = combGroup == nullptr From 093bfaf0426363a6310a0b4945b428ba19cac8ac Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Wed, 8 Jan 2025 22:28:19 -0800 Subject: [PATCH 03/12] change test name. --- test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir b/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir index a1471463354a..42dec167733e 100644 --- a/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir +++ b/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir @@ -278,7 +278,7 @@ module { //CHECK-NEXT: } //CHECK-NEXT: } module { - func.func @gemm(%arg0: i32, %arg1: memref<30x30xi32>) attributes {llvm.linkage = #llvm.linkage} { + func.func @foo(%arg0: i32, %arg1: memref<30x30xi32>) attributes {llvm.linkage = #llvm.linkage} { %alloca = memref.alloca() : memref<30x30xi32> cf.br ^bb4 ^bb4: From 21119e601cc1438e8f6ce3dc0cadb44366de973a Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 08:37:52 -0800 Subject: [PATCH 04/12] multi-stage r2r writes --- .../LoopScheduleToCalyx.cpp | 14 ++++++++++++-- .../LoopScheduleToCalyx/convert_pipeline.mlir | 19 +++++++++++++------ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index d6a7266ca97f..f2be59bac1b3 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -1076,11 +1076,21 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { std::optional evaluatingGroup = state.findEvaluatingGroup(value); if (!evaluatingGroup.has_value()) { - if (!state.isPipelineReg(value)) { + if (auto stage = + dyn_cast(value.getDefiningOp())) { + // The pipeline register for this stage needs to be rediscovered. + auto opResult = cast(value); + unsigned int opNumber = opResult.getResultNumber(); + auto &stageRegisters = state.getPipelineRegs(stage); + calyx::RegisterOp stageRegister = + stageRegisters.find(opNumber)->second; + value = stageRegister.getOut(); + } + if (value.getDefiningOp() == nullptr) { // We add this for any unhandled cases. llvm::errs() << "unexpected: input value: " << value << ", in stage " << stage.getStageNumber() << " register " << i - << " is not a pipeline register and was not previously " + << " is not a register and was not previously " "evaluated in a Calyx group. Please open an issue.\n"; return LogicalResult::failure(); } diff --git a/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir b/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir index 42dec167733e..9bf2e9ad39a8 100644 --- a/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir +++ b/test/Conversion/LoopScheduleToCalyx/convert_pipeline.mlir @@ -271,12 +271,16 @@ module { // Pipeline register to pipeline register writes are valid. -//CHECK: calyx.group @bb0_3 { -//CHECK-NEXT: calyx.assign %stage_1_register_0_reg.in = %stage_0_register_0_reg.out : i32 -//CHECK-NEXT: calyx.assign %stage_1_register_0_reg.write_en = %true : i1 -//CHECK-NEXT: calyx.group_done %stage_1_register_0_reg.done : i1 -//CHECK-NEXT: } -//CHECK-NEXT: } +//CHECK: calyx.group @bb0_3 { +//CHECK-NEXT: calyx.assign %stage_1_register_0_reg.in = %stage_0_register_0_reg.out : i32 +//CHECK-NEXT: calyx.assign %stage_1_register_0_reg.write_en = %true : i1 +//CHECK-NEXT: calyx.group_done %stage_1_register_0_reg.done : i1 +//CHECK-NEXT: } +//CHECK-NEXT: calyx.group @bb0_4 { +//CHECK-NEXT: calyx.assign %stage_2_register_0_reg.in = %stage_1_register_0_reg.out : i32 +//CHECK-NEXT: calyx.assign %stage_2_register_0_reg.write_en = %true : i1 +//CHECK-NEXT: calyx.group_done %stage_2_register_0_reg.done : i1 +//CHECK-NEXT: } module { func.func @foo(%arg0: i32, %arg1: memref<30x30xi32>) attributes {llvm.linkage = #llvm.linkage} { %alloca = memref.alloca() : memref<30x30xi32> @@ -296,6 +300,9 @@ module { %7 = loopschedule.pipeline.stage start = 1 { loopschedule.register %6#0 : i32 } : i32 + %8 = loopschedule.pipeline.stage start = 2 { + loopschedule.register %7 : i32 + } : i32 loopschedule.terminator iter_args(%6#1), results() : (index) -> () } cf.br ^bb6 From 54243fccfd694a1951d122acf945f2c29657e1a2 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 08:40:20 -0800 Subject: [PATCH 05/12] remove isPipelineReg. --- .../LoopScheduleToCalyx/LoopScheduleToCalyx.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index f2be59bac1b3..58012a4dc5d6 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -126,20 +126,6 @@ class PipelineScheduler : public calyx::SchedulerInterface { return pipelineRegs[stage]; } - /// Returns whether this value is a pipeline register. - bool isPipelineReg(Value value) { - auto regOp = value.getDefiningOp(); - if (regOp == nullptr) - return false; - for (const auto &[_, registers] : pipelineRegs) { - for (const auto &[_, r] : registers) { - if (r == regOp) - return true; - } - } - return false; - } - /// Add a stage's groups to the pipeline prologue. void addPipelinePrologue(Operation *op, SmallVector groupNames) { pipelinePrologue[op].push_back(groupNames); From f553f0a4948548f05256f0ff74843313b61a8597 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 08:41:18 -0800 Subject: [PATCH 06/12] update docs --- lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index 58012a4dc5d6..db209b0b2b8b 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -1080,8 +1080,8 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { "evaluated in a Calyx group. Please open an issue.\n"; return LogicalResult::failure(); } - // This is a pipeline register being written to another pipeline - // register. We create a new group to build this assignment. + // This is a register's value being written to this pipeline register. + // We create a new group to build this assignment. std::string groupName = state.getUniqueName( loweringState().blockName(pipelineRegister->getBlock())); group = calyx::createGroup( From 1a568422c04b77a7ce07ce29159ab125488602b8 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 08:46:07 -0800 Subject: [PATCH 07/12] add docs --- .../LoopScheduleToCalyx/LoopScheduleToCalyx.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index db209b0b2b8b..32625b351843 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -1062,15 +1062,14 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { std::optional evaluatingGroup = state.findEvaluatingGroup(value); if (!evaluatingGroup.has_value()) { - if (auto stage = + if (auto opStage = dyn_cast(value.getDefiningOp())) { - // The pipeline register for this stage needs to be rediscovered. + // The pipeline register for this input value needs to be discovered. auto opResult = cast(value); unsigned int opNumber = opResult.getResultNumber(); - auto &stageRegisters = state.getPipelineRegs(stage); - calyx::RegisterOp stageRegister = - stageRegisters.find(opNumber)->second; - value = stageRegister.getOut(); + auto &stageRegisters = state.getPipelineRegs(opStage); + calyx::RegisterOp opRegister = stageRegisters.find(opNumber)->second; + value = opRegister.getOut(); // Pass the `out` wire of this register. } if (value.getDefiningOp() == nullptr) { // We add this for any unhandled cases. @@ -1080,8 +1079,8 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { "evaluated in a Calyx group. Please open an issue.\n"; return LogicalResult::failure(); } - // This is a register's value being written to this pipeline register. - // We create a new group to build this assignment. + // This is a register's `out` value being written to this pipeline + // register. We create a new group to build this assignment. std::string groupName = state.getUniqueName( loweringState().blockName(pipelineRegister->getBlock())); group = calyx::createGroup( From 8d8ea7667747ddec7acbacd4399e7d3056422ccb Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 13:21:48 -0800 Subject: [PATCH 08/12] deduplication --- .../LoopScheduleToCalyx.cpp | 6 +- lib/Dialect/Calyx/CalyxOps.cpp | 57 ++++++++++--------- .../remove_duplicates.mlir | 28 +++++++++ test/Dialect/Calyx/errors.mlir | 23 -------- 4 files changed, 62 insertions(+), 52 deletions(-) create mode 100644 test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index 32625b351843..b5e3fe0175ee 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -1166,8 +1166,10 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { } doneOp.getSrcMutable().assign(pipelineRegister.getDone()); - // Remove the old register completely. - rewriter.eraseOp(tempReg); + // Remove the old register completely if it has no uses. + if (tempReg->use_empty()) { + rewriter.eraseOp(tempReg); + } return group; } diff --git a/lib/Dialect/Calyx/CalyxOps.cpp b/lib/Dialect/Calyx/CalyxOps.cpp index 7ae18c59da69..0094f05da8d7 100644 --- a/lib/Dialect/Calyx/CalyxOps.cpp +++ b/lib/Dialect/Calyx/CalyxOps.cpp @@ -326,6 +326,33 @@ static LogicalResult collapseControl(OpTy controlOp, return failure(); } +// Remove duplicates from a parallel operation. This may inadvertently occur +// during lowering. +template +static LogicalResult deduplicate(OpTy parOp, PatternRewriter &rewriter) { + static_assert(IsAny(), + "requires a parallel operation"); + auto *body = parOp.getBodyBlock(); + if (body->getOperations().size() < 2) + return failure(); + + LogicalResult result = LogicalResult::failure(); + SetVector members; + for (auto &op : make_early_inc_range(*body)) { + auto enableOp = dyn_cast(&op); + if (enableOp == nullptr) + continue; + StringRef groupName = enableOp.getGroupName(); + if (members.contains(groupName)) { + rewriter.eraseOp(enableOp); + result = LogicalResult::success(); + continue; + } + members.insert(groupName); + } + return result; +} + template static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter) { if (controlOp.getBodyBlock()->empty()) { @@ -942,24 +969,11 @@ void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns, // ParOp //===----------------------------------------------------------------------===// -LogicalResult ParOp::verify() { - llvm::SmallSet groupNames; - - // Add loose requirement that the body of a ParOp may not enable the same - // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G } - for (EnableOp op : getBodyBlock()->getOps()) { - StringRef groupName = op.getGroupName(); - if (groupNames.count(groupName)) - return emitOpError() << "cannot enable the same group: \"" << groupName - << "\" more than once."; - groupNames.insert(groupName); - } - - return success(); -} +LogicalResult ParOp::verify() { return success(); } void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns, MLIRContext *context) { + patterns.add(deduplicate); patterns.add(collapseControl); patterns.add(emptyControl); patterns.insert>(context); @@ -970,18 +984,6 @@ void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns, //===----------------------------------------------------------------------===// LogicalResult StaticParOp::verify() { - llvm::SmallSet groupNames; - - // Add loose requirement that the body of a ParOp may not enable the same - // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G } - for (EnableOp op : getBodyBlock()->getOps()) { - StringRef groupName = op.getGroupName(); - if (groupNames.count(groupName)) - return emitOpError() << "cannot enable the same group: \"" << groupName - << "\" more than once."; - groupNames.insert(groupName); - } - // static par must only have static control in it auto &ops = (*this).getBodyBlock()->getOperations(); for (Operation &op : ops) { @@ -997,6 +999,7 @@ void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &patterns, MLIRContext *context) { patterns.add(collapseControl); patterns.add(emptyControl); + patterns.add(deduplicate); patterns.insert>(context); } diff --git a/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir b/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir new file mode 100644 index 000000000000..b951cee72e5a --- /dev/null +++ b/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir @@ -0,0 +1,28 @@ +// RUN: circt-opt %s -lower-loopschedule-to-calyx -canonicalize -split-input-file | FileCheck %s + +// This will introduce duplicate groups; these should be subsequently removed during canonicalization. + +// CHECK: calyx.while %std_lt_0.out with @bb0_0 { +// CHECK-NEXT: calyx.par { +// CHECK-NEXT: calyx.enable @bb0_1 +// CHECK-NEXT: } +// CHECK-NEXT: } {bound = 19 : i64} +module { + func.func @foo() attributes {} { + %const = arith.constant 1 : index + loopschedule.pipeline II = 1 trip_count = 20 iter_args(%counter = %const) : (index) -> () { + %latch = arith.cmpi ult, %counter, %const : index + loopschedule.register %latch : i1 + } do { + %S0 = loopschedule.pipeline.stage start = 0 { + %op = arith.addi %counter, %const : index + loopschedule.register %op : index + } : index + %S1 = loopschedule.pipeline.stage start = 1 { + loopschedule.register %S0: index + } : index + loopschedule.terminator iter_args(%S0), results() : (index) -> () + } + return + } +} diff --git a/test/Dialect/Calyx/errors.mlir b/test/Dialect/Calyx/errors.mlir index 39cd93060dce..a96859f653fb 100644 --- a/test/Dialect/Calyx/errors.mlir +++ b/test/Dialect/Calyx/errors.mlir @@ -478,29 +478,6 @@ module attributes {calyx.entrypoint = "main"} { // ----- -module attributes {calyx.entrypoint = "main"} { - calyx.component @main(%go: i1 {go}, %clk: i1 {clk}, %reset: i1 {reset}) -> (%done: i1 {done}) { - %r.in, %r.write_en, %r.clk, %r.reset, %r.out, %r.done = calyx.register @r : i1, i1, i1, i1, i1, i1 - %c1_1 = hw.constant 1 : i1 - calyx.wires { - calyx.group @A { - calyx.assign %r.in = %c1_1 : i1 - calyx.assign %r.write_en = %c1_1 : i1 - calyx.group_done %r.done : i1 - } - } - calyx.control { - // expected-error @+1 {{'calyx.par' op cannot enable the same group: "A" more than once.}} - calyx.par { - calyx.enable @A - calyx.enable @A - } - } - } -} - -// ----- - module attributes {calyx.entrypoint = "main"} { calyx.component @A(%go: i1 {go}, %clk: i1 {clk}, %reset: i1 {reset}) -> (%out: i1, %done: i1 {done}) { %c1_1 = hw.constant 1 : i1 From 107e7dbde3207a26df7c6b31271a9723e69ddaf8 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 13:30:35 -0800 Subject: [PATCH 09/12] cleanup. --- lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp | 5 ++--- lib/Dialect/Calyx/CalyxOps.cpp | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index b5e3fe0175ee..4a717bb8f36d 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -1166,10 +1166,9 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { } doneOp.getSrcMutable().assign(pipelineRegister.getDone()); - // Remove the old register completely if it has no uses. - if (tempReg->use_empty()) { + // Remove the old register if it has no more uses. + if (tempReg->use_empty()) rewriter.eraseOp(tempReg); - } return group; } diff --git a/lib/Dialect/Calyx/CalyxOps.cpp b/lib/Dialect/Calyx/CalyxOps.cpp index 0094f05da8d7..332bcf6e0d2c 100644 --- a/lib/Dialect/Calyx/CalyxOps.cpp +++ b/lib/Dialect/Calyx/CalyxOps.cpp @@ -342,13 +342,11 @@ static LogicalResult deduplicate(OpTy parOp, PatternRewriter &rewriter) { auto enableOp = dyn_cast(&op); if (enableOp == nullptr) continue; - StringRef groupName = enableOp.getGroupName(); - if (members.contains(groupName)) { + bool inserted = members.insert(enableOp.getGroupName()); + if (!inserted) { rewriter.eraseOp(enableOp); result = LogicalResult::success(); - continue; } - members.insert(groupName); } return result; } From 04f2d130edb9d7c2bcfa426be1841a4a982d0a90 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 13:34:19 -0800 Subject: [PATCH 10/12] cleanup. --- lib/Dialect/Calyx/CalyxOps.cpp | 2 +- test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Dialect/Calyx/CalyxOps.cpp b/lib/Dialect/Calyx/CalyxOps.cpp index 332bcf6e0d2c..74c1142de6ba 100644 --- a/lib/Dialect/Calyx/CalyxOps.cpp +++ b/lib/Dialect/Calyx/CalyxOps.cpp @@ -995,9 +995,9 @@ LogicalResult StaticParOp::verify() { void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &patterns, MLIRContext *context) { + patterns.add(deduplicate); patterns.add(collapseControl); patterns.add(emptyControl); - patterns.add(deduplicate); patterns.insert>(context); } diff --git a/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir b/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir index b951cee72e5a..84359f30bc66 100644 --- a/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir +++ b/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir @@ -6,11 +6,11 @@ // CHECK-NEXT: calyx.par { // CHECK-NEXT: calyx.enable @bb0_1 // CHECK-NEXT: } -// CHECK-NEXT: } {bound = 19 : i64} +// CHECK-NEXT: } module { func.func @foo() attributes {} { %const = arith.constant 1 : index - loopschedule.pipeline II = 1 trip_count = 20 iter_args(%counter = %const) : (index) -> () { + loopschedule.pipeline II = 1 trip_count = 20 iter_args(%counter = %const) : (index) -> () { %latch = arith.cmpi ult, %counter, %const : index loopschedule.register %latch : i1 } do { From 47839e4fed4bc2a9b0f8e5d6ca599a0b55d24b71 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 14:57:17 -0800 Subject: [PATCH 11/12] update for test. --- .../LoopScheduleToCalyx.cpp | 40 ++++++++---- .../pipeline_register_pass.mlir | 62 +++++++++++++++++++ .../remove_duplicates.mlir | 28 --------- 3 files changed, 89 insertions(+), 41 deletions(-) create mode 100644 test/Conversion/LoopScheduleToCalyx/pipeline_register_pass.mlir delete mode 100644 test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index 4a717bb8f36d..a8bd9b7eda5a 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -29,6 +29,7 @@ #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "llvm/ADT/TypeSwitch.h" +#include #include namespace circt { @@ -126,6 +127,19 @@ class PipelineScheduler : public calyx::SchedulerInterface { return pipelineRegs[stage]; } + /// Returns the pipeline register for this value if its defining operation is + /// a stage, and std::nullopt otherwise. + std::optional getPipelineRegister(Value value) { + auto opStage = dyn_cast(value.getDefiningOp()); + if (opStage == nullptr) + return std::nullopt; + // The pipeline register for this input value needs to be discovered. + auto opResult = cast(value); + unsigned int opNumber = opResult.getResultNumber(); + auto &stageRegisters = getPipelineRegs(opStage); + return stageRegisters.find(opNumber)->second; + } + /// Add a stage's groups to the pipeline prologue. void addPipelinePrologue(Operation *op, SmallVector groupNames) { pipelinePrologue[op].push_back(groupNames); @@ -306,9 +320,14 @@ class BuildOpGroups : public calyx::FuncOpPartialLoweringPattern { /// Create assignments to the inputs of the library op. auto group = createGroupForOp(rewriter, op); rewriter.setInsertionPointToEnd(group.getBodyBlock()); - for (auto dstOp : enumerate(opInputPorts)) - rewriter.create(op.getLoc(), dstOp.value(), - op->getOperand(dstOp.index())); + for (auto dstOp : enumerate(opInputPorts)) { + Value srcOp = op->getOperand(dstOp.index()); + std::optional pipelineRegister = + getState().getPipelineRegister(srcOp); + if (pipelineRegister.has_value()) + srcOp = pipelineRegister->getOut(); + rewriter.create(op.getLoc(), dstOp.value(), srcOp); + } /// Replace the result values of the source operator with the new operator. for (auto res : enumerate(opOutputPorts)) { @@ -1055,22 +1074,17 @@ class BuildPipelineGroups : public calyx::FuncOpPartialLoweringPattern { Value value = operand.get(); // Get the pipeline register for that result. - auto pipelineRegister = pipelineRegisters[i]; + calyx::RegisterOp pipelineRegister = pipelineRegisters[i]; + if (std::optional pr = + state.getPipelineRegister(value)) { + value = pr->getOut(); + } calyx::GroupOp group; // Get the evaluating group for that value. std::optional evaluatingGroup = state.findEvaluatingGroup(value); if (!evaluatingGroup.has_value()) { - if (auto opStage = - dyn_cast(value.getDefiningOp())) { - // The pipeline register for this input value needs to be discovered. - auto opResult = cast(value); - unsigned int opNumber = opResult.getResultNumber(); - auto &stageRegisters = state.getPipelineRegs(opStage); - calyx::RegisterOp opRegister = stageRegisters.find(opNumber)->second; - value = opRegister.getOut(); // Pass the `out` wire of this register. - } if (value.getDefiningOp() == nullptr) { // We add this for any unhandled cases. llvm::errs() << "unexpected: input value: " << value << ", in stage " diff --git a/test/Conversion/LoopScheduleToCalyx/pipeline_register_pass.mlir b/test/Conversion/LoopScheduleToCalyx/pipeline_register_pass.mlir new file mode 100644 index 000000000000..9598f75c448d --- /dev/null +++ b/test/Conversion/LoopScheduleToCalyx/pipeline_register_pass.mlir @@ -0,0 +1,62 @@ +// RUN: circt-opt %s -lower-loopschedule-to-calyx -canonicalize -split-input-file | FileCheck %s + +// This will introduce duplicate groups; these should be subsequently removed during canonicalization. + +// CHECK: calyx.while %std_lt_0.out with @bb0_0 { +// CHECK-NEXT: calyx.par { +// CHECK-NEXT: calyx.enable @bb0_1 +// CHECK-NEXT: } +// CHECK-NEXT: } +module { + func.func @foo() attributes {} { + %const = arith.constant 1 : index + loopschedule.pipeline II = 1 trip_count = 20 iter_args(%counter = %const) : (index) -> () { + %latch = arith.cmpi ult, %counter, %const : index + loopschedule.register %latch : i1 + } do { + %S0 = loopschedule.pipeline.stage start = 0 { + %op = arith.addi %counter, %const : index + loopschedule.register %op : index + } : index + %S1 = loopschedule.pipeline.stage start = 1 { + loopschedule.register %S0: index + } : index + loopschedule.terminator iter_args(%S0), results() : (index) -> () + } + return + } +} + +// ----- + +// Stage pipeline registers passed directly to the next stage +// should also be updated when used in computations. + +// CHECK: calyx.group @bb0_2 { +// CHECK-NEXT: calyx.assign %std_add_1.left = %while_0_arg0_reg.out : i32 +// CHECK-NEXT: calyx.assign %std_add_1.right = %c1_i32 : i32 +// CHECK-NEXT: calyx.assign %stage_1_register_0_reg.in = %std_add_1.out : i32 +// CHECK-NEXT: calyx.assign %stage_1_register_0_reg.write_en = %true : i1 +// CHECK-NEXT: calyx.group_done %stage_1_register_0_reg.done : i1 +// CHECK-NEXT: } +module { + func.func @foo() attributes {} { + %const = arith.constant 1 : index + loopschedule.pipeline II = 1 trip_count = 20 iter_args(%counter = %const) : (index) -> () { + %latch = arith.cmpi ult, %counter, %const : index + loopschedule.register %latch : i1 + } do { + %S0 = loopschedule.pipeline.stage start = 0 { + %op = arith.addi %counter, %const : index + loopschedule.register %op : index + } : index + %S1 = loopschedule.pipeline.stage start = 1 { + %math = arith.addi %S0, %const : index + loopschedule.register %math : index + } : index + loopschedule.terminator iter_args(%S0), results() : (index) -> () + } + return + } +} + diff --git a/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir b/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir deleted file mode 100644 index 84359f30bc66..000000000000 --- a/test/Conversion/LoopScheduleToCalyx/remove_duplicates.mlir +++ /dev/null @@ -1,28 +0,0 @@ -// RUN: circt-opt %s -lower-loopschedule-to-calyx -canonicalize -split-input-file | FileCheck %s - -// This will introduce duplicate groups; these should be subsequently removed during canonicalization. - -// CHECK: calyx.while %std_lt_0.out with @bb0_0 { -// CHECK-NEXT: calyx.par { -// CHECK-NEXT: calyx.enable @bb0_1 -// CHECK-NEXT: } -// CHECK-NEXT: } -module { - func.func @foo() attributes {} { - %const = arith.constant 1 : index - loopschedule.pipeline II = 1 trip_count = 20 iter_args(%counter = %const) : (index) -> () { - %latch = arith.cmpi ult, %counter, %const : index - loopschedule.register %latch : i1 - } do { - %S0 = loopschedule.pipeline.stage start = 0 { - %op = arith.addi %counter, %const : index - loopschedule.register %op : index - } : index - %S1 = loopschedule.pipeline.stage start = 1 { - loopschedule.register %S0: index - } : index - loopschedule.terminator iter_args(%S0), results() : (index) -> () - } - return - } -} From 4a0a7700ef2f3edb3c1a7498c2c8e5e8018dd607 Mon Sep 17 00:00:00 2001 From: Chris Gyurgyik Date: Thu, 9 Jan 2025 15:21:19 -0800 Subject: [PATCH 12/12] fix canonicalization. --- .../circt/Dialect/Calyx/CalyxLoweringUtils.h | 17 ++++++ .../LoopScheduleToCalyx.cpp | 3 + lib/Dialect/Calyx/CalyxOps.cpp | 55 +++++++++---------- .../Calyx/Transforms/CalyxLoweringUtils.cpp | 38 +++++++++++++ test/Dialect/Calyx/errors.mlir | 23 ++++++++ 5 files changed, 108 insertions(+), 28 deletions(-) diff --git a/include/circt/Dialect/Calyx/CalyxLoweringUtils.h b/include/circt/Dialect/Calyx/CalyxLoweringUtils.h index 8cb05f9c115f..220be105e4ee 100644 --- a/include/circt/Dialect/Calyx/CalyxLoweringUtils.h +++ b/include/circt/Dialect/Calyx/CalyxLoweringUtils.h @@ -756,6 +756,23 @@ struct EliminateUnusedCombGroups : mlir::OpRewritePattern { PatternRewriter &rewriter) const override; }; +/// Removes duplicate EnableOps in parallel operations. +struct DeduplicateParallelOp : mlir::OpRewritePattern { + using mlir::OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(calyx::ParOp parOp, + PatternRewriter &rewriter) const override; +}; + +/// Removes duplicate EnableOps in static parallel operations. +struct DeduplicateStaticParallelOp + : mlir::OpRewritePattern { + using mlir::OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(calyx::StaticParOp parOp, + PatternRewriter &rewriter) const override; +}; + /// This pass recursively inlines use-def chains of combinational logic (from /// non-stateful groups) into groups referenced in the control schedule. class InlineCombGroups diff --git a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp index a8bd9b7eda5a..1763df012a2f 100644 --- a/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp +++ b/lib/Conversion/LoopScheduleToCalyx/LoopScheduleToCalyx.cpp @@ -1644,6 +1644,9 @@ void LoopScheduleToCalyxPass::runOnOperation() { addOncePattern(loweringPatterns, patternState, *loweringState); + addGreedyPattern(loweringPatterns); + addGreedyPattern(loweringPatterns); + /// This pattern performs various SSA replacements that must be done /// after control generation. addOncePattern(loweringPatterns, patternState, funcMap, diff --git a/lib/Dialect/Calyx/CalyxOps.cpp b/lib/Dialect/Calyx/CalyxOps.cpp index 74c1142de6ba..7ae18c59da69 100644 --- a/lib/Dialect/Calyx/CalyxOps.cpp +++ b/lib/Dialect/Calyx/CalyxOps.cpp @@ -326,31 +326,6 @@ static LogicalResult collapseControl(OpTy controlOp, return failure(); } -// Remove duplicates from a parallel operation. This may inadvertently occur -// during lowering. -template -static LogicalResult deduplicate(OpTy parOp, PatternRewriter &rewriter) { - static_assert(IsAny(), - "requires a parallel operation"); - auto *body = parOp.getBodyBlock(); - if (body->getOperations().size() < 2) - return failure(); - - LogicalResult result = LogicalResult::failure(); - SetVector members; - for (auto &op : make_early_inc_range(*body)) { - auto enableOp = dyn_cast(&op); - if (enableOp == nullptr) - continue; - bool inserted = members.insert(enableOp.getGroupName()); - if (!inserted) { - rewriter.eraseOp(enableOp); - result = LogicalResult::success(); - } - } - return result; -} - template static LogicalResult emptyControl(OpTy controlOp, PatternRewriter &rewriter) { if (controlOp.getBodyBlock()->empty()) { @@ -967,11 +942,24 @@ void StaticSeqOp::getCanonicalizationPatterns(RewritePatternSet &patterns, // ParOp //===----------------------------------------------------------------------===// -LogicalResult ParOp::verify() { return success(); } +LogicalResult ParOp::verify() { + llvm::SmallSet groupNames; + + // Add loose requirement that the body of a ParOp may not enable the same + // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G } + for (EnableOp op : getBodyBlock()->getOps()) { + StringRef groupName = op.getGroupName(); + if (groupNames.count(groupName)) + return emitOpError() << "cannot enable the same group: \"" << groupName + << "\" more than once."; + groupNames.insert(groupName); + } + + return success(); +} void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns, MLIRContext *context) { - patterns.add(deduplicate); patterns.add(collapseControl); patterns.add(emptyControl); patterns.insert>(context); @@ -982,6 +970,18 @@ void ParOp::getCanonicalizationPatterns(RewritePatternSet &patterns, //===----------------------------------------------------------------------===// LogicalResult StaticParOp::verify() { + llvm::SmallSet groupNames; + + // Add loose requirement that the body of a ParOp may not enable the same + // Group more than once, e.g. calyx.par { calyx.enable @G calyx.enable @G } + for (EnableOp op : getBodyBlock()->getOps()) { + StringRef groupName = op.getGroupName(); + if (groupNames.count(groupName)) + return emitOpError() << "cannot enable the same group: \"" << groupName + << "\" more than once."; + groupNames.insert(groupName); + } + // static par must only have static control in it auto &ops = (*this).getBodyBlock()->getOperations(); for (Operation &op : ops) { @@ -995,7 +995,6 @@ LogicalResult StaticParOp::verify() { void StaticParOp::getCanonicalizationPatterns(RewritePatternSet &patterns, MLIRContext *context) { - patterns.add(deduplicate); patterns.add(collapseControl); patterns.add(emptyControl); patterns.insert>(context); diff --git a/lib/Dialect/Calyx/Transforms/CalyxLoweringUtils.cpp b/lib/Dialect/Calyx/Transforms/CalyxLoweringUtils.cpp index 1507e9e1e82f..a8616ea2b1a0 100644 --- a/lib/Dialect/Calyx/Transforms/CalyxLoweringUtils.cpp +++ b/lib/Dialect/Calyx/Transforms/CalyxLoweringUtils.cpp @@ -29,6 +29,28 @@ using namespace mlir::arith; namespace circt { namespace calyx { +template +static LogicalResult deduplicateParallelOperation(OpTy parOp, + PatternRewriter &rewriter) { + auto *body = parOp.getBodyBlock(); + if (body->getOperations().size() < 2) + return failure(); + + LogicalResult result = LogicalResult::failure(); + SetVector members; + for (auto &op : make_early_inc_range(*body)) { + auto enableOp = dyn_cast(&op); + if (enableOp == nullptr) + continue; + bool inserted = members.insert(enableOp.getGroupName()); + if (!inserted) { + rewriter.eraseOp(enableOp); + result = LogicalResult::success(); + } + } + return result; +} + void appendPortsForExternalMemref(PatternRewriter &rewriter, StringRef memName, Value memref, unsigned memoryID, SmallVectorImpl &inPorts, @@ -609,6 +631,22 @@ EliminateUnusedCombGroups::matchAndRewrite(calyx::CombGroupOp combGroupOp, return success(); } +//===----------------------------------------------------------------------===// +// DeduplicateParallelOperations +//===----------------------------------------------------------------------===// + +LogicalResult +DeduplicateParallelOp::matchAndRewrite(calyx::ParOp parOp, + PatternRewriter &rewriter) const { + return deduplicateParallelOperation(parOp, rewriter); +} + +LogicalResult +DeduplicateStaticParallelOp::matchAndRewrite(calyx::StaticParOp parOp, + PatternRewriter &rewriter) const { + return deduplicateParallelOperation(parOp, rewriter); +} + //===----------------------------------------------------------------------===// // InlineCombGroups //===----------------------------------------------------------------------===// diff --git a/test/Dialect/Calyx/errors.mlir b/test/Dialect/Calyx/errors.mlir index a96859f653fb..39cd93060dce 100644 --- a/test/Dialect/Calyx/errors.mlir +++ b/test/Dialect/Calyx/errors.mlir @@ -478,6 +478,29 @@ module attributes {calyx.entrypoint = "main"} { // ----- +module attributes {calyx.entrypoint = "main"} { + calyx.component @main(%go: i1 {go}, %clk: i1 {clk}, %reset: i1 {reset}) -> (%done: i1 {done}) { + %r.in, %r.write_en, %r.clk, %r.reset, %r.out, %r.done = calyx.register @r : i1, i1, i1, i1, i1, i1 + %c1_1 = hw.constant 1 : i1 + calyx.wires { + calyx.group @A { + calyx.assign %r.in = %c1_1 : i1 + calyx.assign %r.write_en = %c1_1 : i1 + calyx.group_done %r.done : i1 + } + } + calyx.control { + // expected-error @+1 {{'calyx.par' op cannot enable the same group: "A" more than once.}} + calyx.par { + calyx.enable @A + calyx.enable @A + } + } + } +} + +// ----- + module attributes {calyx.entrypoint = "main"} { calyx.component @A(%go: i1 {go}, %clk: i1 {clk}, %reset: i1 {reset}) -> (%out: i1, %done: i1 {done}) { %c1_1 = hw.constant 1 : i1