CIRCT 24.0.0git
Loading...
Searching...
No Matches
ChainingSupport.cpp
Go to the documentation of this file.
1//===- ChainingSupport.cpp - Utilities for chaining-aware schedulers ------===//
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// Helpers to add chaining support to existing algorithms via chain-breaking
10// auxiliary dependences, i.e. edges whose endpoints must be separated by at
11// least one time steps.
12//
13//===----------------------------------------------------------------------===//
14
16
17#include "mlir/IR/Operation.h"
18
19using namespace circt;
20using namespace circt::scheduling;
21
23
25 ChainingProblem &prob, float cycleTime,
26 SmallVectorImpl<Dependence> &result) {
27 // Sanity check: This approach treats the given `cycleTime` as a hard
28 // constraint, so all individual delays must be shorter.
29 for (auto opr : prob.getOperatorTypes())
30 if (*prob.getIncomingDelay(opr) > cycleTime ||
31 *prob.getOutgoingDelay(opr) > cycleTime)
32 return prob.getContainingOp()->emitError()
33 << "Delays of operator type '" << opr.getValue()
34 << "' exceed maximum cycle time: " << cycleTime;
35
36 // chains[v][u] denotes the accumulated delay incoming at `v`, of the longest
37 // combinational chain originating from `u`.
38 DenseMap<Operation *, SmallDenseMap<Operation *, float>> chains;
39
40 // Do a simple DFA-style pass over the dependence graph to determine
41 // combinational chains and their respective accumulated delays.
42 return handleOperationsInTopologicalOrder(prob, [&](Operation *op) {
43 // Mark `op` to be the origin of its own chain.
44 chains[op][op] = 0.0f;
45
46 for (auto dep : prob.getDependences(op)) {
47 // Skip auxiliary deps, as these don't carry values.
48 if (dep.isAuxiliary())
49 continue;
50
51 Operation *pred = dep.getSource();
52 if (!chains.count(pred))
53 return failure(); // Predecessor hasn't been handled yet.
54
55 auto predOpr = *prob.getLinkedOperatorType(pred);
56 if (*prob.getLatency(predOpr) > 0) {
57 // `pred` is not combinational, so none of its incoming chains are
58 // extended. Hence, it only contributes its outgoing delay to `op`'s
59 // incoming delay.
60 chains[op][pred] = *prob.getOutgoingDelay(predOpr);
61 continue;
62 }
63
64 // Otherwise, `pred` is combinational. This means that all of its incoming
65 // chains, extended by `pred`, are incoming chains for `op`.
66 for (auto incomingChain : chains[pred]) {
67 Operation *origin = incomingChain.first;
68 float delay = incomingChain.second;
69 chains[op][origin] = std::max(delay + *prob.getOutgoingDelay(predOpr),
70 chains[op][origin]);
71 }
72 }
73
74 // All chains/accumulated delays incoming at `op` are now known.
75 auto opr = *prob.getLinkedOperatorType(op);
76 // Collect the chains that need to be broken.
77 SmallVector<Operation *, 4> chainsToBreak;
78 for (auto incomingChain : chains[op]) {
79 Operation *origin = incomingChain.first;
80 float delay = incomingChain.second;
81 // Check whether `op` could be appended to the incoming chain without
82 // violating the cycle time constraint.
83 if (delay + *prob.getIncomingDelay(opr) > cycleTime) {
84 chainsToBreak.push_back(origin);
85 }
86 }
87 // Break the chains and add the auxiliary dependences.
88 for (Operation *origin : chainsToBreak) {
89 result.emplace_back(origin, op);
90 chains[op].erase(origin);
91 }
92
93 return success();
94 });
95}
96
99 return handleOperationsInTopologicalOrder(prob, [&](Operation *op) {
100 // `op` will start within its abstract time step as soon as all operand
101 // values have reached it.
102 unsigned startTime = *prob.getStartTime(op);
103 float startTimeInCycle = 0.0f;
104
105 for (auto dep : prob.getDependences(op)) {
106 // Skip auxiliary deps, as these don't carry values.
107 if (dep.isAuxiliary())
108 continue;
109
110 Operation *pred = dep.getSource();
111 auto predStartTimeInCycle = prob.getStartTimeInCycle(pred);
112 if (!predStartTimeInCycle)
113 return failure(); // Predecessor hasn't been handled yet.
114
115 auto predOpr = *prob.getLinkedOperatorType(pred);
116 unsigned predStartTime = *prob.getStartTime(pred);
117 unsigned predEndTime = predStartTime + *prob.getLatency(predOpr);
118
119 if (predEndTime < startTime)
120 // Incoming value is completely registered/available with the beginning
121 // of the cycle.
122 continue;
123
124 // So, `pred` ends in the same cycle as `op` starts.
125 assert(predEndTime == startTime);
126 // If `pred` uses a multi-cycle operator, only its outgoing delay counts.
127 float predEndTimeInCycle =
128 (predStartTime == predEndTime ? *predStartTimeInCycle : 0.0f) +
129 *prob.getOutgoingDelay(predOpr);
130 startTimeInCycle = std::max(predEndTimeInCycle, startTimeInCycle);
131 }
132
133 prob.setStartTimeInCycle(op, startTimeInCycle);
134 return success();
135 });
136}
assert(baseType &&"element must be base type")
Problem::Dependence Dependence
false return failure()
Parse an optional keyword or string and set instance into 'result'.
return success()
This class models the accumulation of physical propagation delays on combinational paths along SSA de...
Definition Problems.h:412
std::optional< float > getOutgoingDelay(OperatorType opr)
The outgoing delay denotes the propagation time from either the operand inputs (combinational operato...
Definition Problems.h:438
void setStartTimeInCycle(Operation *op, float time)
Definition Problems.h:450
std::optional< float > getIncomingDelay(OperatorType opr)
The incoming delay denotes the propagation time from the operand inputs to either the result outputs ...
Definition Problems.h:428
std::optional< float > getStartTimeInCycle(Operation *op)
Computed by the scheduler, this start time is relative to the beginning of the cycle that op starts i...
Definition Problems.h:447
std::optional< unsigned > getLatency(OperatorType opr)
The latency is the number of cycles opr needs to compute its result.
Definition Problems.h:273
std::optional< OperatorType > getLinkedOperatorType(Operation *op)
The linked operator type provides the runtime characteristics for op.
Definition Problems.h:256
std::optional< unsigned > getStartTime(Operation *op)
Return the start time for op, as computed by the scheduler.
Definition Problems.h:281
detail::Dependence Dependence
A thin wrapper to allow a uniform handling of def-use and auxiliary dependences.
Definition Problems.h:95
DependenceRange getDependences(Operation *op)
Return a range object to transparently iterate over op's incoming 1) implicit def-use dependences (ba...
Definition Problems.cpp:59
Operation * getContainingOp()
Return the operation containing this problem, e.g. to emit diagnostics.
Definition Problems.h:219
const OperatorTypeSet & getOperatorTypes()
Return the set of operator types.
Definition Problems.h:243
LogicalResult handleOperationsInTopologicalOrder(Problem &prob, HandleOpFn fun)
Visit prob's operations in topological order, using an internal worklist.
Definition Utilities.cpp:21
LogicalResult computeChainBreakingDependences(ChainingProblem &prob, float cycleTime, SmallVectorImpl< Problem::Dependence > &result)
Analyse combinational chains in prob's dependence graph and determine pairs of operations that must b...
LogicalResult computeStartTimesInCycle(ChainingProblem &prob)
Assuming prob is scheduled and contains (integer) start times, this method fills in the start times i...
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.