Skip to content

Commit

Permalink
[MooreToCore] Add always_comb and always_latch lowering support
Browse files Browse the repository at this point in the history
  • Loading branch information
maerhart committed Aug 19, 2024
1 parent fcbd718 commit 6bc9822
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 50 deletions.
130 changes: 80 additions & 50 deletions lib/Conversion/MooreToCore/MooreToCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/DialectConversion.h"
#include "llvm/ADT/TypeSwitch.h"
#include <mlir/IR/Iterators.h>

namespace circt {
#define GEN_PASS_DEF_CONVERTMOORETOCORE
Expand Down Expand Up @@ -104,27 +105,31 @@ static hw::ModulePortInfo getModulePortInfo(const TypeConverter &typeConverter,
}

/// Erase all dead blocks in a region.
static void eraseDeadBlocks(PatternRewriter &rewriter, Region &region) {
static void eraseDeadBlocks(Region &region) {
SmallVector<Block *> worklist;
for (auto &block : llvm::make_early_inc_range(llvm::drop_begin(region, 1))) {
if (!block.use_empty())
for (auto &block : llvm::make_early_inc_range(llvm::drop_begin(region, 1)))
if (block.use_empty())
worklist.push_back(&block);

DenseSet<Block *> erased;
while (!worklist.empty()) {
auto *block = worklist.pop_back_val();
if (erased.contains(block) ||
llvm::any_of(block->getPredecessors(),
[&](Block *pred) { return !erased.contains(pred); }))
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);
}

for (auto *successor : block->getSuccessors())
worklist.push_back(successor);

erased.insert(block);
block->erase();
}
}

/// Erase all dead blocks in an op.
static void eraseDeadBlocks(PatternRewriter &rewriter, Operation *op) {
for (auto &region : op->getRegions())
eraseDeadBlocks(rewriter, region);
static void eraseDeadBlocks(Operation *op) {
op->walk([&](Region *region) { eraseDeadBlocks(*region); });
}

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -188,16 +193,63 @@ struct InstanceOpConversion : public OpConversionPattern<InstanceOp> {
}
};

static void getValuesToObserve(Region *region,
function_ref<void(Value)> setInsertionPoint,
const TypeConverter *typeConverter,
ConversionPatternRewriter &rewriter,
SmallVector<Value> &observeValues) {
SmallDenseSet<Value> alreadyObserved;
Location loc = region->getLoc();

auto probeIfSignal = [&](Value value) -> Value {
if (!isa<hw::InOutType>(value.getType()))
return value;
return rewriter.create<llhd::PrbOp>(loc, value);
};

region->getParentOp()->walk<WalkOrder::PreOrder, ForwardDominanceIterator<>>(
[&](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<ProcedureOp> {
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<Value> 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.
Expand Down Expand Up @@ -239,9 +291,10 @@ struct ProcedureOpConversion : public OpConversionPattern<ProcedureOp> {
// 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<llhd::WaitOp>(loc, observedValues, Value(), ValueRange{},
block);
block = waitBlock;
}

// Make all `moore.return` ops branch back up to the beginning of the
Expand Down Expand Up @@ -322,44 +375,19 @@ struct WaitEventOpConversion : public OpConversionPattern<WaitEventOp> {
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<Value> observeValues;
auto setInsertionPointAfterDef = [&](Value value) {
if (auto *op = value.getDefiningOp())
rewriter.setInsertionPointAfter(op);
if (auto arg = dyn_cast<BlockArgument>(value))
rewriter.setInsertionPointToStart(value.getParentBlock());
};

auto probeIfSignal = [&](Value value) -> Value {
if (!isa<hw::InOutType>(value.getType()))
return value;
return rewriter.create<llhd::PrbOp>(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<Value> observeValues;
SmallDenseSet<Value> 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
Expand Down Expand Up @@ -1388,6 +1416,8 @@ void MooreToCorePass::runOnOperation() {
MLIRContext &context = getContext();
ModuleOp module = getOperation();

eraseDeadBlocks(module);

ConversionTarget target(context);
TypeConverter typeConverter;
RewritePatternSet patterns(&context);
Expand Down
74 changes: 74 additions & 0 deletions test/Conversion/MooreToCore/basic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -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 : <i1>
moore.yield %2 : !moore.i1
} {
%3 = moore.read %b : <i1>
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 : <i1>
moore.return
}

// CHECK: %d = llhd.sig %false
%d = moore.variable : <i1>

// CHECK: llhd.process {
moore.procedure initial {
// CHECK: llhd.wait ([[PRB_A6]], [[PRB_B2]] :
Expand All @@ -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 : <array<2 x l32>>
%mem = moore.variable : <array<32 x l32>>
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 : <array<32 x l32>> -> <l32>
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 : <array<2 x l32>>
moore.output %3 : !moore.array<2 x l32>
}

0 comments on commit 6bc9822

Please sign in to comment.