21#include "mlir/Dialect/Arith/IR/Arith.h"
22#include "mlir/Dialect/Index/IR/IndexDialect.h"
23#include "mlir/Dialect/Index/IR/IndexOps.h"
24#include "mlir/Dialect/SCF/IR/SCF.h"
25#include "mlir/IR/IRMapping.h"
26#include "mlir/IR/PatternMatch.h"
27#include "llvm/ADT/DenseMapInfoVariant.h"
28#include "llvm/ADT/ScopeExit.h"
29#include "llvm/Support/Debug.h"
35#define GEN_PASS_DEF_ELABORATIONPASS
36#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc"
45#define DEBUG_TYPE "rtg-elaboration"
51 RngScope(
const RngScope &) =
delete;
52 RngScope &operator=(
const RngScope &) =
delete;
54 RngScope(RngScope &&) =
default;
55 RngScope &operator=(RngScope &&) =
default;
57 explicit RngScope(uint32_t seed) : rng(seed) {}
69 uint32_t getUniformlyInRange(uint32_t a, uint32_t b) {
70 const uint32_t diff = b - a + 1;
78 std::numeric_limits<uint32_t>::max() >> (32 - llvm::Log2_32_Ceil(diff));
87 RngScope getNested() {
return RngScope(rng()); }
101struct SequenceStorage;
102struct RandomizedSequenceStorage;
103struct InterleavedSequenceStorage;
105struct VirtualRegisterStorage;
106struct UniqueLabelStorage;
109struct MemoryBlockStorage;
110struct SymbolicComputationWithIdentityStorage;
111struct SymbolicComputationWithIdentityValue;
112struct SymbolicComputationStorage;
113struct OpaqueExternalStorage;
114struct ContinuationStorage;
117using ElaboratorValue =
118 std::variant<TypedAttr, BagStorage *, bool, size_t, SequenceStorage *,
119 RandomizedSequenceStorage *, InterleavedSequenceStorage *,
120 SetStorage *, VirtualRegisterStorage *, UniqueLabelStorage *,
121 ArrayStorage *, TupleStorage *, MemoryStorage *,
122 MemoryBlockStorage *, SymbolicComputationWithIdentityStorage *,
123 SymbolicComputationWithIdentityValue *,
124 SymbolicComputationStorage *, OpaqueExternalStorage *,
125 ContinuationStorage *>;
128llvm::hash_code
hash_value(
const ElaboratorValue &val) {
130 [&val](
const auto &alternative) {
133 return llvm::hash_combine(val.index(), alternative);
146 static bool isEqual(
const bool &lhs,
const bool &rhs) {
return lhs == rhs; }
161template <
typename StorageTy>
162struct HashedStorage {
163 HashedStorage(
unsigned hashcode = 0, StorageTy *storage =
nullptr)
164 : hashcode(hashcode), storage(storage) {}
174template <
typename StorageTy>
175struct StorageKeyInfo {
176 static inline unsigned getHashValue(
const HashedStorage<StorageTy> &key) {
179 static inline unsigned getHashValue(
const StorageTy &key) {
183 static inline bool isEqual(
const HashedStorage<StorageTy> &lhs,
184 const HashedStorage<StorageTy> &rhs) {
185 return lhs.storage == rhs.storage;
187 static inline bool isEqual(
const StorageTy &lhs,
188 const HashedStorage<StorageTy> &rhs) {
192 return lhs.isEqual(rhs.storage);
202struct CachableStorage {
207struct SetStorage : CachableStorage {
208 static unsigned computeHash(
const SetVector<ElaboratorValue> &set,
210 llvm::hash_code setHash = 0;
211 for (
auto el : set) {
216 setHash = setHash ^ llvm::hash_combine(el);
218 return llvm::hash_combine(type, setHash);
221 SetStorage(SetVector<ElaboratorValue> &&set, Type type)
222 : hashcode(computeHash(set, type)), set(std::move(set)), type(type) {}
224 bool isEqual(
const SetStorage *other)
const {
229 bool allContained =
true;
231 allContained &= other->set.contains(el);
233 return hashcode == other->hashcode && set.size() == other->set.size() &&
234 allContained && type == other->type;
238 const unsigned hashcode;
241 const SetVector<ElaboratorValue> set;
252 type,
llvm::hash_combine_range(bag.begin(), bag.
end()))),
253 bag(std::move(bag)), type(type) {}
255 bool isEqual(
const BagStorage *other)
const {
256 return hashcode == other->hashcode && llvm::equal(bag, other->bag) &&
261 const unsigned hashcode;
273struct SequenceStorage {
274 SequenceStorage(StringAttr familyName, SmallVector<ElaboratorValue> &&args)
276 familyName,
llvm::hash_combine_range(args.begin(), args.
end()))),
277 familyName(familyName), args(std::move(args)) {}
279 bool isEqual(
const SequenceStorage *other)
const {
280 return hashcode == other->hashcode && familyName == other->familyName &&
285 const unsigned hashcode;
288 const StringAttr familyName;
291 const SmallVector<ElaboratorValue> args;
295struct InterleavedSequenceStorage {
296 InterleavedSequenceStorage(SmallVector<ElaboratorValue> &&sequences,
298 : sequences(std::move(sequences)), batchSize(batchSize),
300 llvm::hash_combine_range(sequences.begin(), sequences.
end()),
303 explicit InterleavedSequenceStorage(RandomizedSequenceStorage *sequence)
304 : sequences(SmallVector<ElaboratorValue>(1, sequence)), batchSize(1),
306 llvm::hash_combine_range(sequences.begin(), sequences.
end()),
309 bool isEqual(
const InterleavedSequenceStorage *other)
const {
310 return hashcode == other->hashcode && sequences == other->sequences &&
311 batchSize == other->batchSize;
314 const SmallVector<ElaboratorValue> sequences;
316 const uint32_t batchSize;
319 const unsigned hashcode;
324 ArrayStorage(Type type, SmallVector<ElaboratorValue> &&array)
326 type,
llvm::hash_combine_range(array.begin(), array.
end()))),
327 type(type), array(array) {}
329 bool isEqual(
const ArrayStorage *other)
const {
330 return hashcode == other->hashcode && type == other->type &&
331 array == other->array;
335 const unsigned hashcode;
342 const SmallVector<ElaboratorValue> array;
346struct TupleStorage : CachableStorage {
347 TupleStorage(SmallVector<ElaboratorValue> &&values)
348 : hashcode(
llvm::hash_combine_range(values.begin(), values.
end())),
349 values(std::move(values)) {}
351 bool isEqual(
const TupleStorage *other)
const {
352 return hashcode == other->hashcode && values == other->values;
356 const unsigned hashcode;
358 const SmallVector<ElaboratorValue> values;
361struct SymbolicComputationStorage {
362 SymbolicComputationStorage(
const DenseMap<Value, ElaboratorValue> &state,
364 : name(op->
getName()), resultTypes(op->getResultTypes()),
365 operands(
llvm::map_range(op->getOperands(),
366 [&](Value v) {
return state.lookup(v); })),
367 attributes(op->getAttrDictionary()),
368 properties(op->getPropertiesAsAttribute()),
369 hashcode(llvm::hash_combine(name, llvm::hash_combine_range(resultTypes),
370 llvm::hash_combine_range(operands),
371 attributes, op->hashProperties())) {}
373 bool isEqual(
const SymbolicComputationStorage *other)
const {
374 return hashcode == other->hashcode && name == other->name &&
375 resultTypes == other->resultTypes && operands == other->operands &&
376 attributes == other->attributes && properties == other->properties;
379 const OperationName name;
380 const SmallVector<Type> resultTypes;
381 const SmallVector<ElaboratorValue> operands;
382 const DictionaryAttr attributes;
383 const Attribute properties;
384 const unsigned hashcode;
395struct IdentityValue {
397 IdentityValue(Type type, Location loc) : type(type), loc(loc) {}
410 bool alreadyMaterialized =
false;
419struct VirtualRegisterStorage : IdentityValue {
420 VirtualRegisterStorage(VirtualRegisterConfigAttr allowedRegs, Type type,
422 : IdentityValue(type, loc), allowedRegs(allowedRegs) {}
429 const VirtualRegisterConfigAttr allowedRegs;
432struct UniqueLabelStorage : IdentityValue {
433 UniqueLabelStorage(
const ElaboratorValue &name, Location loc)
434 : IdentityValue(LabelType::
get(loc->getContext()), loc), name(name) {}
440 const ElaboratorValue name;
444struct MemoryBlockStorage : IdentityValue {
445 MemoryBlockStorage(
const APInt &baseAddress,
const APInt &endAddress,
446 Type type, Location loc)
447 : IdentityValue(type, loc), baseAddress(baseAddress),
448 endAddress(endAddress) {}
453 const APInt baseAddress;
456 const APInt endAddress;
460struct MemoryStorage : IdentityValue {
461 MemoryStorage(MemoryBlockStorage *memoryBlock,
size_t size,
size_t alignment,
463 : IdentityValue(MemoryType::
get(memoryBlock->type.getContext(),
466 memoryBlock(memoryBlock), size(size), alignment(alignment) {}
468 MemoryBlockStorage *memoryBlock;
470 const size_t alignment;
474struct RandomizedSequenceStorage : IdentityValue {
475 RandomizedSequenceStorage(ContextResourceAttrInterface context,
476 SequenceStorage *sequence, Location loc)
478 RandomizedSequenceType::
get(sequence->familyName.getContext()),
480 context(context), sequence(sequence) {}
483 const ContextResourceAttrInterface context;
485 const SequenceStorage *sequence;
489struct SymbolicComputationWithIdentityStorage : IdentityValue {
490 SymbolicComputationWithIdentityStorage(
491 const DenseMap<Value, ElaboratorValue> &state, Operation *op)
492 : IdentityValue(op->getResult(0).getType(), op->
getLoc()),
493 name(op->
getName()), resultTypes(op->getResultTypes()),
494 operands(
llvm::map_range(op->getOperands(),
495 [&](Value v) {
return state.lookup(v); })),
496 attributes(op->getAttrDictionary()),
497 properties(op->getPropertiesAsAttribute()) {}
499 const OperationName name;
500 const SmallVector<Type> resultTypes;
501 const SmallVector<ElaboratorValue> operands;
502 const DictionaryAttr attributes;
503 const Attribute properties;
506struct SymbolicComputationWithIdentityValue : IdentityValue {
507 SymbolicComputationWithIdentityValue(
508 Type type,
const SymbolicComputationWithIdentityStorage *storage,
510 : IdentityValue(type, storage->loc), storage(storage), idx(idx) {
513 "Use SymbolicComputationWithIdentityStorage for result with index 0.");
516 const SymbolicComputationWithIdentityStorage *storage;
525struct OpaqueExternalStorage : IdentityValue {
526 OpaqueExternalStorage(Type type, Location loc) : IdentityValue(type, loc) {}
531 DenseMap<StringAttr, Region *> handlers;
536struct ContinuationStorage : IdentityValue {
537 ContinuationStorage(SmallVector<Operation *> remainingOps,
539 SmallVector<HandlerFrame> capturedHandlerStack,
540 Type resumeType, Location loc)
541 : IdentityValue(ContinuationType::
get(loc.getContext(), resumeType), loc),
542 remainingOps(std::move(remainingOps)), performResult(performResult),
543 capturedHandlerStack(std::move(capturedHandlerStack)) {}
546 SmallVector<Operation *> remainingOps;
550 SmallVector<HandlerFrame> capturedHandlerStack;
563 template <
typename StorageTy,
typename... Args>
564 StorageTy *internalize(Args &&...args) {
565 static_assert(!std::is_base_of_v<IdentityValue, StorageTy> &&
566 "values with identity must not be internalized");
568 StorageTy storage(std::forward<Args>(args)...);
570 auto existing = getInternSet<StorageTy>().insert_as(
571 HashedStorage<StorageTy>(storage.hashcode), storage);
572 StorageTy *&storagePtr = existing.first->storage;
575 new (allocator.Allocate<StorageTy>()) StorageTy(std::move(storage));
580 template <
typename StorageTy,
typename... Args>
581 StorageTy *create(Args &&...args) {
582 static_assert(std::is_base_of_v<IdentityValue, StorageTy> &&
583 "values with structural equivalence must be internalized");
585 return new (allocator.Allocate<StorageTy>())
586 StorageTy(std::forward<Args>(args)...);
590 template <
typename StorageTy>
591 DenseSet<HashedStorage<StorageTy>, StorageKeyInfo<StorageTy>> &
593 if constexpr (std::is_same_v<StorageTy, ArrayStorage>)
594 return internedArrays;
595 else if constexpr (std::is_same_v<StorageTy, SetStorage>)
597 else if constexpr (std::is_same_v<StorageTy, BagStorage>)
599 else if constexpr (std::is_same_v<StorageTy, SequenceStorage>)
600 return internedSequences;
601 else if constexpr (std::is_same_v<StorageTy, RandomizedSequenceStorage>)
602 return internedRandomizedSequences;
603 else if constexpr (std::is_same_v<StorageTy, InterleavedSequenceStorage>)
604 return internedInterleavedSequences;
605 else if constexpr (std::is_same_v<StorageTy, TupleStorage>)
606 return internedTuples;
607 else if constexpr (std::is_same_v<StorageTy, SymbolicComputationStorage>)
608 return internedSymbolicComputationWithIdentityValues;
610 static_assert(!
sizeof(StorageTy),
611 "no intern set available for this storage type.");
616 llvm::BumpPtrAllocator allocator;
621 DenseSet<HashedStorage<ArrayStorage>, StorageKeyInfo<ArrayStorage>>
623 DenseSet<HashedStorage<SetStorage>, StorageKeyInfo<SetStorage>> internedSets;
624 DenseSet<HashedStorage<BagStorage>, StorageKeyInfo<BagStorage>> internedBags;
625 DenseSet<HashedStorage<SequenceStorage>, StorageKeyInfo<SequenceStorage>>
627 DenseSet<HashedStorage<RandomizedSequenceStorage>,
628 StorageKeyInfo<RandomizedSequenceStorage>>
629 internedRandomizedSequences;
630 DenseSet<HashedStorage<InterleavedSequenceStorage>,
631 StorageKeyInfo<InterleavedSequenceStorage>>
632 internedInterleavedSequences;
633 DenseSet<HashedStorage<TupleStorage>, StorageKeyInfo<TupleStorage>>
635 DenseSet<HashedStorage<SymbolicComputationStorage>,
636 StorageKeyInfo<SymbolicComputationStorage>>
637 internedSymbolicComputationWithIdentityValues;
644static llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
645 const ElaboratorValue &value);
647static void print(TypedAttr val, llvm::raw_ostream &os) {
648 os <<
"<attr " << val <<
">";
651static void print(BagStorage *val, llvm::raw_ostream &os) {
653 llvm::interleaveComma(val->bag, os,
654 [&](
const std::pair<ElaboratorValue, uint64_t> &el) {
655 os << el.first <<
" -> " << el.second;
657 os <<
"} at " << val <<
">";
660static void print(
bool val, llvm::raw_ostream &os) {
661 os <<
"<bool " << (val ?
"true" :
"false") <<
">";
664static void print(
size_t val, llvm::raw_ostream &os) {
665 os <<
"<index " << val <<
">";
668static void print(SequenceStorage *val, llvm::raw_ostream &os) {
669 os <<
"<sequence @" << val->familyName.getValue() <<
"(";
670 llvm::interleaveComma(val->args, os,
671 [&](
const ElaboratorValue &val) { os << val; });
672 os <<
") at " << val <<
">";
675static void print(RandomizedSequenceStorage *val, llvm::raw_ostream &os) {
676 os <<
"<randomized-sequence derived from @"
677 << val->sequence->familyName.getValue() <<
" under context "
678 << val->context <<
"(";
679 llvm::interleaveComma(val->sequence->args, os,
680 [&](
const ElaboratorValue &val) { os << val; });
681 os <<
") at " << val <<
">";
684static void print(InterleavedSequenceStorage *val, llvm::raw_ostream &os) {
685 os <<
"<interleaved-sequence [";
686 llvm::interleaveComma(val->sequences, os,
687 [&](
const ElaboratorValue &val) { os << val; });
688 os <<
"] batch-size " << val->batchSize <<
" at " << val <<
">";
691static void print(ArrayStorage *val, llvm::raw_ostream &os) {
693 llvm::interleaveComma(val->array, os,
694 [&](
const ElaboratorValue &val) { os << val; });
695 os <<
"] at " << val <<
">";
698static void print(SetStorage *val, llvm::raw_ostream &os) {
700 llvm::interleaveComma(val->set, os,
701 [&](
const ElaboratorValue &val) { os << val; });
702 os <<
"} at " << val <<
">";
705static void print(
const VirtualRegisterStorage *val, llvm::raw_ostream &os) {
706 os <<
"<virtual-register " << val <<
" " << val->allowedRegs <<
">";
709static void print(
const UniqueLabelStorage *val, llvm::raw_ostream &os) {
710 os <<
"<unique-label " << val <<
" " << val->name <<
">";
713static void print(
const TupleStorage *val, llvm::raw_ostream &os) {
715 llvm::interleaveComma(val->values, os,
716 [&](
const ElaboratorValue &val) { os << val; });
720static void print(
const MemoryStorage *val, llvm::raw_ostream &os) {
721 os <<
"<memory {" << ElaboratorValue(val->memoryBlock)
722 <<
", size=" << val->size <<
", alignment=" << val->alignment <<
"}>";
725static void print(
const MemoryBlockStorage *val, llvm::raw_ostream &os) {
726 os <<
"<memory-block {"
727 <<
", address-width=" << val->baseAddress.getBitWidth()
728 <<
", base-address=" << val->baseAddress
729 <<
", end-address=" << val->endAddress <<
"}>";
732static void print(
const SymbolicComputationWithIdentityValue *val,
733 llvm::raw_ostream &os) {
734 os <<
"<symbolic-computation-with-identity-value (" << val->storage <<
") at "
738static void print(
const SymbolicComputationWithIdentityStorage *val,
739 llvm::raw_ostream &os) {
740 os <<
"<symbolic-computation-with-identity " << val->name <<
"(";
741 llvm::interleaveComma(val->operands, os,
742 [&](
const ElaboratorValue &val) { os << val; });
743 os <<
") -> " << val->resultTypes <<
" with attributes " << val->attributes
744 <<
" and properties " << val->properties;
748static void print(
const SymbolicComputationStorage *val,
749 llvm::raw_ostream &os) {
750 os <<
"<symbolic-computation " << val->name <<
"(";
751 llvm::interleaveComma(val->operands, os,
752 [&](
const ElaboratorValue &val) { os << val; });
753 os <<
") -> " << val->resultTypes <<
" with attributes " << val->attributes
754 <<
" and properties " << val->properties;
758static void print(
const OpaqueExternalStorage *val, llvm::raw_ostream &os) {
759 os <<
"<opaque-external " << val->type <<
">";
762static void print(
const ContinuationStorage *val, llvm::raw_ostream &os) {
763 os <<
"<continuation with " << val->remainingOps.size() <<
" ops at " << val
768 const ElaboratorValue &value) {
769 std::visit([&](
auto val) {
print(val, os); }, value);
784class AttributeToElaboratorValueConverter {
786 AttributeToElaboratorValueConverter(Internalizer &internalizer)
787 : internalizer(internalizer) {}
790 FailureOr<ElaboratorValue>
convert(Attribute attr) {
791 return llvm::TypeSwitch<Attribute, FailureOr<ElaboratorValue>>(attr)
792 .Case<IntegerAttr, SetAttr, TupleAttr>(
793 [&](
auto attr) {
return convert(attr); })
794 .Case<TypedAttr>([&](
auto typedAttr) -> FailureOr<ElaboratorValue> {
795 return ElaboratorValue(typedAttr);
798 [&](Attribute) -> FailureOr<ElaboratorValue> {
return failure(); });
802 FailureOr<ElaboratorValue>
convert(IntegerAttr attr) {
803 if (attr.getType().isSignlessInteger(1))
804 return ElaboratorValue(
bool(attr.getInt()));
805 if (isa<IndexType>(attr.getType()))
806 return ElaboratorValue(
size_t(attr.getInt()));
807 return ElaboratorValue(attr);
810 FailureOr<ElaboratorValue>
convert(SetAttr setAttr) {
811 SetVector<ElaboratorValue> set;
812 for (
auto element : *setAttr.getElements()) {
813 auto converted =
convert(element);
814 if (failed(converted))
816 set.insert(*converted);
819 internalizer.internalize<SetStorage>(std::move(set), setAttr.getType());
821 storage->attrCache = setAttr;
822 return ElaboratorValue(storage);
825 FailureOr<ElaboratorValue>
convert(TupleAttr tupleAttr) {
826 SmallVector<ElaboratorValue> values;
827 for (
auto element : tupleAttr.getElements()) {
828 auto converted =
convert(element);
829 if (failed(converted))
831 values.push_back(*converted);
833 auto *storage = internalizer.internalize<TupleStorage>(std::move(values));
835 storage->attrCache = tupleAttr;
836 return ElaboratorValue(storage);
839 Internalizer &internalizer;
845class ElaboratorValueToAttributeConverter {
847 ElaboratorValueToAttributeConverter(MLIRContext *context)
848 : context(context) {}
852 TypedAttr
convert(
const ElaboratorValue &value) {
854 [&](
auto val) -> TypedAttr {
855 if constexpr (std::is_base_of_v<CachableStorage,
856 std::remove_pointer_t<
857 std::decay_t<
decltype(value)>>>) {
859 return val->attrCache;
867 TypedAttr visit(TypedAttr val) {
return val; }
869 TypedAttr visit(
bool val) {
870 return IntegerAttr::get(IntegerType::get(context, 1), val);
873 TypedAttr visit(
size_t val) {
874 return IntegerAttr::get(IndexType::get(context), val);
877 TypedAttr visit(SetStorage *val) {
878 DenseSet<TypedAttr> elements;
879 for (
auto element : val->set) {
880 auto converted =
convert(element);
883 auto typedAttr = dyn_cast<TypedAttr>(converted);
886 elements.insert(typedAttr);
888 return SetAttr::get(cast<SetType>(val->type), &elements);
891 TypedAttr visit(TupleStorage *val) {
892 SmallVector<TypedAttr> elements;
893 for (
auto element : val->values) {
894 auto converted =
convert(element);
897 auto typedAttr = dyn_cast<TypedAttr>(converted);
900 elements.push_back(typedAttr);
902 return TupleAttr::get(context, elements);
908#define VISIT_UNSUPPORTED(STORAGETYPE) \
910 TypedAttr visit(STORAGETYPE *val) { return {}; }
927#undef VISIT_UNSUPPORTED
929 MLIRContext *context;
942 SharedState(MLIRContext *ctxt, SymbolTable &table)
943 : ctxt(ctxt), table(table) {}
948 Internalizer internalizer;
953 explicit TestState(
unsigned seed) : rng(RngScope(seed)) {}
960 std::pair<ContextResourceAttrInterface, ContextResourceAttrInterface>,
971 Materializer(OpBuilder builder, TestState &testState,
972 SharedState &sharedState,
973 SmallVector<ElaboratorValue> &blockArgs)
974 : builder(builder), rootBlock(builder.getBlock()), testState(testState),
975 sharedState(sharedState), blockArgs(blockArgs),
976 attrConverter(builder.getContext()) {}
980 Value materialize(ElaboratorValue val, Location loc,
981 function_ref<InFlightDiagnostic()> emitError) {
982 auto iter = materializedValues.find(val);
983 if (iter != materializedValues.end())
986 LLVM_DEBUG(llvm::dbgs() <<
"Materializing " << val);
988 if (
auto res = tryMaterializeAsConstant(val, loc))
993 Value res = std::visit(
995 if constexpr (std::is_base_of_v<IdentityValue,
996 std::remove_pointer_t<
997 std::decay_t<
decltype(value)>>>) {
998 if (identityValueRoot.contains(value)) {
1000 bool &materialized =
1001 static_cast<IdentityValue *
>(value)->alreadyMaterialized;
1002 assert(!materialized &&
"must not already be materialized");
1003 materialized =
true;
1006 return visit(value, loc, emitError);
1009 Value arg = builder.getBlock()->addArgument(value->type, loc);
1010 blockArgs.push_back(val);
1011 blockArgTypes.push_back(arg.getType());
1012 materializedValues[val] = arg;
1016 return visit(value, loc, emitError);
1020 LLVM_DEBUG(llvm::dbgs() <<
" to\n" << res <<
"\n\n");
1025 bool isInPlace(Operation *op)
const {
1026 return builder.getBlock()->getParent() == op->getParentRegion();
1035 LogicalResult materialize(Operation *op,
1036 DenseMap<Value, ElaboratorValue> &state) {
1042 if (op->getNumRegions() > 0)
1043 return op->emitOpError(
"ops with nested regions must be elaborated away");
1051 for (
auto res : op->getResults())
1052 if (!res.use_empty())
1053 return op->emitOpError(
1054 "ops with results that have uses are not supported");
1056 if (isInPlace(op)) {
1059 deleteOpsUntil([&](
auto iter) {
return &*iter == op; });
1061 if (builder.getInsertionPoint() == builder.getBlock()->end())
1062 return op->emitError(
"operation did not occur after the current "
1063 "materializer insertion point");
1065 LLVM_DEBUG(llvm::dbgs() <<
"Modifying in-place: " << *op <<
"\n\n");
1067 LLVM_DEBUG(llvm::dbgs() <<
"Materializing a clone of " << *op <<
"\n\n");
1068 op = builder.clone(*op);
1069 builder.setInsertionPoint(op);
1072 for (
auto &operand : op->getOpOperands()) {
1073 auto emitError = [&]() {
1074 auto diag = op->emitError();
1075 diag.attachNote(op->getLoc())
1076 <<
"while materializing value for operand#"
1077 << operand.getOperandNumber();
1081 auto elabVal = state.at(operand.get());
1082 Value val = materialize(elabVal, op->getLoc(), emitError);
1086 state[val] = elabVal;
1090 builder.setInsertionPointAfter(op);
1097 deleteOpsUntil([](
auto iter) {
return false; });
1099 for (
auto *op :
llvm::reverse(toDelete))
1106 void registerIdentityValue(IdentityValue *val) {
1107 identityValueRoot.insert(val);
1110 ArrayRef<Type> getBlockArgTypes()
const {
return blockArgTypes; }
1112 void map(ElaboratorValue eval, Value val) { materializedValues[eval] = val; }
1114 OpBuilder &getBuilder() {
return builder; }
1116 template <
typename OpTy,
typename... Args>
1117 OpTy create(Location location, Args &&...args) {
1118 return OpTy::create(builder, location, std::forward<Args>(args)...);
1122 Value tryMaterializeAsConstant(ElaboratorValue val, Location loc) {
1123 if (
auto attr = attrConverter.convert(val)) {
1128 OpBuilder::InsertionGuard guard(builder);
1129 if (builder.getBlock() != rootBlock)
1130 builder.setInsertionPointToStart(rootBlock);
1131 Value res = ConstantOp::create(builder, loc, attr);
1132 materializedValues[val] = res;
1139 SequenceOp elaborateSequence(
const RandomizedSequenceStorage *
seq,
1140 SmallVector<ElaboratorValue> &elabArgs);
1142 void deleteOpsUntil(function_ref<
bool(Block::iterator)> stop) {
1143 auto ip = builder.getInsertionPoint();
1144 while (ip != builder.getBlock()->end() && !stop(ip)) {
1145 LLVM_DEBUG(llvm::dbgs() <<
"Marking to be deleted: " << *ip <<
"\n\n");
1146 toDelete.push_back(&*ip);
1148 builder.setInsertionPointAfter(&*ip);
1149 ip = builder.getInsertionPoint();
1153 Value visit(TypedAttr val, Location loc,
1154 function_ref<InFlightDiagnostic()> emitError) {
1158 Value visit(
size_t val, Location loc,
1159 function_ref<InFlightDiagnostic()> emitError) {
1163 Value visit(
bool val, Location loc,
1164 function_ref<InFlightDiagnostic()> emitError) {
1168 Value visit(ArrayStorage *val, Location loc,
1169 function_ref<InFlightDiagnostic()> emitError) {
1170 SmallVector<Value> elements;
1171 elements.reserve(val->array.size());
1172 for (
auto el : val->array) {
1173 auto materialized = materialize(el, loc, emitError);
1177 elements.push_back(materialized);
1180 Value res = ArrayCreateOp::create(builder, loc, val->type, elements);
1181 materializedValues[val] = res;
1185 Value visit(SetStorage *val, Location loc,
1186 function_ref<InFlightDiagnostic()> emitError) {
1187 SmallVector<Value> elements;
1188 elements.reserve(val->set.size());
1189 for (
auto el : val->set) {
1190 auto materialized = materialize(el, loc, emitError);
1194 elements.push_back(materialized);
1197 auto res = SetCreateOp::create(builder, loc, val->type, elements);
1198 materializedValues[val] = res;
1202 Value visit(BagStorage *val, Location loc,
1203 function_ref<InFlightDiagnostic()> emitError) {
1204 SmallVector<Value> values, weights;
1205 values.reserve(val->bag.size());
1206 weights.reserve(val->bag.size());
1207 for (
auto [val, weight] : val->bag) {
1208 auto materializedVal = materialize(val, loc, emitError);
1209 auto materializedWeight = materialize(weight, loc, emitError);
1210 if (!materializedVal || !materializedWeight)
1213 values.push_back(materializedVal);
1214 weights.push_back(materializedWeight);
1217 auto res = BagCreateOp::create(builder, loc, val->type, values, weights);
1218 materializedValues[val] = res;
1222 Value visit(MemoryBlockStorage *val, Location loc,
1223 function_ref<InFlightDiagnostic()> emitError) {
1224 auto intType = builder.getIntegerType(val->baseAddress.getBitWidth());
1225 Value res = MemoryBlockDeclareOp::create(
1226 builder, val->loc, val->type,
1227 IntegerAttr::get(intType, val->baseAddress),
1228 IntegerAttr::get(intType, val->endAddress));
1229 materializedValues[val] = res;
1233 Value visit(MemoryStorage *val, Location loc,
1234 function_ref<InFlightDiagnostic()> emitError) {
1235 auto memBlock = materialize(val->memoryBlock, val->loc, emitError);
1236 auto memSize = materialize(val->size, val->loc, emitError);
1237 auto memAlign = materialize(val->alignment, val->loc, emitError);
1238 if (!(memBlock && memSize && memAlign))
1242 MemoryAllocOp::create(builder, val->loc, memBlock, memSize, memAlign);
1243 materializedValues[val] = res;
1247 Value visit(SequenceStorage *val, Location loc,
1248 function_ref<InFlightDiagnostic()> emitError) {
1249 emitError() <<
"materializing a non-randomized sequence not supported yet";
1253 Value visit(RandomizedSequenceStorage *val, Location loc,
1254 function_ref<InFlightDiagnostic()> emitError) {
1260 SmallVector<ElaboratorValue> elabArgs;
1263 SequenceOp seqOp = elaborateSequence(val, elabArgs);
1269 SmallVector<Value> args;
1270 SmallVector<Type> argTypes;
1271 for (
auto arg : elabArgs) {
1272 Value materialized = materialize(arg, val->loc, emitError);
1276 args.push_back(materialized);
1277 argTypes.push_back(materialized.getType());
1280 Value res = GetSequenceOp::create(
1281 builder, val->loc, SequenceType::get(builder.getContext(), argTypes),
1282 seqOp.getSymName());
1287 res = SubstituteSequenceOp::create(builder, val->loc, res, args);
1289 res = RandomizeSequenceOp::create(builder, val->loc, res);
1291 materializedValues[val] = res;
1295 Value visit(InterleavedSequenceStorage *val, Location loc,
1296 function_ref<InFlightDiagnostic()> emitError) {
1297 SmallVector<Value> sequences;
1298 for (
auto seqVal : val->sequences) {
1299 Value materialized = materialize(seqVal, loc, emitError);
1303 sequences.push_back(materialized);
1306 if (sequences.size() == 1)
1307 return sequences[0];
1310 InterleaveSequencesOp::create(builder, loc, sequences, val->batchSize);
1311 materializedValues[val] = res;
1315 Value visit(VirtualRegisterStorage *val, Location loc,
1316 function_ref<InFlightDiagnostic()> emitError) {
1317 Value res = VirtualRegisterOp::create(builder, val->loc, val->allowedRegs);
1318 materializedValues[val] = res;
1322 Value visit(UniqueLabelStorage *val, Location loc,
1323 function_ref<InFlightDiagnostic()> emitError) {
1324 auto materialized = materialize(val->name, val->loc, emitError);
1327 Value res = LabelUniqueDeclOp::create(builder, val->loc, materialized);
1328 materializedValues[val] = res;
1332 Value visit(TupleStorage *val, Location loc,
1333 function_ref<InFlightDiagnostic()> emitError) {
1334 SmallVector<Value> materialized;
1335 materialized.reserve(val->values.size());
1336 for (
auto v : val->values)
1337 materialized.push_back(materialize(v, loc, emitError));
1338 Value res = TupleCreateOp::create(builder, loc, materialized);
1339 materializedValues[val] = res;
1343 Value visit(ContinuationStorage *val, Location loc,
1344 function_ref<InFlightDiagnostic()> emitError) {
1345 emitError() <<
"continuation cannot be materialized into IR";
1349 Value visit(SymbolicComputationWithIdentityValue *val, Location loc,
1350 function_ref<InFlightDiagnostic()> emitError) {
1351 auto *noConstStorage =
1352 const_cast<SymbolicComputationWithIdentityStorage *
>(val->storage);
1353 auto res0 = materialize(noConstStorage, loc, emitError);
1357 auto *op = res0.getDefiningOp();
1358 auto res = op->getResults()[val->idx];
1359 materializedValues[val] = res;
1363 Value visit(SymbolicComputationWithIdentityStorage *val, Location loc,
1364 function_ref<InFlightDiagnostic()> emitError) {
1365 SmallVector<Value> operands;
1366 for (
auto operand : val->operands) {
1367 auto materialized = materialize(operand, val->loc, emitError);
1371 operands.push_back(materialized);
1374 OperationState state(val->loc, val->name);
1375 state.addTypes(val->resultTypes);
1376 state.attributes = val->attributes;
1377 state.propertiesAttr = val->properties;
1378 state.addOperands(operands);
1379 auto *op = builder.create(state);
1381 materializedValues[val] = op->getResult(0);
1382 return op->getResult(0);
1385 Value visit(SymbolicComputationStorage *val, Location loc,
1386 function_ref<InFlightDiagnostic()> emitError) {
1387 SmallVector<Value> operands;
1388 for (
auto operand : val->operands) {
1389 auto materialized = materialize(operand, loc, emitError);
1393 operands.push_back(materialized);
1396 OperationState state(loc, val->name);
1397 state.addTypes(val->resultTypes);
1398 state.attributes = val->attributes;
1399 state.propertiesAttr = val->properties;
1400 state.addOperands(operands);
1401 auto *op = builder.create(state);
1403 for (
auto res : op->getResults())
1404 materializedValues[val] = res;
1406 return op->getResult(0);
1409 Value visit(OpaqueExternalStorage *val, Location loc,
1410 function_ref<InFlightDiagnostic()> emitError) {
1415 emitError() <<
"cannot materialize opaque external value";
1425 DenseMap<ElaboratorValue, Value> materializedValues;
1435 SmallVector<Operation *> toDelete;
1437 TestState &testState;
1438 SharedState &sharedState;
1443 SmallVector<ElaboratorValue> &blockArgs;
1444 SmallVector<Type> blockArgTypes;
1449 DenseSet<IdentityValue *> identityValueRoot;
1452 ElaboratorValueToAttributeConverter attrConverter;
1461enum class DeletionKind {
1470class Elaborator :
public RTGOpVisitor<Elaborator, FailureOr<DeletionKind>> {
1473 using RTGBase::visitOp;
1475 Elaborator(SharedState &sharedState, TestState &testState,
1476 Materializer &materializer,
1477 ContextResourceAttrInterface currentContext = {})
1478 : sharedState(sharedState), testState(testState),
1479 materializer(materializer), currentContext(currentContext),
1480 attrConverter(sharedState.internalizer),
1481 elabValConverter(sharedState.ctxt) {}
1483 template <
typename ValueTy>
1484 inline ValueTy
get(Value val)
const {
1485 return std::get<ValueTy>(state.at(val));
1490 return visitOpGeneric(op);
1494 if (op->getNumRegions() == 0)
1495 return visitOpGeneric(op);
1511 auto *newOp = op->cloneWithoutRegions();
1512 materializer.getBuilder().insert(newOp);
1519 materializer.getBuilder().setInsertionPoint(newOp);
1520 for (
auto &operand : newOp->getOpOperands()) {
1521 auto emitError = [&]() {
1522 auto diag = newOp->emitError();
1523 diag.attachNote(newOp->getLoc())
1524 <<
"while materializing operand#" << operand.getOperandNumber()
1525 <<
" of external region op";
1528 auto elabVal = state.at(operand.get());
1529 Value val = materializer.materialize(elabVal, newOp->getLoc(), emitError);
1534 materializer.getBuilder().setInsertionPointAfter(newOp);
1540 auto mapOpaque = [&](Value oldVal, Value newVal) {
1541 auto *storage = sharedState.internalizer.create<OpaqueExternalStorage>(
1542 oldVal.getType(), oldVal.getLoc());
1543 state[oldVal] = storage;
1544 materializer.map(storage, newVal);
1547 for (
auto [oldRegion, newRegion] :
1548 llvm::zip(op->getRegions(), newOp->getRegions())) {
1549 if (oldRegion.empty())
1555 Block &oldBlock = oldRegion.front();
1556 Block &newBlock = newRegion.emplaceBlock();
1557 for (
auto oldArg : oldBlock.getArguments()) {
1558 Value newArg = newBlock.addArgument(oldArg.getType(), oldArg.getLoc());
1559 mapOpaque(oldArg, newArg);
1563 OpBuilder::InsertionGuard guard(materializer.getBuilder());
1564 materializer.getBuilder().setInsertionPoint(&newBlock,
1566 SmallVector<ElaboratorValue> unused;
1570 if (failed(elaborate(oldRegion, {},
1578 for (
auto [oldRes, newRes] :
1579 llvm::zip(op->getResults(), newOp->getResults()))
1580 mapOpaque(oldRes, newRes);
1582 return DeletionKind::Delete;
1585 FailureOr<DeletionKind> visitOp(GetSequenceOp op) {
1586 SmallVector<ElaboratorValue> replacements;
1587 state[op.getResult()] =
1588 sharedState.internalizer.internalize<SequenceStorage>(
1589 op.getSequenceAttr().getAttr(), std::move(replacements));
1590 return DeletionKind::Delete;
1593 FailureOr<DeletionKind> visitOp(SubstituteSequenceOp op) {
1594 if (isSymbolic(state.at(op.getSequence())))
1595 return visitOpGeneric(op);
1597 auto *
seq = get<SequenceStorage *>(op.getSequence());
1599 SmallVector<ElaboratorValue> replacements(
seq->args);
1600 for (
auto replacement : op.getReplacements())
1601 replacements.push_back(state.at(replacement));
1603 state[op.getResult()] =
1604 sharedState.internalizer.internalize<SequenceStorage>(
1605 seq->familyName, std::move(replacements));
1607 return DeletionKind::Delete;
1610 FailureOr<DeletionKind> visitOp(RandomizeSequenceOp op) {
1611 auto *
seq = get<SequenceStorage *>(op.getSequence());
1612 auto *randomizedSeq =
1613 sharedState.internalizer.create<RandomizedSequenceStorage>(
1614 currentContext,
seq, op.getLoc());
1615 materializer.registerIdentityValue(randomizedSeq);
1616 state[op.getResult()] =
1617 sharedState.internalizer.internalize<InterleavedSequenceStorage>(
1619 return DeletionKind::Delete;
1622 FailureOr<DeletionKind> visitOp(InterleaveSequencesOp op) {
1623 SmallVector<ElaboratorValue> sequences;
1624 for (
auto seq : op.getSequences())
1625 sequences.push_back(state.at(
seq));
1627 state[op.getResult()] =
1628 sharedState.internalizer.internalize<InterleavedSequenceStorage>(
1629 std::move(sequences), op.getBatchSize());
1630 return DeletionKind::Delete;
1634 LogicalResult isValidContext(ElaboratorValue value, Operation *op)
const {
1635 if (std::holds_alternative<RandomizedSequenceStorage *>(value)) {
1636 auto *
seq = std::get<RandomizedSequenceStorage *>(value);
1637 if (
seq->context != currentContext) {
1638 auto err = op->emitError(
"attempting to place sequence derived from ")
1639 <<
seq->sequence->familyName.getValue() <<
" under context "
1641 <<
", but it was previously randomized for context ";
1643 err <<
seq->context;
1651 auto *interVal = std::get<InterleavedSequenceStorage *>(value);
1652 for (
auto val : interVal->sequences)
1653 if (failed(isValidContext(val, op)))
1658 FailureOr<DeletionKind> visitOp(EmbedSequenceOp op) {
1659 auto *seqVal = get<InterleavedSequenceStorage *>(op.getSequence());
1660 if (failed(isValidContext(seqVal, op)))
1663 return DeletionKind::Keep;
1666 FailureOr<DeletionKind> visitOp(SetCreateOp op) {
1667 SetVector<ElaboratorValue> set;
1668 for (
auto val : op.getElements())
1669 set.insert(state.at(val));
1671 state[op.getSet()] = sharedState.internalizer.internalize<SetStorage>(
1672 std::move(set), op.getSet().getType());
1673 return DeletionKind::Delete;
1676 FailureOr<DeletionKind> visitOp(SetSelectRandomOp op) {
1677 auto set = get<SetStorage *>(op.getSet())->set;
1680 return op->emitError(
"cannot select from an empty set");
1682 size_t selected = testState.rng.getUniformlyInRange(0, set.size() - 1);
1683 state[op.getResult()] = set[selected];
1684 return DeletionKind::Delete;
1687 FailureOr<DeletionKind> visitOp(SetDifferenceOp op) {
1688 auto original = get<SetStorage *>(op.getOriginal())->set;
1689 auto diff = get<SetStorage *>(op.getDiff())->set;
1691 SetVector<ElaboratorValue> result(original);
1692 result.set_subtract(diff);
1694 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1695 std::move(result), op.getResult().getType());
1696 return DeletionKind::Delete;
1699 FailureOr<DeletionKind> visitOp(SetUnionOp op) {
1700 SetVector<ElaboratorValue> result;
1701 for (
auto set : op.getSets())
1702 result.set_union(
get<SetStorage *>(set)->set);
1704 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1705 std::move(result), op.getType());
1706 return DeletionKind::Delete;
1709 FailureOr<DeletionKind> visitOp(SetSizeOp op) {
1710 auto size = get<SetStorage *>(op.getSet())->set.size();
1711 state[op.getResult()] = size;
1712 return DeletionKind::Delete;
1718 FailureOr<DeletionKind> visitOp(SetCartesianProductOp op) {
1719 SetVector<ElaboratorValue> result;
1720 SmallVector<SmallVector<ElaboratorValue>> tuples;
1721 tuples.push_back({});
1723 for (
auto input : op.getInputs()) {
1724 auto &set = get<SetStorage *>(input)->set;
1726 SetVector<ElaboratorValue>
empty;
1727 state[op.getResult()] =
1728 sharedState.internalizer.internalize<SetStorage>(std::move(
empty),
1730 return DeletionKind::Delete;
1733 for (
unsigned i = 0, e = tuples.size(); i < e; ++i) {
1734 for (
auto setEl : set.getArrayRef().drop_back()) {
1735 tuples.push_back(tuples[i]);
1736 tuples.back().push_back(setEl);
1738 tuples[i].push_back(set.back());
1742 for (
auto &tup : tuples)
1744 sharedState.internalizer.internalize<TupleStorage>(std::move(tup)));
1746 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1747 std::move(result), op.getType());
1748 return DeletionKind::Delete;
1751 FailureOr<DeletionKind> visitOp(SetConvertToBagOp op) {
1752 auto set = get<SetStorage *>(op.getInput())->set;
1754 for (
auto val : set)
1755 bag.insert({val, 1});
1756 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1757 std::move(bag), op.getType());
1758 return DeletionKind::Delete;
1761 FailureOr<DeletionKind> visitOp(BagCreateOp op) {
1763 for (
auto [val, multiple] :
1764 llvm::zip(op.getElements(), op.getMultiples())) {
1768 bag[state.at(val)] += get<size_t>(multiple);
1771 state[op.getBag()] = sharedState.internalizer.internalize<BagStorage>(
1772 std::move(bag), op.getType());
1773 return DeletionKind::Delete;
1776 FailureOr<DeletionKind> visitOp(BagSelectRandomOp op) {
1777 auto bag = get<BagStorage *>(op.getBag())->bag;
1780 return op->emitError(
"cannot select from an empty bag");
1782 SmallVector<std::pair<ElaboratorValue, uint32_t>> prefixSum;
1783 prefixSum.reserve(bag.size());
1784 uint32_t accumulator = 0;
1785 for (
auto [val, weight] : bag) {
1786 accumulator += weight;
1787 prefixSum.push_back({val, accumulator});
1790 auto idx = testState.rng.getUniformlyInRange(0, accumulator - 1);
1791 auto *iter = llvm::upper_bound(
1793 [](uint32_t a,
const std::pair<ElaboratorValue, uint32_t> &b) {
1794 return a < b.second;
1797 state[op.getResult()] = iter->first;
1798 return DeletionKind::Delete;
1801 FailureOr<DeletionKind> visitOp(BagDifferenceOp op) {
1802 auto original = get<BagStorage *>(op.getOriginal())->bag;
1803 auto diff = get<BagStorage *>(op.getDiff())->bag;
1806 for (
const auto &el : original) {
1807 if (!diff.contains(el.first)) {
1815 auto toDiff = diff.lookup(el.first);
1816 if (el.second <= toDiff)
1819 result.insert({el.first, el.second - toDiff});
1822 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1823 std::move(result), op.getType());
1824 return DeletionKind::Delete;
1827 FailureOr<DeletionKind> visitOp(BagUnionOp op) {
1829 for (
auto bag : op.getBags()) {
1830 auto val = get<BagStorage *>(bag)->bag;
1831 for (
auto [el, multiple] : val)
1832 result[el] += multiple;
1835 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1836 std::move(result), op.getType());
1837 return DeletionKind::Delete;
1840 FailureOr<DeletionKind> visitOp(BagUniqueSizeOp op) {
1841 auto size = get<BagStorage *>(op.getBag())->bag.size();
1842 state[op.getResult()] = size;
1843 return DeletionKind::Delete;
1846 FailureOr<DeletionKind> visitOp(BagConvertToSetOp op) {
1847 auto bag = get<BagStorage *>(op.getInput())->bag;
1848 SetVector<ElaboratorValue> set;
1849 for (
auto [k, v] : bag)
1851 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1852 std::move(set), op.getType());
1853 return DeletionKind::Delete;
1856 FailureOr<DeletionKind> visitOp(VirtualRegisterOp op) {
1857 auto *val = sharedState.internalizer.create<VirtualRegisterStorage>(
1858 op.getAllowedRegsAttr(), op.getType(), op.getLoc());
1859 state[op.getResult()] = val;
1860 materializer.registerIdentityValue(val);
1861 return DeletionKind::Delete;
1864 FailureOr<DeletionKind> visitOp(ArrayCreateOp op) {
1865 SmallVector<ElaboratorValue> array;
1866 array.reserve(op.getElements().size());
1867 for (
auto val : op.getElements())
1868 array.emplace_back(state.at(val));
1870 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1871 op.getResult().getType(), std::move(array));
1872 return DeletionKind::Delete;
1875 FailureOr<DeletionKind> visitOp(StringToASCIIArrayOp op) {
1876 auto opaque = state.at(op.getString());
1877 if (isSymbolic(opaque))
1878 return visitOpGeneric(op);
1880 auto strAttr = dyn_cast<StringAttr>(std::get<TypedAttr>(opaque));
1882 return op->emitError(
"expected a string attribute");
1884 auto i8Ty = IntegerType::get(op.getContext(), 8);
1885 SmallVector<ElaboratorValue> array;
1886 array.reserve(strAttr.getValue().size());
1887 for (
unsigned char c : strAttr.getValue())
1888 array.push_back(ElaboratorValue(IntegerAttr::
get(i8Ty, c)));
1890 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1891 op.getResult().getType(), std::move(array));
1892 return DeletionKind::Delete;
1895 FailureOr<DeletionKind> visitOp(ArrayExtractOp op) {
1896 auto array = get<ArrayStorage *>(op.getArray())->array;
1897 size_t idx = get<size_t>(op.getIndex());
1899 if (array.size() <= idx)
1900 return op->emitError(
"invalid to access index ")
1901 << idx <<
" of an array with " << array.size() <<
" elements";
1903 state[op.getResult()] = array[idx];
1904 return DeletionKind::Delete;
1907 FailureOr<DeletionKind> visitOp(ArrayInjectOp op) {
1908 auto arrayOpaque = state.at(op.getArray());
1909 auto idxOpaque = state.at(op.getIndex());
1910 if (isSymbolic(arrayOpaque) || isSymbolic(idxOpaque))
1911 return visitOpGeneric(op);
1913 auto array = std::get<ArrayStorage *>(arrayOpaque)->array;
1914 size_t idx = std::get<size_t>(idxOpaque);
1916 if (array.size() <= idx)
1917 return op->emitError(
"invalid to access index ")
1918 << idx <<
" of an array with " << array.size() <<
" elements";
1920 array[idx] = state.at(op.getValue());
1921 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1922 op.getResult().getType(), std::move(array));
1923 return DeletionKind::Delete;
1926 FailureOr<DeletionKind> visitOp(ArrayAppendOp op) {
1927 auto array = std::get<ArrayStorage *>(state.at(op.getArray()))->array;
1928 array.push_back(state.at(op.getElement()));
1929 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1930 op.getResult().getType(), std::move(array));
1931 return DeletionKind::Delete;
1934 FailureOr<DeletionKind> visitOp(ArraySizeOp op) {
1935 auto array = get<ArrayStorage *>(op.getArray())->array;
1936 state[op.getResult()] = array.size();
1937 return DeletionKind::Delete;
1940 FailureOr<DeletionKind> visitOp(LabelUniqueDeclOp op) {
1941 auto *val = sharedState.internalizer.create<UniqueLabelStorage>(
1942 state.at(op.getNamePrefix()), op.getLoc());
1943 state[op.getLabel()] = val;
1944 materializer.registerIdentityValue(val);
1945 return DeletionKind::Delete;
1948 FailureOr<DeletionKind> visitOp(RandomScopeOp op) {
1949 auto getNestedRng = [&]() -> RngScope {
1951 return RngScope(op.getSeed()->getZExtValue());
1952 return testState.rng.getNested();
1955 RngScope nestedRng = getNestedRng();
1956 std::swap(testState.rng, nestedRng);
1964 SmallVector<ElaboratorValue> yieldedVals;
1965 if (failed(elaborate(op.getBodyRegion(), {},
false,
1970 std::swap(testState.rng, nestedRng);
1973 for (
auto [res, out] :
llvm::zip(op.getResults(), yieldedVals))
1976 return DeletionKind::Delete;
1979 FailureOr<DeletionKind> visitOp(RandomNumberInRangeOp op) {
1980 size_t lower = get<size_t>(op.getLowerBound());
1981 size_t upper = get<size_t>(op.getUpperBound());
1983 return op->emitError(
"cannot select a number from an empty range");
1985 state[op.getResult()] =
1986 size_t(testState.rng.getUniformlyInRange(lower, upper));
1987 return DeletionKind::Delete;
1990 FailureOr<DeletionKind> visitOp(IntToImmediateOp op) {
1991 size_t input = get<size_t>(op.getInput());
1992 auto width = op.getType().getWidth();
1993 auto emitError = [&]() {
return op->emitError(); };
1994 if (input > APInt::getAllOnes(width).getZExtValue())
1995 return emitError() <<
"cannot represent " << input <<
" with " << width
1998 state[op.getResult()] =
1999 IntegerAttr::get(IntegerType::get(op.getContext(), width), input);
2000 return DeletionKind::Delete;
2003 FailureOr<DeletionKind> visitOp(OnContextOp op) {
2004 ContextResourceAttrInterface from = currentContext,
2005 to = cast<ContextResourceAttrInterface>(
2006 get<TypedAttr>(op.getContext()));
2007 if (!currentContext)
2008 from = DefaultContextAttr::get(op->getContext(), to.getType());
2010 auto emitError = [&]() {
2011 auto diag = op.emitError();
2012 diag.attachNote(op.getLoc())
2013 <<
"while materializing value for context switching for " << op;
2018 Value seqVal = materializer.materialize(
2019 get<SequenceStorage *>(op.getSequence()), op.getLoc(), emitError);
2024 materializer.create<RandomizeSequenceOp>(op.getLoc(), seqVal);
2025 materializer.create<EmbedSequenceOp>(op.getLoc(), randSeqVal);
2026 return DeletionKind::Delete;
2032 auto *iter = testState.contextSwitches.find({from, to});
2035 if (iter == testState.contextSwitches.end())
2036 iter = testState.contextSwitches.find(
2037 {from, AnyContextAttr::get(op->getContext(), to.getType())});
2040 if (iter == testState.contextSwitches.end())
2041 iter = testState.contextSwitches.find(
2042 {AnyContextAttr::get(op->getContext(), from.getType()), to});
2045 if (iter == testState.contextSwitches.end())
2046 iter = testState.contextSwitches.find(
2047 {AnyContextAttr::get(op->getContext(), from.getType()),
2048 AnyContextAttr::get(op->getContext(), to.getType())});
2054 if (iter == testState.contextSwitches.end())
2055 return op->emitError(
"no context transition registered to switch from ")
2056 << from <<
" to " << to;
2058 auto familyName = iter->second->familyName;
2059 SmallVector<ElaboratorValue> args{from, to,
2060 get<SequenceStorage *>(op.getSequence())};
2061 auto *
seq = sharedState.internalizer.internalize<SequenceStorage>(
2062 familyName, std::move(args));
2063 auto *randSeq = sharedState.internalizer.create<RandomizedSequenceStorage>(
2064 to,
seq, op.getLoc());
2065 materializer.registerIdentityValue(randSeq);
2066 Value seqVal = materializer.materialize(randSeq, op.getLoc(), emitError);
2070 materializer.create<EmbedSequenceOp>(op.getLoc(), seqVal);
2071 return DeletionKind::Delete;
2074 FailureOr<DeletionKind> visitOp(ContextSwitchOp op) {
2075 testState.contextSwitches[{op.getFromAttr(), op.getToAttr()}] =
2076 get<SequenceStorage *>(op.getSequence());
2077 return DeletionKind::Delete;
2080 FailureOr<DeletionKind> visitOp(MemoryBlockDeclareOp op) {
2081 auto *val = sharedState.internalizer.create<MemoryBlockStorage>(
2082 op.getBaseAddress(), op.getEndAddress(), op.getType(), op.getLoc());
2083 state[op.getResult()] = val;
2084 materializer.registerIdentityValue(val);
2085 return DeletionKind::Delete;
2088 FailureOr<DeletionKind> visitOp(MemoryAllocOp op) {
2089 size_t size = get<size_t>(op.getSize());
2090 size_t alignment = get<size_t>(op.getAlignment());
2091 auto *memBlock = get<MemoryBlockStorage *>(op.getMemoryBlock());
2092 auto *val = sharedState.internalizer.create<MemoryStorage>(
2093 memBlock, size, alignment, op.getLoc());
2094 state[op.getResult()] = val;
2095 materializer.registerIdentityValue(val);
2096 return DeletionKind::Delete;
2099 FailureOr<DeletionKind> visitOp(MemorySizeOp op) {
2100 auto *memory = get<MemoryStorage *>(op.getMemory());
2101 state[op.getResult()] = memory->size;
2102 return DeletionKind::Delete;
2108 FailureOr<DeletionKind> visitOp(EffectOp op) {
return DeletionKind::Keep; }
2110 FailureOr<DeletionKind> visitOp(WithHandlersOp op) {
2113 for (
auto [effectAttr, handlerRegion] :
2114 llvm::zip(op.getEffects(), op.getHandlerRegions()))
2115 frame.handlers[cast<FlatSymbolRefAttr>(effectAttr).getAttr()] =
2117 handlerStack.push_back(std::move(frame));
2119 SmallVector<ElaboratorValue> unused;
2120 if (failed(elaborate(op.getBody(), {},
false, unused)))
2124 if (!handlerStack.empty())
2125 handlerStack.pop_back();
2127 return DeletionKind::Delete;
2130 FailureOr<DeletionKind> visitOp(PerformOp op) {
2131 StringAttr effectName = op.getEffectAttr().getAttr();
2134 Region *handlerRegion =
nullptr;
2135 for (
int i = (
int)handlerStack.size() - 1; i >= 0; --i) {
2136 auto it = handlerStack[i].handlers.find(effectName);
2137 if (it != handlerStack[i].handlers.end()) {
2138 handlerRegion = it->second;
2143 return op->emitError(
"no handler for effect @") << effectName.getValue();
2146 SmallVector<Operation *> remaining;
2147 auto *block = op->getBlock();
2148 for (
auto it = std::next(op->getIterator());
2149 it != block->end() && !it->hasTrait<OpTrait::IsTerminator>(); ++it)
2150 remaining.push_back(&*it);
2153 Value performResultSSAVal =
2154 op.getNumResults() > 0 ? Value(op.getResult()) : Value();
2155 Type resumeType = performResultSSAVal ? performResultSSAVal.getType()
2156 : NoneType::get(op.getContext());
2158 auto *cont = sharedState.internalizer.create<ContinuationStorage>(
2159 std::move(remaining), performResultSSAVal,
2160 SmallVector<HandlerFrame>(handlerStack), resumeType, op.getLoc());
2163 SmallVector<ElaboratorValue> handlerArgs;
2164 for (
auto operand : op.getOperands())
2165 handlerArgs.push_back(state.at(operand));
2166 handlerArgs.push_back(cont);
2170 HandlerFrame savedFrame = handlerStack.back();
2171 handlerStack.pop_back();
2173 SmallVector<ElaboratorValue> unused;
2174 if (failed(elaborate(*handlerRegion, handlerArgs,
2178 handlerStack.push_back(std::move(savedFrame));
2180 return DeletionKind::StopElaboration;
2183 FailureOr<DeletionKind> visitOp(ResumeOp op) {
2185 std::get<ContinuationStorage *>(state.at(op.getContinuation()));
2188 if (cont->performResult && op.getValue())
2189 state[cont->performResult] = state.at(op.getValue());
2192 auto savedStack = std::move(handlerStack);
2193 handlerStack = cont->capturedHandlerStack;
2195 for (
auto *contOp : cont->remainingOps) {
2196 auto result = dispatchOpVisitor(contOp);
2197 if (failed(result)) {
2198 handlerStack = std::move(savedStack);
2201 if (*result == DeletionKind::StopElaboration)
2203 if (*result == DeletionKind::Keep)
2204 if (failed(materializer.materialize(contOp, state))) {
2205 handlerStack = std::move(savedStack);
2209 llvm::dbgs() <<
"Elaborated continuation op " << *contOp <<
" to\n[";
2210 llvm::interleaveComma(contOp->getResults(), llvm::dbgs(),
2212 if (state.contains(res))
2213 llvm::dbgs() << state.at(res);
2215 llvm::dbgs() <<
"unknown";
2217 llvm::dbgs() <<
"]\n\n";
2221 handlerStack = std::move(savedStack);
2222 return DeletionKind::Delete;
2227 FailureOr<DeletionKind> visitOp(TupleCreateOp op) {
2228 SmallVector<ElaboratorValue> values;
2229 values.reserve(op.getElements().size());
2230 for (
auto el : op.getElements())
2231 values.push_back(state.at(el));
2233 state[op.getResult()] =
2234 sharedState.internalizer.internalize<TupleStorage>(std::move(values));
2235 return DeletionKind::Delete;
2238 FailureOr<DeletionKind> visitOp(TupleExtractOp op) {
2239 auto *tuple = get<TupleStorage *>(op.getTuple());
2240 state[op.getResult()] = tuple->values[op.getIndex().getZExtValue()];
2241 return DeletionKind::Delete;
2244 FailureOr<DeletionKind> visitOp(scf::IfOp op) {
2245 bool cond = get<bool>(op.getCondition());
2246 auto &toElaborate = cond ? op.getThenRegion() : op.getElseRegion();
2247 if (toElaborate.empty())
2248 return DeletionKind::Delete;
2254 SmallVector<ElaboratorValue> yieldedVals;
2256 elaborate(toElaborate, {},
false, yieldedVals)))
2260 for (
auto [res, out] :
llvm::zip(op.getResults(), yieldedVals))
2263 return DeletionKind::Delete;
2266 FailureOr<DeletionKind> visitOp(scf::ForOp op) {
2267 if (!(std::holds_alternative<size_t>(state.at(op.getLowerBound())) &&
2268 std::holds_alternative<size_t>(state.at(op.getStep())) &&
2269 std::holds_alternative<size_t>(state.at(op.getUpperBound()))))
2270 return op->emitOpError(
"can only elaborate index type iterator");
2272 auto lowerBound = get<size_t>(op.getLowerBound());
2273 auto step = get<size_t>(op.getStep());
2274 auto upperBound = get<size_t>(op.getUpperBound());
2280 state[op.getInductionVar()] = lowerBound;
2281 for (
auto [iterArg, initArg] :
2282 llvm::zip(op.getRegionIterArgs(), op.getInitArgs()))
2283 state[iterArg] = state.at(initArg);
2286 SmallVector<ElaboratorValue> yieldedVals;
2287 for (
size_t i = lowerBound; i < upperBound; i += step) {
2288 yieldedVals.clear();
2289 if (failed(elaborate(op.getBodyRegion(), {},
false,
2295 state[op.getInductionVar()] = i + step;
2296 for (
auto [iterArg, prevIterArg] :
2297 llvm::zip(op.getRegionIterArgs(), yieldedVals))
2298 state[iterArg] = prevIterArg;
2302 for (
auto [res, iterArg] :
2303 llvm::zip(op->getResults(), op.getRegionIterArgs()))
2304 state[res] = state.at(iterArg);
2306 return DeletionKind::Delete;
2309 FailureOr<DeletionKind> visitOp(arith::AddIOp op) {
2310 if (!isa<IndexType>(op.getType()))
2311 return visitOpGeneric(op);
2313 size_t lhs = get<size_t>(op.getLhs());
2314 size_t rhs = get<size_t>(op.getRhs());
2315 state[op.getResult()] = lhs + rhs;
2316 return DeletionKind::Delete;
2319 FailureOr<DeletionKind> visitOp(arith::AndIOp op) {
2320 if (!op.getType().isSignlessInteger(1))
2321 return visitOpGeneric(op);
2323 bool lhs = get<bool>(op.getLhs());
2324 bool rhs = get<bool>(op.getRhs());
2325 state[op.getResult()] = lhs && rhs;
2326 return DeletionKind::Delete;
2329 FailureOr<DeletionKind> visitOp(arith::XOrIOp op) {
2330 if (!op.getType().isSignlessInteger(1))
2331 return visitOpGeneric(op);
2333 bool lhs = get<bool>(op.getLhs());
2334 bool rhs = get<bool>(op.getRhs());
2335 state[op.getResult()] = lhs != rhs;
2336 return DeletionKind::Delete;
2339 FailureOr<DeletionKind> visitOp(arith::OrIOp op) {
2340 if (!op.getType().isSignlessInteger(1))
2341 return visitOpGeneric(op);
2343 bool lhs = get<bool>(op.getLhs());
2344 bool rhs = get<bool>(op.getRhs());
2345 state[op.getResult()] = lhs || rhs;
2346 return DeletionKind::Delete;
2349 FailureOr<DeletionKind> visitOp(arith::SelectOp op) {
2350 auto condOpaque = state.at(op.getCondition());
2351 if (isSymbolic(condOpaque))
2352 return visitOpGeneric(op);
2354 bool cond = std::get<bool>(condOpaque);
2355 auto trueVal = state.at(op.getTrueValue());
2356 auto falseVal = state.at(op.getFalseValue());
2357 state[op.getResult()] = cond ? trueVal : falseVal;
2358 return DeletionKind::Delete;
2361 FailureOr<DeletionKind> visitOp(index::AddOp op) {
2362 size_t lhs = get<size_t>(op.getLhs());
2363 size_t rhs = get<size_t>(op.getRhs());
2364 state[op.getResult()] = lhs + rhs;
2365 return DeletionKind::Delete;
2368 FailureOr<DeletionKind> visitOp(index::SubOp op) {
2369 size_t lhs = get<size_t>(op.getLhs());
2370 size_t rhs = get<size_t>(op.getRhs());
2371 state[op.getResult()] = lhs - rhs;
2372 return DeletionKind::Delete;
2375 FailureOr<DeletionKind> visitOp(index::MulOp op) {
2376 size_t lhs = get<size_t>(op.getLhs());
2377 size_t rhs = get<size_t>(op.getRhs());
2378 state[op.getResult()] = lhs * rhs;
2379 return DeletionKind::Delete;
2382 FailureOr<DeletionKind> visitOp(index::DivUOp op) {
2383 size_t lhs = get<size_t>(op.getLhs());
2384 size_t rhs = get<size_t>(op.getRhs());
2387 return op->emitOpError(
"attempted division by zero");
2389 state[op.getResult()] = lhs / rhs;
2390 return DeletionKind::Delete;
2393 FailureOr<DeletionKind> visitOp(index::CeilDivUOp op) {
2394 size_t lhs = get<size_t>(op.getLhs());
2395 size_t rhs = get<size_t>(op.getRhs());
2398 return op->emitOpError(
"attempted division by zero");
2401 state[op.getResult()] = (lhs + rhs - 1) / rhs;
2403 state[op.getResult()] = 1 + ((lhs - 1) / rhs);
2405 return DeletionKind::Delete;
2408 FailureOr<DeletionKind> visitOp(index::RemUOp op) {
2409 size_t lhs = get<size_t>(op.getLhs());
2410 size_t rhs = get<size_t>(op.getRhs());
2413 return op->emitOpError(
"attempted division by zero");
2415 state[op.getResult()] = lhs % rhs;
2416 return DeletionKind::Delete;
2419 FailureOr<DeletionKind> visitOp(index::AndOp op) {
2420 size_t lhs = get<size_t>(op.getLhs());
2421 size_t rhs = get<size_t>(op.getRhs());
2422 state[op.getResult()] = lhs & rhs;
2423 return DeletionKind::Delete;
2426 FailureOr<DeletionKind> visitOp(index::OrOp op) {
2427 size_t lhs = get<size_t>(op.getLhs());
2428 size_t rhs = get<size_t>(op.getRhs());
2429 state[op.getResult()] = lhs | rhs;
2430 return DeletionKind::Delete;
2433 FailureOr<DeletionKind> visitOp(index::XOrOp op) {
2434 size_t lhs = get<size_t>(op.getLhs());
2435 size_t rhs = get<size_t>(op.getRhs());
2436 state[op.getResult()] = lhs ^ rhs;
2437 return DeletionKind::Delete;
2440 FailureOr<DeletionKind> visitOp(index::ShlOp op) {
2441 size_t lhs = get<size_t>(op.getLhs());
2442 size_t rhs = get<size_t>(op.getRhs());
2443 state[op.getResult()] = lhs << rhs;
2444 return DeletionKind::Delete;
2447 FailureOr<DeletionKind> visitOp(index::ShrUOp op) {
2448 size_t lhs = get<size_t>(op.getLhs());
2449 size_t rhs = get<size_t>(op.getRhs());
2450 state[op.getResult()] = lhs >> rhs;
2451 return DeletionKind::Delete;
2454 FailureOr<DeletionKind> visitOp(index::MaxUOp op) {
2455 size_t lhs = get<size_t>(op.getLhs());
2456 size_t rhs = get<size_t>(op.getRhs());
2457 state[op.getResult()] = std::max(lhs, rhs);
2458 return DeletionKind::Delete;
2461 FailureOr<DeletionKind> visitOp(index::MinUOp op) {
2462 size_t lhs = get<size_t>(op.getLhs());
2463 size_t rhs = get<size_t>(op.getRhs());
2464 state[op.getResult()] = std::min(lhs, rhs);
2465 return DeletionKind::Delete;
2468 FailureOr<DeletionKind> visitOp(index::CmpOp op) {
2469 size_t lhs = get<size_t>(op.getLhs());
2470 size_t rhs = get<size_t>(op.getRhs());
2472 switch (op.getPred()) {
2473 case index::IndexCmpPredicate::EQ:
2474 result = lhs == rhs;
2476 case index::IndexCmpPredicate::NE:
2477 result = lhs != rhs;
2479 case index::IndexCmpPredicate::ULT:
2482 case index::IndexCmpPredicate::ULE:
2483 result = lhs <= rhs;
2485 case index::IndexCmpPredicate::UGT:
2488 case index::IndexCmpPredicate::UGE:
2489 result = lhs >= rhs;
2492 return op->emitOpError(
"elaboration not supported");
2494 state[op.getResult()] = result;
2495 return DeletionKind::Delete;
2498 bool isSymbolic(ElaboratorValue val) {
2499 return std::holds_alternative<SymbolicComputationWithIdentityValue *>(
2501 std::holds_alternative<SymbolicComputationWithIdentityStorage *>(
2503 std::holds_alternative<SymbolicComputationStorage *>(val) ||
2504 std::holds_alternative<OpaqueExternalStorage *>(val);
2507 bool isSymbolic(Operation *op) {
2508 return llvm::any_of(op->getOperands(), [&](
auto operand) {
2509 auto val = state.at(operand);
2510 return isSymbolic(val);
2517 bool attemptConcreteCase(Operation *op) {
2518 if (op->getNumResults() == 0)
2521 SmallVector<Attribute> operands;
2522 for (
auto operand : op->getOperands()) {
2523 auto evalValue = state[operand];
2524 auto attr = elabValConverter.convert(evalValue);
2525 operands.push_back(attr);
2528 SmallVector<OpFoldResult> results;
2529 if (failed(op->fold(operands, results)))
2532 if (results.size() != op->getNumResults())
2535 for (
auto [res, val] :
llvm::zip(results, op->getResults())) {
2536 auto attr = llvm::dyn_cast_or_null<TypedAttr>(res.dyn_cast<Attribute>());
2540 if (attr.getType() != val.getType())
2544 auto converted = attrConverter.convert(attr);
2545 if (succeeded(converted)) {
2546 state[val] = *converted;
2555 FailureOr<DeletionKind> visitOpGeneric(Operation *op) {
2556 if (op->getNumResults() == 0)
2557 return DeletionKind::Keep;
2559 if (attemptConcreteCase(op))
2560 return DeletionKind::Delete;
2562 if (mlir::isMemoryEffectFree(op)) {
2563 if (op->getNumResults() != 1)
2564 return op->emitOpError(
2565 "symbolic elaboration of memory-effect-free operations with "
2566 "multiple results not supported");
2568 state[op->getResult(0)] =
2569 sharedState.internalizer.internalize<SymbolicComputationStorage>(
2571 return DeletionKind::Delete;
2582 bool onlyAlloc = mlir::hasSingleEffect<mlir::MemoryEffects::Allocate>(op);
2583 onlyAlloc |= isa<ValidateOp>(op);
2585 auto *validationVal =
2586 sharedState.internalizer.create<SymbolicComputationWithIdentityStorage>(
2588 materializer.registerIdentityValue(validationVal);
2589 state[op->getResult(0)] = validationVal;
2591 for (
auto [i, res] :
llvm::enumerate(op->getResults())) {
2595 sharedState.internalizer.create<SymbolicComputationWithIdentityValue>(
2596 res.getType(), validationVal, i);
2598 materializer.registerIdentityValue(val);
2600 return onlyAlloc ? DeletionKind::Delete : DeletionKind::Keep;
2603 bool supportsSymbolicValuesNonGenerically(Operation *op) {
2604 return isa<SubstituteSequenceOp, ArrayCreateOp, ArrayInjectOp,
2605 TupleCreateOp, arith::SelectOp>(op);
2608 FailureOr<DeletionKind> dispatchOpVisitor(Operation *op) {
2609 if (isSymbolic(op) && !supportsSymbolicValuesNonGenerically(op))
2610 return visitOpGeneric(op);
2612 return TypeSwitch<Operation *, FailureOr<DeletionKind>>(op)
2615 arith::AddIOp, arith::XOrIOp, arith::AndIOp, arith::OrIOp,
2618 index::AddOp, index::SubOp, index::MulOp, index::DivUOp,
2619 index::CeilDivUOp, index::RemUOp, index::AndOp, index::OrOp,
2620 index::XOrOp, index::ShlOp, index::ShrUOp, index::MaxUOp,
2621 index::MinUOp, index::CmpOp,
2623 scf::IfOp, scf::ForOp>([&](
auto op) {
return visitOp(op); })
2624 .Default([&](Operation *op) {
return RTGBase::dispatchOpVisitor(op); });
2628 LogicalResult elaborate(Region ®ion,
2629 ArrayRef<ElaboratorValue> regionArguments,
2630 bool keepTerminator,
2631 SmallVector<ElaboratorValue> &terminatorOperands) {
2632 if (region.getBlocks().size() > 1)
2633 return region.getParentOp()->emitOpError(
2634 "regions with more than one block are not supported");
2643 SmallVector<std::pair<Value, std::optional<ElaboratorValue>>> savedArgs;
2644 savedArgs.reserve(region.getNumArguments());
2645 for (
auto arg : region.getArguments()) {
2646 auto it = state.find(arg);
2647 if (it != state.end())
2648 savedArgs.emplace_back(arg, it->second);
2650 savedArgs.emplace_back(arg, std::nullopt);
2652 llvm::scope_exit restoreArgs([&] {
2653 for (
auto &[arg, prev] : savedArgs) {
2654 if (prev.has_value())
2661 for (
auto [arg, elabArg] :
2662 llvm::zip(region.getArguments(), regionArguments))
2663 state[arg] = elabArg;
2665 Block *block = ®ion.front();
2666 auto iter = keepTerminator ? *block : block->without_terminator();
2667 for (
auto &op : iter) {
2668 auto result = dispatchOpVisitor(&op);
2674 if (*result == DeletionKind::StopElaboration)
2677 if (*result == DeletionKind::Keep)
2678 if (failed(materializer.materialize(&op, state)))
2682 llvm::dbgs() <<
"Elaborated " << op <<
" to\n[";
2684 llvm::interleaveComma(op.getResults(), llvm::dbgs(), [&](
auto res) {
2685 if (state.contains(res))
2686 llvm::dbgs() << state.at(res);
2688 llvm::dbgs() <<
"unknown";
2691 llvm::dbgs() <<
"]\n\n";
2695 if (!block->empty() && block->back().hasTrait<OpTrait::IsTerminator>()) {
2696 auto *terminator = block->getTerminator();
2697 for (
auto val : terminator->getOperands())
2698 terminatorOperands.push_back(state.at(val));
2700 if (!keepTerminator && materializer.isInPlace(terminator))
2701 terminator->erase();
2709 SharedState &sharedState;
2712 TestState &testState;
2716 Materializer &materializer;
2719 DenseMap<Value, ElaboratorValue> state;
2722 ContextResourceAttrInterface currentContext;
2725 AttributeToElaboratorValueConverter attrConverter;
2728 ElaboratorValueToAttributeConverter elabValConverter;
2731 SmallVector<HandlerFrame> handlerStack;
2736Materializer::elaborateSequence(
const RandomizedSequenceStorage *
seq,
2737 SmallVector<ElaboratorValue> &elabArgs) {
2739 sharedState.table.lookup<SequenceOp>(
seq->sequence->familyName);
2742 OpBuilder builder(familyOp);
2743 auto seqOp = builder.cloneWithoutRegions(familyOp);
2744 auto name = sharedState.names.newName(
seq->sequence->familyName.getValue());
2745 seqOp.setSymName(name);
2746 seqOp.getBodyRegion().emplaceBlock();
2747 sharedState.table.insert(seqOp);
2748 assert(seqOp.getSymName() == name &&
"should not have been renamed");
2750 LLVM_DEBUG(llvm::dbgs() <<
"\n=== Elaborating sequence family @"
2751 << familyOp.getSymName() <<
" into @"
2752 << seqOp.getSymName() <<
" under context "
2753 <<
seq->context <<
"\n\n");
2755 Materializer materializer(OpBuilder::atBlockBegin(seqOp.getBody()), testState,
2756 sharedState, elabArgs);
2757 Elaborator elaborator(sharedState, testState, materializer,
seq->context);
2758 SmallVector<ElaboratorValue> yieldedVals;
2759 if (failed(elaborator.elaborate(familyOp.getBodyRegion(),
seq->sequence->args,
2760 false, yieldedVals)))
2763 seqOp.setSequenceType(
2764 SequenceType::get(builder.getContext(), materializer.getBlockArgTypes()));
2765 materializer.finalize();
2775struct ElaborationPass
2776 :
public rtg::impl::ElaborationPassBase<ElaborationPass> {
2779 void runOnOperation()
override;
2780 void matchTestsAgainstTargets(SymbolTable &table);
2781 LogicalResult elaborateModule(ModuleOp moduleOp, SymbolTable &table);
2785void ElaborationPass::runOnOperation() {
2786 auto moduleOp = getOperation();
2787 SymbolTable table(moduleOp);
2789 matchTestsAgainstTargets(table);
2791 if (failed(elaborateModule(moduleOp, table)))
2792 return signalPassFailure();
2795void ElaborationPass::matchTestsAgainstTargets(SymbolTable &table) {
2796 auto moduleOp = getOperation();
2798 for (
auto test :
llvm::make_early_inc_range(moduleOp.getOps<TestOp>())) {
2799 if (test.getTargetAttr())
2802 bool matched =
false;
2804 for (
auto target : moduleOp.getOps<TargetOp>()) {
2808 bool isSubtype =
true;
2809 auto testEntries = test.getTargetType().getEntries();
2810 auto targetEntries = target.getTarget().getEntries();
2814 size_t targetIdx = 0;
2815 for (
auto testEntry : testEntries) {
2817 while (targetIdx < targetEntries.size() &&
2818 targetEntries[targetIdx].name.getValue() <
2819 testEntry.name.getValue())
2823 if (targetIdx >= targetEntries.size() ||
2824 targetEntries[targetIdx].name != testEntry.name ||
2825 targetEntries[targetIdx].type != testEntry.type) {
2834 IRRewriter rewriter(test);
2836 auto newTest = cast<TestOp>(test->clone());
2837 newTest.setSymName(test.getSymName().str() +
"_" +
2838 target.getSymName().str());
2842 newTest.setTargetAttr(target.getSymNameAttr());
2844 table.insert(newTest, rewriter.getInsertionPoint());
2848 if (matched || deleteUnmatchedTests)
2854 return isa<MemoryBlockType, ContextResourceTypeInterface>(type);
2857LogicalResult ElaborationPass::elaborateModule(ModuleOp moduleOp,
2858 SymbolTable &table) {
2859 SharedState state(moduleOp.getContext(), table);
2862 state.names.add(moduleOp);
2864 struct TargetElabResult {
2865 TargetElabResult(DictType targetType, uint32_t seed)
2866 : targetType(targetType), testState(seed) {}
2868 DictType targetType;
2869 SmallVector<ElaboratorValue> yields;
2870 TestState testState;
2874 DenseMap<StringAttr, TargetElabResult> targetMap;
2875 for (
auto targetOp : moduleOp.getOps<TargetOp>()) {
2876 LLVM_DEBUG(llvm::dbgs() <<
"=== Elaborating target @"
2877 << targetOp.getSymName() <<
"\n\n");
2879 auto [it, inserted] = targetMap.try_emplace(targetOp.getSymNameAttr(),
2880 targetOp.getTarget(), seed);
2881 auto &result = it->second;
2883 SmallVector<ElaboratorValue> blockArgs;
2884 Materializer targetMaterializer(OpBuilder::atBlockBegin(targetOp.getBody()),
2885 result.testState, state, blockArgs);
2886 Elaborator targetElaborator(state, result.testState, targetMaterializer);
2889 if (failed(targetElaborator.elaborate(targetOp.getBodyRegion(), {},
2894 targetMaterializer.finalize();
2899 for (
auto testOp : moduleOp.getOps<TestOp>()) {
2903 if (!testOp.getTargetAttr())
2906 LLVM_DEBUG(llvm::dbgs()
2907 <<
"\n=== Elaborating test @" << testOp.getTemplateName()
2908 <<
" for target @" << *testOp.getTarget() <<
"\n\n");
2911 auto &targetResult = targetMap.at(testOp.getTargetAttr());
2912 TestState testState(seed);
2913 testState.contextSwitches = targetResult.testState.contextSwitches;
2914 testState.name = testOp.getSymNameAttr();
2916 SmallVector<ElaboratorValue> filteredYields;
2918 for (
auto [entry, yield] :
2919 llvm::zip(targetResult.targetType.getEntries(), targetResult.yields)) {
2920 if (i >= testOp.getTargetType().getEntries().size())
2923 if (entry.name == testOp.getTargetType().getEntries()[i].name) {
2924 filteredYields.push_back(yield);
2931 SmallVector<ElaboratorValue> blockArgs;
2932 Materializer materializer(OpBuilder::atBlockBegin(testOp.getBody()),
2933 testState, state, blockArgs);
2935 for (
auto [arg, val] :
2936 llvm::zip(testOp.getBody()->getArguments(), filteredYields))
2938 materializer.map(val, arg);
2940 Elaborator elaborator(state, testState, materializer);
2941 SmallVector<ElaboratorValue> ignore;
2942 if (failed(elaborator.elaborate(testOp.getBodyRegion(), filteredYields,
2946 materializer.finalize();
assert(baseType &&"element must be base type")
static bool onlyLegalToMaterializeInTarget(Type type)
#define VISIT_UNSUPPORTED(STORAGETYPE)
static void print(TypedAttr val, llvm::raw_ostream &os)
static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
static Location getLoc(DefSlot slot)
static InstancePath empty
A namespace that is used to store existing names and generate new names in some scope within the IR.
This helps visit TypeOp nodes.
ResultType visitExternalOp(Operation *op, ExtraArgs... args)
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
This callback is invoked on any operations that are not handled by the concrete visitor.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
static llvm::hash_code hash_value(const ModulePort &port)
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
@ Delete
Erase the matched ops.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
size_t hash_combine(size_t h1, size_t h2)
C++'s stdlib doesn't have a hash_combine function. This is a simple one.
static bool isEqual(const bool &lhs, const bool &rhs)
static unsigned getHashValue(const bool &val)