21#include "mlir/Dialect/Arith/IR/Arith.h"
22#include "mlir/Dialect/Func/IR/FuncOps.h"
23#include "mlir/Dialect/Index/IR/IndexDialect.h"
24#include "mlir/Dialect/Index/IR/IndexOps.h"
25#include "mlir/Dialect/SCF/IR/SCF.h"
26#include "mlir/IR/IRMapping.h"
27#include "mlir/IR/PatternMatch.h"
28#include "llvm/ADT/DenseMapInfoVariant.h"
29#include "llvm/ADT/ScopeExit.h"
30#include "llvm/ADT/SmallPtrSet.h"
31#include "llvm/Support/Debug.h"
38#define GEN_PASS_DEF_ELABORATIONPASS
39#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc"
47#define DEBUG_TYPE "rtg-elaboration"
53 RngScope(
const RngScope &) =
default;
54 RngScope &operator=(
const RngScope &) =
default;
56 RngScope(RngScope &&) =
default;
57 RngScope &operator=(RngScope &&) =
default;
59 explicit RngScope(uint32_t seed) : rng(seed) {}
71 uint32_t getUniformlyInRange(uint32_t a, uint32_t b) {
72 const uint32_t diff = b - a + 1;
80 std::numeric_limits<uint32_t>::max() >> (32 - llvm::Log2_32_Ceil(diff));
89 RngScope getNested() {
return RngScope(rng()); }
103struct SequenceStorage;
104struct RandomizedSequenceStorage;
105struct InterleavedSequenceStorage;
107struct VirtualRegisterStorage;
108struct UniqueLabelStorage;
111struct MemoryBlockStorage;
112struct SymbolicComputationWithIdentityStorage;
113struct SymbolicComputationWithIdentityValue;
114struct SymbolicComputationStorage;
115struct OpaqueExternalStorage;
117struct ContinuationStorage;
120using ElaboratorValue =
121 std::variant<TypedAttr, BagStorage *, bool, size_t, SequenceStorage *,
122 RandomizedSequenceStorage *, InterleavedSequenceStorage *,
123 SetStorage *, VirtualRegisterStorage *, UniqueLabelStorage *,
124 ArrayStorage *, TupleStorage *, MemoryStorage *,
125 MemoryBlockStorage *, SymbolicComputationWithIdentityStorage *,
126 SymbolicComputationWithIdentityValue *,
127 SymbolicComputationStorage *, OpaqueExternalStorage *,
128 MutStorage *, ContinuationStorage *>;
129using LexicalEnvironment = DenseMap<Value, ElaboratorValue>;
132llvm::hash_code
hash_value(
const ElaboratorValue &val) {
134 [&val](
const auto &alternative) {
137 return llvm::hash_combine(val.index(), alternative);
150 static bool isEqual(
const bool &lhs,
const bool &rhs) {
return lhs == rhs; }
165template <
typename StorageTy>
166struct HashedStorage {
167 HashedStorage(
unsigned hashcode = 0, StorageTy *storage =
nullptr)
168 : hashcode(hashcode), storage(storage) {}
178template <
typename StorageTy>
179struct StorageKeyInfo {
180 static inline unsigned getHashValue(
const HashedStorage<StorageTy> &key) {
183 static inline unsigned getHashValue(
const StorageTy &key) {
187 static inline bool isEqual(
const HashedStorage<StorageTy> &lhs,
188 const HashedStorage<StorageTy> &rhs) {
189 return lhs.storage == rhs.storage;
191 static inline bool isEqual(
const StorageTy &lhs,
192 const HashedStorage<StorageTy> &rhs) {
196 return lhs.isEqual(rhs.storage);
206struct CachableStorage {
211struct SetStorage : CachableStorage {
212 static unsigned computeHash(
const SetVector<ElaboratorValue> &set,
214 llvm::hash_code setHash = 0;
215 for (
auto el : set) {
220 setHash = setHash ^ llvm::hash_combine(el);
222 return llvm::hash_combine(type, setHash);
225 SetStorage(SetVector<ElaboratorValue> &&set, Type type)
226 : hashcode(computeHash(set, type)), set(std::move(set)), type(type) {}
228 bool isEqual(
const SetStorage *other)
const {
233 bool allContained =
true;
235 allContained &= other->set.contains(el);
237 return hashcode == other->hashcode && set.size() == other->set.size() &&
238 allContained && type == other->type;
242 const unsigned hashcode;
245 const SetVector<ElaboratorValue> set;
254 BagStorage(MapVector<ElaboratorValue, uint64_t> &&bag, Type type)
256 type, llvm::hash_combine_range(bag.begin(), bag.
end()))),
257 bag(std::move(bag)), type(type) {}
259 bool isEqual(
const BagStorage *other)
const {
260 return hashcode == other->hashcode && llvm::equal(bag, other->bag) &&
265 const unsigned hashcode;
269 const MapVector<ElaboratorValue, uint64_t> bag;
277struct SequenceStorage {
278 SequenceStorage(StringAttr familyName, SmallVector<ElaboratorValue> &&args)
280 familyName, llvm::hash_combine_range(args.begin(), args.
end()))),
281 familyName(familyName), args(std::move(args)) {}
283 bool isEqual(
const SequenceStorage *other)
const {
284 return hashcode == other->hashcode && familyName == other->familyName &&
289 const unsigned hashcode;
292 const StringAttr familyName;
295 const SmallVector<ElaboratorValue> args;
299struct InterleavedSequenceStorage {
300 InterleavedSequenceStorage(SmallVector<ElaboratorValue> &&sequences,
302 : sequences(std::move(sequences)), batchSize(batchSize),
304 llvm::hash_combine_range(sequences.begin(), sequences.
end()),
307 explicit InterleavedSequenceStorage(RandomizedSequenceStorage *sequence)
308 : sequences(SmallVector<ElaboratorValue>(1, sequence)), batchSize(1),
310 llvm::hash_combine_range(sequences.begin(), sequences.
end()),
313 bool isEqual(
const InterleavedSequenceStorage *other)
const {
314 return hashcode == other->hashcode && sequences == other->sequences &&
315 batchSize == other->batchSize;
318 const SmallVector<ElaboratorValue> sequences;
320 const uint32_t batchSize;
323 const unsigned hashcode;
328 ArrayStorage(Type type, SmallVector<ElaboratorValue> &&array)
330 type, llvm::hash_combine_range(array.begin(), array.
end()))),
331 type(type), array(array) {}
333 bool isEqual(
const ArrayStorage *other)
const {
334 return hashcode == other->hashcode && type == other->type &&
335 array == other->array;
339 const unsigned hashcode;
346 const SmallVector<ElaboratorValue> array;
350struct TupleStorage : CachableStorage {
351 TupleStorage(SmallVector<ElaboratorValue> &&values)
352 : hashcode(llvm::hash_combine_range(values.begin(), values.
end())),
353 values(std::move(values)) {}
355 bool isEqual(
const TupleStorage *other)
const {
356 return hashcode == other->hashcode && values == other->values;
360 const unsigned hashcode;
362 const SmallVector<ElaboratorValue> values;
365struct SymbolicComputationStorage {
366 SymbolicComputationStorage(
const DenseMap<Value, ElaboratorValue> &state,
368 : name(op->
getName()), resultTypes(op->getResultTypes()),
369 operands(llvm::map_range(op->getOperands(),
370 [&](Value v) {
return state.lookup(v); })),
371 attributes(op->getAttrDictionary()),
372 properties(op->getPropertiesAsAttribute()),
373 hashcode(llvm::hash_combine(name, llvm::hash_combine_range(resultTypes),
374 llvm::hash_combine_range(operands),
375 attributes, op->hashProperties())) {}
377 bool isEqual(
const SymbolicComputationStorage *other)
const {
378 return hashcode == other->hashcode && name == other->name &&
379 resultTypes == other->resultTypes && operands == other->operands &&
380 attributes == other->attributes && properties == other->properties;
383 const OperationName name;
384 const SmallVector<Type> resultTypes;
385 const SmallVector<ElaboratorValue> operands;
386 const DictionaryAttr attributes;
387 const Attribute properties;
388 const unsigned hashcode;
399struct IdentityValue {
401 IdentityValue(Type type, Location loc) : type(type), loc(loc) {}
414 bool alreadyMaterialized =
false;
423struct VirtualRegisterStorage : IdentityValue {
424 VirtualRegisterStorage(VirtualRegisterConfigAttr allowedRegs, Type type,
426 : IdentityValue(type, loc), allowedRegs(allowedRegs) {}
433 const VirtualRegisterConfigAttr allowedRegs;
436struct UniqueLabelStorage : IdentityValue {
437 UniqueLabelStorage(
const ElaboratorValue &name, Location loc)
438 : IdentityValue(LabelType::
get(loc->getContext()), loc), name(name) {}
444 const ElaboratorValue name;
448struct MemoryBlockStorage : IdentityValue {
449 MemoryBlockStorage(
const APInt &baseAddress,
const APInt &endAddress,
450 Type type, Location loc)
451 : IdentityValue(type, loc), baseAddress(baseAddress),
452 endAddress(endAddress) {}
457 const APInt baseAddress;
460 const APInt endAddress;
464struct MemoryStorage : IdentityValue {
465 MemoryStorage(MemoryBlockStorage *memoryBlock,
size_t size,
size_t alignment,
467 : IdentityValue(MemoryType::
get(memoryBlock->type.getContext(),
470 memoryBlock(memoryBlock), size(size), alignment(alignment) {}
472 MemoryBlockStorage *memoryBlock;
474 const size_t alignment;
478struct RandomizedSequenceStorage : IdentityValue {
479 RandomizedSequenceStorage(ContextResourceAttrInterface context,
480 SequenceStorage *sequence, Location loc)
482 RandomizedSequenceType::
get(sequence->familyName.getContext()),
484 context(context), sequence(sequence) {}
487 const ContextResourceAttrInterface context;
489 const SequenceStorage *sequence;
493struct SymbolicComputationWithIdentityStorage : IdentityValue {
494 SymbolicComputationWithIdentityStorage(
495 const DenseMap<Value, ElaboratorValue> &state, Operation *op)
496 : IdentityValue(op->getResult(0).getType(), op->
getLoc()),
497 name(op->
getName()), resultTypes(op->getResultTypes()),
498 operands(llvm::map_range(op->getOperands(),
499 [&](Value v) {
return state.lookup(v); })),
500 attributes(op->getAttrDictionary()),
501 properties(op->getPropertiesAsAttribute()) {}
503 const OperationName name;
504 const SmallVector<Type> resultTypes;
505 const SmallVector<ElaboratorValue> operands;
506 const DictionaryAttr attributes;
507 const Attribute properties;
510struct SymbolicComputationWithIdentityValue : IdentityValue {
511 SymbolicComputationWithIdentityValue(
512 Type type,
const SymbolicComputationWithIdentityStorage *storage,
514 : IdentityValue(type, storage->loc), storage(storage), idx(idx) {
517 "Use SymbolicComputationWithIdentityStorage for result with index 0.");
520 const SymbolicComputationWithIdentityStorage *storage;
529struct OpaqueExternalStorage : IdentityValue {
530 OpaqueExternalStorage(Type type, Location loc) : IdentityValue(type, loc) {}
533struct ContinuationBoundary {
534 uint64_t sequence = 0;
535 SmallVector<uint64_t> externalRegions;
536 uint64_t elaborator = 0;
541struct MutStorage : IdentityValue {
542 MutStorage(Type type, Location loc, ContinuationBoundary boundary)
543 : IdentityValue(type, loc), boundary(std::move(boundary)) {}
546 ContinuationBoundary boundary;
551 uint64_t promptToken = 0;
552 DenseMap<StringAttr, Region *> handlers;
557 Block *block =
nullptr;
558 Operation *nextOp =
nullptr;
559 bool keepTerminator =
false;
560 bool eraseTerminator =
true;
562 LexicalEnvironment environment;
572 LogicalResult step(Elaborator &elaborator,
573 ArrayRef<ElaboratorValue> yieldedValues);
578 size_t nextIteration = 0;
579 size_t upperBound = 0;
582 bool completed =
false;
583 LexicalEnvironment loopEnvironment;
585 LogicalResult step(Elaborator &elaborator,
586 ArrayRef<ElaboratorValue> yieldedValues);
589struct RandomScopeFrame {
594 RandomScopeFrame(RandomScopeOp op, RngScope outerRng,
bool replay)
595 : op(op), outerRng(std::move(outerRng)), replay(replay) {}
598struct WithHandlersFrame {
600 uint64_t promptId = 0;
604struct HandlerClauseFrame {};
606struct FunctionCallFrame {
611struct ExternalOpFrame {
612 Operation *originalOp =
nullptr;
613 Operation *materializedOp =
nullptr;
614 size_t nextRegion = 0;
615 uint64_t activeBoundary = 0;
617 LexicalEnvironment parentEnvironment;
618 OpBuilder::InsertPoint parentInsertionPoint;
623 SmallVector<HandlerFrame> handlerStack;
626 ResumeFrame(SmallVector<HandlerFrame> handlerStack, RngScope rng)
627 : handlerStack(std::move(handlerStack)), rng(std::move(rng)) {}
630using EvalFramePayload =
631 std::variant<BlockFrame, ScfIfFrame, ScfForFrame, RandomScopeFrame,
632 WithHandlersFrame, HandlerClauseFrame, FunctionCallFrame,
633 ExternalOpFrame, ResumeFrame>;
636 EvalFramePayload payload;
638 template <
typename FrameTy>
639 explicit EvalFrame(FrameTy &&frame) : payload(std::forward<FrameTy>(frame)) {}
641 EvalFrame(
const EvalFrame &) =
default;
643 std::unique_ptr<EvalFrame> clone()
const {
644 return std::make_unique<EvalFrame>(*
this);
647 template <
typename FrameTy>
649 return std::get_if<FrameTy>(&payload);
652 template <
typename FrameTy>
653 const FrameTy *getIf()
const {
654 return std::get_if<FrameTy>(&payload);
657 template <
typename FrameTy>
659 return std::get<FrameTy>(payload);
662 template <
typename FrameTy>
664 return std::holds_alternative<FrameTy>(payload);
668using EvalFrameStack = SmallVector<std::unique_ptr<EvalFrame>, 0>;
672struct ContinuationStorage : IdentityValue {
673 ContinuationStorage(EvalFrameStack capturedFrames, Value performResult,
674 SmallVector<HandlerFrame> capturedHandlerStack,
675 const LexicalEnvironment &capturedEnvironment,
676 RngScope capturedRng, uint64_t promptToken,
677 ContinuationBoundary boundary, Type resumeType,
679 : IdentityValue(ContinuationType::
get(loc.getContext(), resumeType), loc),
680 capturedFrames(std::move(capturedFrames)), performResult(performResult),
681 capturedHandlerStack(std::move(capturedHandlerStack)),
682 capturedEnvironment(capturedEnvironment),
683 capturedRng(std::move(capturedRng)), promptToken(promptToken),
684 boundary(std::move(boundary)) {}
687 EvalFrameStack capturedFrames;
691 SmallVector<HandlerFrame> capturedHandlerStack;
693 LexicalEnvironment capturedEnvironment;
695 RngScope capturedRng;
697 uint64_t promptToken;
699 ContinuationBoundary boundary;
708 for (
auto it = destructors.rbegin(); it != destructors.rend(); ++it)
709 it->second(it->first);
717 template <
typename StorageTy,
typename... Args>
718 StorageTy *internalize(Args &&...args) {
719 static_assert(!std::is_base_of_v<IdentityValue, StorageTy> &&
720 "values with identity must not be internalized");
722 StorageTy storage(std::forward<Args>(args)...);
724 auto existing = getInternSet<StorageTy>().insert_as(
725 HashedStorage<StorageTy>(storage.hashcode), storage);
726 StorageTy *&storagePtr = existing.first->storage;
727 if (existing.second) {
729 new (allocator.Allocate<StorageTy>()) StorageTy(std::move(storage));
730 registerDestructor(storagePtr);
736 template <
typename StorageTy,
typename... Args>
737 StorageTy *create(Args &&...args) {
738 static_assert(std::is_base_of_v<IdentityValue, StorageTy> &&
739 "values with structural equivalence must be internalized");
741 StorageTy *storage =
new (allocator.Allocate<StorageTy>())
742 StorageTy(std::forward<Args>(args)...);
743 registerDestructor(storage);
748 template <
typename StorageTy>
749 void registerDestructor(StorageTy *storage) {
750 if constexpr (!std::is_trivially_destructible_v<StorageTy>)
751 destructors.emplace_back(storage, [](
void *ptr) {
752 static_cast<StorageTy *>(ptr)->~StorageTy();
756 template <
typename StorageTy>
757 DenseSet<HashedStorage<StorageTy>, StorageKeyInfo<StorageTy>> &
759 if constexpr (std::is_same_v<StorageTy, ArrayStorage>)
760 return internedArrays;
761 else if constexpr (std::is_same_v<StorageTy, SetStorage>)
763 else if constexpr (std::is_same_v<StorageTy, BagStorage>)
765 else if constexpr (std::is_same_v<StorageTy, SequenceStorage>)
766 return internedSequences;
767 else if constexpr (std::is_same_v<StorageTy, RandomizedSequenceStorage>)
768 return internedRandomizedSequences;
769 else if constexpr (std::is_same_v<StorageTy, InterleavedSequenceStorage>)
770 return internedInterleavedSequences;
771 else if constexpr (std::is_same_v<StorageTy, TupleStorage>)
772 return internedTuples;
773 else if constexpr (std::is_same_v<StorageTy, SymbolicComputationStorage>)
774 return internedSymbolicComputationWithIdentityValues;
776 static_assert(!
sizeof(StorageTy),
777 "no intern set available for this storage type.");
782 llvm::BumpPtrAllocator allocator;
787 SmallVector<std::pair<
void *, void (*)(
void *)>> destructors;
792 DenseSet<HashedStorage<ArrayStorage>, StorageKeyInfo<ArrayStorage>>
794 DenseSet<HashedStorage<SetStorage>, StorageKeyInfo<SetStorage>> internedSets;
795 DenseSet<HashedStorage<BagStorage>, StorageKeyInfo<BagStorage>> internedBags;
796 DenseSet<HashedStorage<SequenceStorage>, StorageKeyInfo<SequenceStorage>>
798 DenseSet<HashedStorage<RandomizedSequenceStorage>,
799 StorageKeyInfo<RandomizedSequenceStorage>>
800 internedRandomizedSequences;
801 DenseSet<HashedStorage<InterleavedSequenceStorage>,
802 StorageKeyInfo<InterleavedSequenceStorage>>
803 internedInterleavedSequences;
804 DenseSet<HashedStorage<TupleStorage>, StorageKeyInfo<TupleStorage>>
806 DenseSet<HashedStorage<SymbolicComputationStorage>,
807 StorageKeyInfo<SymbolicComputationStorage>>
808 internedSymbolicComputationWithIdentityValues;
815static llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
816 const ElaboratorValue &value);
818static void print(TypedAttr val, llvm::raw_ostream &os) {
819 os <<
"<attr " << val <<
">";
822static void print(BagStorage *val, llvm::raw_ostream &os) {
824 llvm::interleaveComma(val->bag, os,
825 [&](
const std::pair<ElaboratorValue, uint64_t> &el) {
826 os << el.first <<
" -> " << el.second;
828 os <<
"} at " << val <<
">";
831static void print(
bool val, llvm::raw_ostream &os) {
832 os <<
"<bool " << (val ?
"true" :
"false") <<
">";
835static void print(
size_t val, llvm::raw_ostream &os) {
836 os <<
"<index " << val <<
">";
839static void print(SequenceStorage *val, llvm::raw_ostream &os) {
840 os <<
"<sequence @" << val->familyName.getValue() <<
"(";
841 llvm::interleaveComma(val->args, os,
842 [&](
const ElaboratorValue &val) { os << val; });
843 os <<
") at " << val <<
">";
846static void print(RandomizedSequenceStorage *val, llvm::raw_ostream &os) {
847 os <<
"<randomized-sequence derived from @"
848 << val->sequence->familyName.getValue() <<
" under context "
849 << val->context <<
"(";
850 llvm::interleaveComma(val->sequence->args, os,
851 [&](
const ElaboratorValue &val) { os << val; });
852 os <<
") at " << val <<
">";
855static void print(InterleavedSequenceStorage *val, llvm::raw_ostream &os) {
856 os <<
"<interleaved-sequence [";
857 llvm::interleaveComma(val->sequences, os,
858 [&](
const ElaboratorValue &val) { os << val; });
859 os <<
"] batch-size " << val->batchSize <<
" at " << val <<
">";
862static void print(ArrayStorage *val, llvm::raw_ostream &os) {
864 llvm::interleaveComma(val->array, os,
865 [&](
const ElaboratorValue &val) { os << val; });
866 os <<
"] at " << val <<
">";
869static void print(SetStorage *val, llvm::raw_ostream &os) {
871 llvm::interleaveComma(val->set, os,
872 [&](
const ElaboratorValue &val) { os << val; });
873 os <<
"} at " << val <<
">";
876static void print(
const VirtualRegisterStorage *val, llvm::raw_ostream &os) {
877 os <<
"<virtual-register " << val <<
" " << val->allowedRegs <<
">";
880static void print(
const UniqueLabelStorage *val, llvm::raw_ostream &os) {
881 os <<
"<unique-label " << val <<
" " << val->name <<
">";
884static void print(
const TupleStorage *val, llvm::raw_ostream &os) {
886 llvm::interleaveComma(val->values, os,
887 [&](
const ElaboratorValue &val) { os << val; });
891static void print(
const MemoryStorage *val, llvm::raw_ostream &os) {
892 os <<
"<memory {" << ElaboratorValue(val->memoryBlock)
893 <<
", size=" << val->size <<
", alignment=" << val->alignment <<
"}>";
896static void print(
const MemoryBlockStorage *val, llvm::raw_ostream &os) {
897 os <<
"<memory-block {"
898 <<
", address-width=" << val->baseAddress.getBitWidth()
899 <<
", base-address=" << val->baseAddress
900 <<
", end-address=" << val->endAddress <<
"}>";
903static void print(
const SymbolicComputationWithIdentityValue *val,
904 llvm::raw_ostream &os) {
905 os <<
"<symbolic-computation-with-identity-value (" << val->storage <<
") at "
909static void print(
const SymbolicComputationWithIdentityStorage *val,
910 llvm::raw_ostream &os) {
911 os <<
"<symbolic-computation-with-identity " << val->name <<
"(";
912 llvm::interleaveComma(val->operands, os,
913 [&](
const ElaboratorValue &val) { os << val; });
914 os <<
") -> " << val->resultTypes <<
" with attributes " << val->attributes
915 <<
" and properties " << val->properties;
919static void print(
const SymbolicComputationStorage *val,
920 llvm::raw_ostream &os) {
921 os <<
"<symbolic-computation " << val->name <<
"(";
922 llvm::interleaveComma(val->operands, os,
923 [&](
const ElaboratorValue &val) { os << val; });
924 os <<
") -> " << val->resultTypes <<
" with attributes " << val->attributes
925 <<
" and properties " << val->properties;
929static void print(
const OpaqueExternalStorage *val, llvm::raw_ostream &os) {
930 os <<
"<opaque-external " << val->type <<
">";
933static void print(
const MutStorage *val, llvm::raw_ostream &os) {
934 os <<
"<mut at " << val <<
">";
937static void print(
const ContinuationStorage *val, llvm::raw_ostream &os) {
938 os <<
"<continuation with " << val->capturedFrames.size() <<
" frames at "
943 const ElaboratorValue &value) {
944 std::visit([&](
auto val) {
print(val, os); }, value);
959class AttributeToElaboratorValueConverter {
961 AttributeToElaboratorValueConverter(Internalizer &internalizer)
962 : internalizer(internalizer) {}
965 FailureOr<ElaboratorValue>
convert(Attribute attr) {
966 return llvm::TypeSwitch<Attribute, FailureOr<ElaboratorValue>>(attr)
967 .Case<IntegerAttr, SetAttr, TupleAttr>(
968 [&](
auto attr) {
return convert(attr); })
969 .Case<TypedAttr>([&](
auto typedAttr) -> FailureOr<ElaboratorValue> {
970 return ElaboratorValue(typedAttr);
973 [&](Attribute) -> FailureOr<ElaboratorValue> {
return failure(); });
977 FailureOr<ElaboratorValue>
convert(IntegerAttr attr) {
978 if (attr.getType().isSignlessInteger(1))
979 return ElaboratorValue(
bool(attr.getInt()));
980 if (isa<IndexType>(attr.getType()))
981 return ElaboratorValue(
size_t(attr.getInt()));
982 return ElaboratorValue(attr);
985 FailureOr<ElaboratorValue>
convert(SetAttr setAttr) {
986 SetVector<ElaboratorValue> set;
987 for (
auto element : *setAttr.getElements()) {
988 auto converted =
convert(element);
989 if (failed(converted))
991 set.insert(*converted);
994 internalizer.internalize<SetStorage>(std::move(set), setAttr.getType());
996 storage->attrCache = setAttr;
997 return ElaboratorValue(storage);
1000 FailureOr<ElaboratorValue>
convert(TupleAttr tupleAttr) {
1001 SmallVector<ElaboratorValue> values;
1002 for (
auto element : tupleAttr.getElements()) {
1003 auto converted =
convert(element);
1004 if (failed(converted))
1006 values.push_back(*converted);
1008 auto *storage = internalizer.internalize<TupleStorage>(std::move(values));
1010 storage->attrCache = tupleAttr;
1011 return ElaboratorValue(storage);
1014 Internalizer &internalizer;
1020class ElaboratorValueToAttributeConverter {
1022 ElaboratorValueToAttributeConverter(MLIRContext *context)
1023 : context(context) {}
1027 TypedAttr
convert(
const ElaboratorValue &value) {
1029 [&](
auto val) -> TypedAttr {
1030 if constexpr (std::is_base_of_v<CachableStorage,
1031 std::remove_pointer_t<
1032 std::decay_t<
decltype(value)>>>) {
1034 return val->attrCache;
1042 TypedAttr visit(TypedAttr val) {
return val; }
1044 TypedAttr visit(
bool val) {
1045 return IntegerAttr::get(IntegerType::get(context, 1), val);
1048 TypedAttr visit(
size_t val) {
1049 return IntegerAttr::get(IndexType::get(context), val);
1052 TypedAttr visit(SetStorage *val) {
1053 DenseSet<TypedAttr> elements;
1054 for (
auto element : val->set) {
1055 auto converted =
convert(element);
1058 auto typedAttr = dyn_cast<TypedAttr>(converted);
1061 elements.insert(typedAttr);
1063 return SetAttr::get(cast<SetType>(val->type), &elements);
1066 TypedAttr visit(TupleStorage *val) {
1067 SmallVector<TypedAttr> elements;
1068 for (
auto element : val->values) {
1069 auto converted =
convert(element);
1072 auto typedAttr = dyn_cast<TypedAttr>(converted);
1075 elements.push_back(typedAttr);
1077 return TupleAttr::get(context, elements);
1083#define VISIT_UNSUPPORTED(STORAGETYPE) \
1085 TypedAttr visit(STORAGETYPE *val) { return {}; }
1103#undef VISIT_UNSUPPORTED
1118 SharedState(MLIRContext *ctxt, SymbolTable &table)
1119 : ctxt(ctxt), table(table) {}
1121 uint64_t createBoundaryToken() {
return nextBoundaryToken++; }
1126 Internalizer internalizer;
1127 DenseMap<MutStorage *, ElaboratorValue> mutableStore;
1130 uint64_t nextBoundaryToken = 1;
1135 explicit TestState(
unsigned seed) : rng(RngScope(seed)) {}
1142 std::pair<ContextResourceAttrInterface, ContextResourceAttrInterface>,
1153 Materializer(OpBuilder builder, TestState &testState,
1154 SharedState &sharedState,
1155 SmallVector<ElaboratorValue> &blockArgs)
1156 : builder(builder), rootBlock(builder.getBlock()), testState(testState),
1157 sharedState(sharedState), blockArgs(blockArgs),
1158 attrConverter(builder.getContext()) {}
1162 Value materialize(ElaboratorValue val, Location loc,
1163 function_ref<InFlightDiagnostic()> emitError) {
1164 auto iter = materializedValues.find(val);
1165 if (iter != materializedValues.end())
1166 return iter->second;
1168 LLVM_DEBUG(llvm::dbgs() <<
"Materializing " << val);
1170 if (
auto res = tryMaterializeAsConstant(val, loc))
1175 Value res = std::visit(
1177 if constexpr (std::is_base_of_v<IdentityValue,
1178 std::remove_pointer_t<
1179 std::decay_t<
decltype(value)>>>) {
1180 if (identityValueRoot.contains(value)) {
1182 bool &materialized =
1183 static_cast<IdentityValue *
>(value)->alreadyMaterialized;
1184 assert(!materialized &&
"must not already be materialized");
1185 materialized =
true;
1188 return visit(value, loc, emitError);
1191 Value arg = builder.getBlock()->addArgument(value->type, loc);
1192 blockArgs.push_back(val);
1193 blockArgTypes.push_back(arg.getType());
1194 materializedValues[val] = arg;
1198 return visit(value, loc, emitError);
1202 LLVM_DEBUG(llvm::dbgs() <<
" to\n" << res <<
"\n\n");
1207 bool isInPlace(Operation *op)
const {
1208 return builder.getBlock()->getParent() == op->getParentRegion();
1217 LogicalResult materialize(Operation *op,
1218 DenseMap<Value, ElaboratorValue> &state,
1219 bool forceClone =
false) {
1225 if (op->getNumRegions() > 0)
1226 return op->emitOpError(
"ops with nested regions must be elaborated away");
1230 for (
auto res : op->getResults())
1231 if (!res.use_empty() && !state.contains(res))
1232 return op->emitOpError(
1233 "ops with results that have uses are not supported");
1235 Operation *sourceOp = op;
1236 if (isInPlace(op) && !forceClone) {
1239 deleteOpsUntil([&](
auto iter) {
return &*iter == op; });
1241 if (builder.getInsertionPoint() == builder.getBlock()->end())
1242 return op->emitError(
"operation did not occur after the current "
1243 "materializer insertion point");
1245 LLVM_DEBUG(llvm::dbgs() <<
"Modifying in-place: " << *op <<
"\n\n");
1247 LLVM_DEBUG(llvm::dbgs() <<
"Materializing a clone of " << *op <<
"\n\n");
1248 op = builder.clone(*op);
1249 builder.setInsertionPoint(op);
1252 for (
auto &operand : op->getOpOperands()) {
1253 auto emitError = [&]() {
1254 auto diag = op->emitError();
1255 diag.attachNote(op->getLoc())
1256 <<
"while materializing value for operand#"
1257 << operand.getOperandNumber();
1261 auto elabVal = state.at(operand.get());
1262 Value val = materialize(elabVal, op->getLoc(), emitError);
1266 state[val] = elabVal;
1270 for (
auto [sourceResult, result] :
1271 llvm::zip_equal(sourceOp->getResults(), op->getResults()))
1272 if (
auto it = state.find(sourceResult); it != state.end())
1273 materializedValues[it->second] = result;
1275 builder.setInsertionPointAfter(op);
1282 deleteOpsUntil([](
auto iter) {
return false; });
1284 for (
auto *op : llvm::reverse(toDelete))
1291 void registerIdentityValue(IdentityValue *val) {
1292 identityValueRoot.insert(val);
1295 ArrayRef<Type> getBlockArgTypes()
const {
return blockArgTypes; }
1297 void map(ElaboratorValue eval, Value val) { materializedValues[eval] = val; }
1299 OpBuilder &getBuilder() {
return builder; }
1301 template <
typename OpTy,
typename... Args>
1302 OpTy create(Location location, Args &&...args) {
1303 return OpTy::create(builder, location, std::forward<Args>(args)...);
1307 Value tryMaterializeAsConstant(ElaboratorValue val, Location loc) {
1308 if (
auto attr = attrConverter.convert(val)) {
1313 OpBuilder::InsertionGuard guard(builder);
1314 if (builder.getBlock() != rootBlock)
1315 builder.setInsertionPointToStart(rootBlock);
1316 Value res = ConstantOp::create(builder, loc, attr);
1317 materializedValues[val] = res;
1324 SequenceOp elaborateSequence(
const RandomizedSequenceStorage *seq,
1325 SmallVector<ElaboratorValue> &elabArgs);
1327 void deleteOpsUntil(function_ref<
bool(Block::iterator)> stop) {
1328 auto ip = builder.getInsertionPoint();
1329 while (ip != builder.getBlock()->end() && !stop(ip)) {
1330 LLVM_DEBUG(llvm::dbgs() <<
"Marking to be deleted: " << *ip <<
"\n\n");
1331 toDelete.push_back(&*ip);
1333 builder.setInsertionPointAfter(&*ip);
1334 ip = builder.getInsertionPoint();
1338 Value visit(TypedAttr val, Location loc,
1339 function_ref<InFlightDiagnostic()> emitError) {
1343 Value visit(
size_t val, Location loc,
1344 function_ref<InFlightDiagnostic()> emitError) {
1348 Value visit(
bool val, Location loc,
1349 function_ref<InFlightDiagnostic()> emitError) {
1353 Value visit(ArrayStorage *val, Location loc,
1354 function_ref<InFlightDiagnostic()> emitError) {
1355 SmallVector<Value> elements;
1356 elements.reserve(val->array.size());
1357 for (
auto el : val->array) {
1358 auto materialized = materialize(el, loc, emitError);
1362 elements.push_back(materialized);
1365 Value res = ArrayCreateOp::create(builder, loc, val->type, elements);
1366 materializedValues[val] = res;
1370 Value visit(SetStorage *val, Location loc,
1371 function_ref<InFlightDiagnostic()> emitError) {
1372 SmallVector<Value> elements;
1373 elements.reserve(val->set.size());
1374 for (
auto el : val->set) {
1375 auto materialized = materialize(el, loc, emitError);
1379 elements.push_back(materialized);
1382 auto res = SetCreateOp::create(builder, loc, val->type, elements);
1383 materializedValues[val] = res;
1387 Value visit(BagStorage *val, Location loc,
1388 function_ref<InFlightDiagnostic()> emitError) {
1389 SmallVector<Value> values, weights;
1390 values.reserve(val->bag.size());
1391 weights.reserve(val->bag.size());
1392 for (
auto [val, weight] : val->bag) {
1393 auto materializedVal = materialize(val, loc, emitError);
1394 auto materializedWeight = materialize(weight, loc, emitError);
1395 if (!materializedVal || !materializedWeight)
1398 values.push_back(materializedVal);
1399 weights.push_back(materializedWeight);
1402 auto res = BagCreateOp::create(builder, loc, val->type, values, weights);
1403 materializedValues[val] = res;
1407 Value visit(MemoryBlockStorage *val, Location loc,
1408 function_ref<InFlightDiagnostic()> emitError) {
1409 auto intType = builder.getIntegerType(val->baseAddress.getBitWidth());
1410 Value res = MemoryBlockDeclareOp::create(
1411 builder, val->loc, val->type,
1412 IntegerAttr::get(intType, val->baseAddress),
1413 IntegerAttr::get(intType, val->endAddress));
1414 materializedValues[val] = res;
1418 Value visit(MemoryStorage *val, Location loc,
1419 function_ref<InFlightDiagnostic()> emitError) {
1420 auto memBlock = materialize(val->memoryBlock, val->loc, emitError);
1421 auto memSize = materialize(val->size, val->loc, emitError);
1422 auto memAlign = materialize(val->alignment, val->loc, emitError);
1423 if (!(memBlock && memSize && memAlign))
1427 MemoryAllocOp::create(builder, val->loc, memBlock, memSize, memAlign);
1428 materializedValues[val] = res;
1432 Value visit(SequenceStorage *val, Location loc,
1433 function_ref<InFlightDiagnostic()> emitError) {
1434 emitError() <<
"materializing a non-randomized sequence not supported yet";
1438 Value visit(RandomizedSequenceStorage *val, Location loc,
1439 function_ref<InFlightDiagnostic()> emitError) {
1445 SmallVector<ElaboratorValue> elabArgs;
1448 SequenceOp seqOp = elaborateSequence(val, elabArgs);
1454 SmallVector<Value> args;
1455 SmallVector<Type> argTypes;
1456 for (
auto arg : elabArgs) {
1457 Value materialized = materialize(arg, val->loc, emitError);
1461 args.push_back(materialized);
1462 argTypes.push_back(materialized.getType());
1465 Value res = GetSequenceOp::create(
1466 builder, val->loc, SequenceType::get(builder.getContext(), argTypes),
1467 seqOp.getSymName());
1472 res = SubstituteSequenceOp::create(builder, val->loc, res, args);
1474 res = RandomizeSequenceOp::create(builder, val->loc, res);
1476 materializedValues[val] = res;
1480 Value visit(InterleavedSequenceStorage *val, Location loc,
1481 function_ref<InFlightDiagnostic()> emitError) {
1482 SmallVector<Value> sequences;
1483 for (
auto seqVal : val->sequences) {
1484 Value materialized = materialize(seqVal, loc, emitError);
1488 sequences.push_back(materialized);
1491 if (sequences.size() == 1)
1492 return sequences[0];
1495 InterleaveSequencesOp::create(builder, loc, sequences, val->batchSize);
1496 materializedValues[val] = res;
1500 Value visit(VirtualRegisterStorage *val, Location loc,
1501 function_ref<InFlightDiagnostic()> emitError) {
1502 Value res = VirtualRegisterOp::create(builder, val->loc, val->allowedRegs);
1503 materializedValues[val] = res;
1507 Value visit(UniqueLabelStorage *val, Location loc,
1508 function_ref<InFlightDiagnostic()> emitError) {
1509 auto materialized = materialize(val->name, val->loc, emitError);
1512 Value res = LabelUniqueDeclOp::create(builder, val->loc, materialized);
1513 materializedValues[val] = res;
1517 Value visit(TupleStorage *val, Location loc,
1518 function_ref<InFlightDiagnostic()> emitError) {
1519 SmallVector<Value> materialized;
1520 materialized.reserve(val->values.size());
1521 for (
auto v : val->values) {
1522 Value value = materialize(v, loc, emitError);
1525 materialized.push_back(value);
1527 Value res = TupleCreateOp::create(builder, loc, materialized);
1528 materializedValues[val] = res;
1532 Value visit(MutStorage *val, Location loc,
1533 function_ref<InFlightDiagnostic()> emitError) {
1534 emitError() <<
"mutable cell cannot escape into materialized IR";
1538 Value visit(ContinuationStorage *val, Location loc,
1539 function_ref<InFlightDiagnostic()> emitError) {
1540 emitError() <<
"continuation cannot escape through materialized IR, "
1541 "a sequence, or an opaque external-region shell";
1545 Value visit(SymbolicComputationWithIdentityValue *val, Location loc,
1546 function_ref<InFlightDiagnostic()> emitError) {
1547 auto *noConstStorage =
1548 const_cast<SymbolicComputationWithIdentityStorage *
>(val->storage);
1549 auto res0 = materialize(noConstStorage, loc, emitError);
1553 auto *op = res0.getDefiningOp();
1554 auto res = op->getResults()[val->idx];
1555 materializedValues[val] = res;
1559 Value visit(SymbolicComputationWithIdentityStorage *val, Location loc,
1560 function_ref<InFlightDiagnostic()> emitError) {
1561 SmallVector<Value> operands;
1562 for (
auto operand : val->operands) {
1563 auto materialized = materialize(operand, val->loc, emitError);
1567 operands.push_back(materialized);
1570 OperationState state(val->loc, val->name);
1571 state.addTypes(val->resultTypes);
1572 state.attributes = val->attributes;
1573 state.propertiesAttr = val->properties;
1574 state.addOperands(operands);
1575 auto *op = builder.create(state);
1577 materializedValues[val] = op->getResult(0);
1578 return op->getResult(0);
1581 Value visit(SymbolicComputationStorage *val, Location loc,
1582 function_ref<InFlightDiagnostic()> emitError) {
1583 SmallVector<Value> operands;
1584 for (
auto operand : val->operands) {
1585 auto materialized = materialize(operand, loc, emitError);
1589 operands.push_back(materialized);
1592 OperationState state(loc, val->name);
1593 state.addTypes(val->resultTypes);
1594 state.attributes = val->attributes;
1595 state.propertiesAttr = val->properties;
1596 state.addOperands(operands);
1597 auto *op = builder.create(state);
1599 for (
auto res : op->getResults())
1600 materializedValues[val] = res;
1602 return op->getResult(0);
1605 Value visit(OpaqueExternalStorage *val, Location loc,
1606 function_ref<InFlightDiagnostic()> emitError) {
1611 emitError() <<
"cannot materialize opaque external value";
1621 DenseMap<ElaboratorValue, Value> materializedValues;
1631 SmallVector<Operation *> toDelete;
1633 TestState &testState;
1634 SharedState &sharedState;
1639 SmallVector<ElaboratorValue> &blockArgs;
1640 SmallVector<Type> blockArgTypes;
1645 DenseSet<IdentityValue *> identityValueRoot;
1648 ElaboratorValueToAttributeConverter attrConverter;
1657enum class DeletionKind { Keep,
Delete };
1661struct ControlTransfer {
1667class Elaborator :
public RTGOpVisitor<Elaborator, FailureOr<DeletionKind>> {
1669 using RTGBase = RTGOpVisitor<Elaborator, FailureOr<DeletionKind>>;
1670 using RTGBase::visitOp;
1672 Elaborator(SharedState &sharedState, TestState &testState,
1673 Materializer &materializer,
1674 ContextResourceAttrInterface currentContext = {},
1675 uint64_t sequenceBoundaryToken = 0)
1676 : sharedState(sharedState), testState(testState),
1677 materializer(materializer), currentContext(currentContext),
1678 sequenceBoundaryToken(sequenceBoundaryToken),
1679 elaboratorBoundaryToken(sharedState.createBoundaryToken()),
1680 attrConverter(sharedState.internalizer),
1681 elabValConverter(sharedState.ctxt) {}
1683 template <
typename ValueTy>
1684 inline ValueTy
get(Value val)
const {
1685 return std::get<ValueTy>(state.at(val));
1689 FailureOr<DeletionKind> visitUnhandledOp(Operation *op) {
1690 return visitOpGeneric(op);
1693 FailureOr<DeletionKind> visitExternalOp(Operation *op) {
1694 if (op->getNumRegions() == 0)
1695 return visitOpGeneric(op);
1696 return op->emitOpError(
1697 "region-bearing operations must be scheduled by the evaluator");
1700 FailureOr<DeletionKind> visitOp(GetSequenceOp op) {
1701 SmallVector<ElaboratorValue> replacements;
1702 state[op.getResult()] =
1703 sharedState.internalizer.internalize<SequenceStorage>(
1704 op.getSequenceAttr().getAttr(), std::move(replacements));
1705 return DeletionKind::Delete;
1708 FailureOr<DeletionKind> visitOp(SubstituteSequenceOp op) {
1709 if (isSymbolic(state.at(op.getSequence())))
1710 return visitOpGeneric(op);
1712 auto *seq = get<SequenceStorage *>(op.getSequence());
1714 SmallVector<ElaboratorValue> replacements(seq->args);
1715 for (
auto replacement : op.getReplacements())
1716 replacements.push_back(state.at(replacement));
1718 state[op.getResult()] =
1719 sharedState.internalizer.internalize<SequenceStorage>(
1720 seq->familyName, std::move(replacements));
1722 return DeletionKind::Delete;
1725 FailureOr<DeletionKind> visitOp(RandomizeSequenceOp op) {
1726 auto *seq = get<SequenceStorage *>(op.getSequence());
1727 auto *randomizedSeq =
1728 sharedState.internalizer.create<RandomizedSequenceStorage>(
1729 currentContext, seq, op.getLoc());
1730 materializer.registerIdentityValue(randomizedSeq);
1731 state[op.getResult()] =
1732 sharedState.internalizer.internalize<InterleavedSequenceStorage>(
1734 return DeletionKind::Delete;
1737 FailureOr<DeletionKind> visitOp(InterleaveSequencesOp op) {
1738 SmallVector<ElaboratorValue> sequences;
1739 for (
auto seq : op.getSequences())
1740 sequences.push_back(state.at(seq));
1742 state[op.getResult()] =
1743 sharedState.internalizer.internalize<InterleavedSequenceStorage>(
1744 std::move(sequences), op.getBatchSize());
1745 return DeletionKind::Delete;
1749 LogicalResult isValidContext(ElaboratorValue value, Operation *op)
const {
1750 if (std::holds_alternative<RandomizedSequenceStorage *>(value)) {
1751 auto *seq = std::get<RandomizedSequenceStorage *>(value);
1752 if (seq->context != currentContext) {
1753 auto err = op->emitError(
"attempting to place sequence derived from ")
1754 << seq->sequence->familyName.getValue() <<
" under context "
1756 <<
", but it was previously randomized for context ";
1758 err << seq->context;
1766 auto *interVal = std::get<InterleavedSequenceStorage *>(value);
1767 for (
auto val : interVal->sequences)
1768 if (failed(isValidContext(val, op)))
1773 FailureOr<DeletionKind> visitOp(EmbedSequenceOp op) {
1774 auto *seqVal = get<InterleavedSequenceStorage *>(op.getSequence());
1775 if (failed(isValidContext(seqVal, op)))
1778 return DeletionKind::Keep;
1781 FailureOr<DeletionKind> visitOp(SetCreateOp op) {
1782 SetVector<ElaboratorValue> set;
1783 for (
auto val : op.getElements())
1784 set.insert(state.at(val));
1786 state[op.getSet()] = sharedState.internalizer.internalize<SetStorage>(
1787 std::move(set), op.getSet().getType());
1788 return DeletionKind::Delete;
1791 FailureOr<DeletionKind> visitOp(SetSelectRandomOp op) {
1792 auto set = get<SetStorage *>(op.getSet())->set;
1795 return op->emitError(
"cannot select from an empty set");
1797 size_t selected = testState.rng.getUniformlyInRange(0, set.size() - 1);
1798 state[op.getResult()] = set[selected];
1799 return DeletionKind::Delete;
1802 FailureOr<DeletionKind> visitOp(SetDifferenceOp op) {
1803 auto original = get<SetStorage *>(op.getOriginal())->set;
1804 auto diff = get<SetStorage *>(op.getDiff())->set;
1806 SetVector<ElaboratorValue> result(original);
1807 result.set_subtract(diff);
1809 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1810 std::move(result), op.getResult().getType());
1811 return DeletionKind::Delete;
1814 FailureOr<DeletionKind> visitOp(SetUnionOp op) {
1815 SetVector<ElaboratorValue> result;
1816 for (
auto set : op.getSets())
1817 result.set_union(get<SetStorage *>(set)->set);
1819 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1820 std::move(result), op.getType());
1821 return DeletionKind::Delete;
1824 FailureOr<DeletionKind> visitOp(SetSizeOp op) {
1825 auto size = get<SetStorage *>(op.getSet())->set.size();
1826 state[op.getResult()] = size;
1827 return DeletionKind::Delete;
1833 FailureOr<DeletionKind> visitOp(SetCartesianProductOp op) {
1834 SetVector<ElaboratorValue> result;
1835 SmallVector<SmallVector<ElaboratorValue>> tuples;
1836 tuples.push_back({});
1838 for (
auto input : op.getInputs()) {
1839 auto &set = get<SetStorage *>(input)->set;
1841 SetVector<ElaboratorValue>
empty;
1842 state[op.getResult()] =
1843 sharedState.internalizer.internalize<SetStorage>(std::move(
empty),
1845 return DeletionKind::Delete;
1848 for (
unsigned i = 0, e = tuples.size(); i < e; ++i) {
1849 for (
auto setEl : set.getArrayRef().drop_back()) {
1850 tuples.push_back(tuples[i]);
1851 tuples.back().push_back(setEl);
1853 tuples[i].push_back(set.back());
1857 for (
auto &tup : tuples)
1859 sharedState.internalizer.internalize<TupleStorage>(std::move(tup)));
1861 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1862 std::move(result), op.getType());
1863 return DeletionKind::Delete;
1866 FailureOr<DeletionKind> visitOp(SetConvertToBagOp op) {
1867 auto set = get<SetStorage *>(op.getInput())->set;
1868 MapVector<ElaboratorValue, uint64_t> bag;
1869 for (
auto val : set)
1870 bag.insert({val, 1});
1871 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1872 std::move(bag), op.getType());
1873 return DeletionKind::Delete;
1876 FailureOr<DeletionKind> visitOp(BagCreateOp op) {
1877 MapVector<ElaboratorValue, uint64_t> bag;
1878 for (
auto [val, multiple] :
1879 llvm::zip(op.getElements(), op.getMultiples())) {
1883 bag[state.at(val)] += get<size_t>(multiple);
1886 state[op.getBag()] = sharedState.internalizer.internalize<BagStorage>(
1887 std::move(bag), op.getType());
1888 return DeletionKind::Delete;
1891 FailureOr<DeletionKind> visitOp(BagSelectRandomOp op) {
1892 auto bag = get<BagStorage *>(op.getBag())->bag;
1895 return op->emitError(
"cannot select from an empty bag");
1897 SmallVector<std::pair<ElaboratorValue, uint32_t>> prefixSum;
1898 prefixSum.reserve(bag.size());
1899 uint32_t accumulator = 0;
1900 for (
auto [val, weight] : bag) {
1901 accumulator += weight;
1902 prefixSum.push_back({val, accumulator});
1905 auto idx = testState.rng.getUniformlyInRange(0, accumulator - 1);
1906 auto *iter = llvm::upper_bound(
1908 [](uint32_t a,
const std::pair<ElaboratorValue, uint32_t> &b) {
1909 return a < b.second;
1912 state[op.getResult()] = iter->first;
1913 return DeletionKind::Delete;
1916 FailureOr<DeletionKind> visitOp(BagDifferenceOp op) {
1917 auto original = get<BagStorage *>(op.getOriginal())->bag;
1918 auto diff = get<BagStorage *>(op.getDiff())->bag;
1920 MapVector<ElaboratorValue, uint64_t> result;
1921 for (
const auto &el : original) {
1922 if (!diff.contains(el.first)) {
1930 auto toDiff = diff.lookup(el.first);
1931 if (el.second <= toDiff)
1934 result.insert({el.first, el.second - toDiff});
1937 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1938 std::move(result), op.getType());
1939 return DeletionKind::Delete;
1942 FailureOr<DeletionKind> visitOp(BagUnionOp op) {
1943 MapVector<ElaboratorValue, uint64_t> result;
1944 for (
auto bag : op.getBags()) {
1945 auto val = get<BagStorage *>(bag)->bag;
1946 for (
auto [el, multiple] : val)
1947 result[el] += multiple;
1950 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1951 std::move(result), op.getType());
1952 return DeletionKind::Delete;
1955 FailureOr<DeletionKind> visitOp(BagUniqueSizeOp op) {
1956 auto size = get<BagStorage *>(op.getBag())->bag.size();
1957 state[op.getResult()] = size;
1958 return DeletionKind::Delete;
1961 FailureOr<DeletionKind> visitOp(BagConvertToSetOp op) {
1962 auto bag = get<BagStorage *>(op.getInput())->bag;
1963 SetVector<ElaboratorValue> set;
1964 for (
auto [k, v] : bag)
1966 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1967 std::move(set), op.getType());
1968 return DeletionKind::Delete;
1971 FailureOr<DeletionKind> visitOp(VirtualRegisterOp op) {
1972 auto *val = sharedState.internalizer.create<VirtualRegisterStorage>(
1973 op.getAllowedRegsAttr(), op.getType(), op.getLoc());
1974 state[op.getResult()] = val;
1975 materializer.registerIdentityValue(val);
1976 return DeletionKind::Delete;
1979 FailureOr<DeletionKind> visitOp(ArrayCreateOp op) {
1980 SmallVector<ElaboratorValue> array;
1981 array.reserve(op.getElements().size());
1982 for (
auto val : op.getElements())
1983 array.emplace_back(state.at(val));
1985 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1986 op.getResult().getType(), std::move(array));
1987 return DeletionKind::Delete;
1990 FailureOr<DeletionKind> visitOp(StringToASCIIArrayOp op) {
1991 auto opaque = state.at(op.getString());
1992 if (isSymbolic(opaque))
1993 return visitOpGeneric(op);
1995 auto strAttr = dyn_cast<StringAttr>(std::get<TypedAttr>(opaque));
1997 return op->emitError(
"expected a string attribute");
1999 auto i8Ty = IntegerType::get(op.getContext(), 8);
2000 SmallVector<ElaboratorValue> array;
2001 array.reserve(strAttr.getValue().size());
2002 for (
unsigned char c : strAttr.getValue())
2003 array.push_back(ElaboratorValue(IntegerAttr::get(i8Ty, c)));
2005 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
2006 op.getResult().getType(), std::move(array));
2007 return DeletionKind::Delete;
2010 FailureOr<DeletionKind> visitOp(ArrayExtractOp op) {
2011 auto array = get<ArrayStorage *>(op.getArray())->array;
2012 size_t idx = get<size_t>(op.getIndex());
2014 if (array.size() <= idx)
2015 return op->emitError(
"invalid to access index ")
2016 << idx <<
" of an array with " << array.size() <<
" elements";
2018 state[op.getResult()] = array[idx];
2019 return DeletionKind::Delete;
2022 FailureOr<DeletionKind> visitOp(ArrayInjectOp op) {
2023 auto arrayOpaque = state.at(op.getArray());
2024 auto idxOpaque = state.at(op.getIndex());
2025 if (isSymbolic(arrayOpaque) || isSymbolic(idxOpaque))
2026 return visitOpGeneric(op);
2028 auto array = std::get<ArrayStorage *>(arrayOpaque)->array;
2029 size_t idx = std::get<size_t>(idxOpaque);
2031 if (array.size() <= idx)
2032 return op->emitError(
"invalid to access index ")
2033 << idx <<
" of an array with " << array.size() <<
" elements";
2035 array[idx] = state.at(op.getValue());
2036 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
2037 op.getResult().getType(), std::move(array));
2038 return DeletionKind::Delete;
2041 FailureOr<DeletionKind> visitOp(ArrayAppendOp op) {
2042 auto array = std::get<ArrayStorage *>(state.at(op.getArray()))->array;
2043 array.push_back(state.at(op.getElement()));
2044 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
2045 op.getResult().getType(), std::move(array));
2046 return DeletionKind::Delete;
2049 FailureOr<DeletionKind> visitOp(ArraySizeOp op) {
2050 auto array = get<ArrayStorage *>(op.getArray())->array;
2051 state[op.getResult()] = array.size();
2052 return DeletionKind::Delete;
2055 FailureOr<DeletionKind> visitOp(LabelUniqueDeclOp op) {
2056 auto *val = sharedState.internalizer.create<UniqueLabelStorage>(
2057 state.at(op.getNamePrefix()), op.getLoc());
2058 state[op.getLabel()] = val;
2059 materializer.registerIdentityValue(val);
2060 return DeletionKind::Delete;
2063 FailureOr<DeletionKind> visitOp(RandomScopeOp op) {
2064 return op->emitOpError(
"random_scope must be scheduled by the evaluator");
2067 FailureOr<DeletionKind> visitOp(RandomNumberInRangeOp op) {
2068 size_t lower = get<size_t>(op.getLowerBound());
2069 size_t upper = get<size_t>(op.getUpperBound());
2071 return op->emitError(
"cannot select a number from an empty range");
2073 state[op.getResult()] =
2074 size_t(testState.rng.getUniformlyInRange(lower, upper));
2075 return DeletionKind::Delete;
2078 FailureOr<DeletionKind> visitOp(IntToImmediateOp op) {
2079 size_t input = get<size_t>(op.getInput());
2080 auto width = op.getType().getWidth();
2081 auto emitError = [&]() {
return op->emitError(); };
2082 if (input > APInt::getAllOnes(width).getZExtValue())
2083 return emitError() <<
"cannot represent " << input <<
" with " << width
2086 state[op.getResult()] =
2087 IntegerAttr::get(IntegerType::get(op.getContext(), width), input);
2088 return DeletionKind::Delete;
2091 FailureOr<DeletionKind> visitOp(OnContextOp op) {
2092 ContextResourceAttrInterface from = currentContext,
2093 to = cast<ContextResourceAttrInterface>(
2094 get<TypedAttr>(op.getContext()));
2095 if (!currentContext)
2096 from = DefaultContextAttr::get(op->getContext(), to.getType());
2098 auto emitError = [&]() {
2099 auto diag = op.emitError();
2100 diag.attachNote(op.getLoc())
2101 <<
"while materializing value for context switching for " << op;
2106 Value seqVal = materializer.materialize(
2107 get<SequenceStorage *>(op.getSequence()), op.getLoc(), emitError);
2112 materializer.create<RandomizeSequenceOp>(op.getLoc(), seqVal);
2113 materializer.create<EmbedSequenceOp>(op.getLoc(), randSeqVal);
2114 return DeletionKind::Delete;
2120 auto *iter = testState.contextSwitches.find({from, to});
2123 if (iter == testState.contextSwitches.end())
2124 iter = testState.contextSwitches.find(
2125 {from, AnyContextAttr::get(op->getContext(), to.getType())});
2128 if (iter == testState.contextSwitches.end())
2129 iter = testState.contextSwitches.find(
2130 {AnyContextAttr::get(op->getContext(), from.getType()), to});
2133 if (iter == testState.contextSwitches.end())
2134 iter = testState.contextSwitches.find(
2135 {AnyContextAttr::get(op->getContext(), from.getType()),
2136 AnyContextAttr::get(op->getContext(), to.getType())});
2142 if (iter == testState.contextSwitches.end())
2143 return op->emitError(
"no context transition registered to switch from ")
2144 << from <<
" to " << to;
2146 auto familyName = iter->second->familyName;
2147 SmallVector<ElaboratorValue> args{from, to,
2148 get<SequenceStorage *>(op.getSequence())};
2149 auto *seq = sharedState.internalizer.internalize<SequenceStorage>(
2150 familyName, std::move(args));
2151 auto *randSeq = sharedState.internalizer.create<RandomizedSequenceStorage>(
2152 to, seq, op.getLoc());
2153 materializer.registerIdentityValue(randSeq);
2154 Value seqVal = materializer.materialize(randSeq, op.getLoc(), emitError);
2158 materializer.create<EmbedSequenceOp>(op.getLoc(), seqVal);
2159 return DeletionKind::Delete;
2162 FailureOr<DeletionKind> visitOp(ContextSwitchOp op) {
2163 testState.contextSwitches[{op.getFromAttr(), op.getToAttr()}] =
2164 get<SequenceStorage *>(op.getSequence());
2165 return DeletionKind::Delete;
2168 FailureOr<DeletionKind> visitOp(MemoryBlockDeclareOp op) {
2169 auto *val = sharedState.internalizer.create<MemoryBlockStorage>(
2170 op.getBaseAddress(), op.getEndAddress(), op.getType(), op.getLoc());
2171 state[op.getResult()] = val;
2172 materializer.registerIdentityValue(val);
2173 return DeletionKind::Delete;
2176 FailureOr<DeletionKind> visitOp(MemoryAllocOp op) {
2177 size_t size = get<size_t>(op.getSize());
2178 size_t alignment = get<size_t>(op.getAlignment());
2179 auto *memBlock = get<MemoryBlockStorage *>(op.getMemoryBlock());
2180 auto *val = sharedState.internalizer.create<MemoryStorage>(
2181 memBlock, size, alignment, op.getLoc());
2182 state[op.getResult()] = val;
2183 materializer.registerIdentityValue(val);
2184 return DeletionKind::Delete;
2187 FailureOr<DeletionKind> visitOp(MemorySizeOp op) {
2188 auto *memory = get<MemoryStorage *>(op.getMemory());
2189 state[op.getResult()] = memory->size;
2190 return DeletionKind::Delete;
2196 FailureOr<DeletionKind> visitOp(MutCreateOp op) {
2197 auto *mut = sharedState.internalizer.create<MutStorage>(
2198 op.getRef().getType(), op.getLoc(),
2199 ContinuationBoundary{sequenceBoundaryToken, externalRegionStack,
2200 elaboratorBoundaryToken});
2201 sharedState.mutableStore[mut] = state.at(op.getInitialValue());
2202 state[op.getRef()] = mut;
2203 materializer.registerIdentityValue(mut);
2204 return DeletionKind::Delete;
2207 FailureOr<MutStorage *> getMutableCell(Value ref, Operation *op) {
2208 auto it = state.find(ref);
2209 if (it == state.end())
2210 return op->emitOpError(
"mutable reference has no elaborated value");
2212 auto *mut = std::get_if<MutStorage *>(&it->second);
2213 if (!mut || !*mut || !sharedState.mutableStore.contains(*mut))
2214 return op->emitOpError(
"invalid mutable cell reference");
2219 void collectContinuations(ElaboratorValue value,
2220 SmallVectorImpl<ContinuationStorage *> &result,
2221 SmallPtrSetImpl<const void *> &visited)
const {
2222 if (
auto *continuation = std::get_if<ContinuationStorage *>(&value)) {
2223 result.push_back(*continuation);
2227 if (
auto *mut = std::get_if<MutStorage *>(&value)) {
2228 if (!visited.insert(*mut).second)
2230 auto it = sharedState.mutableStore.find(*mut);
2231 if (it != sharedState.mutableStore.end())
2232 collectContinuations(it->second, result, visited);
2236 auto collectRange = [&](
auto &&range) {
2237 for (
auto nested : range)
2238 collectContinuations(nested, result, visited);
2240 if (
auto *set = std::get_if<SetStorage *>(&value)) {
2241 if (visited.insert(*set).second)
2242 collectRange((*set)->set);
2243 }
else if (
auto *bag = std::get_if<BagStorage *>(&value)) {
2244 if (visited.insert(*bag).second)
2245 for (
const auto &entry : (*bag)->bag)
2246 collectContinuations(entry.first, result, visited);
2247 }
else if (
auto *array = std::get_if<ArrayStorage *>(&value)) {
2248 if (visited.insert(*array).second)
2249 collectRange((*array)->array);
2250 }
else if (
auto *tuple = std::get_if<TupleStorage *>(&value)) {
2251 if (visited.insert(*tuple).second)
2252 collectRange((*tuple)->values);
2253 }
else if (
auto *symbolic =
2254 std::get_if<SymbolicComputationStorage *>(&value)) {
2255 if (visited.insert(*symbolic).second)
2256 collectRange((*symbolic)->operands);
2257 }
else if (
auto *symbolic =
2258 std::get_if<SymbolicComputationWithIdentityStorage *>(
2260 if (visited.insert(*symbolic).second)
2261 collectRange((*symbolic)->operands);
2262 }
else if (
auto *symbolic =
2263 std::get_if<SymbolicComputationWithIdentityValue *>(
2265 if (visited.insert(*symbolic).second)
2266 collectRange((*symbolic)->storage->operands);
2270 FailureOr<DeletionKind> visitOp(MutReadOp op) {
2271 auto mut = getMutableCell(op.getRef(), op);
2274 state[op.getValue()] = sharedState.mutableStore.at(*mut);
2275 return DeletionKind::Delete;
2278 FailureOr<DeletionKind> visitOp(MutWriteOp op) {
2279 auto mut = getMutableCell(op.getRef(), op);
2283 SmallVector<ContinuationStorage *> continuations;
2284 SmallPtrSet<const void *, 16> visited;
2285 collectContinuations(state.at(op.getNewValue()), continuations, visited);
2286 for (
auto *continuation : continuations) {
2287 if (continuation->boundary.sequence != (*mut)->boundary.sequence)
2288 return op->emitOpError(
"continuation cannot escape a sequence boundary "
2289 "through mutable storage");
2290 if (continuation->boundary.elaborator != (*mut)->boundary.elaborator)
2291 return op->emitOpError(
2292 "continuation cannot escape its elaborator invocation through "
2294 if (!llvm::equal(continuation->boundary.externalRegions,
2295 (*mut)->boundary.externalRegions))
2296 return op->emitOpError(
"continuation cannot escape an opaque "
2297 "external-region shell through mutable storage");
2300 sharedState.mutableStore[*mut] = state.at(op.getNewValue());
2301 return DeletionKind::Delete;
2307 FailureOr<DeletionKind> visitOp(EffectOp op) {
return DeletionKind::Keep; }
2309 FailureOr<DeletionKind> visitOp(WithHandlersOp op) {
2310 return op->emitOpError(
"with_handlers must be scheduled by the evaluator");
2313 FailureOr<DeletionKind> visitOp(PerformOp op) {
2314 return op->emitOpError(
"perform must return a control transfer");
2317 FailureOr<DeletionKind> visitOp(ResumeOp op) {
2318 return op->emitOpError(
"resume must be scheduled by the evaluator");
2323 FailureOr<DeletionKind> visitOp(TupleCreateOp op) {
2324 SmallVector<ElaboratorValue> values;
2325 values.reserve(op.getElements().size());
2326 for (
auto el : op.getElements())
2327 values.push_back(state.at(el));
2329 state[op.getResult()] =
2330 sharedState.internalizer.internalize<TupleStorage>(std::move(values));
2331 return DeletionKind::Delete;
2334 FailureOr<DeletionKind> visitOp(TupleExtractOp op) {
2335 auto *tuple = get<TupleStorage *>(op.getTuple());
2336 state[op.getResult()] = tuple->values[op.getIndex().getZExtValue()];
2337 return DeletionKind::Delete;
2340 FailureOr<DeletionKind> visitOp(scf::IfOp op) {
2341 return op->emitOpError(
"scf.if must be scheduled by the evaluator");
2344 FailureOr<DeletionKind> visitOp(scf::ForOp op) {
2345 return op->emitOpError(
"scf.for must be scheduled by the evaluator");
2348 FailureOr<DeletionKind> visitOp(arith::AddIOp op) {
2349 if (!isa<IndexType>(op.getType()))
2350 return visitOpGeneric(op);
2352 size_t lhs = get<size_t>(op.getLhs());
2353 size_t rhs = get<size_t>(op.getRhs());
2354 state[op.getResult()] = lhs + rhs;
2355 return DeletionKind::Delete;
2358 FailureOr<DeletionKind> visitOp(arith::AndIOp op) {
2359 if (!op.getType().isSignlessInteger(1))
2360 return visitOpGeneric(op);
2362 bool lhs = get<bool>(op.getLhs());
2363 bool rhs = get<bool>(op.getRhs());
2364 state[op.getResult()] = lhs && rhs;
2365 return DeletionKind::Delete;
2368 FailureOr<DeletionKind> visitOp(arith::XOrIOp op) {
2369 if (!op.getType().isSignlessInteger(1))
2370 return visitOpGeneric(op);
2372 bool lhs = get<bool>(op.getLhs());
2373 bool rhs = get<bool>(op.getRhs());
2374 state[op.getResult()] = lhs != rhs;
2375 return DeletionKind::Delete;
2378 FailureOr<DeletionKind> visitOp(arith::OrIOp op) {
2379 if (!op.getType().isSignlessInteger(1))
2380 return visitOpGeneric(op);
2382 bool lhs = get<bool>(op.getLhs());
2383 bool rhs = get<bool>(op.getRhs());
2384 state[op.getResult()] = lhs || rhs;
2385 return DeletionKind::Delete;
2388 FailureOr<DeletionKind> visitOp(arith::SelectOp op) {
2389 auto condOpaque = state.at(op.getCondition());
2390 if (isSymbolic(condOpaque))
2391 return visitOpGeneric(op);
2393 bool cond = std::get<bool>(condOpaque);
2394 auto trueVal = state.at(op.getTrueValue());
2395 auto falseVal = state.at(op.getFalseValue());
2396 state[op.getResult()] = cond ? trueVal : falseVal;
2397 return DeletionKind::Delete;
2400 FailureOr<DeletionKind> visitOp(index::AddOp op) {
2401 size_t lhs = get<size_t>(op.getLhs());
2402 size_t rhs = get<size_t>(op.getRhs());
2403 state[op.getResult()] = lhs + rhs;
2404 return DeletionKind::Delete;
2407 FailureOr<DeletionKind> visitOp(index::SubOp op) {
2408 size_t lhs = get<size_t>(op.getLhs());
2409 size_t rhs = get<size_t>(op.getRhs());
2410 state[op.getResult()] = lhs - rhs;
2411 return DeletionKind::Delete;
2414 FailureOr<DeletionKind> visitOp(index::MulOp op) {
2415 size_t lhs = get<size_t>(op.getLhs());
2416 size_t rhs = get<size_t>(op.getRhs());
2417 state[op.getResult()] = lhs * rhs;
2418 return DeletionKind::Delete;
2421 FailureOr<DeletionKind> visitOp(index::DivUOp op) {
2422 size_t lhs = get<size_t>(op.getLhs());
2423 size_t rhs = get<size_t>(op.getRhs());
2426 return op->emitOpError(
"attempted division by zero");
2428 state[op.getResult()] = lhs / rhs;
2429 return DeletionKind::Delete;
2432 FailureOr<DeletionKind> visitOp(index::CeilDivUOp op) {
2433 size_t lhs = get<size_t>(op.getLhs());
2434 size_t rhs = get<size_t>(op.getRhs());
2437 return op->emitOpError(
"attempted division by zero");
2440 state[op.getResult()] = (lhs + rhs - 1) / rhs;
2442 state[op.getResult()] = 1 + ((lhs - 1) / rhs);
2444 return DeletionKind::Delete;
2447 FailureOr<DeletionKind> visitOp(index::RemUOp op) {
2448 size_t lhs = get<size_t>(op.getLhs());
2449 size_t rhs = get<size_t>(op.getRhs());
2452 return op->emitOpError(
"attempted division by zero");
2454 state[op.getResult()] = lhs % rhs;
2455 return DeletionKind::Delete;
2458 FailureOr<DeletionKind> visitOp(index::AndOp op) {
2459 size_t lhs = get<size_t>(op.getLhs());
2460 size_t rhs = get<size_t>(op.getRhs());
2461 state[op.getResult()] = lhs & rhs;
2462 return DeletionKind::Delete;
2465 FailureOr<DeletionKind> visitOp(index::OrOp op) {
2466 size_t lhs = get<size_t>(op.getLhs());
2467 size_t rhs = get<size_t>(op.getRhs());
2468 state[op.getResult()] = lhs | rhs;
2469 return DeletionKind::Delete;
2472 FailureOr<DeletionKind> visitOp(index::XOrOp op) {
2473 size_t lhs = get<size_t>(op.getLhs());
2474 size_t rhs = get<size_t>(op.getRhs());
2475 state[op.getResult()] = lhs ^ rhs;
2476 return DeletionKind::Delete;
2479 FailureOr<DeletionKind> visitOp(index::ShlOp op) {
2480 size_t lhs = get<size_t>(op.getLhs());
2481 size_t rhs = get<size_t>(op.getRhs());
2482 state[op.getResult()] = lhs << rhs;
2483 return DeletionKind::Delete;
2486 FailureOr<DeletionKind> visitOp(index::ShrUOp op) {
2487 size_t lhs = get<size_t>(op.getLhs());
2488 size_t rhs = get<size_t>(op.getRhs());
2489 state[op.getResult()] = lhs >> rhs;
2490 return DeletionKind::Delete;
2493 FailureOr<DeletionKind> visitOp(index::MaxUOp op) {
2494 size_t lhs = get<size_t>(op.getLhs());
2495 size_t rhs = get<size_t>(op.getRhs());
2496 state[op.getResult()] = std::max(lhs, rhs);
2497 return DeletionKind::Delete;
2500 FailureOr<DeletionKind> visitOp(index::MinUOp op) {
2501 size_t lhs = get<size_t>(op.getLhs());
2502 size_t rhs = get<size_t>(op.getRhs());
2503 state[op.getResult()] = std::min(lhs, rhs);
2504 return DeletionKind::Delete;
2507 FailureOr<DeletionKind> visitOp(index::CmpOp op) {
2508 size_t lhs = get<size_t>(op.getLhs());
2509 size_t rhs = get<size_t>(op.getRhs());
2511 switch (op.getPred()) {
2512 case index::IndexCmpPredicate::EQ:
2513 result = lhs == rhs;
2515 case index::IndexCmpPredicate::NE:
2516 result = lhs != rhs;
2518 case index::IndexCmpPredicate::ULT:
2521 case index::IndexCmpPredicate::ULE:
2522 result = lhs <= rhs;
2524 case index::IndexCmpPredicate::UGT:
2527 case index::IndexCmpPredicate::UGE:
2528 result = lhs >= rhs;
2531 return op->emitOpError(
"elaboration not supported");
2533 state[op.getResult()] = result;
2534 return DeletionKind::Delete;
2537 bool isSymbolic(ElaboratorValue val) {
2538 return std::holds_alternative<SymbolicComputationWithIdentityValue *>(
2540 std::holds_alternative<SymbolicComputationWithIdentityStorage *>(
2542 std::holds_alternative<SymbolicComputationStorage *>(val) ||
2543 std::holds_alternative<OpaqueExternalStorage *>(val);
2546 bool isSymbolic(Operation *op) {
2547 return llvm::any_of(op->getOperands(), [&](
auto operand) {
2548 auto val = state.at(operand);
2549 return isSymbolic(val);
2556 bool attemptConcreteCase(Operation *op) {
2557 if (op->getNumResults() == 0)
2560 SmallVector<Attribute> operands;
2561 for (
auto operand : op->getOperands()) {
2562 auto evalValue = state[operand];
2563 auto attr = elabValConverter.convert(evalValue);
2564 operands.push_back(attr);
2567 SmallVector<OpFoldResult> results;
2568 if (failed(op->fold(operands, results)))
2571 if (results.size() != op->getNumResults())
2574 for (
auto [res, val] : llvm::zip(results, op->getResults())) {
2575 auto attr = llvm::dyn_cast_or_null<TypedAttr>(res.dyn_cast<Attribute>());
2579 if (attr.getType() != val.getType())
2583 auto converted = attrConverter.convert(attr);
2584 if (succeeded(converted)) {
2585 state[val] = *converted;
2594 FailureOr<DeletionKind> visitOpGeneric(Operation *op) {
2595 if (op->getNumResults() == 0)
2596 return DeletionKind::Keep;
2598 if (attemptConcreteCase(op))
2599 return DeletionKind::Delete;
2601 if (mlir::isMemoryEffectFree(op)) {
2602 if (op->getNumResults() != 1)
2603 return op->emitOpError(
2604 "symbolic elaboration of memory-effect-free operations with "
2605 "multiple results not supported");
2607 state[op->getResult(0)] =
2608 sharedState.internalizer.internalize<SymbolicComputationStorage>(
2610 return DeletionKind::Delete;
2621 bool onlyAlloc = mlir::hasSingleEffect<mlir::MemoryEffects::Allocate>(op);
2622 onlyAlloc |= isa<ValidateOp>(op);
2624 auto *validationVal =
2625 sharedState.internalizer.create<SymbolicComputationWithIdentityStorage>(
2627 materializer.registerIdentityValue(validationVal);
2628 state[op->getResult(0)] = validationVal;
2630 for (
auto [i, res] : llvm::enumerate(op->getResults())) {
2634 sharedState.internalizer.create<SymbolicComputationWithIdentityValue>(
2635 res.getType(), validationVal, i);
2637 materializer.registerIdentityValue(val);
2639 return onlyAlloc ? DeletionKind::Delete : DeletionKind::Keep;
2642 bool supportsSymbolicValuesNonGenerically(Operation *op) {
2643 return isa<SubstituteSequenceOp, ArrayCreateOp, ArrayInjectOp,
2644 TupleCreateOp, MutCreateOp, MutReadOp, MutWriteOp,
2645 arith::SelectOp, func::CallOp, func::CallIndirectOp>(op);
2648 FailureOr<DeletionKind> dispatchOpVisitor(Operation *op) {
2649 if (isSymbolic(op) && !supportsSymbolicValuesNonGenerically(op))
2650 return visitOpGeneric(op);
2652 return TypeSwitch<Operation *, FailureOr<DeletionKind>>(op)
2655 arith::AddIOp, arith::XOrIOp, arith::AndIOp, arith::OrIOp,
2658 index::AddOp, index::SubOp, index::MulOp, index::DivUOp,
2659 index::CeilDivUOp, index::RemUOp, index::AndOp, index::OrOp,
2660 index::XOrOp, index::ShlOp, index::ShrUOp, index::MaxUOp,
2661 index::MinUOp, index::CmpOp>([&](
auto op) {
return visitOp(op); })
2665 LogicalResult pushRegionFrame(Region ®ion,
2666 ArrayRef<ElaboratorValue> regionArguments,
2667 bool keepTerminator,
bool replay,
2668 LexicalEnvironment environment,
2669 bool eraseTerminator =
true) {
2670 if (region.getBlocks().size() != 1)
2671 return region.getParentOp()->emitOpError(
2672 "regions with anything other than one block are not supported");
2674 Block &block = region.front();
2675 for (
auto [arg, value] : llvm::zip(block.getArguments(), regionArguments))
2676 environment[arg] = value;
2679 frame.block = █
2680 frame.nextOp = block.empty() ? nullptr : &block.front();
2681 frame.keepTerminator = keepTerminator;
2682 frame.eraseTerminator = eraseTerminator;
2683 frame.replay = replay;
2684 frame.environment = std::move(environment);
2685 workStack.emplace_back(std::make_unique<EvalFrame>(std::move(frame)));
2689 FailureOr<ControlTransfer> visitPerformTransfer(PerformOp op) {
2690 StringAttr effectName = op.getEffectAttr().getAttr();
2691 for (
size_t i = handlerStack.size(); i > 0; --i) {
2692 const HandlerFrame &frame = handlerStack[i - 1];
2693 if (frame.handlers.contains(effectName))
2694 return ControlTransfer{frame.promptToken, op};
2697 auto diag = op->emitError(
"no handler for effect @")
2698 << effectName.getValue();
2699 if (sequenceBoundaryToken)
2700 diag <<
"; effects cannot be handled across a sequence boundary";
2704 LogicalResult handleControlTransfer(ControlTransfer transfer) {
2705 size_t handlerIndex = handlerStack.size();
2706 Region *handlerRegion =
nullptr;
2707 StringAttr effectName = transfer.perform.getEffectAttr().getAttr();
2708 for (
size_t i = handlerStack.size(); i > 0; --i) {
2709 auto it = handlerStack[i - 1].handlers.find(effectName);
2710 if (it != handlerStack[i - 1].handlers.end()) {
2711 handlerIndex = i - 1;
2712 handlerRegion = it->second;
2716 if (!handlerRegion || handlerIndex == handlerStack.size())
2717 return transfer.perform->emitOpError(
2718 "control transfer no longer has a matching handler");
2720 size_t promptIndex = workStack.size();
2721 for (
size_t i = 0; i < workStack.size(); ++i)
2722 if (
auto *frame = workStack[i]->getIf<WithHandlersFrame>();
2723 frame && frame->promptId == transfer.promptId) {
2727 if (promptIndex == workStack.size())
2728 return transfer.perform->emitOpError(
2729 "active handler prompt has no evaluator frame");
2731 for (
size_t i = promptIndex + 1; i < workStack.size(); ++i)
2732 if (workStack[i]->holds<ExternalOpFrame>())
2733 return transfer.perform->emitError(
2734 "effect handler cannot capture control across an opaque "
2735 "external-region shell");
2737 auto *activeBlock = workStack.back()->getIf<BlockFrame>();
2739 return transfer.perform->emitOpError(
2740 "perform is not executing in a block frame");
2741 LexicalEnvironment performEnvironment = activeBlock->environment;
2742 SmallVector<HandlerFrame> capturedHandlerStack(handlerStack);
2743 RngScope capturedRng = testState.rng;
2744 ContinuationBoundary capturedBoundary{
2745 sequenceBoundaryToken, externalRegionStack, elaboratorBoundaryToken};
2746 EvalFrameStack capturedFrames;
2748 Value performResult = transfer.perform.getNumResults()
2749 ? Value(transfer.perform.getResult())
2751 Type resumeType = performResult
2752 ? performResult.getType()
2753 : NoneType::get(transfer.perform.getContext());
2755 while (workStack.size() > promptIndex) {
2756 EvalFrame &frame = *workStack.back();
2757 capturedFrames.push_back(frame.clone());
2758 if (auto *randomScope = frame.getIf<RandomScopeFrame>()) {
2759 testState.rng = randomScope->outerRng;
2760 }
else if (
auto *external = frame.getIf<ExternalOpFrame>()) {
2761 if (external->activeBoundary && !externalRegionStack.empty())
2762 externalRegionStack.pop_back();
2763 materializer.getBuilder().restoreInsertionPoint(
2764 external->parentInsertionPoint);
2765 }
else if (
auto *resume = frame.getIf<ResumeFrame>()) {
2766 handlerStack = resume->handlerStack;
2767 testState.rng = resume->rng;
2769 workStack.pop_back();
2771 std::reverse(capturedFrames.begin(), capturedFrames.end());
2772 auto *continuation = sharedState.internalizer.create<ContinuationStorage>(
2773 std::move(capturedFrames), performResult,
2774 std::move(capturedHandlerStack), performEnvironment,
2775 std::move(capturedRng), transfer.promptId, std::move(capturedBoundary),
2776 resumeType, transfer.perform.getLoc());
2777 materializer.registerIdentityValue(continuation);
2779 SmallVector<ElaboratorValue> handlerArguments;
2780 for (Value operand : transfer.perform.getOperands())
2781 handlerArguments.push_back(performEnvironment.at(operand));
2782 handlerArguments.push_back(continuation);
2784 handlerStack.resize(handlerIndex);
2785 state = performEnvironment;
2786 workStack.emplace_back(std::make_unique<EvalFrame>(HandlerClauseFrame{}));
2787 return pushRegionFrame(*handlerRegion, handlerArguments,
2789 std::move(performEnvironment));
2792 LogicalResult scheduleResume(ResumeOp op) {
2793 auto stateIt = state.find(op.getContinuation());
2794 if (stateIt == state.end())
2795 return op->emitOpError(
"continuation has no elaborated value");
2796 auto *continuation = std::get_if<ContinuationStorage *>(&stateIt->second);
2797 if (!continuation || !*continuation)
2798 return op->emitOpError(
2799 "cannot resume a continuation that is not owned by the elaborator");
2801 ContinuationStorage *cont = *continuation;
2802 if (cont->boundary.sequence != sequenceBoundaryToken)
2803 return op->emitError(
"continuation cannot escape a sequence boundary");
2804 if (cont->boundary.elaborator != elaboratorBoundaryToken)
2805 return op->emitError(
2806 "continuation cannot escape its elaborator invocation");
2807 if (!llvm::equal(cont->boundary.externalRegions, externalRegionStack))
2808 return op->emitError(
2809 "continuation cannot escape an opaque external-region shell");
2811 std::optional<ElaboratorValue> resumeValue;
2812 if (cont->performResult && op.getValue())
2813 resumeValue = state.at(op.getValue());
2815 auto *block = workStack.back()->getIf<BlockFrame>();
2817 return op->emitOpError(
"resume is not executing in a block frame");
2818 block->environment = state;
2820 workStack.emplace_back(
2821 std::make_unique<EvalFrame>(ResumeFrame(handlerStack, testState.rng)));
2822 handlerStack = cont->capturedHandlerStack;
2823 testState.rng = cont->capturedRng;
2825 EvalFrameStack replayFrames;
2826 for (
const auto &prototype : cont->capturedFrames)
2827 replayFrames.push_back(prototype->clone());
2828 for (
auto &framePtr : replayFrames) {
2829 EvalFrame &frame = *framePtr;
2830 if (
auto *blockFrame = frame.getIf<BlockFrame>())
2831 blockFrame->replay =
true;
2832 else if (
auto *ifFrame = frame.getIf<ScfIfFrame>())
2833 ifFrame->replay =
true;
2834 else if (
auto *forFrame = frame.getIf<ScfForFrame>())
2835 forFrame->replay =
true;
2836 else if (
auto *scopeFrame = frame.getIf<RandomScopeFrame>())
2837 scopeFrame->replay =
true;
2838 else if (
auto *handlerFrame = frame.getIf<WithHandlersFrame>())
2839 handlerFrame->replay =
true;
2840 else if (
auto *externalFrame = frame.getIf<ExternalOpFrame>())
2841 externalFrame->replay =
true;
2844 auto *performBlock = replayFrames.back()->getIf<BlockFrame>();
2846 return op->emitOpError(
2847 "captured continuation does not end in a block frame");
2848 performBlock->environment[cont->performResult] = *resumeValue;
2850 for (
auto &frame : replayFrames)
2851 workStack.push_back(std::move(frame));
2855 LogicalResult startExternalRegion(
size_t frameIndex,
bool &started) {
2856 auto &frame = workStack[frameIndex]->get<ExternalOpFrame>();
2857 RegionRange oldRegions = frame.originalOp->getRegions();
2858 while (frame.nextRegion < oldRegions.size()) {
2859 size_t regionIndex = frame.nextRegion++;
2860 Region &oldRegion = *oldRegions[regionIndex];
2861 if (oldRegion.empty())
2863 if (oldRegion.getBlocks().size() != 1)
2864 return frame.originalOp->emitOpError(
2865 "regions with anything other than one block are not supported");
2867 Region &newRegion = frame.materializedOp->getRegion(regionIndex);
2868 Block &oldBlock = oldRegion.front();
2869 Block &newBlock = newRegion.emplaceBlock();
2870 LexicalEnvironment environment = frame.parentEnvironment;
2871 for (
auto oldArg : oldBlock.getArguments()) {
2872 Value newArg = newBlock.addArgument(oldArg.getType(), oldArg.getLoc());
2873 auto *storage = sharedState.internalizer.create<OpaqueExternalStorage>(
2874 oldArg.getType(), oldArg.getLoc());
2875 environment[oldArg] = storage;
2876 materializer.map(storage, newArg);
2879 uint64_t boundary = sharedState.createBoundaryToken();
2880 frame.activeBoundary = boundary;
2881 externalRegionStack.push_back(boundary);
2882 materializer.getBuilder().setInsertionPoint(&newBlock, newBlock.begin());
2884 return pushRegionFrame(oldRegion, {},
true,
2885 frame.replay, std::move(environment));
2891 LogicalResult startExternalOp(Operation *op,
bool replay) {
2892 auto &builder = materializer.getBuilder();
2893 Operation *newOp = op->cloneWithoutRegions();
2894 builder.insert(newOp);
2895 builder.setInsertionPoint(newOp);
2896 for (
auto &operand : newOp->getOpOperands()) {
2897 auto emitError = [&]() {
2898 auto diag = newOp->emitError();
2899 diag.attachNote(newOp->getLoc())
2900 <<
"while materializing operand#" << operand.getOperandNumber()
2901 <<
" of external region op";
2904 Value value = materializer.materialize(state.at(operand.get()),
2905 newOp->getLoc(), emitError);
2910 builder.setInsertionPointAfter(newOp);
2912 ExternalOpFrame frame;
2913 frame.originalOp = op;
2914 frame.materializedOp = newOp;
2915 frame.replay = replay;
2916 frame.parentEnvironment = state;
2917 frame.parentInsertionPoint = builder.saveInsertionPoint();
2918 workStack.emplace_back(std::make_unique<EvalFrame>(std::move(frame)));
2919 bool started =
false;
2920 if (failed(startExternalRegion(workStack.size() - 1, started)))
2923 return finishExternalOp(workStack.size() - 1);
2927 LogicalResult finishExternalOp(
size_t frameIndex) {
2928 auto &frame = workStack[frameIndex]->get<ExternalOpFrame>();
2929 auto *parent = workStack[frameIndex - 1]->getIf<BlockFrame>();
2931 return frame.originalOp->emitOpError(
2932 "external region op lost its parent block frame");
2933 for (
auto [oldResult, newResult] :
2934 llvm::zip(frame.originalOp->getResults(),
2935 frame.materializedOp->getResults())) {
2936 auto *storage = sharedState.internalizer.create<OpaqueExternalStorage>(
2937 oldResult.getType(), oldResult.getLoc());
2938 parent->environment[oldResult] = storage;
2939 materializer.map(storage, newResult);
2941 workStack.pop_back();
2945 LogicalResult startForIteration(
size_t frameIndex) {
2946 auto &frame = workStack[frameIndex]->get<ScfForFrame>();
2947 if (frame.nextIteration >= frame.upperBound) {
2948 auto *parent = workStack[frameIndex - 1]->getIf<BlockFrame>();
2950 return frame.op->emitOpError(
"scf.for lost its parent block frame");
2951 for (
auto [result, iterArg] :
2952 llvm::zip(frame.op->getResults(), frame.op.getRegionIterArgs()))
2953 parent->environment[result] = frame.loopEnvironment.at(iterArg);
2954 frame.completed =
true;
2958 size_t iteration = frame.nextIteration;
2959 frame.nextIteration += frame.loopStep;
2960 LexicalEnvironment environment = frame.loopEnvironment;
2961 environment[frame.op.getInductionVar()] = iteration;
2962 return pushRegionFrame(frame.op.getBodyRegion(), {},
2963 false, frame.replay,
2964 std::move(environment));
2967 LogicalResult completeFrame(SmallVector<ElaboratorValue> yieldedValues) {
2968 if (workStack.empty() || workStack.back()->holds<BlockFrame>())
2971 if (
auto *frame = workStack.back()->getIf<ScfIfFrame>()) {
2972 if (failed(frame->step(*
this, yieldedValues)))
2974 workStack.pop_back();
2978 if (
auto *frame = workStack.back()->getIf<ScfForFrame>()) {
2979 if (failed(frame->step(*
this, yieldedValues)))
2981 if (frame->completed)
2982 workStack.pop_back();
2986 if (
auto *frame = workStack.back()->getIf<RandomScopeFrame>()) {
2987 auto *parent = workStack[workStack.size() - 2]->getIf<BlockFrame>();
2989 return frame->op->emitOpError(
2990 "random_scope lost its parent block frame");
2991 for (
auto [result, value] :
2992 llvm::zip(frame->op.getResults(), yieldedValues))
2993 parent->environment[result] = value;
2994 testState.rng = frame->outerRng;
2995 workStack.pop_back();
2999 if (
auto *frame = workStack.back()->getIf<WithHandlersFrame>()) {
3000 if (handlerStack.empty() ||
3001 handlerStack.back().promptToken != frame->promptId)
3002 return frame->op->emitOpError(
3003 "handler stack does not match completed scope");
3004 handlerStack.pop_back();
3005 workStack.pop_back();
3009 if (workStack.back()->holds<HandlerClauseFrame>()) {
3010 workStack.pop_back();
3014 if (
auto *frame = workStack.back()->getIf<FunctionCallFrame>()) {
3015 if (yieldedValues.size() != frame->call.getNumResults())
3016 return frame->call.emitOpError(
3017 "callee return values do not match call results");
3018 assert(workStack.size() >= 2 &&
3019 "function call frame must have a parent block frame");
3020 auto *parent = workStack[workStack.size() - 2]->getIf<BlockFrame>();
3022 return frame->call.emitOpError(
3023 "function call lost its parent block frame");
3024 for (
auto [result, value] :
3025 llvm::zip_equal(frame->call.getResults(), yieldedValues))
3026 parent->environment[result] = value;
3027 workStack.pop_back();
3031 if (
auto *frame = workStack.back()->getIf<ExternalOpFrame>()) {
3032 if (frame->activeBoundary) {
3033 assert(!externalRegionStack.empty() &&
3034 externalRegionStack.back() == frame->activeBoundary);
3035 externalRegionStack.pop_back();
3036 frame->activeBoundary = 0;
3038 materializer.getBuilder().restoreInsertionPoint(
3039 frame->parentInsertionPoint);
3040 bool started =
false;
3041 if (failed(startExternalRegion(workStack.size() - 1, started)))
3046 return finishExternalOp(workStack.size() - 1);
3049 if (
auto *frame = workStack.back()->getIf<ResumeFrame>()) {
3050 handlerStack = std::move(frame->handlerStack);
3051 testState.rng = frame->rng;
3052 workStack.pop_back();
3055 llvm_unreachable(
"unknown evaluator frame");
3058 LogicalResult runTrampoline(SmallVector<ElaboratorValue> &rootValues) {
3059 while (!workStack.empty()) {
3060 auto *frame = workStack.back()->getIf<BlockFrame>();
3062 if (failed(completeFrame({})))
3067 Operation *op = frame->nextOp;
3069 (!frame->keepTerminator && op->hasTrait<OpTrait::IsTerminator>())) {
3070 SmallVector<ElaboratorValue> yieldedValues;
3071 if (!frame->block->empty() &&
3072 frame->block->back().hasTrait<OpTrait::IsTerminator>()) {
3073 Operation *terminator = frame->block->getTerminator();
3074 for (Value operand : terminator->getOperands())
3075 yieldedValues.push_back(frame->environment.at(operand));
3076 if (!frame->keepTerminator && frame->eraseTerminator &&
3077 materializer.isInPlace(terminator)) {
3078 auto &builder = materializer.getBuilder();
3079 if (builder.getBlock() == terminator->getBlock() &&
3080 builder.getInsertionPoint() == terminator->getIterator()) {
3083 builder.setInsertionPointToEnd(terminator->getBlock());
3085 terminator->erase();
3088 workStack.pop_back();
3089 if (workStack.empty()) {
3090 rootValues = std::move(yieldedValues);
3093 if (failed(completeFrame(std::move(yieldedValues))))
3098 Operation *nextOp = op->getNextNode();
3099 bool replay = frame->replay;
3100 frame->nextOp = nextOp;
3101 state = frame->environment;
3103 if (isSymbolic(op) && !supportsSymbolicValuesNonGenerically(op)) {
3104 auto result = visitOpGeneric(op);
3107 if (*result == DeletionKind::Keep &&
3108 failed(materializer.materialize(op, state, replay)))
3110 workStack.back()->get<BlockFrame>().environment = state;
3114 if (
auto perform = dyn_cast<PerformOp>(op)) {
3115 auto transfer = visitPerformTransfer(perform);
3116 if (failed(transfer))
3118 workStack.back()->get<BlockFrame>().environment = state;
3119 if (failed(handleControlTransfer(*transfer)))
3124 if (
auto resume = dyn_cast<ResumeOp>(op)) {
3125 if (failed(scheduleResume(resume)))
3130 if (isa<func::CallIndirectOp>(op))
3131 return op->emitOpError(
"cannot elaborate indirect function call");
3133 if (
auto call = dyn_cast<func::CallOp>(op)) {
3134 auto callee = sharedState.table.lookup<func::FuncOp>(call.getCallee());
3136 return call.emitOpError(
"cannot elaborate unresolved function call");
3137 if (callee.isExternal())
3138 return call.emitOpError(
"cannot elaborate external function call");
3139 if (callee.getBody().getBlocks().size() != 1)
3140 return call.emitOpError(
3141 "RTG elaboration only supports single-block callees");
3143 SmallVector<ElaboratorValue> arguments;
3144 arguments.reserve(call.getNumOperands());
3145 for (Value operand : call.getOperands())
3146 arguments.push_back(state.at(operand));
3148 workStack.back()->get<BlockFrame>().environment = state;
3149 workStack.emplace_back(
3150 std::make_unique<EvalFrame>(FunctionCallFrame{call}));
3151 if (failed(pushRegionFrame(callee.getBody(), arguments,
3159 if (
auto ifOp = dyn_cast<scf::IfOp>(op)) {
3160 bool condition = get<bool>(ifOp.getCondition());
3162 condition ? ifOp.getThenRegion() : ifOp.getElseRegion();
3163 workStack.back()->get<BlockFrame>().environment = state;
3166 workStack.emplace_back(
3167 std::make_unique<EvalFrame>(ScfIfFrame{ifOp, replay}));
3168 if (failed(pushRegionFrame(region, {},
false, replay,
3174 if (
auto forOp = dyn_cast<scf::ForOp>(op)) {
3175 if (!(std::holds_alternative<size_t>(state.at(forOp.getLowerBound())) &&
3176 std::holds_alternative<size_t>(state.at(forOp.getStep())) &&
3177 std::holds_alternative<size_t>(state.at(forOp.getUpperBound()))))
3178 return forOp->emitOpError(
"can only elaborate index type iterator");
3179 size_t lower = get<size_t>(forOp.getLowerBound());
3180 size_t step = get<size_t>(forOp.getStep());
3181 size_t upper = get<size_t>(forOp.getUpperBound());
3183 return forOp->emitOpError(
"loop step must be greater than zero");
3184 LexicalEnvironment loopEnvironment = state;
3185 for (
auto [iterArg, initArg] :
3186 llvm::zip(forOp.getRegionIterArgs(), forOp.getInitArgs()))
3187 loopEnvironment[iterArg] = state.at(initArg);
3188 workStack.back()->get<BlockFrame>().environment = state;
3189 if (lower >= upper) {
3190 for (
auto [result, iterArg] :
3191 llvm::zip(forOp->getResults(), forOp.getRegionIterArgs()))
3192 workStack.back()->get<BlockFrame>().environment[result] =
3193 loopEnvironment.at(iterArg);
3196 workStack.emplace_back(std::make_unique<EvalFrame>(
3197 ScfForFrame{forOp, lower, upper, step, replay,
false,
3198 std::move(loopEnvironment)}));
3199 if (failed(startForIteration(workStack.size() - 1)))
3204 if (
auto randomScope = dyn_cast<RandomScopeOp>(op)) {
3205 RngScope outerRng = testState.rng;
3206 testState.rng = randomScope.getSeed()
3207 ? RngScope(randomScope.getSeed()->getZExtValue())
3208 : testState.rng.getNested();
3209 workStack.back()->get<BlockFrame>().environment = state;
3210 workStack.emplace_back(std::make_unique<EvalFrame>(
3211 RandomScopeFrame(randomScope, std::move(outerRng), replay)));
3212 if (failed(pushRegionFrame(randomScope.getBodyRegion(), {},
3213 false, replay, state)))
3218 if (
auto withHandlers = dyn_cast<WithHandlersOp>(op)) {
3219 HandlerFrame handlerFrame;
3220 handlerFrame.promptToken = sharedState.createBoundaryToken();
3221 for (
auto [effect, region] : llvm::zip(
3222 withHandlers.getEffects(), withHandlers.getHandlerRegions()))
3223 handlerFrame.handlers[cast<FlatSymbolRefAttr>(effect).getAttr()] =
3225 uint64_t promptId = handlerFrame.promptToken;
3226 handlerStack.push_back(std::move(handlerFrame));
3227 workStack.back()->get<BlockFrame>().environment = state;
3228 workStack.emplace_back(std::make_unique<EvalFrame>(
3229 WithHandlersFrame{withHandlers, promptId, replay}));
3230 if (failed(pushRegionFrame(withHandlers.getBody(), {},
3231 false, replay, state)))
3236 if (op->getNumRegions() != 0) {
3237 workStack.back()->get<BlockFrame>().environment = state;
3238 if (failed(startExternalOp(op, replay)))
3243 auto result = dispatchOpVisitor(op);
3246 if (*result == DeletionKind::Keep &&
3247 failed(materializer.materialize(op, state, replay)))
3249 workStack.back()->get<BlockFrame>().environment = state;
3252 llvm::dbgs() <<
"Elaborated " << *op <<
" to\n[";
3253 llvm::interleaveComma(op->getResults(), llvm::dbgs(), [&](
auto res) {
3254 if (state.contains(res))
3255 llvm::dbgs() << state.at(res);
3257 llvm::dbgs() <<
"unknown";
3259 llvm::dbgs() <<
"]\n\n";
3265 LogicalResult elaborate(Region ®ion,
3266 ArrayRef<ElaboratorValue> regionArguments,
3267 bool keepTerminator,
3268 SmallVector<ElaboratorValue> &terminatorOperands) {
3269 if (!workStack.empty())
3270 return region.getParentOp()->emitOpError(
3271 "top-level elaboration cannot start with active evaluator frames");
3272 LexicalEnvironment savedState = std::move(state);
3273 LexicalEnvironment environment = savedState;
3274 if (failed(pushRegionFrame(region, regionArguments, keepTerminator,
3275 false, std::move(environment)))) {
3276 state = std::move(savedState);
3279 bool failedEvaluation = failed(runTrampoline(terminatorOperands));
3280 state = std::move(savedState);
3286 SharedState &sharedState;
3289 TestState &testState;
3293 Materializer &materializer;
3296 LexicalEnvironment state;
3299 ContextResourceAttrInterface currentContext;
3303 uint64_t sequenceBoundaryToken;
3306 uint64_t elaboratorBoundaryToken;
3309 SmallVector<uint64_t> externalRegionStack;
3312 AttributeToElaboratorValueConverter attrConverter;
3315 ElaboratorValueToAttributeConverter elabValConverter;
3318 SmallVector<HandlerFrame> handlerStack;
3321 EvalFrameStack workStack;
3323 friend struct ScfIfFrame;
3324 friend struct ScfForFrame;
3327LogicalResult ScfIfFrame::step(Elaborator &elaborator,
3328 ArrayRef<ElaboratorValue> yieldedValues) {
3329 auto *parent = elaborator.workStack[elaborator.workStack.size() - 2]
3330 ->getIf<BlockFrame>();
3332 return op->emitOpError(
"scf.if lost its parent block frame");
3333 for (
auto [result, value] : llvm::zip(op.getResults(), yieldedValues))
3334 parent->environment[result] = value;
3338LogicalResult ScfForFrame::step(Elaborator &elaborator,
3339 ArrayRef<ElaboratorValue> yieldedValues) {
3340 for (
auto [iterArg, value] : llvm::zip(op.getRegionIterArgs(), yieldedValues))
3341 loopEnvironment[iterArg] = value;
3342 return elaborator.startForIteration(elaborator.workStack.size() - 1);
3347Materializer::elaborateSequence(
const RandomizedSequenceStorage *seq,
3348 SmallVector<ElaboratorValue> &elabArgs) {
3350 sharedState.table.lookup<SequenceOp>(seq->sequence->familyName);
3353 OpBuilder builder(familyOp);
3354 auto seqOp = builder.cloneWithoutRegions(familyOp);
3355 auto name = sharedState.names.
newName(seq->sequence->familyName.getValue());
3356 seqOp.setSymName(name);
3357 seqOp.getBodyRegion().emplaceBlock();
3358 sharedState.table.insert(seqOp);
3359 assert(seqOp.getSymName() == name &&
"should not have been renamed");
3361 LLVM_DEBUG(llvm::dbgs() <<
"\n=== Elaborating sequence family @"
3362 << familyOp.getSymName() <<
" into @"
3363 << seqOp.getSymName() <<
" under context "
3364 << seq->context <<
"\n\n");
3366 Materializer materializer(OpBuilder::atBlockBegin(seqOp.getBody()), testState,
3367 sharedState, elabArgs);
3368 Elaborator elaborator(sharedState, testState, materializer, seq->context,
3369 sharedState.createBoundaryToken());
3370 SmallVector<ElaboratorValue> yieldedVals;
3371 if (failed(elaborator.elaborate(familyOp.getBodyRegion(), seq->sequence->args,
3372 false, yieldedVals)))
3375 seqOp.setSequenceType(
3376 SequenceType::get(builder.getContext(), materializer.getBlockArgTypes()));
3377 materializer.finalize();
3387struct ElaborationPass
3388 :
public rtg::impl::ElaborationPassBase<ElaborationPass> {
3391 void runOnOperation()
override;
3392 void matchTestsAgainstTargets(SymbolTable &table);
3393 LogicalResult elaborateModule(ModuleOp moduleOp, SymbolTable &table);
3397void ElaborationPass::runOnOperation() {
3398 auto moduleOp = getOperation();
3399 SymbolTable table(moduleOp);
3401 matchTestsAgainstTargets(table);
3403 if (failed(elaborateModule(moduleOp, table)))
3404 return signalPassFailure();
3407void ElaborationPass::matchTestsAgainstTargets(SymbolTable &table) {
3408 auto moduleOp = getOperation();
3410 for (
auto test : llvm::make_early_inc_range(moduleOp.getOps<TestOp>())) {
3411 if (test.getTargetAttr())
3414 bool matched =
false;
3416 for (
auto target : moduleOp.getOps<TargetOp>()) {
3420 bool isSubtype =
true;
3421 auto testEntries = test.getTargetType().getEntries();
3422 auto targetEntries = target.getTarget().getEntries();
3426 size_t targetIdx = 0;
3427 for (
auto testEntry : testEntries) {
3429 while (targetIdx < targetEntries.size() &&
3430 targetEntries[targetIdx].name.getValue() <
3431 testEntry.name.getValue())
3435 if (targetIdx >= targetEntries.size() ||
3436 targetEntries[targetIdx].name != testEntry.name ||
3437 targetEntries[targetIdx].type != testEntry.type) {
3446 IRRewriter rewriter(test);
3448 auto newTest = cast<TestOp>(test->clone());
3449 newTest.setSymName(test.getSymName().str() +
"_" +
3450 target.getSymName().str());
3454 newTest.setTargetAttr(target.getSymNameAttr());
3456 table.insert(newTest, rewriter.getInsertionPoint());
3460 if (matched || deleteUnmatchedTests)
3466 return isa<MemoryBlockType, ContextResourceTypeInterface>(type);
3469LogicalResult ElaborationPass::elaborateModule(ModuleOp moduleOp,
3470 SymbolTable &table) {
3471 SharedState state(moduleOp.getContext(), table);
3474 state.names.add(moduleOp);
3476 struct TargetElabResult {
3477 TargetElabResult(DictType targetType, uint32_t seed)
3478 : targetType(targetType), testState(seed) {}
3480 DictType targetType;
3481 SmallVector<ElaboratorValue> yields;
3482 TestState testState;
3486 DenseMap<StringAttr, TargetElabResult> targetMap;
3487 for (
auto targetOp : moduleOp.getOps<TargetOp>()) {
3488 LLVM_DEBUG(llvm::dbgs() <<
"=== Elaborating target @"
3489 << targetOp.getSymName() <<
"\n\n");
3491 auto [it, inserted] = targetMap.try_emplace(targetOp.getSymNameAttr(),
3492 targetOp.getTarget(), seed);
3493 auto &result = it->second;
3495 SmallVector<ElaboratorValue> blockArgs;
3496 Materializer targetMaterializer(OpBuilder::atBlockBegin(targetOp.getBody()),
3497 result.testState, state, blockArgs);
3498 Elaborator targetElaborator(state, result.testState, targetMaterializer);
3501 if (failed(targetElaborator.elaborate(targetOp.getBodyRegion(), {},
3506 targetMaterializer.finalize();
3511 for (
auto testOp : moduleOp.getOps<TestOp>()) {
3515 if (!testOp.getTargetAttr())
3518 LLVM_DEBUG(llvm::dbgs()
3519 <<
"\n=== Elaborating test @" << testOp.getTemplateName()
3520 <<
" for target @" << *testOp.getTarget() <<
"\n\n");
3523 auto &targetResult = targetMap.at(testOp.getTargetAttr());
3524 TestState testState(seed);
3525 testState.contextSwitches = targetResult.testState.contextSwitches;
3526 testState.name = testOp.getSymNameAttr();
3528 SmallVector<ElaboratorValue> filteredYields;
3530 for (
auto [entry, yield] :
3531 llvm::zip(targetResult.targetType.getEntries(), targetResult.yields)) {
3532 if (i >= testOp.getTargetType().getEntries().size())
3535 if (entry.name == testOp.getTargetType().getEntries()[i].name) {
3536 filteredYields.push_back(yield);
3543 SmallVector<ElaboratorValue> blockArgs;
3544 Materializer materializer(OpBuilder::atBlockBegin(testOp.getBody()),
3545 testState, state, blockArgs);
3547 for (
auto [arg, val] :
3548 llvm::zip(testOp.getBody()->getArguments(), filteredYields))
3550 materializer.map(val, arg);
3552 Elaborator elaborator(state, testState, materializer);
3553 SmallVector<ElaboratorValue> ignore;
3554 if (failed(elaborator.elaborate(testOp.getBodyRegion(), filteredYields,
3558 materializer.finalize();
assert(baseType &&"element must be base type")
static std::unique_ptr< Context > context
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)
false return failure()
Parse an optional keyword or string and set instance into 'result'.
if(parseOptionalKeywordOrOptionalString(parser, result.rawName, found)) return failure()
static InstancePath empty
StringRef newName(const Twine &name)
Return a unique name, derived from the input name, and add the new name to the internal namespace.
This helps visit TypeOp nodes.
FailureOr< DeletionKind > dispatchOpVisitor(Operation *op, ExtraArgs... args)
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)