CIRCT  20.0.0git
Evaluator.cpp
Go to the documentation of this file.
1 //===- Evaluator.cpp - Object Model dialect evaluator ---------------------===//
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 implements the Object Model dialect Evaluator.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "mlir/IR/BuiltinAttributeInterfaces.h"
15 #include "mlir/IR/Location.h"
16 #include "mlir/IR/SymbolTable.h"
17 #include "llvm/ADT/TypeSwitch.h"
18 #include "llvm/Support/Debug.h"
19 
20 #define DEBUG_TYPE "om-evaluator"
21 
22 using namespace mlir;
23 using namespace circt::om;
24 
25 /// Construct an Evaluator with an IR module.
26 circt::om::Evaluator::Evaluator(ModuleOp mod) : symbolTable(mod) {}
27 
28 /// Get the Module this Evaluator is built from.
30  return cast<ModuleOp>(symbolTable.getOp());
31 }
32 
33 SmallVector<evaluator::EvaluatorValuePtr>
35  ArrayRef<Attribute> attributes) {
36  SmallVector<evaluator::EvaluatorValuePtr> values;
37  values.reserve(attributes.size());
38  for (auto attr : attributes)
39  values.push_back(std::make_shared<evaluator::AttributeValue>(attr));
40  return values;
41 }
42 
44  using namespace evaluator;
45  // Early return if already finalized.
46  if (finalized)
47  return success();
48  // Enable the flag to avoid infinite recursions.
49  finalized = true;
50  assert(isFullyEvaluated());
51  return llvm::TypeSwitch<EvaluatorValue *, LogicalResult>(this)
54  [](auto v) { return v->finalizeImpl(); });
55 }
56 
58  return llvm::TypeSwitch<const EvaluatorValue *, Type>(this)
59  .Case<AttributeValue>([](auto *attr) -> Type { return attr->getType(); })
60  .Case<ObjectValue>([](auto *object) { return object->getObjectType(); })
61  .Case<ListValue>([](auto *list) { return list->getListType(); })
62  .Case<MapValue>([](auto *map) { return map->getMapType(); })
63  .Case<ReferenceValue>([](auto *ref) { return ref->getValueType(); })
64  .Case<TupleValue>([](auto *tuple) { return tuple->getTupleType(); })
65  .Case<BasePathValue>(
66  [this](auto *tuple) { return FrozenBasePathType::get(ctx); })
67  .Case<PathValue>(
68  [this](auto *tuple) { return FrozenPathType::get(ctx); });
69 }
70 
71 FailureOr<evaluator::EvaluatorValuePtr>
73  using namespace circt::om::evaluator;
74 
75  return TypeSwitch<mlir::Type, FailureOr<evaluator::EvaluatorValuePtr>>(type)
76  .Case([&](circt::om::MapType type) {
78  std::make_shared<evaluator::MapValue>(type, loc);
79  return success(result);
80  })
81  .Case([&](circt::om::ListType type) {
83  std::make_shared<evaluator::ListValue>(type, loc);
84  return success(result);
85  })
86  .Case([&](mlir::TupleType type) {
88  std::make_shared<evaluator::TupleValue>(type, loc);
89  return success(result);
90  })
91 
92  .Case([&](circt::om::ClassType type)
93  -> FailureOr<evaluator::EvaluatorValuePtr> {
94  ClassOp cls =
95  symbolTable.lookup<ClassOp>(type.getClassName().getValue());
96  if (!cls)
97  return symbolTable.getOp()->emitError("unknown class name ")
98  << type.getClassName();
99 
101  std::make_shared<evaluator::ObjectValue>(cls, loc);
102 
103  return success(result);
104  })
105  .Default([&](auto type) { return failure(); });
106 }
107 
108 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::getOrCreateValue(
109  Value value, ActualParameters actualParams, Location loc) {
110  auto it = objects.find({value, actualParams});
111  if (it != objects.end()) {
112  auto evalVal = it->second;
113  evalVal->setLocIfUnknown(loc);
114  return evalVal;
115  }
116 
117  FailureOr<evaluator::EvaluatorValuePtr> result =
118  TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
119  .Case([&](BlockArgument arg) {
120  auto val = (*actualParams)[arg.getArgNumber()];
121  val->setLoc(loc);
122  return val;
123  })
124  .Case([&](OpResult result) {
125  return TypeSwitch<Operation *,
126  FailureOr<evaluator::EvaluatorValuePtr>>(
127  result.getDefiningOp())
128  .Case([&](ConstantOp op) {
129  return evaluateConstant(op, actualParams, loc);
130  })
131  .Case([&](IntegerBinaryArithmeticOp op) {
132  // Create a partially evaluated AttributeValue of
133  // om::IntegerType in case we need to delay evaluation.
135  std::make_shared<evaluator::AttributeValue>(
136  op.getResult().getType(), loc);
137  return success(result);
138  })
139  .Case<ObjectFieldOp>([&](auto op) {
140  // Create a reference value since the value pointed by object
141  // field op is not created yet.
143  std::make_shared<evaluator::ReferenceValue>(
144  value.getType(), loc);
145  return success(result);
146  })
147  .Case<AnyCastOp>([&](AnyCastOp op) {
148  return getOrCreateValue(op.getInput(), actualParams, loc);
149  })
150  .Case<FrozenBasePathCreateOp>([&](FrozenBasePathCreateOp op) {
152  std::make_shared<evaluator::BasePathValue>(
153  op.getPathAttr(), loc);
154  return success(result);
155  })
156  .Case<FrozenPathCreateOp>([&](FrozenPathCreateOp op) {
158  std::make_shared<evaluator::PathValue>(
159  op.getTargetKindAttr(), op.getPathAttr(),
160  op.getModuleAttr(), op.getRefAttr(),
161  op.getFieldAttr(), loc);
162  return success(result);
163  })
164  .Case<FrozenEmptyPathOp>([&](FrozenEmptyPathOp op) {
166  std::make_shared<evaluator::PathValue>(
168  return success(result);
169  })
170  .Case<ListCreateOp, ListConcatOp, TupleCreateOp, MapCreateOp,
171  ObjectFieldOp>([&](auto op) {
172  return getPartiallyEvaluatedValue(op.getType(), loc);
173  })
174  .Case<ObjectOp>([&](auto op) {
175  return getPartiallyEvaluatedValue(op.getType(), op.getLoc());
176  })
177  .Default([&](Operation *op) {
178  auto error = op->emitError("unable to evaluate value");
179  error.attachNote() << "value: " << value;
180  return error;
181  });
182  });
183  if (failed(result))
184  return result;
185 
186  objects[{value, actualParams}] = result.value();
187  return result;
188 }
189 
190 FailureOr<evaluator::EvaluatorValuePtr>
192  ActualParameters actualParams,
193  Location loc,
194  ObjectKey instanceKey) {
195  ClassOp cls = symbolTable.lookup<ClassOp>(className);
196  if (!cls)
197  return symbolTable.getOp()->emitError("unknown class name ") << className;
198 
199  auto formalParamNames = cls.getFormalParamNames().getAsRange<StringAttr>();
200  auto formalParamTypes = cls.getBodyBlock()->getArgumentTypes();
201 
202  // Verify the actual parameters are the right size and types for this class.
203  if (actualParams->size() != formalParamTypes.size()) {
204  auto error = cls.emitError("actual parameter list length (")
205  << actualParams->size() << ") does not match formal "
206  << "parameter list length (" << formalParamTypes.size() << ")";
207  auto &diag = error.attachNote() << "actual parameters: ";
208  // FIXME: `diag << actualParams` doesn't work for some reason.
209  bool isFirst = true;
210  for (const auto &param : *actualParams) {
211  if (isFirst)
212  isFirst = false;
213  else
214  diag << ", ";
215  diag << param;
216  }
217  error.attachNote(cls.getLoc()) << "formal parameters: " << formalParamTypes;
218  return error;
219  }
220 
221  // Verify the actual parameter types match.
222  for (auto [actualParam, formalParamName, formalParamType] :
223  llvm::zip(*actualParams, formalParamNames, formalParamTypes)) {
224  if (!actualParam || !actualParam.get())
225  return cls.emitError("actual parameter for ")
226  << formalParamName << " is null";
227 
228  // Subtyping: if formal param is any type, any actual param may be passed.
229  if (isa<AnyType>(formalParamType))
230  continue;
231 
232  Type actualParamType = actualParam->getType();
233 
234  assert(actualParamType && "actualParamType must be non-null!");
235 
236  if (actualParamType != formalParamType) {
237  auto error = cls.emitError("actual parameter for ")
238  << formalParamName << " has invalid type";
239  error.attachNote() << "actual parameter: " << *actualParam;
240  error.attachNote() << "format parameter type: " << formalParamType;
241  return error;
242  }
243  }
244 
245  // Instantiate the fields.
247 
248  auto *context = cls.getContext();
249  for (auto &op : cls.getOps())
250  for (auto result : op.getResults()) {
251  // Allocate the value, with unknown loc. It will be later set when
252  // evaluating the fields.
253  if (failed(
254  getOrCreateValue(result, actualParams, UnknownLoc::get(context))))
255  return failure();
256  // Add to the worklist.
257  worklist.push({result, actualParams});
258  }
259 
260  auto fieldNames = cls.getFieldNames();
261  auto operands = cls.getFieldsOp()->getOperands();
262  for (size_t i = 0; i < fieldNames.size(); ++i) {
263  auto name = fieldNames[i];
264  auto value = operands[i];
265  auto fieldLoc = cls.getFieldLocByIndex(i);
266  FailureOr<evaluator::EvaluatorValuePtr> result =
267  evaluateValue(value, actualParams, fieldLoc);
268  if (failed(result))
269  return result;
270 
271  fields[cast<StringAttr>(name)] = result.value();
272  }
273 
274  // If the there is an instance, we must update the object value.
275  if (instanceKey.first) {
276  auto result =
277  getOrCreateValue(instanceKey.first, instanceKey.second, loc).value();
278  auto *object = llvm::cast<evaluator::ObjectValue>(result.get());
279  object->setFields(std::move(fields));
280  return result;
281  }
282 
283  // If it's external call, just allocate new ObjectValue.
285  std::make_shared<evaluator::ObjectValue>(cls, fields, loc);
286  return result;
287 }
288 
289 /// Instantiate an Object with its class name and actual parameters.
290 FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
292  StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
293  ClassOp cls = symbolTable.lookup<ClassOp>(className);
294  if (!cls)
295  return symbolTable.getOp()->emitError("unknown class name ") << className;
296 
297  auto parameters =
298  std::make_unique<SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>(
299  actualParams);
300 
301  actualParametersBuffers.push_back(std::move(parameters));
302 
303  auto loc = cls.getLoc();
304  auto result = evaluateObjectInstance(
305  className, actualParametersBuffers.back().get(), loc);
306 
307  if (failed(result))
308  return failure();
309 
310  // `evaluateObjectInstance` has populated the worklist. Continue evaluations
311  // unless there is a partially evaluated value.
312  while (!worklist.empty()) {
313  auto [value, args] = worklist.front();
314  worklist.pop();
315 
316  auto result = evaluateValue(value, args, loc);
317 
318  if (failed(result))
319  return failure();
320 
321  // It's possible that the value is not fully evaluated.
322  if (!result.value()->isFullyEvaluated())
323  worklist.push({value, args});
324  }
325 
326  auto &object = result.value();
327  // Finalize the value. This will eliminate intermidiate ReferenceValue used as
328  // a placeholder in the initialization.
329  if (failed(object->finalize()))
330  return cls.emitError() << "failed to finalize evaluation. Probably the "
331  "class contains a dataflow cycle";
332  return object;
333 }
334 
335 FailureOr<evaluator::EvaluatorValuePtr>
337  Location loc) {
338  auto evaluatorValue = getOrCreateValue(value, actualParams, loc).value();
339 
340  // Return if the value is already evaluated.
341  if (evaluatorValue->isFullyEvaluated())
342  return evaluatorValue;
343 
344  return llvm::TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
345  .Case([&](BlockArgument arg) {
346  return evaluateParameter(arg, actualParams, loc);
347  })
348  .Case([&](OpResult result) {
349  return TypeSwitch<Operation *, FailureOr<evaluator::EvaluatorValuePtr>>(
350  result.getDefiningOp())
351  .Case([&](ConstantOp op) {
352  return evaluateConstant(op, actualParams, loc);
353  })
354  .Case([&](IntegerBinaryArithmeticOp op) {
355  return evaluateIntegerBinaryArithmetic(op, actualParams, loc);
356  })
357  .Case([&](ObjectOp op) {
358  return evaluateObjectInstance(op, actualParams);
359  })
360  .Case([&](ObjectFieldOp op) {
361  return evaluateObjectField(op, actualParams, loc);
362  })
363  .Case([&](ListCreateOp op) {
364  return evaluateListCreate(op, actualParams, loc);
365  })
366  .Case([&](ListConcatOp op) {
367  return evaluateListConcat(op, actualParams, loc);
368  })
369  .Case([&](TupleCreateOp op) {
370  return evaluateTupleCreate(op, actualParams, loc);
371  })
372  .Case([&](TupleGetOp op) {
373  return evaluateTupleGet(op, actualParams, loc);
374  })
375  .Case([&](AnyCastOp op) {
376  return evaluateValue(op.getInput(), actualParams, loc);
377  })
378  .Case([&](MapCreateOp op) {
379  return evaluateMapCreate(op, actualParams, loc);
380  })
381  .Case([&](FrozenBasePathCreateOp op) {
382  return evaluateBasePathCreate(op, actualParams, loc);
383  })
384  .Case([&](FrozenPathCreateOp op) {
385  return evaluatePathCreate(op, actualParams, loc);
386  })
387  .Case([&](FrozenEmptyPathOp op) {
388  return evaluateEmptyPath(op, actualParams, loc);
389  })
390  .Default([&](Operation *op) {
391  auto error = op->emitError("unable to evaluate value");
392  error.attachNote() << "value: " << value;
393  return error;
394  });
395  });
396 }
397 
398 /// Evaluator dispatch function for parameters.
399 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateParameter(
400  BlockArgument formalParam, ActualParameters actualParams, Location loc) {
401  auto val = (*actualParams)[formalParam.getArgNumber()];
402  val->setLoc(loc);
403  return success(val);
404 }
405 
406 /// Evaluator dispatch function for constants.
407 FailureOr<circt::om::evaluator::EvaluatorValuePtr>
409  ActualParameters actualParams,
410  Location loc) {
411  return success(std::make_shared<circt::om::evaluator::AttributeValue>(
412  op.getValue(), loc));
413 }
414 
415 // Evaluator dispatch function for integer binary arithmetic.
416 FailureOr<EvaluatorValuePtr>
418  IntegerBinaryArithmeticOp op, ActualParameters actualParams, Location loc) {
419  // Get the op's EvaluatorValue handle, in case it hasn't been evaluated yet.
420  auto handle = getOrCreateValue(op.getResult(), actualParams, loc);
421 
422  // If it's fully evaluated, we can return it.
423  if (handle.value()->isFullyEvaluated())
424  return handle;
425 
426  // Evaluate operands if necessary, and return the partially evaluated value if
427  // they aren't ready.
428  auto lhsResult = evaluateValue(op.getLhs(), actualParams, loc);
429  if (failed(lhsResult))
430  return lhsResult;
431  if (!lhsResult.value()->isFullyEvaluated())
432  return handle;
433 
434  auto rhsResult = evaluateValue(op.getRhs(), actualParams, loc);
435  if (failed(rhsResult))
436  return rhsResult;
437  if (!rhsResult.value()->isFullyEvaluated())
438  return handle;
439 
440  // Extract the integer attributes.
441  auto extractAttr = [](evaluator::EvaluatorValue *value) {
442  return std::move(
443  llvm::TypeSwitch<evaluator::EvaluatorValue *, om::IntegerAttr>(value)
444  .Case([](evaluator::AttributeValue *val) {
445  return val->getAs<om::IntegerAttr>();
446  })
447  .Case([](evaluator::ReferenceValue *val) {
448  return cast<evaluator::AttributeValue>(
449  val->getStrippedValue()->get())
450  ->getAs<om::IntegerAttr>();
451  }));
452  };
453 
454  om::IntegerAttr lhs = extractAttr(lhsResult.value().get());
455  om::IntegerAttr rhs = extractAttr(rhsResult.value().get());
456  assert(lhs && rhs &&
457  "expected om::IntegerAttr for IntegerBinaryArithmeticOp operands");
458 
459  // Extend values if necessary to match bitwidth. Most interesting arithmetic
460  // on APSInt asserts that both operands are the same bitwidth, but the
461  // IntegerAttrs we are working with may have used the smallest necessary
462  // bitwidth to represent the number they hold, and won't necessarily match.
463  APSInt lhsVal = lhs.getValue().getAPSInt();
464  APSInt rhsVal = rhs.getValue().getAPSInt();
465  if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
466  rhsVal = rhsVal.extend(lhsVal.getBitWidth());
467  else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
468  lhsVal = lhsVal.extend(rhsVal.getBitWidth());
469 
470  // Perform arbitrary precision signed integer binary arithmetic.
471  FailureOr<APSInt> result = op.evaluateIntegerOperation(lhsVal, rhsVal);
472 
473  if (failed(result))
474  return op->emitError("failed to evaluate integer operation");
475 
476  // Package the result as a new om::IntegerAttr.
477  MLIRContext *ctx = op->getContext();
478  auto resultAttr =
479  om::IntegerAttr::get(ctx, mlir::IntegerAttr::get(ctx, result.value()));
480 
481  // Finalize the op result value.
482  auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get());
483  auto resultStatus = handleValue->setAttr(resultAttr);
484  if (failed(resultStatus))
485  return resultStatus;
486 
487  auto finalizeStatus = handleValue->finalize();
488  if (failed(finalizeStatus))
489  return finalizeStatus;
490 
491  return handle;
492 }
493 
494 /// Evaluator dispatch function for Object instances.
495 FailureOr<circt::om::Evaluator::ActualParameters>
497  ValueRange range, ActualParameters actualParams, Location loc) {
498  // Create an unique storage to store parameters.
499  auto parameters = std::make_unique<
500  SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>();
501 
502  // Collect operands' evaluator values in the current instantiation context.
503  for (auto input : range) {
504  auto inputResult = getOrCreateValue(input, actualParams, loc);
505  if (failed(inputResult))
506  return failure();
507  parameters->push_back(inputResult.value());
508  }
509 
510  actualParametersBuffers.push_back(std::move(parameters));
511  return actualParametersBuffers.back().get();
512 }
513 
514 /// Evaluator dispatch function for Object instances.
515 FailureOr<evaluator::EvaluatorValuePtr>
517  ActualParameters actualParams) {
518  auto loc = op.getLoc();
519  if (isFullyEvaluated({op, actualParams}))
520  return getOrCreateValue(op, actualParams, loc);
521 
522  auto params =
523  createParametersFromOperands(op.getOperands(), actualParams, loc);
524  if (failed(params))
525  return failure();
526  return evaluateObjectInstance(op.getClassNameAttr(), params.value(), loc,
527  {op, actualParams});
528 }
529 
530 /// Evaluator dispatch function for Object fields.
531 FailureOr<evaluator::EvaluatorValuePtr>
533  ActualParameters actualParams,
534  Location loc) {
535  // Evaluate the Object itself, in case it hasn't been evaluated yet.
536  FailureOr<evaluator::EvaluatorValuePtr> currentObjectResult =
537  evaluateValue(op.getObject(), actualParams, loc);
538  if (failed(currentObjectResult))
539  return currentObjectResult;
540 
541  auto *currentObject =
542  llvm::cast<evaluator::ObjectValue>(currentObjectResult.value().get());
543 
544  auto objectFieldValue = getOrCreateValue(op, actualParams, loc).value();
545 
546  // Iteratively access nested fields through the path until we reach the final
547  // field in the path.
548  evaluator::EvaluatorValuePtr finalField;
549  for (auto field : op.getFieldPath().getAsRange<FlatSymbolRefAttr>()) {
550  // `currentObject` might no be fully evaluated.
551  if (!currentObject->getFields().contains(field.getAttr()))
552  return objectFieldValue;
553 
554  auto currentField = currentObject->getField(field.getAttr());
555  finalField = currentField.value();
556  if (auto *nextObject =
557  llvm::dyn_cast<evaluator::ObjectValue>(finalField.get()))
558  currentObject = nextObject;
559  }
560 
561  // Update the reference.
562  llvm::cast<evaluator::ReferenceValue>(objectFieldValue.get())
563  ->setValue(finalField);
564 
565  // Return the field being accessed.
566  return objectFieldValue;
567 }
568 
569 /// Evaluator dispatch function for List creation.
570 FailureOr<evaluator::EvaluatorValuePtr>
572  ActualParameters actualParams,
573  Location loc) {
574  // Evaluate the Object itself, in case it hasn't been evaluated yet.
575  SmallVector<evaluator::EvaluatorValuePtr> values;
576  auto list = getOrCreateValue(op, actualParams, loc);
577  for (auto operand : op.getOperands()) {
578  auto result = evaluateValue(operand, actualParams, loc);
579  if (failed(result))
580  return result;
581  if (!result.value()->isFullyEvaluated())
582  return list;
583  values.push_back(result.value());
584  }
585 
586  // Return the list.
587  llvm::cast<evaluator::ListValue>(list.value().get())
588  ->setElements(std::move(values));
589  return list;
590 }
591 
592 /// Evaluator dispatch function for List concatenation.
593 FailureOr<evaluator::EvaluatorValuePtr>
595  ActualParameters actualParams,
596  Location loc) {
597  // Evaluate the List concat op itself, in case it hasn't been evaluated yet.
598  SmallVector<evaluator::EvaluatorValuePtr> values;
599  auto list = getOrCreateValue(op, actualParams, loc);
600 
601  // Extract the ListValue, either directly or through an object reference.
602  auto extractList = [](evaluator::EvaluatorValue *value) {
603  return std::move(
604  llvm::TypeSwitch<evaluator::EvaluatorValue *, evaluator::ListValue *>(
605  value)
606  .Case([](evaluator::ListValue *val) { return val; })
607  .Case([](evaluator::ReferenceValue *val) {
608  return cast<evaluator::ListValue>(val->getStrippedValue()->get());
609  }));
610  };
611 
612  for (auto operand : op.getOperands()) {
613  auto result = evaluateValue(operand, actualParams, loc);
614  if (failed(result))
615  return result;
616  if (!result.value()->isFullyEvaluated())
617  return list;
618 
619  // Append each EvaluatorValue from the sublist.
620  evaluator::ListValue *subList = extractList(result.value().get());
621  for (auto subValue : subList->getElements())
622  values.push_back(subValue);
623  }
624 
625  // Return the concatenated list.
626  llvm::cast<evaluator::ListValue>(list.value().get())
627  ->setElements(std::move(values));
628  return list;
629 }
630 
631 /// Evaluator dispatch function for Tuple creation.
632 FailureOr<evaluator::EvaluatorValuePtr>
634  ActualParameters actualParams,
635  Location loc) {
636  SmallVector<evaluator::EvaluatorValuePtr> values;
637  for (auto operand : op.getOperands()) {
638  auto result = evaluateValue(operand, actualParams, loc);
639  if (failed(result))
640  return result;
641  values.push_back(result.value());
642  }
643 
644  // Return the tuple.
645  auto val = getOrCreateValue(op, actualParams, loc);
646  llvm::cast<evaluator::TupleValue>(val.value().get())
647  ->setElements(std::move(values));
648  return val;
649 }
650 
651 /// Evaluator dispatch function for List creation.
652 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateTupleGet(
653  TupleGetOp op, ActualParameters actualParams, Location loc) {
654  auto tuple = evaluateValue(op.getInput(), actualParams, loc);
655  if (failed(tuple))
656  return tuple;
658  cast<evaluator::TupleValue>(tuple.value().get())
659  ->getElements()[op.getIndex()];
660  return result;
661 }
662 
663 /// Evaluator dispatch function for Map creation.
664 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateMapCreate(
665  MapCreateOp op, ActualParameters actualParams, Location loc) {
666  // Evaluate the Object itself, in case it hasn't been evaluated yet.
667  DenseMap<Attribute, evaluator::EvaluatorValuePtr> elements;
668  auto valueResult = getOrCreateValue(op, actualParams, loc).value();
669  for (auto operand : op.getOperands()) {
670  auto result = evaluateValue(operand, actualParams, loc);
671  if (failed(result))
672  return result;
673  // The result is a tuple.
674  auto &value = result.value();
675  if (!value->isFullyEvaluated())
676  return valueResult;
677  const auto &element =
678  llvm::cast<evaluator::TupleValue>(value.get())->getElements();
679  assert(element.size() == 2);
680  auto attr =
681  llvm::cast<evaluator::AttributeValue>(element[0].get())->getAttr();
682  if (!elements.insert({attr, element[1]}).second)
683  return op.emitError() << "map contains duplicated keys";
684  }
685 
686  // Return the Map.
687  llvm::cast<evaluator::MapValue>(valueResult.get())
688  ->setElements(std::move(elements));
689  return valueResult;
690 }
691 
692 FailureOr<evaluator::EvaluatorValuePtr>
694  ActualParameters actualParams,
695  Location loc) {
696  // Evaluate the Object itself, in case it hasn't been evaluated yet.
697  auto valueResult = getOrCreateValue(op, actualParams, loc).value();
698  auto *path = llvm::cast<evaluator::BasePathValue>(valueResult.get());
699  auto result = evaluateValue(op.getBasePath(), actualParams, loc);
700  if (failed(result))
701  return result;
702  auto &value = result.value();
703  if (!value->isFullyEvaluated())
704  return valueResult;
705  path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
706  return valueResult;
707 }
708 
709 FailureOr<evaluator::EvaluatorValuePtr>
711  ActualParameters actualParams,
712  Location loc) {
713  // Evaluate the Object itself, in case it hasn't been evaluated yet.
714  auto valueResult = getOrCreateValue(op, actualParams, loc).value();
715  auto *path = llvm::cast<evaluator::PathValue>(valueResult.get());
716  auto result = evaluateValue(op.getBasePath(), actualParams, loc);
717  if (failed(result))
718  return result;
719  auto &value = result.value();
720  if (!value->isFullyEvaluated())
721  return valueResult;
722  path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
723  return valueResult;
724 }
725 
726 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateEmptyPath(
727  FrozenEmptyPathOp op, ActualParameters actualParams, Location loc) {
728  auto valueResult = getOrCreateValue(op, actualParams, loc).value();
729  return valueResult;
730 }
731 
732 //===----------------------------------------------------------------------===//
733 // ObjectValue
734 //===----------------------------------------------------------------------===//
735 
736 /// Get a field of the Object by name.
737 FailureOr<EvaluatorValuePtr>
739  auto field = fields.find(name);
740  if (field == fields.end())
741  return cls.emitError("field ") << name << " does not exist";
742  return success(fields[name]);
743 }
744 
745 /// Get an ArrayAttr with the names of the fields in the Object. Sort the fields
746 /// so there is always a stable order.
748  SmallVector<Attribute> fieldNames;
749  for (auto &f : fields)
750  fieldNames.push_back(f.first);
751 
752  llvm::sort(fieldNames, [](Attribute a, Attribute b) {
753  return cast<StringAttr>(a).getValue() < cast<StringAttr>(b).getValue();
754  });
755 
756  return ArrayAttr::get(cls.getContext(), fieldNames);
757 }
758 
760  for (auto &&[e, value] : fields)
761  if (failed(finalizeEvaluatorValue(value)))
762  return failure();
763 
764  return success();
765 }
766 
767 //===----------------------------------------------------------------------===//
768 // MapValue
769 //===----------------------------------------------------------------------===//
770 
771 /// Return an array of keys in the ascending order.
773  SmallVector<Attribute> attrs;
774  for (auto &[key, _] : elements)
775  attrs.push_back(key);
776 
777  std::sort(attrs.begin(), attrs.end(), [](Attribute l, Attribute r) {
778  if (auto lInt = dyn_cast<mlir::IntegerAttr>(l))
779  if (auto rInt = dyn_cast<mlir::IntegerAttr>(r))
780  return lInt.getValue().ult(rInt.getValue());
781 
782  assert(isa<StringAttr>(l) && isa<StringAttr>(r) &&
783  "key type should be integer or string");
784  return cast<StringAttr>(l).getValue() < cast<StringAttr>(r).getValue();
785  });
786 
787  return ArrayAttr::get(type.getContext(), attrs);
788 }
789 
791  for (auto &&[e, value] : elements)
792  if (failed(finalizeEvaluatorValue(value)))
793  return failure();
794  return success();
795 }
796 
797 //===----------------------------------------------------------------------===//
798 // ReferenceValue
799 //===----------------------------------------------------------------------===//
800 
802  auto result = getStrippedValue();
803  if (failed(result))
804  return result;
805  value = std::move(result.value());
806  return success();
807 }
808 
809 //===----------------------------------------------------------------------===//
810 // ListValue
811 //===----------------------------------------------------------------------===//
812 
814  for (auto &value : elements) {
815  if (failed(finalizeEvaluatorValue(value)))
816  return failure();
817  }
818  return success();
819 }
820 
821 //===----------------------------------------------------------------------===//
822 // BasePathValue
823 //===----------------------------------------------------------------------===//
824 
826  : EvaluatorValue(context, Kind::BasePath, UnknownLoc::get(context)),
827  path(PathAttr::get(context, {})) {
828  markFullyEvaluated();
829 }
830 
831 evaluator::BasePathValue::BasePathValue(PathAttr path, Location loc)
832  : EvaluatorValue(path.getContext(), Kind::BasePath, loc), path(path) {}
833 
835  assert(isFullyEvaluated());
836  return path;
837 }
838 
840  assert(!isFullyEvaluated());
841  auto newPath = llvm::to_vector(basepath.path.getPath());
842  auto oldPath = path.getPath();
843  newPath.append(oldPath.begin(), oldPath.end());
844  path = PathAttr::get(path.getContext(), newPath);
845  markFullyEvaluated();
846 }
847 
848 //===----------------------------------------------------------------------===//
849 // PathValue
850 //===----------------------------------------------------------------------===//
851 
852 evaluator::PathValue::PathValue(TargetKindAttr targetKind, PathAttr path,
853  StringAttr module, StringAttr ref,
854  StringAttr field, Location loc)
855  : EvaluatorValue(loc.getContext(), Kind::Path, loc), targetKind(targetKind),
856  path(path), module(module), ref(ref), field(field) {}
857 
859  PathValue path(nullptr, nullptr, nullptr, nullptr, nullptr, loc);
860  path.markFullyEvaluated();
861  return path;
862 }
863 
865  // If the module is null, then this is a path to a deleted object.
866  if (!targetKind)
867  return StringAttr::get(getContext(), "OMDeleted:");
868  SmallString<64> result;
869  switch (targetKind.getValue()) {
870  case TargetKind::DontTouch:
871  result += "OMDontTouchedReferenceTarget";
872  break;
873  case TargetKind::Instance:
874  result += "OMInstanceTarget";
875  break;
876  case TargetKind::MemberInstance:
877  result += "OMMemberInstanceTarget";
878  break;
879  case TargetKind::MemberReference:
880  result += "OMMemberReferenceTarget";
881  break;
882  case TargetKind::Reference:
883  result += "OMReferenceTarget";
884  break;
885  }
886  result += ":~";
887  if (!path.getPath().empty())
888  result += path.getPath().front().module;
889  else
890  result += module.getValue();
891  result += '|';
892  for (const auto &elt : path) {
893  result += elt.module.getValue();
894  result += '/';
895  result += elt.instance.getValue();
896  result += ':';
897  }
898  if (!module.getValue().empty())
899  result += module.getValue();
900  if (!ref.getValue().empty()) {
901  result += '>';
902  result += ref.getValue();
903  }
904  if (!field.getValue().empty())
905  result += field.getValue();
906  return StringAttr::get(field.getContext(), result);
907 }
908 
910  assert(!isFullyEvaluated());
911  auto newPath = llvm::to_vector(basepath.getPath().getPath());
912  auto oldPath = path.getPath();
913  newPath.append(oldPath.begin(), oldPath.end());
914  path = PathAttr::get(path.getContext(), newPath);
915  markFullyEvaluated();
916 }
917 
918 //===----------------------------------------------------------------------===//
919 // AttributeValue
920 //===----------------------------------------------------------------------===//
921 
922 LogicalResult circt::om::evaluator::AttributeValue::setAttr(Attribute attr) {
923  if (cast<TypedAttr>(attr).getType() != this->type)
924  return mlir::emitError(getLoc(), "cannot set AttributeValue of type ")
925  << this->type << " to Attribute " << attr;
926  if (isFullyEvaluated())
927  return mlir::emitError(
928  getLoc(),
929  "cannot set AttributeValue that has already been fully evaluated");
930  this->attr = attr;
931  markFullyEvaluated();
932  return success();
933 }
934 
936  if (!isFullyEvaluated())
937  return mlir::emitError(
938  getLoc(), "cannot finalize AttributeValue that is not fully evaluated");
939  return success();
940 }
assert(baseType &&"element must be base type")
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
Definition: CalyxOps.cpp:55
static LogicalResult finalizeEvaluatorValue(EvaluatorValuePtr &value)
Definition: Evaluator.h:170
std::shared_ptr< EvaluatorValue > EvaluatorValuePtr
A value of an object in memory.
Definition: Evaluator.h:38
SmallVector< EvaluatorValuePtr > getEvaluatorValuesFromAttributes(MLIRContext *context, ArrayRef< Attribute > attributes)
Definition: Evaluator.cpp:34
FailureOr< evaluator::EvaluatorValuePtr > evaluateBasePathCreate(FrozenBasePathCreateOp op, ActualParameters actualParams, Location loc)
Definition: Evaluator.cpp:693
FailureOr< evaluator::EvaluatorValuePtr > evaluateEmptyPath(FrozenEmptyPathOp op, ActualParameters actualParams, Location loc)
Definition: Evaluator.cpp:726
FailureOr< evaluator::EvaluatorValuePtr > getPartiallyEvaluatedValue(Type type, Location loc)
Definition: Evaluator.cpp:72
FailureOr< EvaluatorValuePtr > evaluateValue(Value value, ActualParameters actualParams, Location loc)
Evaluate a Value in a Class body according to the small expression grammar described in the rationale...
Definition: Evaluator.cpp:336
FailureOr< EvaluatorValuePtr > evaluateConstant(ConstantOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for constants.
Definition: Evaluator.cpp:408
FailureOr< EvaluatorValuePtr > evaluateIntegerBinaryArithmetic(IntegerBinaryArithmeticOp op, ActualParameters actualParams, Location loc)
Definition: Evaluator.cpp:417
mlir::ModuleOp getModule()
Get the Module this Evaluator is built from.
Definition: Evaluator.cpp:29
FailureOr< EvaluatorValuePtr > evaluateObjectField(ObjectFieldOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Object fields.
Definition: Evaluator.cpp:532
Evaluator(ModuleOp mod)
Construct an Evaluator with an IR module.
Definition: Evaluator.cpp:26
FailureOr< evaluator::EvaluatorValuePtr > evaluateMapCreate(MapCreateOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Map creation.
Definition: Evaluator.cpp:664
FailureOr< evaluator::EvaluatorValuePtr > instantiate(StringAttr className, ArrayRef< EvaluatorValuePtr > actualParams)
Instantiate an Object with its class name and actual parameters.
Definition: Evaluator.cpp:291
FailureOr< EvaluatorValuePtr > getOrCreateValue(Value value, ActualParameters actualParams, Location loc)
Definition: Evaluator.cpp:108
SmallVectorImpl< std::shared_ptr< evaluator::EvaluatorValue > > * ActualParameters
Definition: Evaluator.h:435
FailureOr< EvaluatorValuePtr > evaluateListCreate(ListCreateOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for List creation.
Definition: Evaluator.cpp:571
FailureOr< EvaluatorValuePtr > evaluateListConcat(ListConcatOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for List concatenation.
Definition: Evaluator.cpp:594
std::pair< Value, ActualParameters > ObjectKey
Definition: Evaluator.h:437
FailureOr< EvaluatorValuePtr > evaluateParameter(BlockArgument formalParam, ActualParameters actualParams, Location loc)
Evaluator dispatch functions for the small expression grammar.
Definition: Evaluator.cpp:399
FailureOr< EvaluatorValuePtr > evaluateObjectInstance(StringAttr className, ActualParameters actualParams, Location loc, ObjectKey instanceKey={})
Instantiate an Object with its class name and actual parameters.
Definition: Evaluator.cpp:191
FailureOr< EvaluatorValuePtr > evaluateTupleGet(TupleGetOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for List creation.
Definition: Evaluator.cpp:652
FailureOr< EvaluatorValuePtr > evaluateTupleCreate(TupleCreateOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Tuple creation.
Definition: Evaluator.cpp:633
FailureOr< evaluator::EvaluatorValuePtr > evaluatePathCreate(FrozenPathCreateOp op, ActualParameters actualParams, Location loc)
Definition: Evaluator.cpp:710
FailureOr< ActualParameters > createParametersFromOperands(ValueRange range, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Object instances.
Definition: Evaluator.cpp:496
Values which can be directly representable by MLIR attributes.
Definition: Evaluator.h:132
LogicalResult setAttr(Attribute attr)
Definition: Evaluator.cpp:922
BasePathValue(MLIRContext *context)
Definition: Evaluator.cpp:825
void setBasepath(const BasePathValue &basepath)
Set the basepath which this path is relative to.
Definition: Evaluator.cpp:839
Base class for evaluator runtime values.
Definition: Evaluator.h:48
A List which contains variadic length of elements with the same type.
Definition: Evaluator.h:183
const auto & getElements() const
Definition: Evaluator.h:203
ArrayAttr getKeys()
Return an array of keys in the ascending order.
Definition: Evaluator.cpp:772
A composite Object, which has a type and fields.
Definition: Evaluator.h:257
FailureOr< EvaluatorValuePtr > getField(StringAttr field)
Get a field of the Object by name.
Definition: Evaluator.cpp:738
ArrayAttr getFieldNames()
Get all the field names of the Object.
Definition: Evaluator.cpp:747
StringAttr getAsString() const
Definition: Evaluator.cpp:864
void setBasepath(const BasePathValue &basepath)
Definition: Evaluator.cpp:909
PathValue(om::TargetKindAttr targetKind, om::PathAttr path, StringAttr module, StringAttr ref, StringAttr field, Location loc)
Create a path value representing a regular path.
Definition: Evaluator.cpp:852
static PathValue getEmptyPath(Location loc)
Definition: Evaluator.cpp:858
Values which can be used as pointers to different values.
Definition: Evaluator.h:93