Skip to content

Commit

Permalink
[LLHD] Let WaitOp observe plain values instead of signals (#7528)
Browse files Browse the repository at this point in the history
This is necessary to lower the moore dialect's always_comb and always_latch without introducing helper signals. It also allows for more mem2reg at the LLHD level.
  • Loading branch information
maerhart authored Aug 19, 2024
1 parent 246636c commit 9efc2e7
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 80 deletions.
4 changes: 2 additions & 2 deletions include/circt/Dialect/LLHD/IR/LLHDStructureOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,14 @@ def WaitOp : LLHDOp<"wait", [
```
}];

let arguments = (ins Variadic<InOutType>:$obs,
let arguments = (ins Variadic<HWValueType>:$observed,
Optional<LLHDTimeType>:$time,
Variadic<AnyType>:$destOps);

let successors = (successor AnySuccessor:$dest);

let assemblyFormat = [{
(`for` $time^ `,`)? (`(`$obs^ `:` qualified(type($obs))`)` `,`)?
(`for` $time^ `,`)? (`(`$observed^ `:` qualified(type($observed))`)` `,`)?
$dest (`(` $destOps^ `:` qualified(type($destOps)) `)`)? attr-dict
}];
}
Expand Down
21 changes: 19 additions & 2 deletions lib/Conversion/MooreToCore/MooreToCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,19 @@ struct WaitEventOpConversion : public OpConversionPattern<WaitEventOp> {
rewriter.eraseOp(detectOp);
}

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.
Expand All @@ -334,12 +347,16 @@ struct WaitEventOpConversion : public OpConversionPattern<WaitEventOp> {
if (!alreadyObserved.insert(value).second)
continue;
if (auto remapped = rewriter.getRemappedValue(value)) {
observeValues.push_back(remapped);
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(converted);
observeValues.push_back(probeIfSignal(converted));
}
}
});
Expand Down
55 changes: 16 additions & 39 deletions lib/Dialect/LLHD/Transforms/ProcessLoweringPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,6 @@ struct ProcessLoweringPass
void runOnOperation() override;
};

/// Backtrack a signal value and make sure that every part of it is in the
/// observer list at some point. Assumes that there is no operation that adds
/// parts to a signal that it does not take as input (e.g. something like
/// llhd.sig.zext %sig : !hw.inout<i32> -> !hw.inout<i64>).
static LogicalResult checkSignalsAreObserved(OperandRange obs, Value value) {
// If the value in the observer list, we don't need to backtrack further.
if (llvm::is_contained(obs, value))
return success();

if (Operation *op = value.getDefiningOp()) {
// If no input is a signal, this operation creates one and thus this is the
// last point where it could have been observed. As we've already checked
// that, we can fail here. This includes for example llhd.sig
if (llvm::none_of(op->getOperands(), [](Value arg) {
return isa<hw::InOutType>(arg.getType());
}))
return failure();

// Only recusively backtrack signal values. Other values cannot be changed
// from outside or with a delay. If they come from probes at some point,
// they are covered by that probe. As soon as we find a signal that is not
// observed no matter how far we backtrack, we fail.
return success(llvm::all_of(op->getOperands(), [&](Value arg) {
return !isa<hw::InOutType>(arg.getType()) ||
succeeded(checkSignalsAreObserved(obs, arg));
}));
}

// If the value is a module argument (no block arguments except for the entry
// block are allowed here) and was not observed, we cannot backtrack further
// and thus fail.
return failure();
}

static LogicalResult isProcValidToLower(llhd::ProcessOp op) {
size_t numBlocks = op.getBody().getBlocks().size();

Expand Down Expand Up @@ -98,13 +64,24 @@ static LogicalResult isProcValidToLower(llhd::ProcessOp op) {
"during process-lowering: llhd.wait terminators with optional time "
"argument cannot be lowered to structural LLHD");

SmallVector<Value> observedSignals;
for (Value obs : wait.getObserved())
if (auto prb = obs.getDefiningOp<llhd::PrbOp>())
if (!op.getBody().isAncestor(prb->getParentRegion()))
observedSignals.push_back(prb.getSignal());

// Every probed signal has to occur in the observed signals list in
// the wait instruction
WalkResult result = op.walk([&wait](llhd::PrbOp prbOp) -> WalkResult {
if (failed(checkSignalsAreObserved(wait.getObs(), prbOp.getSignal())))
return wait.emitOpError(
"during process-lowering: the wait terminator is required to "
"have all probed signals as arguments");
WalkResult result = op.walk([&](Operation *operation) -> WalkResult {
// TODO: value does not need to be observed if all values this value is
// a combinatorial result of are observed.
for (Value operand : operation->getOperands())
if (!op.getBody().isAncestor(operand.getParentRegion()) &&
!llvm::is_contained(wait.getObserved(), operand) &&
!llvm::is_contained(observedSignals, operand))
return wait.emitOpError(
"during process-lowering: the wait terminator is required to "
"have values used in the process as arguments");

return WalkResult::advance();
});
Expand Down
25 changes: 18 additions & 7 deletions test/Conversion/MooreToCore/basic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -536,8 +536,19 @@ func.func private @dummyC() -> ()
// CHECK-LABEL: hw.module @WaitEvent
moore.module @WaitEvent() {
// CHECK: %a = llhd.sig
// CHECK: [[PRB_A6:%.+]] = llhd.prb %a
// CHECK: [[PRB_A5:%.+]] = llhd.prb %a
// CHECK: [[PRB_A4:%.+]] = llhd.prb %a
// CHECK: [[PRB_A3:%.+]] = llhd.prb %a
// CHECK: [[PRB_A2:%.+]] = llhd.prb %a
// CHECK: [[PRB_A1:%.+]] = llhd.prb %a
// CHECK: [[PRB_A0:%.+]] = llhd.prb %a
// CHECK: %b = llhd.sig
// CHECK: [[PRB_B2:%.+]] = llhd.prb %b
// CHECK: [[PRB_B1:%.+]] = llhd.prb %b
// CHECK: [[PRB_B0:%.+]] = llhd.prb %b
// CHECK: %c = llhd.sig
// CHECK: [[PRB_C:%.+]] = llhd.prb %c
%a = moore.variable : <i1>
%b = moore.variable : <i1>
%c = moore.variable : <i1>
Expand Down Expand Up @@ -569,7 +580,7 @@ moore.module @WaitEvent() {
// CHECK: llhd.process {
moore.procedure initial {
// CHECK: [[BEFORE:%.+]] = llhd.prb %a
// CHECK: llhd.wait (%a : {{.+}}), ^[[CHECK:.+]]
// CHECK: llhd.wait ([[PRB_A0]] : {{.+}}), ^[[CHECK:.+]]
// CHECK: ^[[CHECK]]:
// CHECK: [[AFTER:%.+]] = llhd.prb %a
// CHECK: [[TMP:%.+]] = comb.icmp bin ne [[BEFORE]], [[AFTER]]
Expand All @@ -585,7 +596,7 @@ moore.module @WaitEvent() {
moore.procedure initial {
// CHECK: [[BEFORE_A:%.+]] = llhd.prb %a
// CHECK: [[BEFORE_B:%.+]] = llhd.prb %b
// CHECK: llhd.wait (%a, %b : {{.+}}), ^[[CHECK:.+]]
// CHECK: llhd.wait ([[PRB_A1]], [[PRB_B0]] : {{.+}}), ^[[CHECK:.+]]
// CHECK: ^[[CHECK]]:
// CHECK: [[AFTER_A:%.+]] = llhd.prb %a
// CHECK: [[AFTER_B:%.+]] = llhd.prb %b
Expand All @@ -605,7 +616,7 @@ moore.module @WaitEvent() {
// CHECK: [[BEFORE_A:%.+]] = llhd.prb %a
// CHECK: [[BEFORE_B:%.+]] = llhd.prb %b
// CHECK: [[BEFORE_C:%.+]] = llhd.prb %c
// CHECK: llhd.wait (%a, %b, %c : {{.+}}), ^[[CHECK:.+]]
// CHECK: llhd.wait ([[PRB_A2]], [[PRB_B1]], [[PRB_C]] : {{.+}}), ^[[CHECK:.+]]
// CHECK: ^[[CHECK]]:
// CHECK: [[AFTER_A:%.+]] = llhd.prb %a
// CHECK: [[AFTER_B:%.+]] = llhd.prb %b
Expand All @@ -629,7 +640,7 @@ moore.module @WaitEvent() {
// CHECK: llhd.process {
moore.procedure initial {
// CHECK: [[BEFORE:%.+]] = llhd.prb %a
// CHECK: llhd.wait (%a : {{.+}}), ^[[CHECK:.+]]
// CHECK: llhd.wait ([[PRB_A3]] : {{.+}}), ^[[CHECK:.+]]
// CHECK: ^[[CHECK]]:
// CHECK: [[AFTER:%.+]] = llhd.prb %a
// CHECK: [[TRUE:%.+]] = hw.constant true
Expand All @@ -646,7 +657,7 @@ moore.module @WaitEvent() {
// CHECK: llhd.process {
moore.procedure initial {
// CHECK: [[BEFORE:%.+]] = llhd.prb %a
// CHECK: llhd.wait (%a : {{.+}}), ^[[CHECK:.+]]
// CHECK: llhd.wait ([[PRB_A4]] : {{.+}}), ^[[CHECK:.+]]
// CHECK: ^[[CHECK]]:
// CHECK: [[AFTER:%.+]] = llhd.prb %a
// CHECK: [[TRUE:%.+]] = hw.constant true
Expand All @@ -663,7 +674,7 @@ moore.module @WaitEvent() {
// CHECK: llhd.process {
moore.procedure initial {
// CHECK: [[BEFORE:%.+]] = llhd.prb %a
// CHECK: llhd.wait (%a : {{.+}}), ^[[CHECK:.+]]
// CHECK: llhd.wait ([[PRB_A5]] : {{.+}}), ^[[CHECK:.+]]
// CHECK: ^[[CHECK]]:
// CHECK: [[AFTER:%.+]] = llhd.prb %a
// CHECK: [[TRUE:%.+]] = hw.constant true
Expand All @@ -682,7 +693,7 @@ moore.module @WaitEvent() {

// CHECK: llhd.process {
moore.procedure initial {
// CHECK: llhd.wait (%a, %b :
// CHECK: llhd.wait ([[PRB_A6]], [[PRB_B2]] :
moore.wait_event {
%0 = moore.constant 0 : i1
%1 = moore.conditional %0 : i1 -> i1 {
Expand Down
5 changes: 3 additions & 2 deletions test/Dialect/LLHD/Canonicalization/probeCSE.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ hw.module @checkPrbDceAndCseIn(inout %arg0 : i32, inout %arg1 : i32, inout %arg2

// CHECK-LABEL: @checkPrbDceButNotCse
hw.module @checkPrbDceButNotCse(inout %arg0 : i32, inout %arg1 : i32, inout %arg2 : i32) {
// CHECK-NEXT: llhd.process
%prb = llhd.prb %arg0 : !hw.inout<i32>
// CHECK: llhd.process
llhd.process {
// CHECK-NEXT: llhd.constant_time
%time = llhd.constant_time <0ns, 1d, 0e>

// CHECK-NEXT: [[P1:%.*]] = llhd.prb
%1 = llhd.prb %arg0 : !hw.inout<i32>
// CHECK-NEXT: llhd.wait
llhd.wait (%arg0: !hw.inout<i32>), ^bb1
llhd.wait (%prb: i32), ^bb1
// CHECK-NEXT: ^bb1:
^bb1:
// CHECK-NEXT: [[P2:%.*]] = llhd.prb
Expand Down
16 changes: 12 additions & 4 deletions test/Dialect/LLHD/IR/basic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,14 @@ hw.module @check_wait_1 () {

// CHECK: @check_wait_2(inout %[[ARG0:.*]] : i64, inout %[[ARG1:.*]] : i1) {
hw.module @check_wait_2 (inout %arg0 : i64, inout %arg1 : i1) {
// CHECK: [[PRB0:%.+]] = llhd.prb %arg0
%prb0 = llhd.prb %arg0 : !hw.inout<i64>
// CHECK: [[PRB1:%.+]] = llhd.prb %arg1
%prb1 = llhd.prb %arg1 : !hw.inout<i1>
// CHECK-NEXT: llhd.process
llhd.process {
// CHECK-NEXT: llhd.wait (%[[ARG0]], %[[ARG1]] : !hw.inout<i64>, !hw.inout<i1>), ^[[BB:.*]](%[[ARG1]] : !hw.inout<i1>)
llhd.wait (%arg0, %arg1 : !hw.inout<i64>, !hw.inout<i1>), ^bb1(%arg1 : !hw.inout<i1>)
// CHECK-NEXT: llhd.wait ([[PRB0]], [[PRB1]] : i64, i1), ^[[BB:.*]](%[[ARG1]] : !hw.inout<i1>)
llhd.wait (%prb0, %prb1 : i64, i1), ^bb1(%arg1 : !hw.inout<i1>)
// CHECK: ^[[BB]](%[[A:.*]]: !hw.inout<i1>):
^bb1(%a: !hw.inout<i1>):
llhd.halt
Expand All @@ -188,12 +192,16 @@ hw.module @check_wait_2 (inout %arg0 : i64, inout %arg1 : i1) {

// CHECK: hw.module @check_wait_3(inout %[[ARG0:.*]] : i64, inout %[[ARG1:.*]] : i1) {
hw.module @check_wait_3 (inout %arg0 : i64, inout %arg1 : i1) {
// CHECK: [[PRB0:%.+]] = llhd.prb %arg0
%prb0 = llhd.prb %arg0 : !hw.inout<i64>
// CHECK: [[PRB1:%.+]] = llhd.prb %arg1
%prb1 = llhd.prb %arg1 : !hw.inout<i1>
// CHECK-NEXT: llhd.process
llhd.process {
// CHECK-NEXT: %[[TIME:.*]] = llhd.constant_time
%time = llhd.constant_time #llhd.time<0ns, 0d, 0e>
// CHECK-NEXT: llhd.wait for %[[TIME]], (%[[ARG0]], %[[ARG1]] : !hw.inout<i64>, !hw.inout<i1>), ^[[BB:.*]](%[[ARG1]], %[[ARG0]] : !hw.inout<i1>, !hw.inout<i64>)
llhd.wait for %time, (%arg0, %arg1 : !hw.inout<i64>, !hw.inout<i1>), ^bb1(%arg1, %arg0 : !hw.inout<i1>, !hw.inout<i64>)
// CHECK-NEXT: llhd.wait for %[[TIME]], ([[PRB0]], [[PRB1]] : i64, i1), ^[[BB:.*]](%[[ARG1]], %[[ARG0]] : !hw.inout<i1>, !hw.inout<i64>)
llhd.wait for %time, (%prb0, %prb1 : i64, i1), ^bb1(%arg1, %arg0 : !hw.inout<i1>, !hw.inout<i64>)
// CHECK: ^[[BB]](%[[A:.*]]: !hw.inout<i1>, %[[B:.*]]: !hw.inout<i64>):
^bb1(%a: !hw.inout<i1>, %b: !hw.inout<i64>):
llhd.halt
Expand Down
14 changes: 10 additions & 4 deletions test/Dialect/LLHD/Transforms/earlyCodeMotion.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ hw.module @check_blockarg(inout %sig : i32) {

// CHECK-LABEL: @loop
// CHECK-SAME: (inout %[[VAL_0:.*]] : i2)
// CHECK: [[PRB:%.+]] = llhd.prb %in_i
// CHECK: llhd.process
// CHECK: %[[VAL_1:.*]] = hw.constant 0 : i32
// CHECK: %[[VAL_2:.*]] = hw.constant 2 : i32
Expand All @@ -138,14 +139,15 @@ hw.module @check_blockarg(inout %sig : i32) {
// CHECK: %[[VAL_8:.*]] = llhd.prb %[[VAL_0]] : !hw.inout<i2>
// CHECK: cf.cond_br %[[VAL_7]], ^[[BB4:.+]], ^[[BB3:.+]]
// CHECK: ^[[BB3]]:
// CHECK: llhd.wait (%[[VAL_0]] : !hw.inout<i2>), ^[[BB1]]
// CHECK: llhd.wait ([[PRB]] : i2), ^[[BB1]]
// CHECK: ^[[BB4]]:
// CHECK: %[[VAL_9:.*]] = llhd.load %[[VAL_5]] : !llhd.ptr<i32>
// CHECK: %[[VAL_10:.*]] = comb.add %[[VAL_9]], %[[VAL_4]] : i32
// CHECK: llhd.store %[[VAL_5]], %[[VAL_10]] : !llhd.ptr<i32>
// CHECK: cf.br ^[[BB2]]
// CHECK: }
hw.module @loop(inout %in_i : i2) {
%prb0 = llhd.prb %in_i : !hw.inout<i2>
llhd.process {
// TR: -1
cf.br ^body
Expand All @@ -162,7 +164,7 @@ hw.module @loop(inout %in_i : i2) {
cf.cond_br %2, ^loop_continue, ^check
^check:
// TR: 1
llhd.wait (%in_i : !hw.inout<i2>), ^body
llhd.wait (%prb0 : i2), ^body
^loop_continue:
// TR: 1
%3 = hw.constant 0 : i2
Expand All @@ -177,6 +179,8 @@ hw.module @loop(inout %in_i : i2) {

// CHECK-LABEL: @complicated
// CHECK-SAME: (inout %[[VAL_0:.*]] : i1, inout %[[VAL_1:.*]] : i1, inout %[[VAL_2:.*]] : i1, inout %[[VAL_3:.*]] : i1, inout %[[VAL_4:.*]] : i1)
// CHECK: [[PRB_CLK:%.+]] = llhd.prb %[[VAL_1]]
// CHECK: [[PRB_RST:%.+]] = llhd.prb %[[VAL_0]]
// CHECK: llhd.process
// CHECK: %[[ALLSET:.*]] = hw.constant true
// CHECK: %[[VAL_5:.*]] = hw.constant false
Expand All @@ -191,7 +195,7 @@ hw.module @loop(inout %in_i : i2) {
// CHECK: %[[VAL_10:.*]] = llhd.prb %[[VAL_0]] : !hw.inout<i1>
// CHECK: %[[VAL_11:.*]] = comb.icmp eq %[[VAL_9]], %[[VAL_5]] : i1
// CHECK: %[[VAL_12:.*]] = comb.icmp ne %[[VAL_10]], %[[VAL_5]] : i1
// CHECK: llhd.wait (%[[VAL_1]], %[[VAL_0]] : !hw.inout<i1>, !hw.inout<i1>), ^[[BB3:.+]]
// CHECK: llhd.wait ([[PRB_CLK]], [[PRB_RST]] : i1, i1), ^[[BB3:.+]]
// CHECK: ^[[BB3]]:
// CHECK: %[[VAL_13:.*]] = llhd.prb %[[VAL_3]] : !hw.inout<i1>
// CHECK: llhd.store %[[VAL_8]], %[[VAL_13]] : !llhd.ptr<i1>
Expand Down Expand Up @@ -221,6 +225,8 @@ hw.module @loop(inout %in_i : i2) {
// CHECK: cf.br ^[[BB1]]
// CHECK: }
hw.module @complicated(inout %rst_ni: i1, inout %clk_i: i1, inout %async_ack_i: i1, inout %ack_src_q: i1, inout %ack_q: i1) {
%prb_clk = llhd.prb %clk_i : !hw.inout<i1>
%prb_rst = llhd.prb %rst_ni : !hw.inout<i1>
llhd.process {
%allset = hw.constant 1 : i1
// TR: -1
Expand All @@ -234,7 +240,7 @@ hw.module @complicated(inout %rst_ni: i1, inout %clk_i: i1, inout %async_ack_i:
// TR: 2
%clk_i_prb = llhd.prb %clk_i : !hw.inout<i1>
%rst_ni_prb = llhd.prb %rst_ni : !hw.inout<i1>
llhd.wait (%clk_i, %rst_ni : !hw.inout<i1>, !hw.inout<i1>), ^check
llhd.wait (%prb_clk, %prb_rst : i1, i1), ^check
^check:
// TR: 0
%2 = llhd.prb %ack_src_q : !hw.inout<i1>
Expand Down
6 changes: 4 additions & 2 deletions test/Dialect/LLHD/Transforms/memoryToBlockArgument.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ hw.module @multiple_store_one_block() {

// CHECK-LABEL: @loop
// CHECK-SAME: (inout %[[VAL_0:.*]] : i2)
// CHECK: [[PRB:%.+]] = llhd.prb %[[VAL_0]]
// CHECK: cf.br ^bb1
// CHECK: ^bb1:
// CHECK: %[[VAL_1:.*]] = hw.constant 0 : i32
Expand All @@ -271,14 +272,15 @@ hw.module @multiple_store_one_block() {
// CHECK: %[[VAL_4:.*]] = comb.icmp ult %[[VAL_2]], %[[VAL_3]] : i32
// CHECK: cf.cond_br %[[VAL_4]], ^bb4, ^bb3
// CHECK: ^bb3:
// CHECK: llhd.wait (%[[VAL_0]] : !hw.inout<i2>), ^bb1
// CHECK: llhd.wait ([[PRB]] : i2), ^bb1
// CHECK: ^bb4:
// CHECK: %[[VAL_5:.*]] = hw.constant 0 : i2
// CHECK: %[[VAL_6:.*]] = hw.constant 1 : i32
// CHECK: %[[VAL_7:.*]] = comb.add %[[VAL_2]], %[[VAL_6]] : i32
// CHECK: cf.br ^bb2(%[[VAL_7]] : i32)
// CHECK: }
hw.module @loop(inout %in_i : i2) {
%prb = llhd.prb %in_i : !hw.inout<i2>
llhd.process {
cf.br ^body
^body:
Expand All @@ -291,7 +293,7 @@ hw.module @loop(inout %in_i : i2) {
%2 = comb.icmp ult %i_ld, %1 : i32
cf.cond_br %2, ^loop_continue, ^check
^check:
llhd.wait (%in_i : !hw.inout<i2>), ^body
llhd.wait (%prb : i2), ^body
^loop_continue:
%3 = hw.constant 0 : i2
%5 = hw.constant 1 : i32
Expand Down
Loading

0 comments on commit 9efc2e7

Please sign in to comment.