CIRCT  19.0.0git
HandshakeExecutableOps.cpp
Go to the documentation of this file.
1 //===- HandshakeExecutableOps.cpp - Handshake executable Operations -------===//
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 // This file contains the declaration of execution semantics for Handshake
10 // operations.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 #include "mlir/IR/Value.h"
16 #include "llvm/ADT/Any.h"
17 #include "llvm/Support/Debug.h"
18 
19 using namespace circt;
20 using namespace circt::handshake;
21 
22 static constexpr int INDEX_WIDTH = 32;
23 
24 // Convert ValueRange to vectors
25 static std::vector<mlir::Value> toVector(mlir::ValueRange range) {
26  return std::vector<mlir::Value>(range.begin(), range.end());
27 }
28 
29 // Returns whether the precondition holds for a general op to execute
30 static bool isReadyToExecute(ArrayRef<mlir::Value> ins,
31  ArrayRef<mlir::Value> outs,
32  llvm::DenseMap<mlir::Value, llvm::Any> &valueMap) {
33  for (auto in : ins)
34  if (valueMap.count(in) == 0)
35  return false;
36 
37  for (auto out : outs)
38  if (valueMap.count(out) > 0)
39  return false;
40 
41  return true;
42 }
43 
44 // Fetch values from the value map and consume them
45 static std::vector<llvm::Any>
46 fetchValues(ArrayRef<mlir::Value> values,
47  llvm::DenseMap<mlir::Value, llvm::Any> &valueMap) {
48  std::vector<llvm::Any> ins;
49  for (auto &value : values) {
50  assert(valueMap[value].has_value());
51  ins.push_back(valueMap[value]);
52  valueMap.erase(value);
53  }
54  return ins;
55 }
56 
57 // Store values to the value map
58 static void storeValues(std::vector<llvm::Any> &values,
59  ArrayRef<mlir::Value> outs,
60  llvm::DenseMap<mlir::Value, llvm::Any> &valueMap) {
61  assert(values.size() == outs.size());
62  for (unsigned long i = 0; i < outs.size(); ++i)
63  valueMap[outs[i]] = values[i];
64 }
65 
66 // Update the time map after the execution
67 static void updateTime(ArrayRef<mlir::Value> ins, ArrayRef<mlir::Value> outs,
68  llvm::DenseMap<mlir::Value, double> &timeMap,
69  double latency) {
70  double time = 0;
71  for (auto &in : ins)
72  time = std::max(time, timeMap[in]);
73  time += latency;
74  for (auto &out : outs)
75  timeMap[out] = time;
76 }
77 
78 static bool tryToExecute(Operation *op,
79  llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
80  llvm::DenseMap<mlir::Value, double> &timeMap,
81  std::vector<mlir::Value> &scheduleList,
82  double latency) {
83  auto ins = toVector(op->getOperands());
84  auto outs = toVector(op->getResults());
85 
86  if (isReadyToExecute(ins, outs, valueMap)) {
87  auto in = fetchValues(ins, valueMap);
88  std::vector<llvm::Any> out(outs.size());
89  auto generalOp = dyn_cast<GeneralOpInterface>(op);
90  if (!generalOp)
91  op->emitOpError("Undefined execution for the current op");
92  generalOp.execute(in, out);
93  storeValues(out, outs, valueMap);
94  updateTime(ins, outs, timeMap, latency);
95  scheduleList = outs;
96  return true;
97  }
98  return false;
99 }
100 
101 namespace circt {
102 namespace handshake {
103 
104 bool ForkOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
105  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
106  llvm::DenseMap<mlir::Value, double> &timeMap,
107  std::vector<std::vector<llvm::Any>> & /*store*/,
108  std::vector<mlir::Value> &scheduleList) {
109  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
110 }
111 
112 bool MergeOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
113  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
114  llvm::DenseMap<mlir::Value, double> &timeMap,
115  std::vector<std::vector<llvm::Any>> & /*store*/,
116  std::vector<mlir::Value> &scheduleList) {
117  bool found = false;
118  for (mlir::Value in : getOperands()) {
119  if (valueMap.count(in) == 1) {
120  if (found)
121  emitOpError("More than one valid input to Merge!");
122  auto t = valueMap[in];
123  valueMap[getResult()] = t;
124  timeMap[getResult()] = timeMap[in];
125  // Consume the inputs.
126  valueMap.erase(in);
127  found = true;
128  }
129  }
130  if (!found)
131  emitOpError("No valid input to Merge!");
132  scheduleList.push_back(getResult());
133  return true;
134 }
135 bool MuxOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
136  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
137  llvm::DenseMap<mlir::Value, double> &timeMap,
138  std::vector<std::vector<llvm::Any>> & /*store*/,
139  std::vector<mlir::Value> &scheduleList) {
140  mlir::Value control = getSelectOperand();
141  if (valueMap.count(control) == 0)
142  return false;
143  auto controlValue = valueMap[control];
144  auto controlTime = timeMap[control];
145  auto opIdx = llvm::any_cast<APInt>(controlValue).getZExtValue();
146  assert(opIdx < getDataOperands().size() &&
147  "Trying to select a non-existing mux operand");
148 
149  mlir::Value in = getDataOperands()[opIdx];
150  if (valueMap.count(in) == 0)
151  return false;
152  auto inValue = valueMap[in];
153  auto inTime = timeMap[in];
154  double time = std::max(controlTime, inTime);
155  valueMap[getResult()] = inValue;
156  timeMap[getResult()] = time;
157 
158  // Consume the inputs.
159  valueMap.erase(control);
160  valueMap.erase(in);
161  scheduleList.push_back(getResult());
162  return true;
163 }
164 bool ControlMergeOp::tryExecute(
165  llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
166  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
167  llvm::DenseMap<mlir::Value, double> &timeMap,
168  std::vector<std::vector<llvm::Any>> & /*store*/,
169  std::vector<mlir::Value> &scheduleList) {
170  bool found = false;
171  for (auto in : llvm::enumerate(getOperands())) {
172  if (valueMap.count(in.value()) == 1) {
173  if (found)
174  emitOpError("More than one valid input to CMerge!");
175  valueMap[getResult()] = valueMap[in.value()];
176  timeMap[getResult()] = timeMap[in.value()];
177 
178  valueMap[getIndex()] = APInt(INDEX_WIDTH, in.index());
179  timeMap[getIndex()] = timeMap[in.value()];
180 
181  // Consume the inputs.
182  valueMap.erase(in.value());
183 
184  found = true;
185  }
186  }
187  if (!found)
188  emitOpError("No valid input to CMerge!");
189  scheduleList = toVector(getResults());
190  return true;
191 }
192 
193 void BranchOp::execute(std::vector<llvm::Any> &ins,
194  std::vector<llvm::Any> &outs) {
195  outs[0] = ins[0];
196 }
197 
198 bool BranchOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
199  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
200  llvm::DenseMap<mlir::Value, double> &timeMap,
201  std::vector<std::vector<llvm::Any>> & /*store*/,
202  std::vector<mlir::Value> &scheduleList) {
203  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 0);
204 }
205 
206 bool ConditionalBranchOp::tryExecute(
207  llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
208  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
209  llvm::DenseMap<mlir::Value, double> &timeMap,
210  std::vector<std::vector<llvm::Any>> & /*store*/,
211  std::vector<mlir::Value> &scheduleList) {
212  mlir::Value control = getConditionOperand();
213  if (valueMap.count(control) == 0)
214  return false;
215  auto controlValue = valueMap[control];
216  auto controlTime = timeMap[control];
217  mlir::Value in = getDataOperand();
218  if (valueMap.count(in) == 0)
219  return false;
220  auto inValue = valueMap[in];
221  auto inTime = timeMap[in];
222  mlir::Value out = llvm::any_cast<APInt>(controlValue) != 0 ? getTrueResult()
223  : getFalseResult();
224  double time = std::max(controlTime, inTime);
225  valueMap[out] = inValue;
226  timeMap[out] = time;
227  scheduleList.push_back(out);
228 
229  // Consume the inputs.
230  valueMap.erase(control);
231  valueMap.erase(in);
232  return true;
233 }
234 
235 bool SinkOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
236  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
237  llvm::DenseMap<mlir::Value, double> & /*timeMap*/,
238  std::vector<std::vector<llvm::Any>> & /*store*/,
239  std::vector<mlir::Value> & /*scheduleList*/) {
240  valueMap.erase(getOperand());
241  return true;
242 }
243 
244 void BufferOp::execute(std::vector<llvm::Any> &ins,
245  std::vector<llvm::Any> &outs) {
246  outs[0] = ins[0];
247 }
248 
249 bool BufferOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
250  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
251  llvm::DenseMap<mlir::Value, double> &timeMap,
252  std::vector<std::vector<llvm::Any>> & /*store*/,
253  std::vector<mlir::Value> &scheduleList) {
254  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList,
255  getNumSlots());
256 }
257 
258 void ConstantOp::execute(std::vector<llvm::Any> & /*ins*/,
259  std::vector<llvm::Any> &outs) {
260  auto attr = (*this)->getAttrOfType<mlir::IntegerAttr>("value");
261  outs[0] = attr.getValue();
262 }
263 
264 bool ConstantOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
265  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
266  llvm::DenseMap<mlir::Value, double> &timeMap,
267  std::vector<std::vector<llvm::Any>> & /*store*/,
268  std::vector<mlir::Value> &scheduleList) {
269  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 0);
270 }
271 
272 template <typename TMemOp>
273 static bool
274 executeMemoryOperation(TMemOp op, unsigned buffer, int opIndex,
275  llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
276  llvm::DenseMap<unsigned, unsigned> &memoryMap,
277  llvm::DenseMap<mlir::Value, double> &timeMap,
278  std::vector<std::vector<llvm::Any>> &store,
279  std::vector<mlir::Value> &scheduleList) {
280  bool notReady = false;
281  for (unsigned i = 0; i < op.getStCount(); i++) {
282  mlir::Value data = op->getOperand(opIndex++);
283  mlir::Value address = op->getOperand(opIndex++);
284  mlir::Value nonceOut = op->getResult(op.getLdCount() + i);
285  if ((!valueMap.count(data) || !valueMap.count(address))) {
286  notReady = true;
287  continue;
288  }
289  auto addressValue = valueMap[address];
290  auto addressTime = timeMap[address];
291  auto dataValue = valueMap[data];
292  auto dataTime = timeMap[data];
293 
294  assert(buffer < store.size());
295  auto &ref = store[buffer];
296  unsigned offset = llvm::any_cast<APInt>(addressValue).getZExtValue();
297  assert(offset < ref.size());
298  ref[offset] = dataValue;
299 
300  // Implicit none argument
301  APInt apnonearg(1, 0);
302  valueMap[nonceOut] = apnonearg;
303  double time = std::max(addressTime, dataTime);
304  timeMap[nonceOut] = time;
305  scheduleList.push_back(nonceOut);
306  // Consume the inputs.
307  valueMap.erase(data);
308  valueMap.erase(address);
309  }
310 
311  for (unsigned i = 0; i < op.getLdCount(); i++) {
312  mlir::Value address = op->getOperand(opIndex++);
313  mlir::Value dataOut = op->getResult(i);
314  mlir::Value nonceOut = op->getResult(op.getLdCount() + op.getStCount() + i);
315  if (!valueMap.count(address)) {
316  notReady = true;
317  continue;
318  }
319  auto addressValue = valueMap[address];
320  auto addressTime = timeMap[address];
321  assert(buffer < store.size());
322  auto &ref = store[buffer];
323  unsigned offset = llvm::any_cast<APInt>(addressValue).getZExtValue();
324  assert(offset < ref.size());
325 
326  valueMap[dataOut] = ref[offset];
327  timeMap[dataOut] = addressTime;
328  // Implicit none argument
329  APInt apnonearg(1, 0);
330  valueMap[nonceOut] = apnonearg;
331  timeMap[nonceOut] = addressTime;
332  scheduleList.push_back(dataOut);
333  scheduleList.push_back(nonceOut);
334  // Consume the inputs.
335  valueMap.erase(address);
336  }
337  return (notReady) ? false : true;
338 }
339 
340 bool MemoryOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
341  llvm::DenseMap<unsigned, unsigned> &memoryMap,
342  llvm::DenseMap<mlir::Value, double> &timeMap,
343  std::vector<std::vector<llvm::Any>> &store,
344  std::vector<mlir::Value> &scheduleList) {
345  unsigned buffer = memoryMap[getId()];
346  return executeMemoryOperation(*this, buffer, 0, valueMap, memoryMap, timeMap,
347  store, scheduleList);
348 }
349 
350 bool LoadOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
351  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
352  llvm::DenseMap<mlir::Value, double> &timeMap,
353  std::vector<std::vector<llvm::Any>> & /*store*/,
354  std::vector<mlir::Value> &scheduleList) {
355  mlir::Value address = getOperand(0);
356  mlir::Value data = getOperand(1);
357  mlir::Value nonce = getOperand(2);
358  mlir::Value addressOut = getResult(1);
359  mlir::Value dataOut = getResult(0);
360  if ((valueMap.count(address) && !valueMap.count(nonce)) ||
361  (!valueMap.count(address) && valueMap.count(nonce)) ||
362  (!valueMap.count(address) && !valueMap.count(nonce) &&
363  !valueMap.count(data)))
364  return false;
365  if (valueMap.count(address) && valueMap.count(nonce)) {
366  auto addressValue = valueMap[address];
367  auto addressTime = timeMap[address];
368  auto nonceValue = valueMap[nonce];
369  auto nonceTime = timeMap[nonce];
370  valueMap[addressOut] = addressValue;
371  double time = std::max(addressTime, nonceTime);
372  timeMap[addressOut] = time;
373  scheduleList.push_back(addressOut);
374  // Consume the inputs.
375  valueMap.erase(address);
376  valueMap.erase(nonce);
377  } else if (valueMap.count(data)) {
378  auto dataValue = valueMap[data];
379  auto dataTime = timeMap[data];
380  valueMap[dataOut] = dataValue;
381  timeMap[dataOut] = dataTime;
382  scheduleList.push_back(dataOut);
383  // Consume the inputs.
384  valueMap.erase(data);
385  } else {
386  llvm_unreachable("why?");
387  }
388  return true;
389 }
390 
391 bool ExternalMemoryOp::tryExecute(
392  llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
393  llvm::DenseMap<unsigned, unsigned> &memoryMap,
394  llvm::DenseMap<mlir::Value, double> &timeMap,
395  std::vector<std::vector<llvm::Any>> &store,
396  std::vector<mlir::Value> &scheduleList) {
397  unsigned buffer = llvm::any_cast<unsigned>(valueMap[getMemref()]);
398  return executeMemoryOperation(*this, buffer, 1, valueMap, memoryMap, timeMap,
399  store, scheduleList);
400 }
401 
402 bool StoreOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
403  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
404  llvm::DenseMap<mlir::Value, double> &timeMap,
405  std::vector<std::vector<llvm::Any>> & /*store*/,
406  std::vector<mlir::Value> &scheduleList) {
407  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
408 }
409 
410 void JoinOp::execute(std::vector<llvm::Any> &ins,
411  std::vector<llvm::Any> &outs) {
412  outs[0] = ins[0];
413 }
414 
415 bool JoinOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
416  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
417  llvm::DenseMap<mlir::Value, double> &timeMap,
418  std::vector<std::vector<llvm::Any>> & /*store*/,
419  std::vector<mlir::Value> &scheduleList) {
420  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
421 }
422 
423 void SyncOp::execute(std::vector<llvm::Any> &ins,
424  std::vector<llvm::Any> &outs) {
425  outs = ins;
426 }
427 
428 bool SyncOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
429  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
430  llvm::DenseMap<mlir::Value, double> &timeMap,
431  std::vector<std::vector<llvm::Any>> & /*store*/,
432  std::vector<mlir::Value> &scheduleList) {
433  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
434 }
435 
436 void StoreOp::execute(std::vector<llvm::Any> &ins,
437  std::vector<llvm::Any> &outs) {
438  // Forward the address and data to the memory op.
439  outs[0] = ins[1];
440  outs[1] = ins[0];
441 }
442 
443 void ForkOp::execute(std::vector<llvm::Any> &ins,
444  std::vector<llvm::Any> &outs) {
445  for (auto &out : outs)
446  out = ins[0];
447 }
448 
449 bool UnpackOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
450  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
451  llvm::DenseMap<mlir::Value, double> &timeMap,
452  std::vector<std::vector<llvm::Any>> & /*store*/,
453  std::vector<mlir::Value> &scheduleList) {
454  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
455 }
456 
457 void UnpackOp::execute(std::vector<llvm::Any> &ins,
458  std::vector<llvm::Any> &outs) {
459  auto ins0Vec = llvm::any_cast<std::vector<llvm::Any>>(ins[0]);
460  assert(ins0Vec.size() == getNumResults() &&
461  "expected that the number of tuple elements matches the number of "
462  "outputs");
463  for (auto [in, out] : llvm::zip(ins0Vec, outs))
464  out = in;
465 }
466 
467 bool PackOp::tryExecute(llvm::DenseMap<mlir::Value, llvm::Any> &valueMap,
468  llvm::DenseMap<unsigned, unsigned> & /*memoryMap*/,
469  llvm::DenseMap<mlir::Value, double> &timeMap,
470  std::vector<std::vector<llvm::Any>> & /*store*/,
471  std::vector<mlir::Value> &scheduleList) {
472  return tryToExecute(getOperation(), valueMap, timeMap, scheduleList, 1);
473 }
474 
475 void PackOp::execute(std::vector<llvm::Any> &ins,
476  std::vector<llvm::Any> &outs) {
477  assert(ins.size() == getNumOperands() &&
478  "expected that the number inputs match the number of tuple elements");
479  outs[0] = ins;
480 }
481 
482 } // namespace handshake
483 } // namespace circt
assert(baseType &&"element must be base type")
static void storeValues(std::vector< llvm::Any > &values, ArrayRef< mlir::Value > outs, llvm::DenseMap< mlir::Value, llvm::Any > &valueMap)
static void updateTime(ArrayRef< mlir::Value > ins, ArrayRef< mlir::Value > outs, llvm::DenseMap< mlir::Value, double > &timeMap, double latency)
static std::vector< llvm::Any > fetchValues(ArrayRef< mlir::Value > values, llvm::DenseMap< mlir::Value, llvm::Any > &valueMap)
static bool tryToExecute(Operation *op, llvm::DenseMap< mlir::Value, llvm::Any > &valueMap, llvm::DenseMap< mlir::Value, double > &timeMap, std::vector< mlir::Value > &scheduleList, double latency)
static constexpr int INDEX_WIDTH
static std::vector< mlir::Value > toVector(mlir::ValueRange range)
static bool isReadyToExecute(ArrayRef< mlir::Value > ins, ArrayRef< mlir::Value > outs, llvm::DenseMap< mlir::Value, llvm::Any > &valueMap)
static bool executeMemoryOperation(TMemOp op, unsigned buffer, int opIndex, llvm::DenseMap< mlir::Value, llvm::Any > &valueMap, llvm::DenseMap< unsigned, unsigned > &memoryMap, llvm::DenseMap< mlir::Value, double > &timeMap, std::vector< std::vector< llvm::Any >> &store, std::vector< mlir::Value > &scheduleList)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
Definition: DebugAnalysis.h:21