CIRCT  20.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 #include "llvm/Support/Debug.h"
20 
21 #define DEBUG_TYPE "llhd-process-lowering"
22 
23 namespace circt {
24 namespace llhd {
25 #define GEN_PASS_DEF_PROCESSLOWERING
26 #include "circt/Dialect/LLHD/Transforms/Passes.h.inc"
27 } // namespace llhd
28 } // namespace circt
29 
30 using namespace mlir;
31 using namespace circt;
32 
33 namespace {
34 
35 struct ProcessLoweringPass
36  : public circt::llhd::impl::ProcessLoweringBase<ProcessLoweringPass> {
37  void runOnOperation() override;
38 };
39 
40 static LogicalResult isProcValidToLower(llhd::ProcessOp op) {
41  if (op.getBody().getBlocks().size() != 2) {
42  LLVM_DEBUG({
43  llvm::dbgs() << "process-lowering only supports processes with "
44  "two basic blocks where the first "
45  "contains a 'cf.br' terminator and the second one is "
46  "terminated by a 'llhd.wait' operation\n";
47  });
48  return failure();
49  }
50 
51  Block &first = op.getBody().front();
52  Block &last = op.getBody().back();
53 
54  if (!last.getArguments().empty()) {
55  LLVM_DEBUG({
56  llvm::dbgs() << "the second block (containing the "
57  "llhd.wait) is not allowed to have arguments\n";
58  });
59  return failure();
60  }
61 
62  if (!isa<cf::BranchOp>(first.getTerminator())) {
63  LLVM_DEBUG({
64  llvm::dbgs() << "the first block has to "
65  "be terminated by a cf.br operation\n";
66  });
67  return failure();
68  }
69 
70  if (auto wait = dyn_cast<llhd::WaitOp>(last.getTerminator())) {
71  // No optional time argument is allowed
72  if (wait.getTime()) {
73  LLVM_DEBUG({
74  llvm::dbgs() << "llhd.wait terminators with optional time "
75  "argument cannot be lowered to structural LLHD\n";
76  });
77  return failure();
78  }
79 
80  SmallVector<Value> observedSignals;
81  for (Value obs : wait.getObserved())
82  if (auto prb = obs.getDefiningOp<llhd::PrbOp>())
83  if (!op.getBody().isAncestor(prb->getParentRegion()))
84  observedSignals.push_back(prb.getSignal());
85 
86  // Every probed signal has to occur in the observed signals list in
87  // the wait instruction
88  WalkResult result = op.walk([&](Operation *operation) -> WalkResult {
89  // TODO: value does not need to be observed if all values this value is
90  // a combinatorial result of are observed.
91  for (Value operand : operation->getOperands()) {
92  if (op.getBody().isAncestor(operand.getParentRegion()))
93  continue;
94  if (llvm::is_contained(wait.getObserved(), operand))
95  continue;
96  if (llvm::is_contained(observedSignals, operand))
97  continue;
98  if (auto *defOp = operand.getDefiningOp();
99  defOp && defOp->hasTrait<OpTrait::ConstantLike>())
100  continue;
101  if (auto bitcastOp = operand.getDefiningOp<hw::BitcastOp>())
102  if (auto *defOp = bitcastOp.getInput().getDefiningOp();
103  defOp && defOp->hasTrait<OpTrait::ConstantLike>())
104  continue;
105 
106  LLVM_DEBUG({
107  llvm::dbgs() << "the wait terminator is required to "
108  "have values used in the process as arguments\n";
109  });
110  return failure();
111  }
112 
113  return WalkResult::advance();
114  });
115 
116  return failure(result.wasInterrupted());
117  }
118 
119  LLVM_DEBUG({
120  llvm::dbgs() << "the second block must be "
121  "terminated by llhd.wait\n";
122  });
123  return failure();
124 }
125 
126 void ProcessLoweringPass::runOnOperation() {
127  ModuleOp module = getOperation();
128 
129  module.walk([](llhd::ProcessOp op) {
130  LLVM_DEBUG({ llvm::dbgs() << "\n=== Process\n"; });
131  // Check invariants
132  if (failed(isProcValidToLower(op)))
133  return;
134 
135  // In the case that wait is used to suspend the process, we need to merge
136  // the two blocks as we needed the second block to have a target for wait
137  // (the entry block cannot be targeted).
138  if (op.getBody().getBlocks().size() == 2) {
139  Block &first = op.getBody().front();
140  Block &second = op.getBody().back();
141  // Delete the BranchOp operation in the entry block
142  first.getTerminator()->erase();
143  // Move operations of second block in entry block.
144  first.getOperations().splice(first.end(), second.getOperations());
145  // Drop all references to the second block and delete it.
146  second.dropAllReferences();
147  second.dropAllDefinedValueUses();
148  second.erase();
149  }
150 
151  // Remove the remaining llhd.halt or llhd.wait terminator
152  op.getBody().front().getTerminator()->erase();
153 
154  IRRewriter rewriter(op);
155  rewriter.inlineBlockBefore(&op.getBody().front(), op);
156  op.erase();
157 
158  LLVM_DEBUG({ llvm::dbgs() << "Process lowered successfully!\n"; });
159  });
160 }
161 } // namespace
162 
163 std::unique_ptr<OperationPass<ModuleOp>>
165  return std::make_unique<ProcessLoweringPass>();
166 }
std::unique_ptr< OperationPass< ModuleOp > > createProcessLoweringPass()
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21