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