CIRCT  19.0.0git
ProcessLoweringPass.cpp
Go to the documentation of this file.
1 //===- ProcessLoweringPass.cpp - Implement Process Lowering Pass ----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Implement Pass to transform combinational processes to entities.
10 //
11 //===----------------------------------------------------------------------===//
12 
15 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
16 #include "mlir/IR/PatternMatch.h"
17 #include "mlir/IR/Visitors.h"
18 #include "mlir/Pass/Pass.h"
19 
20 namespace circt {
21 namespace llhd {
22 #define GEN_PASS_DEF_PROCESSLOWERING
23 #include "circt/Dialect/LLHD/Transforms/Passes.h.inc"
24 } // namespace llhd
25 } // namespace circt
26 
27 using namespace mlir;
28 using namespace circt;
29 
30 namespace {
31 
32 struct ProcessLoweringPass
33  : public circt::llhd::impl::ProcessLoweringBase<ProcessLoweringPass> {
34  void runOnOperation() override;
35 };
36 
37 /// Backtrack a signal value and make sure that every part of it is in the
38 /// observer list at some point. Assumes that there is no operation that adds
39 /// parts to a signal that it does not take as input (e.g. something like
40 /// llhd.sig.zext %sig : !hw.inout<i32> -> !hw.inout<i64>).
41 static LogicalResult checkSignalsAreObserved(OperandRange obs, Value value) {
42  // If the value in the observer list, we don't need to backtrack further.
43  if (llvm::is_contained(obs, value))
44  return success();
45 
46  if (Operation *op = value.getDefiningOp()) {
47  // If no input is a signal, this operation creates one and thus this is the
48  // last point where it could have been observed. As we've already checked
49  // that, we can fail here. This includes for example llhd.sig
50  if (llvm::none_of(op->getOperands(), [](Value arg) {
51  return isa<hw::InOutType>(arg.getType());
52  }))
53  return failure();
54 
55  // Only recusively backtrack signal values. Other values cannot be changed
56  // from outside or with a delay. If they come from probes at some point,
57  // they are covered by that probe. As soon as we find a signal that is not
58  // observed no matter how far we backtrack, we fail.
59  return success(llvm::all_of(op->getOperands(), [&](Value arg) {
60  return !isa<hw::InOutType>(arg.getType()) ||
61  succeeded(checkSignalsAreObserved(obs, arg));
62  }));
63  }
64 
65  // If the value is a module argument (no block arguments except for the entry
66  // block are allowed here) and was not observed, we cannot backtrack further
67  // and thus fail.
68  return failure();
69 }
70 
71 static LogicalResult isProcValidToLower(llhd::ProcessOp op) {
72  size_t numBlocks = op.getBody().getBlocks().size();
73 
74  if (numBlocks == 1) {
75  if (!isa<llhd::HaltOp>(op.getBody().back().getTerminator()))
76  return op.emitOpError("during process-lowering: entry block is required "
77  "to be terminated by llhd.halt");
78  return success();
79  }
80 
81  if (numBlocks == 2) {
82  Block &first = op.getBody().front();
83  Block &last = op.getBody().back();
84 
85  if (!last.getArguments().empty())
86  return op.emitOpError(
87  "during process-lowering: the second block (containing the "
88  "llhd.wait) is not allowed to have arguments");
89 
90  if (!isa<cf::BranchOp>(first.getTerminator()))
91  return op.emitOpError("during process-lowering: the first block has to "
92  "be terminated by a cf.br operation");
93 
94  if (auto wait = dyn_cast<llhd::WaitOp>(last.getTerminator())) {
95  // No optional time argument is allowed
96  if (wait.getTime())
97  return wait.emitOpError(
98  "during process-lowering: llhd.wait terminators with optional time "
99  "argument cannot be lowered to structural LLHD");
100 
101  // Every probed signal has to occur in the observed signals list in
102  // the wait instruction
103  WalkResult result = op.walk([&wait](llhd::PrbOp prbOp) -> WalkResult {
104  if (failed(checkSignalsAreObserved(wait.getObs(), prbOp.getSignal())))
105  return wait.emitOpError(
106  "during process-lowering: the wait terminator is required to "
107  "have all probed signals as arguments");
108 
109  return WalkResult::advance();
110  });
111  return failure(result.wasInterrupted());
112  }
113 
114  return op.emitOpError("during process-lowering: the second block must be "
115  "terminated by llhd.wait");
116  }
117 
118  return op.emitOpError(
119  "process-lowering only supports processes with either one basic block "
120  "terminated by a llhd.halt operation or two basic blocks where the first "
121  "one contains a cf.br terminator and the second one is terminated by a "
122  "llhd.wait operation");
123 }
124 
125 void ProcessLoweringPass::runOnOperation() {
126  ModuleOp module = getOperation();
127 
128  WalkResult result = module.walk([](llhd::ProcessOp op) -> WalkResult {
129  // Check invariants
130  if (failed(isProcValidToLower(op)))
131  return WalkResult::interrupt();
132 
133  // In the case that wait is used to suspend the process, we need to merge
134  // the two blocks as we needed the second block to have a target for wait
135  // (the entry block cannot be targeted).
136  if (op.getBody().getBlocks().size() == 2) {
137  Block &first = op.getBody().front();
138  Block &second = op.getBody().back();
139  // Delete the BranchOp operation in the entry block
140  first.getTerminator()->erase();
141  // Move operations of second block in entry block.
142  first.getOperations().splice(first.end(), second.getOperations());
143  // Drop all references to the second block and delete it.
144  second.dropAllReferences();
145  second.dropAllDefinedValueUses();
146  second.erase();
147  }
148 
149  // Remove the remaining llhd.halt or llhd.wait terminator
150  op.getBody().front().getTerminator()->erase();
151 
152  IRRewriter rewriter(op);
153  rewriter.inlineBlockBefore(&op.getBody().front(), op);
154  op.erase();
155 
156  return WalkResult::advance();
157  });
158 
159  if (result.wasInterrupted())
160  signalPassFailure();
161 }
162 } // namespace
163 
164 std::unique_ptr<OperationPass<ModuleOp>>
166  return std::make_unique<ProcessLoweringPass>();
167 }
std::unique_ptr< OperationPass< ModuleOp > > createProcessLoweringPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21