CIRCT  19.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, TupleCreateOp, MapCreateOp, ObjectFieldOp>(
171  [&](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  for (auto field : cls.getOps<ClassFieldOp>()) {
261  StringAttr name = field.getNameAttr();
262  Value value = field.getValue();
263  FailureOr<evaluator::EvaluatorValuePtr> result =
264  evaluateValue(value, actualParams, field.getLoc());
265  if (failed(result))
266  return result;
267 
268  fields[name] = result.value();
269  }
270 
271  // If the there is an instance, we must update the object value.
272  if (instanceKey.first) {
273  auto result =
274  getOrCreateValue(instanceKey.first, instanceKey.second, loc).value();
275  auto *object = llvm::cast<evaluator::ObjectValue>(result.get());
276  object->setFields(std::move(fields));
277  return result;
278  }
279 
280  // If it's external call, just allocate new ObjectValue.
282  std::make_shared<evaluator::ObjectValue>(cls, fields, loc);
283  return result;
284 }
285 
286 /// Instantiate an Object with its class name and actual parameters.
287 FailureOr<std::shared_ptr<evaluator::EvaluatorValue>>
289  StringAttr className, ArrayRef<evaluator::EvaluatorValuePtr> actualParams) {
290  ClassOp cls = symbolTable.lookup<ClassOp>(className);
291  if (!cls)
292  return symbolTable.getOp()->emitError("unknown class name ") << className;
293 
294  auto parameters =
295  std::make_unique<SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>(
296  actualParams);
297 
298  actualParametersBuffers.push_back(std::move(parameters));
299 
300  auto loc = cls.getLoc();
301  auto result = evaluateObjectInstance(
302  className, actualParametersBuffers.back().get(), loc);
303 
304  if (failed(result))
305  return failure();
306 
307  // `evaluateObjectInstance` has populated the worklist. Continue evaluations
308  // unless there is a partially evaluated value.
309  while (!worklist.empty()) {
310  auto [value, args] = worklist.front();
311  worklist.pop();
312 
313  auto result = evaluateValue(value, args, loc);
314 
315  if (failed(result))
316  return failure();
317 
318  // It's possible that the value is not fully evaluated.
319  if (!result.value()->isFullyEvaluated())
320  worklist.push({value, args});
321  }
322 
323  auto &object = result.value();
324  // Finalize the value. This will eliminate intermidiate ReferenceValue used as
325  // a placeholder in the initialization.
326  if (failed(object->finalize()))
327  return cls.emitError() << "failed to finalize evaluation. Probably the "
328  "class contains a dataflow cycle";
329  return object;
330 }
331 
332 FailureOr<evaluator::EvaluatorValuePtr>
334  Location loc) {
335  auto evaluatorValue = getOrCreateValue(value, actualParams, loc).value();
336 
337  // Return if the value is already evaluated.
338  if (evaluatorValue->isFullyEvaluated())
339  return evaluatorValue;
340 
341  return llvm::TypeSwitch<Value, FailureOr<evaluator::EvaluatorValuePtr>>(value)
342  .Case([&](BlockArgument arg) {
343  return evaluateParameter(arg, actualParams, loc);
344  })
345  .Case([&](OpResult result) {
346  return TypeSwitch<Operation *, FailureOr<evaluator::EvaluatorValuePtr>>(
347  result.getDefiningOp())
348  .Case([&](ConstantOp op) {
349  return evaluateConstant(op, actualParams, loc);
350  })
351  .Case([&](IntegerBinaryArithmeticOp op) {
352  return evaluateIntegerBinaryArithmetic(op, actualParams, loc);
353  })
354  .Case([&](ObjectOp op) {
355  return evaluateObjectInstance(op, actualParams);
356  })
357  .Case([&](ObjectFieldOp op) {
358  return evaluateObjectField(op, actualParams, loc);
359  })
360  .Case([&](ListCreateOp op) {
361  return evaluateListCreate(op, actualParams, loc);
362  })
363  .Case([&](TupleCreateOp op) {
364  return evaluateTupleCreate(op, actualParams, loc);
365  })
366  .Case([&](TupleGetOp op) {
367  return evaluateTupleGet(op, actualParams, loc);
368  })
369  .Case([&](AnyCastOp op) {
370  return evaluateValue(op.getInput(), actualParams, loc);
371  })
372  .Case([&](MapCreateOp op) {
373  return evaluateMapCreate(op, actualParams, loc);
374  })
375  .Case([&](FrozenBasePathCreateOp op) {
376  return evaluateBasePathCreate(op, actualParams, loc);
377  })
378  .Case([&](FrozenPathCreateOp op) {
379  return evaluatePathCreate(op, actualParams, loc);
380  })
381  .Case([&](FrozenEmptyPathOp op) {
382  return evaluateEmptyPath(op, actualParams, loc);
383  })
384  .Default([&](Operation *op) {
385  auto error = op->emitError("unable to evaluate value");
386  error.attachNote() << "value: " << value;
387  return error;
388  });
389  });
390 }
391 
392 /// Evaluator dispatch function for parameters.
393 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateParameter(
394  BlockArgument formalParam, ActualParameters actualParams, Location loc) {
395  auto val = (*actualParams)[formalParam.getArgNumber()];
396  val->setLoc(loc);
397  return success(val);
398 }
399 
400 /// Evaluator dispatch function for constants.
401 FailureOr<circt::om::evaluator::EvaluatorValuePtr>
403  ActualParameters actualParams,
404  Location loc) {
405  return success(std::make_shared<circt::om::evaluator::AttributeValue>(
406  op.getValue(), loc));
407 }
408 
409 // Evaluator dispatch function for integer binary arithmetic.
410 FailureOr<EvaluatorValuePtr>
412  IntegerBinaryArithmeticOp op, ActualParameters actualParams, Location loc) {
413  // Get the op's EvaluatorValue handle, in case it hasn't been evaluated yet.
414  auto handle = getOrCreateValue(op.getResult(), actualParams, loc);
415 
416  // If it's fully evaluated, we can return it.
417  if (handle.value()->isFullyEvaluated())
418  return handle;
419 
420  // Evaluate operands if necessary, and return the partially evaluated value if
421  // they aren't ready.
422  auto lhsResult = evaluateValue(op.getLhs(), actualParams, loc);
423  if (failed(lhsResult))
424  return lhsResult;
425  if (!lhsResult.value()->isFullyEvaluated())
426  return handle;
427 
428  auto rhsResult = evaluateValue(op.getRhs(), actualParams, loc);
429  if (failed(rhsResult))
430  return rhsResult;
431  if (!rhsResult.value()->isFullyEvaluated())
432  return handle;
433 
434  // Extract the integer attributes.
435  auto extractAttr = [](evaluator::EvaluatorValue *value) {
436  return std::move(
437  llvm::TypeSwitch<evaluator::EvaluatorValue *, om::IntegerAttr>(value)
438  .Case([](evaluator::AttributeValue *val) {
439  return val->getAs<om::IntegerAttr>();
440  })
441  .Case([](evaluator::ReferenceValue *val) {
442  return cast<evaluator::AttributeValue>(
443  val->getStrippedValue()->get())
444  ->getAs<om::IntegerAttr>();
445  }));
446  };
447 
448  om::IntegerAttr lhs = extractAttr(lhsResult.value().get());
449  om::IntegerAttr rhs = extractAttr(rhsResult.value().get());
450  assert(lhs && rhs &&
451  "expected om::IntegerAttr for IntegerBinaryArithmeticOp operands");
452 
453  // Extend values if necessary to match bitwidth. Most interesting arithmetic
454  // on APSInt asserts that both operands are the same bitwidth, but the
455  // IntegerAttrs we are working with may have used the smallest necessary
456  // bitwidth to represent the number they hold, and won't necessarily match.
457  APSInt lhsVal = lhs.getValue().getAPSInt();
458  APSInt rhsVal = rhs.getValue().getAPSInt();
459  if (lhsVal.getBitWidth() > rhsVal.getBitWidth())
460  rhsVal = rhsVal.extend(lhsVal.getBitWidth());
461  else if (rhsVal.getBitWidth() > lhsVal.getBitWidth())
462  lhsVal = lhsVal.extend(rhsVal.getBitWidth());
463 
464  // Perform arbitrary precision signed integer binary arithmetic.
465  FailureOr<APSInt> result = op.evaluateIntegerOperation(lhsVal, rhsVal);
466 
467  if (failed(result))
468  return op->emitError("failed to evaluate integer operation");
469 
470  // Package the result as a new om::IntegerAttr.
471  MLIRContext *ctx = op->getContext();
472  auto resultAttr =
473  om::IntegerAttr::get(ctx, mlir::IntegerAttr::get(ctx, result.value()));
474 
475  // Finalize the op result value.
476  auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get());
477  auto resultStatus = handleValue->setAttr(resultAttr);
478  if (failed(resultStatus))
479  return resultStatus;
480 
481  auto finalizeStatus = handleValue->finalize();
482  if (failed(finalizeStatus))
483  return finalizeStatus;
484 
485  return handle;
486 }
487 
488 /// Evaluator dispatch function for Object instances.
489 FailureOr<circt::om::Evaluator::ActualParameters>
491  ValueRange range, ActualParameters actualParams, Location loc) {
492  // Create an unique storage to store parameters.
493  auto parameters = std::make_unique<
494  SmallVector<std::shared_ptr<evaluator::EvaluatorValue>>>();
495 
496  // Collect operands' evaluator values in the current instantiation context.
497  for (auto input : range) {
498  auto inputResult = getOrCreateValue(input, actualParams, loc);
499  if (failed(inputResult))
500  return failure();
501  parameters->push_back(inputResult.value());
502  }
503 
504  actualParametersBuffers.push_back(std::move(parameters));
505  return actualParametersBuffers.back().get();
506 }
507 
508 /// Evaluator dispatch function for Object instances.
509 FailureOr<evaluator::EvaluatorValuePtr>
511  ActualParameters actualParams) {
512  auto loc = op.getLoc();
513  if (isFullyEvaluated({op, actualParams}))
514  return getOrCreateValue(op, actualParams, loc);
515 
516  auto params =
517  createParametersFromOperands(op.getOperands(), actualParams, loc);
518  if (failed(params))
519  return failure();
520  return evaluateObjectInstance(op.getClassNameAttr(), params.value(), loc,
521  {op, actualParams});
522 }
523 
524 /// Evaluator dispatch function for Object fields.
525 FailureOr<evaluator::EvaluatorValuePtr>
527  ActualParameters actualParams,
528  Location loc) {
529  // Evaluate the Object itself, in case it hasn't been evaluated yet.
530  FailureOr<evaluator::EvaluatorValuePtr> currentObjectResult =
531  evaluateValue(op.getObject(), actualParams, loc);
532  if (failed(currentObjectResult))
533  return currentObjectResult;
534 
535  auto *currentObject =
536  llvm::cast<evaluator::ObjectValue>(currentObjectResult.value().get());
537 
538  auto objectFieldValue = getOrCreateValue(op, actualParams, loc).value();
539 
540  // Iteratively access nested fields through the path until we reach the final
541  // field in the path.
542  evaluator::EvaluatorValuePtr finalField;
543  for (auto field : op.getFieldPath().getAsRange<FlatSymbolRefAttr>()) {
544  // `currentObject` might no be fully evaluated.
545  if (!currentObject->getFields().contains(field.getAttr()))
546  return objectFieldValue;
547 
548  auto currentField = currentObject->getField(field.getAttr());
549  finalField = currentField.value();
550  if (auto *nextObject =
551  llvm::dyn_cast<evaluator::ObjectValue>(finalField.get()))
552  currentObject = nextObject;
553  }
554 
555  // Update the reference.
556  llvm::cast<evaluator::ReferenceValue>(objectFieldValue.get())
557  ->setValue(finalField);
558 
559  // Return the field being accessed.
560  return objectFieldValue;
561 }
562 
563 /// Evaluator dispatch function for List creation.
564 FailureOr<evaluator::EvaluatorValuePtr>
566  ActualParameters actualParams,
567  Location loc) {
568  // Evaluate the Object itself, in case it hasn't been evaluated yet.
569  SmallVector<evaluator::EvaluatorValuePtr> values;
570  auto list = getOrCreateValue(op, actualParams, loc);
571  for (auto operand : op.getOperands()) {
572  auto result = evaluateValue(operand, actualParams, loc);
573  if (failed(result))
574  return result;
575  if (!result.value()->isFullyEvaluated())
576  return list;
577  values.push_back(result.value());
578  }
579 
580  // Return the list.
581  llvm::cast<evaluator::ListValue>(list.value().get())
582  ->setElements(std::move(values));
583  return list;
584 }
585 
586 /// Evaluator dispatch function for Tuple creation.
587 FailureOr<evaluator::EvaluatorValuePtr>
589  ActualParameters actualParams,
590  Location loc) {
591  SmallVector<evaluator::EvaluatorValuePtr> values;
592  for (auto operand : op.getOperands()) {
593  auto result = evaluateValue(operand, actualParams, loc);
594  if (failed(result))
595  return result;
596  values.push_back(result.value());
597  }
598 
599  // Return the tuple.
600  auto val = getOrCreateValue(op, actualParams, loc);
601  llvm::cast<evaluator::TupleValue>(val.value().get())
602  ->setElements(std::move(values));
603  return val;
604 }
605 
606 /// Evaluator dispatch function for List creation.
607 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateTupleGet(
608  TupleGetOp op, ActualParameters actualParams, Location loc) {
609  auto tuple = evaluateValue(op.getInput(), actualParams, loc);
610  if (failed(tuple))
611  return tuple;
613  cast<evaluator::TupleValue>(tuple.value().get())
614  ->getElements()[op.getIndex()];
615  return result;
616 }
617 
618 /// Evaluator dispatch function for Map creation.
619 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateMapCreate(
620  MapCreateOp op, ActualParameters actualParams, Location loc) {
621  // Evaluate the Object itself, in case it hasn't been evaluated yet.
622  DenseMap<Attribute, evaluator::EvaluatorValuePtr> elements;
623  auto valueResult = getOrCreateValue(op, actualParams, loc).value();
624  for (auto operand : op.getOperands()) {
625  auto result = evaluateValue(operand, actualParams, loc);
626  if (failed(result))
627  return result;
628  // The result is a tuple.
629  auto &value = result.value();
630  if (!value->isFullyEvaluated())
631  return valueResult;
632  const auto &element =
633  llvm::cast<evaluator::TupleValue>(value.get())->getElements();
634  assert(element.size() == 2);
635  auto attr =
636  llvm::cast<evaluator::AttributeValue>(element[0].get())->getAttr();
637  if (!elements.insert({attr, element[1]}).second)
638  return op.emitError() << "map contains duplicated keys";
639  }
640 
641  // Return the Map.
642  llvm::cast<evaluator::MapValue>(valueResult.get())
643  ->setElements(std::move(elements));
644  return valueResult;
645 }
646 
647 FailureOr<evaluator::EvaluatorValuePtr>
649  ActualParameters actualParams,
650  Location loc) {
651  // Evaluate the Object itself, in case it hasn't been evaluated yet.
652  auto valueResult = getOrCreateValue(op, actualParams, loc).value();
653  auto *path = llvm::cast<evaluator::BasePathValue>(valueResult.get());
654  auto result = evaluateValue(op.getBasePath(), actualParams, loc);
655  if (failed(result))
656  return result;
657  auto &value = result.value();
658  if (!value->isFullyEvaluated())
659  return valueResult;
660  path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
661  return valueResult;
662 }
663 
664 FailureOr<evaluator::EvaluatorValuePtr>
666  ActualParameters actualParams,
667  Location loc) {
668  // Evaluate the Object itself, in case it hasn't been evaluated yet.
669  auto valueResult = getOrCreateValue(op, actualParams, loc).value();
670  auto *path = llvm::cast<evaluator::PathValue>(valueResult.get());
671  auto result = evaluateValue(op.getBasePath(), actualParams, loc);
672  if (failed(result))
673  return result;
674  auto &value = result.value();
675  if (!value->isFullyEvaluated())
676  return valueResult;
677  path->setBasepath(*llvm::cast<evaluator::BasePathValue>(value.get()));
678  return valueResult;
679 }
680 
681 FailureOr<evaluator::EvaluatorValuePtr> circt::om::Evaluator::evaluateEmptyPath(
682  FrozenEmptyPathOp op, ActualParameters actualParams, Location loc) {
683  auto valueResult = getOrCreateValue(op, actualParams, loc).value();
684  return valueResult;
685 }
686 
687 //===----------------------------------------------------------------------===//
688 // ObjectValue
689 //===----------------------------------------------------------------------===//
690 
691 /// Get a field of the Object by name.
692 FailureOr<EvaluatorValuePtr>
694  auto field = fields.find(name);
695  if (field == fields.end())
696  return cls.emitError("field ") << name << " does not exist";
697  return success(fields[name]);
698 }
699 
700 /// Get an ArrayAttr with the names of the fields in the Object. Sort the fields
701 /// so there is always a stable order.
703  SmallVector<Attribute> fieldNames;
704  for (auto &f : fields)
705  fieldNames.push_back(f.first);
706 
707  llvm::sort(fieldNames, [](Attribute a, Attribute b) {
708  return cast<StringAttr>(a).getValue() < cast<StringAttr>(b).getValue();
709  });
710 
711  return ArrayAttr::get(cls.getContext(), fieldNames);
712 }
713 
715  for (auto &&[e, value] : fields)
716  if (failed(finalizeEvaluatorValue(value)))
717  return failure();
718 
719  return success();
720 }
721 
722 //===----------------------------------------------------------------------===//
723 // MapValue
724 //===----------------------------------------------------------------------===//
725 
726 /// Return an array of keys in the ascending order.
728  SmallVector<Attribute> attrs;
729  for (auto &[key, _] : elements)
730  attrs.push_back(key);
731 
732  std::sort(attrs.begin(), attrs.end(), [](Attribute l, Attribute r) {
733  if (auto lInt = dyn_cast<mlir::IntegerAttr>(l))
734  if (auto rInt = dyn_cast<mlir::IntegerAttr>(r))
735  return lInt.getValue().ult(rInt.getValue());
736 
737  assert(isa<StringAttr>(l) && isa<StringAttr>(r) &&
738  "key type should be integer or string");
739  return cast<StringAttr>(l).getValue() < cast<StringAttr>(r).getValue();
740  });
741 
742  return ArrayAttr::get(type.getContext(), attrs);
743 }
744 
746  for (auto &&[e, value] : elements)
747  if (failed(finalizeEvaluatorValue(value)))
748  return failure();
749  return success();
750 }
751 
752 //===----------------------------------------------------------------------===//
753 // ReferenceValue
754 //===----------------------------------------------------------------------===//
755 
757  auto result = getStrippedValue();
758  if (failed(result))
759  return result;
760  value = std::move(result.value());
761  return success();
762 }
763 
764 //===----------------------------------------------------------------------===//
765 // ListValue
766 //===----------------------------------------------------------------------===//
767 
769  for (auto &value : elements) {
770  if (failed(finalizeEvaluatorValue(value)))
771  return failure();
772  }
773  return success();
774 }
775 
776 //===----------------------------------------------------------------------===//
777 // BasePathValue
778 //===----------------------------------------------------------------------===//
779 
781  : EvaluatorValue(context, Kind::BasePath, UnknownLoc::get(context)),
782  path(PathAttr::get(context, {})) {
783  markFullyEvaluated();
784 }
785 
786 evaluator::BasePathValue::BasePathValue(PathAttr path, Location loc)
787  : EvaluatorValue(path.getContext(), Kind::BasePath, loc), path(path) {}
788 
790  assert(isFullyEvaluated());
791  return path;
792 }
793 
795  assert(!isFullyEvaluated());
796  auto newPath = llvm::to_vector(basepath.path.getPath());
797  auto oldPath = path.getPath();
798  newPath.append(oldPath.begin(), oldPath.end());
799  path = PathAttr::get(path.getContext(), newPath);
800  markFullyEvaluated();
801 }
802 
803 //===----------------------------------------------------------------------===//
804 // PathValue
805 //===----------------------------------------------------------------------===//
806 
807 evaluator::PathValue::PathValue(TargetKindAttr targetKind, PathAttr path,
808  StringAttr module, StringAttr ref,
809  StringAttr field, Location loc)
810  : EvaluatorValue(loc.getContext(), Kind::Path, loc), targetKind(targetKind),
811  path(path), module(module), ref(ref), field(field) {}
812 
814  PathValue path(nullptr, nullptr, nullptr, nullptr, nullptr, loc);
815  path.markFullyEvaluated();
816  return path;
817 }
818 
820  // If the module is null, then this is a path to a deleted object.
821  if (!targetKind)
822  return StringAttr::get(getContext(), "OMDeleted:");
823  SmallString<64> result;
824  switch (targetKind.getValue()) {
825  case TargetKind::DontTouch:
826  result += "OMDontTouchedReferenceTarget";
827  break;
828  case TargetKind::Instance:
829  result += "OMInstanceTarget";
830  break;
831  case TargetKind::MemberInstance:
832  result += "OMMemberInstanceTarget";
833  break;
834  case TargetKind::MemberReference:
835  result += "OMMemberReferenceTarget";
836  break;
837  case TargetKind::Reference:
838  result += "OMReferenceTarget";
839  break;
840  }
841  result += ":~";
842  if (!path.getPath().empty())
843  result += path.getPath().front().module;
844  else
845  result += module.getValue();
846  result += '|';
847  for (const auto &elt : path) {
848  result += elt.module.getValue();
849  result += '/';
850  result += elt.instance.getValue();
851  result += ':';
852  }
853  if (!module.getValue().empty())
854  result += module.getValue();
855  if (!ref.getValue().empty()) {
856  result += '>';
857  result += ref.getValue();
858  }
859  if (!field.getValue().empty())
860  result += field.getValue();
861  return StringAttr::get(field.getContext(), result);
862 }
863 
865  assert(!isFullyEvaluated());
866  auto newPath = llvm::to_vector(basepath.getPath().getPath());
867  auto oldPath = path.getPath();
868  newPath.append(oldPath.begin(), oldPath.end());
869  path = PathAttr::get(path.getContext(), newPath);
870  markFullyEvaluated();
871 }
872 
873 //===----------------------------------------------------------------------===//
874 // AttributeValue
875 //===----------------------------------------------------------------------===//
876 
877 LogicalResult circt::om::evaluator::AttributeValue::setAttr(Attribute attr) {
878  if (cast<TypedAttr>(attr).getType() != this->type)
879  return mlir::emitError(getLoc(), "cannot set AttributeValue of type ")
880  << this->type << " to Attribute " << attr;
881  if (isFullyEvaluated())
882  return mlir::emitError(
883  getLoc(),
884  "cannot set AttributeValue that has already been fully evaluated");
885  this->attr = attr;
886  markFullyEvaluated();
887  return success();
888 }
889 
891  if (!isFullyEvaluated())
892  return mlir::emitError(
893  getLoc(), "cannot finalize AttributeValue that is not fully evaluated");
894  return success();
895 }
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:54
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:648
FailureOr< evaluator::EvaluatorValuePtr > evaluateEmptyPath(FrozenEmptyPathOp op, ActualParameters actualParams, Location loc)
Definition: Evaluator.cpp:681
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:333
FailureOr< EvaluatorValuePtr > evaluateConstant(ConstantOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for constants.
Definition: Evaluator.cpp:402
FailureOr< EvaluatorValuePtr > evaluateIntegerBinaryArithmetic(IntegerBinaryArithmeticOp op, ActualParameters actualParams, Location loc)
Definition: Evaluator.cpp:411
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:526
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:619
FailureOr< evaluator::EvaluatorValuePtr > instantiate(StringAttr className, ArrayRef< EvaluatorValuePtr > actualParams)
Instantiate an Object with its class name and actual parameters.
Definition: Evaluator.cpp:288
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:565
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:393
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:607
FailureOr< EvaluatorValuePtr > evaluateTupleCreate(TupleCreateOp op, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Tuple creation.
Definition: Evaluator.cpp:588
FailureOr< evaluator::EvaluatorValuePtr > evaluatePathCreate(FrozenPathCreateOp op, ActualParameters actualParams, Location loc)
Definition: Evaluator.cpp:665
FailureOr< ActualParameters > createParametersFromOperands(ValueRange range, ActualParameters actualParams, Location loc)
Evaluator dispatch function for Object instances.
Definition: Evaluator.cpp:490
Values which can be directly representable by MLIR attributes.
Definition: Evaluator.h:132
LogicalResult setAttr(Attribute attr)
Definition: Evaluator.cpp:877
BasePathValue(MLIRContext *context)
Definition: Evaluator.cpp:780
void setBasepath(const BasePathValue &basepath)
Set the basepath which this path is relative to.
Definition: Evaluator.cpp:794
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
ArrayAttr getKeys()
Return an array of keys in the ascending order.
Definition: Evaluator.cpp:727
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:693
ArrayAttr getFieldNames()
Get all the field names of the Object.
Definition: Evaluator.cpp:702
StringAttr getAsString() const
Definition: Evaluator.cpp:819
void setBasepath(const BasePathValue &basepath)
Definition: Evaluator.cpp:864
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:807
static PathValue getEmptyPath(Location loc)
Definition: Evaluator.cpp:813
Values which can be used as pointers to different values.
Definition: Evaluator.h:93