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