CIRCT 23.0.0git
Loading...
Searching...
No Matches
SquashSimTriggered.cpp
Go to the documentation of this file.
1//===- SquashSimTriggered.cpp - Merge sim.triggered ops by clock ---------===//
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// Merge multiple sim.triggered ops in the same block that share a clock into a
10// single sim.triggered. Conditions that cannot be represented on the merged op
11// directly are materialized as scf.if operations inside the merged body.
12//
13//===----------------------------------------------------------------------===//
14
19#include "mlir/Dialect/SCF/IR/SCF.h"
20#include "mlir/Pass/Pass.h"
21#include "mlir/Transforms/RegionUtils.h"
22#include "llvm/ADT/MapVector.h"
23#include "llvm/ADT/SetVector.h"
24
25namespace circt {
26namespace sim {
27#define GEN_PASS_DEF_SQUASHSIMTRIGGERED
28#include "circt/Dialect/Sim/SimPasses.h.inc"
29} // namespace sim
30} // namespace circt
31
32using namespace llvm;
33using namespace circt;
34using namespace sim;
35
36namespace {
37
38static Block *getOrCreateConditionBlock(OpBuilder &builder, Location loc,
39 Value condition,
40 Value &prevConditionValue,
41 Block *&prevConditionBlock) {
42 if (condition != prevConditionValue)
43 prevConditionBlock = nullptr;
44
45 if (prevConditionBlock)
46 return prevConditionBlock;
47
48 auto ifOp = mlir::scf::IfOp::create(builder, loc, TypeRange{}, condition,
49 true, false);
50 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
51 mlir::scf::YieldOp::create(builder, loc);
52 prevConditionValue = condition;
53 prevConditionBlock = builder.getBlock();
54 return prevConditionBlock;
55}
56
57static void moveBodyOperations(Block *source, Block *dest,
58 Block::iterator insertPt) {
59 dest->getOperations().splice(insertPt, source->getOperations(),
60 source->begin(), source->end());
61}
62
63static void finalizeHWTriggered(hw::TriggeredOp op) {
64 mlir::IRRewriter builder(op->getContext());
65 auto captures = mlir::makeRegionIsolatedFromAbove(builder, op.getBody());
66 op.getInputsMutable().append(captures);
67}
68
69struct SquashSimTriggeredPass
70 : impl::SquashSimTriggeredBase<SquashSimTriggeredPass> {
71public:
72 using SquashSimTriggeredBase::SquashSimTriggeredBase;
73 void runOnOperation() override;
74
75private:
76 bool squashTriggeredOpsInBlock(Block &block);
77};
78
79} // namespace
80
81bool SquashSimTriggeredPass::squashTriggeredOpsInBlock(Block &block) {
82 // Group all top-level sim.triggered ops in this block by their clock.
84 for (Operation &op : block)
85 if (auto triggered = dyn_cast<TriggeredOp>(op))
86 triggerMap[triggered.getClock()].push_back(triggered);
87
88 bool changed = false;
89 for (auto &[clock, triggers] : triggerMap) {
90 if (triggers.size() < 2 && !convertToHW)
91 continue;
92
93 SmallVector<Location> locs;
94 locs.reserve(triggers.size());
95
96 bool hasUnconditionalTrigger = false;
97 Value commonCondition;
98 bool allConditionsIdentical = true;
99 for (auto triggered : triggers) {
100 locs.push_back(triggered.getLoc());
101 if (auto condition = triggered.getCondition()) {
102 if (!commonCondition)
103 commonCondition = condition;
104 else if (condition != commonCondition)
105 allConditionsIdentical = false;
106 } else {
107 hasUnconditionalTrigger = true;
108 }
109 }
110
111 // Hoist a shared condition to the merged sim.triggered when possible. When
112 // converting to hw.triggered, keep all conditions inside the body instead.
113 Value outerCondition;
114 if (!convertToHW && !hasUnconditionalTrigger && allConditionsIdentical)
115 outerCondition = commonCondition;
116
117 OpBuilder builder(triggers.back());
118 auto fusedLoc = builder.getFusedLoc(locs);
119
120 Block *mergedBlock = nullptr;
121 hw::TriggeredOp hwTriggered;
122 TriggeredOp mergedTriggered;
123 if (convertToHW) {
124 auto event = hw::EventControlAttr::get(builder.getContext(),
125 hw::EventControl::AtPosEdge);
126 auto trigger = builder.createOrFold<seq::FromClockOp>(fusedLoc, clock);
127 hwTriggered = hw::TriggeredOp::create(builder, fusedLoc, event, trigger,
128 ValueRange{});
129 mergedBlock = hwTriggered.getBodyBlock();
130 } else {
131 mergedTriggered =
132 TriggeredOp::create(builder, fusedLoc, clock, outerCondition);
133 mergedBlock = mergedTriggered.getBodyBlock();
134 }
135
136 Value prevConditionValue;
137 Block *prevConditionBlock = nullptr;
138 for (auto triggered : triggers) {
139 auto condition = triggered.getCondition();
140 auto *sourceBlock = triggered.getBodyBlock();
141
142 // Conditions not represented on the outer op become inner scf.if guards.
143 if (!sourceBlock->empty() && condition && condition != outerCondition) {
144 builder.setInsertionPointToEnd(mergedBlock);
145 auto *condBlock =
146 getOrCreateConditionBlock(builder, triggered.getLoc(), condition,
147 prevConditionValue, prevConditionBlock);
148 moveBodyOperations(sourceBlock, condBlock,
149 Block::iterator(condBlock->getTerminator()));
150 } else {
151 prevConditionValue = Value();
152 prevConditionBlock = nullptr;
153 moveBodyOperations(sourceBlock, mergedBlock, mergedBlock->end());
154 }
155
156 triggered.erase();
157 }
158 if (convertToHW)
159 finalizeHWTriggered(hwTriggered);
160 changed = true;
161 }
162
163 return changed;
164}
165
166void SquashSimTriggeredPass::runOnOperation() {
167 hw::HWModuleOp module = getOperation();
168 bool changed = squashTriggeredOpsInBlock(*module.getBodyBlock());
169 if (!changed)
170 markAllAnalysesPreserved();
171}
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition sim.py:1