diff --git a/lib/Conversion/MooreToCore/MooreToCore.cpp b/lib/Conversion/MooreToCore/MooreToCore.cpp index a5e184051121..49b13552a994 100644 --- a/lib/Conversion/MooreToCore/MooreToCore.cpp +++ b/lib/Conversion/MooreToCore/MooreToCore.cpp @@ -19,9 +19,11 @@ #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/SCF/IR/SCF.h" #include "mlir/IR/BuiltinDialect.h" +#include "mlir/IR/Iterators.h" #include "mlir/Interfaces/SideEffectInterfaces.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/DialectConversion.h" +#include "mlir/Transforms/RegionUtils.h" #include "llvm/ADT/TypeSwitch.h" namespace circt { @@ -103,30 +105,6 @@ static hw::ModulePortInfo getModulePortInfo(const TypeConverter &typeConverter, return hw::ModulePortInfo(inputs, outputs); } -/// Erase all dead blocks in a region. -static void eraseDeadBlocks(PatternRewriter &rewriter, Region ®ion) { - SmallVector worklist; - for (auto &block : llvm::make_early_inc_range(llvm::drop_begin(region, 1))) { - if (!block.use_empty()) - continue; - worklist.push_back(&block); - while (!worklist.empty()) { - auto *block = worklist.pop_back_val(); - if (!block->use_empty()) - continue; - for (auto *successor : block->getSuccessors()) - worklist.push_back(successor); - rewriter.eraseBlock(block); - } - } -} - -/// Erase all dead blocks in an op. -static void eraseDeadBlocks(PatternRewriter &rewriter, Operation *op) { - for (auto ®ion : op->getRegions()) - eraseDeadBlocks(rewriter, region); -} - //===----------------------------------------------------------------------===// // Structural Conversion //===----------------------------------------------------------------------===// @@ -188,16 +166,63 @@ struct InstanceOpConversion : public OpConversionPattern { } }; +static void getValuesToObserve(Region *region, + function_ref setInsertionPoint, + const TypeConverter *typeConverter, + ConversionPatternRewriter &rewriter, + SmallVector &observeValues) { + SmallDenseSet alreadyObserved; + Location loc = region->getLoc(); + + auto probeIfSignal = [&](Value value) -> Value { + if (!isa(value.getType())) + return value; + return rewriter.create(loc, value); + }; + + region->getParentOp()->walk>( + [&](Operation *operation) { + for (auto value : operation->getOperands()) { + if (region->isAncestor(value.getParentRegion())) + continue; + if (!alreadyObserved.insert(value).second) + continue; + + OpBuilder::InsertionGuard g(rewriter); + if (auto remapped = rewriter.getRemappedValue(value)) { + setInsertionPoint(remapped); + observeValues.push_back(probeIfSignal(remapped)); + } else { + setInsertionPoint(value); + auto type = typeConverter->convertType(value.getType()); + auto converted = typeConverter->materializeTargetConversion( + rewriter, loc, type, value); + observeValues.push_back(probeIfSignal(converted)); + } + } + }); +} + struct ProcedureOpConversion : public OpConversionPattern { using OpConversionPattern::OpConversionPattern; LogicalResult matchAndRewrite(ProcedureOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override { + // Collect values to observe before we do any modifications to the region. + SmallVector observedValues; + if (op.getKind() == ProcedureKind::AlwaysComb || + op.getKind() == ProcedureKind::AlwaysLatch) { + auto setInsertionPoint = [&](Value value) { + rewriter.setInsertionPoint(op); + }; + getValuesToObserve(&op.getBody(), setInsertionPoint, typeConverter, + rewriter, observedValues); + } + auto loc = op.getLoc(); if (failed(rewriter.convertRegionTypes(&op.getBody(), *typeConverter))) return failure(); - eraseDeadBlocks(rewriter, op); // Handle initial and final procedures. These lower to a corresponding // `llhd.process` or `llhd.final` op that executes the body and then halts. @@ -239,9 +264,10 @@ struct ProcedureOpConversion : public OpConversionPattern { // jumping back up to the body. if (op.getKind() == ProcedureKind::AlwaysComb || op.getKind() == ProcedureKind::AlwaysLatch) { - block = rewriter.createBlock(&newOp.getBody()); - // TODO: Collect observed signals and add LLHD wait op. - return failure(); + Block *waitBlock = rewriter.createBlock(&newOp.getBody()); + rewriter.create(loc, observedValues, Value(), ValueRange{}, + block); + block = waitBlock; } // Make all `moore.return` ops branch back up to the beginning of the @@ -322,6 +348,10 @@ struct WaitEventOpConversion : public OpConversionPattern { rewriter.eraseOp(detectOp); } + // Determine the values used during event detection that are defined outside + // the `wait_event`'s body region. We want to wait for a change on these + // signals before we check if any interesting event happened. + SmallVector observeValues; auto setInsertionPointAfterDef = [&](Value value) { if (auto *op = value.getDefiningOp()) rewriter.setInsertionPointAfter(op); @@ -329,37 +359,8 @@ struct WaitEventOpConversion : public OpConversionPattern { rewriter.setInsertionPointToStart(value.getParentBlock()); }; - auto probeIfSignal = [&](Value value) -> Value { - if (!isa(value.getType())) - return value; - return rewriter.create(loc, value); - }; - - // Determine the values used during event detection that are defined outside - // the `wait_event`'s body region. We want to wait for a change on these - // signals before we check if any interesting event happened. - SmallVector observeValues; - SmallDenseSet alreadyObserved; - clonedOp.getBody().walk([&](Operation *operation) { - for (auto value : operation->getOperands()) { - if (clonedOp.getBody().isAncestor(value.getParentRegion())) - continue; - if (!alreadyObserved.insert(value).second) - continue; - if (auto remapped = rewriter.getRemappedValue(value)) { - OpBuilder::InsertionGuard g(rewriter); - setInsertionPointAfterDef(remapped); - observeValues.push_back(probeIfSignal(remapped)); - } else { - OpBuilder::InsertionGuard g(rewriter); - setInsertionPointAfterDef(value); - auto type = typeConverter->convertType(value.getType()); - auto converted = typeConverter->materializeTargetConversion( - rewriter, loc, type, value); - observeValues.push_back(probeIfSignal(converted)); - } - } - }); + getValuesToObserve(&clonedOp.getBody(), setInsertionPointAfterDef, + typeConverter, rewriter, observeValues); // Create the `llhd.wait` op that suspends the current process and waits for // a change in the interesting values listed in `observeValues`. When a @@ -1388,6 +1389,11 @@ void MooreToCorePass::runOnOperation() { MLIRContext &context = getContext(); ModuleOp module = getOperation(); + IRRewriter rewriter(module); + module->walk([&](Operation *op) { + (void)mlir::eraseUnreachableBlocks(rewriter, op->getRegions()); + }); + ConversionTarget target(context); TypeConverter typeConverter; RewritePatternSet patterns(&context); diff --git a/test/Conversion/MooreToCore/basic.mlir b/test/Conversion/MooreToCore/basic.mlir index 3a0c87213131..b90dbb6f7958 100644 --- a/test/Conversion/MooreToCore/basic.mlir +++ b/test/Conversion/MooreToCore/basic.mlir @@ -691,6 +691,45 @@ moore.module @WaitEvent() { moore.return } + // CHECK: [[FALSE:%.+]] = hw.constant false + // CHECK: [[PRB_A:%.+]] = llhd.prb %a + // CHECK: [[PRB_B:%.+]] = llhd.prb %b + // CHECK: llhd.process { + %cond = moore.constant 0 : i1 + moore.procedure always_comb { + // CHECK: cf.br ^[[BB1:.+]] + // CHECK: ^[[BB1]]: + // CHECK: llhd.prb %a + // CHECK: llhd.prb %b + // CHECK: cf.br ^[[BB2:.+]] + // CHECK: ^[[BB2]]: + // CHECK: llhd.wait ([[FALSE]], [[PRB_A]], [[PRB_B]] : {{.*}}), ^[[BB1]] + %1 = moore.conditional %cond : i1 -> i1 { + %2 = moore.read %a : + moore.yield %2 : !moore.i1 + } { + %3 = moore.read %b : + moore.yield %3 : !moore.i1 + } + moore.return + } + + // CHECK: [[PRB_D:%.+]] = llhd.prb %d + // CHECK: llhd.process { + // CHECK: cf.br ^[[BB1:.+]] + // CHECK: ^[[BB1]]: + // CHECK: llhd.prb %d + // CHECK: cf.br ^[[BB2:.+]] + // CHECK: ^[[BB2]]: + // CHECK: llhd.wait ([[PRB_D]] : {{.*}}), ^[[BB1]] + moore.procedure always_latch { + %3 = moore.read %d : + moore.return + } + + // CHECK: %d = llhd.sig %false + %d = moore.variable : + // CHECK: llhd.process { moore.procedure initial { // CHECK: llhd.wait ([[PRB_A6]], [[PRB_B2]] : @@ -708,3 +747,38 @@ moore.module @WaitEvent() { moore.return } } + +// Just check that block without predecessors are handled without crashing +// CHECK-LABEL: @NoPredecessorBlockErasure +moore.module @NoPredecessorBlockErasure(in %clk_i : !moore.l1, in %raddr_i : !moore.array<2 x l5>, out rdata_o : !moore.array<2 x l32>, in %waddr_i : !moore.array<1 x l5>, in %wdata_i : !moore.array<1 x l32>, in %we_i : !moore.l1) { + %0 = moore.constant 0 : l32 + %1 = moore.constant 1 : i32 + %2 = moore.constant 0 : i32 + %rdata_o = moore.variable : > + %mem = moore.variable : > + moore.procedure always_ff { + cf.br ^bb1(%2 : !moore.i32) + ^bb1(%4: !moore.i32): // 2 preds: ^bb0, ^bb8 + moore.return + ^bb2: // no predecessors + cf.br ^bb3(%2 : !moore.i32) + ^bb3(%5: !moore.i32): // 2 preds: ^bb2, ^bb6 + cf.br ^bb8 + ^bb4: // no predecessors + cf.br ^bb6 + ^bb5: // no predecessors + cf.br ^bb6 + ^bb6: // 2 preds: ^bb4, ^bb5 + %6 = moore.add %5, %1 : i32 + cf.br ^bb3(%6 : !moore.i32) + ^bb7: // no predecessors + %7 = moore.extract_ref %mem from 0 : > -> + moore.nonblocking_assign %7, %0 : l32 + cf.br ^bb8 + ^bb8: // 2 preds: ^bb3, ^bb7 + %8 = moore.add %4, %1 : i32 + cf.br ^bb1(%8 : !moore.i32) + } + %3 = moore.read %rdata_o : > + moore.output %3 : !moore.array<2 x l32> +}