21#include "mlir/Dialect/Arith/IR/Arith.h"
22#include "mlir/Dialect/Index/IR/IndexDialect.h"
23#include "mlir/Dialect/Index/IR/IndexOps.h"
24#include "mlir/Dialect/SCF/IR/SCF.h"
25#include "mlir/IR/IRMapping.h"
26#include "mlir/IR/PatternMatch.h"
27#include "llvm/ADT/DenseMapInfoVariant.h"
28#include "llvm/Support/Debug.h"
33#define GEN_PASS_DEF_ELABORATIONPASS
34#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc"
43#define DEBUG_TYPE "rtg-elaboration"
49 RngScope(
const RngScope &) =
delete;
50 RngScope &operator=(
const RngScope &) =
delete;
52 RngScope(RngScope &&) =
default;
53 RngScope &operator=(RngScope &&) =
default;
55 explicit RngScope(uint32_t seed) : rng(seed) {}
67 uint32_t getUniformlyInRange(uint32_t a, uint32_t b) {
68 const uint32_t diff = b - a + 1;
76 std::numeric_limits<uint32_t>::max() >> (32 - llvm::Log2_32_Ceil(diff));
85 RngScope getNested() {
return RngScope(rng()); }
99struct SequenceStorage;
100struct RandomizedSequenceStorage;
101struct InterleavedSequenceStorage;
103struct VirtualRegisterStorage;
104struct UniqueLabelStorage;
107struct MemoryBlockStorage;
108struct SymbolicComputationWithIdentityStorage;
109struct SymbolicComputationWithIdentityValue;
110struct SymbolicComputationStorage;
113using ElaboratorValue =
114 std::variant<TypedAttr, BagStorage *, bool, size_t, SequenceStorage *,
115 RandomizedSequenceStorage *, InterleavedSequenceStorage *,
116 SetStorage *, VirtualRegisterStorage *, UniqueLabelStorage *,
117 ArrayStorage *, TupleStorage *, MemoryStorage *,
118 MemoryBlockStorage *, SymbolicComputationWithIdentityStorage *,
119 SymbolicComputationWithIdentityValue *,
120 SymbolicComputationStorage *>;
123llvm::hash_code
hash_value(
const ElaboratorValue &val) {
125 [&val](
const auto &alternative) {
128 return llvm::hash_combine(val.index(), alternative);
141 static bool isEqual(
const bool &lhs,
const bool &rhs) {
return lhs == rhs; }
156template <
typename StorageTy>
157struct HashedStorage {
158 HashedStorage(
unsigned hashcode = 0, StorageTy *storage =
nullptr)
159 : hashcode(hashcode), storage(storage) {}
169template <
typename StorageTy>
170struct StorageKeyInfo {
171 static inline unsigned getHashValue(
const HashedStorage<StorageTy> &key) {
174 static inline unsigned getHashValue(
const StorageTy &key) {
178 static inline bool isEqual(
const HashedStorage<StorageTy> &lhs,
179 const HashedStorage<StorageTy> &rhs) {
180 return lhs.storage == rhs.storage;
182 static inline bool isEqual(
const StorageTy &lhs,
183 const HashedStorage<StorageTy> &rhs) {
187 return lhs.isEqual(rhs.storage);
197struct CachableStorage {
202struct SetStorage : CachableStorage {
203 static unsigned computeHash(
const SetVector<ElaboratorValue> &set,
205 llvm::hash_code setHash = 0;
206 for (
auto el : set) {
211 setHash = setHash ^ llvm::hash_combine(el);
213 return llvm::hash_combine(type, setHash);
216 SetStorage(SetVector<ElaboratorValue> &&set, Type type)
217 : hashcode(computeHash(set, type)), set(std::move(set)), type(type) {}
219 bool isEqual(
const SetStorage *other)
const {
224 bool allContained =
true;
226 allContained &= other->set.contains(el);
228 return hashcode == other->hashcode && set.size() == other->set.size() &&
229 allContained && type == other->type;
233 const unsigned hashcode;
236 const SetVector<ElaboratorValue> set;
247 type,
llvm::hash_combine_range(bag.begin(), bag.
end()))),
248 bag(std::move(bag)), type(type) {}
250 bool isEqual(
const BagStorage *other)
const {
251 return hashcode == other->hashcode && llvm::equal(bag, other->bag) &&
256 const unsigned hashcode;
268struct SequenceStorage {
269 SequenceStorage(StringAttr familyName, SmallVector<ElaboratorValue> &&args)
271 familyName,
llvm::hash_combine_range(args.begin(), args.
end()))),
272 familyName(familyName), args(std::move(args)) {}
274 bool isEqual(
const SequenceStorage *other)
const {
275 return hashcode == other->hashcode && familyName == other->familyName &&
280 const unsigned hashcode;
283 const StringAttr familyName;
286 const SmallVector<ElaboratorValue> args;
290struct InterleavedSequenceStorage {
291 InterleavedSequenceStorage(SmallVector<ElaboratorValue> &&sequences,
293 : sequences(std::move(sequences)), batchSize(batchSize),
295 llvm::hash_combine_range(sequences.begin(), sequences.
end()),
298 explicit InterleavedSequenceStorage(RandomizedSequenceStorage *sequence)
299 : sequences(SmallVector<ElaboratorValue>(1, sequence)), batchSize(1),
301 llvm::hash_combine_range(sequences.begin(), sequences.
end()),
304 bool isEqual(
const InterleavedSequenceStorage *other)
const {
305 return hashcode == other->hashcode && sequences == other->sequences &&
306 batchSize == other->batchSize;
309 const SmallVector<ElaboratorValue> sequences;
311 const uint32_t batchSize;
314 const unsigned hashcode;
319 ArrayStorage(Type type, SmallVector<ElaboratorValue> &&array)
321 type,
llvm::hash_combine_range(array.begin(), array.
end()))),
322 type(type), array(array) {}
324 bool isEqual(
const ArrayStorage *other)
const {
325 return hashcode == other->hashcode && type == other->type &&
326 array == other->array;
330 const unsigned hashcode;
337 const SmallVector<ElaboratorValue> array;
341struct TupleStorage : CachableStorage {
342 TupleStorage(SmallVector<ElaboratorValue> &&values)
343 : hashcode(
llvm::hash_combine_range(values.begin(), values.
end())),
344 values(std::move(values)) {}
346 bool isEqual(
const TupleStorage *other)
const {
347 return hashcode == other->hashcode && values == other->values;
351 const unsigned hashcode;
353 const SmallVector<ElaboratorValue> values;
356struct SymbolicComputationStorage {
357 SymbolicComputationStorage(
const DenseMap<Value, ElaboratorValue> &state,
359 : name(op->
getName()), resultTypes(op->getResultTypes()),
360 operands(
llvm::map_range(op->getOperands(),
361 [&](Value v) {
return state.lookup(v); })),
362 attributes(op->getAttrDictionary()),
363 properties(op->getPropertiesAsAttribute()),
364 hashcode(llvm::hash_combine(name, llvm::hash_combine_range(resultTypes),
365 llvm::hash_combine_range(operands),
366 attributes, op->hashProperties())) {}
368 bool isEqual(
const SymbolicComputationStorage *other)
const {
369 return hashcode == other->hashcode && name == other->name &&
370 resultTypes == other->resultTypes && operands == other->operands &&
371 attributes == other->attributes && properties == other->properties;
374 const OperationName name;
375 const SmallVector<Type> resultTypes;
376 const SmallVector<ElaboratorValue> operands;
377 const DictionaryAttr attributes;
378 const Attribute properties;
379 const unsigned hashcode;
390struct IdentityValue {
392 IdentityValue(Type type, Location loc) : type(type), loc(loc) {}
405 bool alreadyMaterialized =
false;
414struct VirtualRegisterStorage : IdentityValue {
415 VirtualRegisterStorage(VirtualRegisterConfigAttr allowedRegs, Type type,
417 : IdentityValue(type, loc), allowedRegs(allowedRegs) {}
424 const VirtualRegisterConfigAttr allowedRegs;
427struct UniqueLabelStorage : IdentityValue {
428 UniqueLabelStorage(
const ElaboratorValue &name, Location loc)
429 : IdentityValue(LabelType::
get(loc->getContext()), loc), name(name) {}
435 const ElaboratorValue name;
439struct MemoryBlockStorage : IdentityValue {
440 MemoryBlockStorage(
const APInt &baseAddress,
const APInt &endAddress,
441 Type type, Location loc)
442 : IdentityValue(type, loc), baseAddress(baseAddress),
443 endAddress(endAddress) {}
448 const APInt baseAddress;
451 const APInt endAddress;
455struct MemoryStorage : IdentityValue {
456 MemoryStorage(MemoryBlockStorage *memoryBlock,
size_t size,
size_t alignment,
458 : IdentityValue(MemoryType::
get(memoryBlock->type.getContext(),
461 memoryBlock(memoryBlock), size(size), alignment(alignment) {}
463 MemoryBlockStorage *memoryBlock;
465 const size_t alignment;
469struct RandomizedSequenceStorage : IdentityValue {
470 RandomizedSequenceStorage(ContextResourceAttrInterface context,
471 SequenceStorage *sequence, Location loc)
473 RandomizedSequenceType::
get(sequence->familyName.getContext()),
475 context(context), sequence(sequence) {}
478 const ContextResourceAttrInterface context;
480 const SequenceStorage *sequence;
484struct SymbolicComputationWithIdentityStorage : IdentityValue {
485 SymbolicComputationWithIdentityStorage(
486 const DenseMap<Value, ElaboratorValue> &state, Operation *op)
487 : IdentityValue(op->getResult(0).getType(), op->
getLoc()),
488 name(op->
getName()), resultTypes(op->getResultTypes()),
489 operands(
llvm::map_range(op->getOperands(),
490 [&](Value v) {
return state.lookup(v); })),
491 attributes(op->getAttrDictionary()),
492 properties(op->getPropertiesAsAttribute()) {}
494 const OperationName name;
495 const SmallVector<Type> resultTypes;
496 const SmallVector<ElaboratorValue> operands;
497 const DictionaryAttr attributes;
498 const Attribute properties;
501struct SymbolicComputationWithIdentityValue : IdentityValue {
502 SymbolicComputationWithIdentityValue(
503 Type type,
const SymbolicComputationWithIdentityStorage *storage,
505 : IdentityValue(type, storage->loc), storage(storage), idx(idx) {
508 "Use SymbolicComputationWithIdentityStorage for result with index 0.");
511 const SymbolicComputationWithIdentityStorage *storage;
525 template <
typename StorageTy,
typename... Args>
526 StorageTy *internalize(Args &&...args) {
527 static_assert(!std::is_base_of_v<IdentityValue, StorageTy> &&
528 "values with identity must not be internalized");
530 StorageTy storage(std::forward<Args>(args)...);
532 auto existing = getInternSet<StorageTy>().insert_as(
533 HashedStorage<StorageTy>(storage.hashcode), storage);
534 StorageTy *&storagePtr = existing.first->storage;
537 new (allocator.Allocate<StorageTy>()) StorageTy(std::move(storage));
542 template <
typename StorageTy,
typename... Args>
543 StorageTy *create(Args &&...args) {
544 static_assert(std::is_base_of_v<IdentityValue, StorageTy> &&
545 "values with structural equivalence must be internalized");
547 return new (allocator.Allocate<StorageTy>())
548 StorageTy(std::forward<Args>(args)...);
552 template <
typename StorageTy>
553 DenseSet<HashedStorage<StorageTy>, StorageKeyInfo<StorageTy>> &
555 if constexpr (std::is_same_v<StorageTy, ArrayStorage>)
556 return internedArrays;
557 else if constexpr (std::is_same_v<StorageTy, SetStorage>)
559 else if constexpr (std::is_same_v<StorageTy, BagStorage>)
561 else if constexpr (std::is_same_v<StorageTy, SequenceStorage>)
562 return internedSequences;
563 else if constexpr (std::is_same_v<StorageTy, RandomizedSequenceStorage>)
564 return internedRandomizedSequences;
565 else if constexpr (std::is_same_v<StorageTy, InterleavedSequenceStorage>)
566 return internedInterleavedSequences;
567 else if constexpr (std::is_same_v<StorageTy, TupleStorage>)
568 return internedTuples;
569 else if constexpr (std::is_same_v<StorageTy, SymbolicComputationStorage>)
570 return internedSymbolicComputationWithIdentityValues;
572 static_assert(!
sizeof(StorageTy),
573 "no intern set available for this storage type.");
578 llvm::BumpPtrAllocator allocator;
583 DenseSet<HashedStorage<ArrayStorage>, StorageKeyInfo<ArrayStorage>>
585 DenseSet<HashedStorage<SetStorage>, StorageKeyInfo<SetStorage>> internedSets;
586 DenseSet<HashedStorage<BagStorage>, StorageKeyInfo<BagStorage>> internedBags;
587 DenseSet<HashedStorage<SequenceStorage>, StorageKeyInfo<SequenceStorage>>
589 DenseSet<HashedStorage<RandomizedSequenceStorage>,
590 StorageKeyInfo<RandomizedSequenceStorage>>
591 internedRandomizedSequences;
592 DenseSet<HashedStorage<InterleavedSequenceStorage>,
593 StorageKeyInfo<InterleavedSequenceStorage>>
594 internedInterleavedSequences;
595 DenseSet<HashedStorage<TupleStorage>, StorageKeyInfo<TupleStorage>>
597 DenseSet<HashedStorage<SymbolicComputationStorage>,
598 StorageKeyInfo<SymbolicComputationStorage>>
599 internedSymbolicComputationWithIdentityValues;
606static llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
607 const ElaboratorValue &value);
609static void print(TypedAttr val, llvm::raw_ostream &os) {
610 os <<
"<attr " << val <<
">";
613static void print(BagStorage *val, llvm::raw_ostream &os) {
615 llvm::interleaveComma(val->bag, os,
616 [&](
const std::pair<ElaboratorValue, uint64_t> &el) {
617 os << el.first <<
" -> " << el.second;
619 os <<
"} at " << val <<
">";
622static void print(
bool val, llvm::raw_ostream &os) {
623 os <<
"<bool " << (val ?
"true" :
"false") <<
">";
626static void print(
size_t val, llvm::raw_ostream &os) {
627 os <<
"<index " << val <<
">";
630static void print(SequenceStorage *val, llvm::raw_ostream &os) {
631 os <<
"<sequence @" << val->familyName.getValue() <<
"(";
632 llvm::interleaveComma(val->args, os,
633 [&](
const ElaboratorValue &val) { os << val; });
634 os <<
") at " << val <<
">";
637static void print(RandomizedSequenceStorage *val, llvm::raw_ostream &os) {
638 os <<
"<randomized-sequence derived from @"
639 << val->sequence->familyName.getValue() <<
" under context "
640 << val->context <<
"(";
641 llvm::interleaveComma(val->sequence->args, os,
642 [&](
const ElaboratorValue &val) { os << val; });
643 os <<
") at " << val <<
">";
646static void print(InterleavedSequenceStorage *val, llvm::raw_ostream &os) {
647 os <<
"<interleaved-sequence [";
648 llvm::interleaveComma(val->sequences, os,
649 [&](
const ElaboratorValue &val) { os << val; });
650 os <<
"] batch-size " << val->batchSize <<
" at " << val <<
">";
653static void print(ArrayStorage *val, llvm::raw_ostream &os) {
655 llvm::interleaveComma(val->array, os,
656 [&](
const ElaboratorValue &val) { os << val; });
657 os <<
"] at " << val <<
">";
660static void print(SetStorage *val, llvm::raw_ostream &os) {
662 llvm::interleaveComma(val->set, os,
663 [&](
const ElaboratorValue &val) { os << val; });
664 os <<
"} at " << val <<
">";
667static void print(
const VirtualRegisterStorage *val, llvm::raw_ostream &os) {
668 os <<
"<virtual-register " << val <<
" " << val->allowedRegs <<
">";
671static void print(
const UniqueLabelStorage *val, llvm::raw_ostream &os) {
672 os <<
"<unique-label " << val <<
" " << val->name <<
">";
675static void print(
const TupleStorage *val, llvm::raw_ostream &os) {
677 llvm::interleaveComma(val->values, os,
678 [&](
const ElaboratorValue &val) { os << val; });
682static void print(
const MemoryStorage *val, llvm::raw_ostream &os) {
683 os <<
"<memory {" << ElaboratorValue(val->memoryBlock)
684 <<
", size=" << val->size <<
", alignment=" << val->alignment <<
"}>";
687static void print(
const MemoryBlockStorage *val, llvm::raw_ostream &os) {
688 os <<
"<memory-block {"
689 <<
", address-width=" << val->baseAddress.getBitWidth()
690 <<
", base-address=" << val->baseAddress
691 <<
", end-address=" << val->endAddress <<
"}>";
694static void print(
const SymbolicComputationWithIdentityValue *val,
695 llvm::raw_ostream &os) {
696 os <<
"<symbolic-computation-with-identity-value (" << val->storage <<
") at "
700static void print(
const SymbolicComputationWithIdentityStorage *val,
701 llvm::raw_ostream &os) {
702 os <<
"<symbolic-computation-with-identity " << val->name <<
"(";
703 llvm::interleaveComma(val->operands, os,
704 [&](
const ElaboratorValue &val) { os << val; });
705 os <<
") -> " << val->resultTypes <<
" with attributes " << val->attributes
706 <<
" and properties " << val->properties;
710static void print(
const SymbolicComputationStorage *val,
711 llvm::raw_ostream &os) {
712 os <<
"<symbolic-computation " << val->name <<
"(";
713 llvm::interleaveComma(val->operands, os,
714 [&](
const ElaboratorValue &val) { os << val; });
715 os <<
") -> " << val->resultTypes <<
" with attributes " << val->attributes
716 <<
" and properties " << val->properties;
721 const ElaboratorValue &value) {
722 std::visit([&](
auto val) {
print(val, os); }, value);
737class AttributeToElaboratorValueConverter {
739 AttributeToElaboratorValueConverter(Internalizer &internalizer)
740 : internalizer(internalizer) {}
743 FailureOr<ElaboratorValue>
convert(Attribute attr) {
744 return llvm::TypeSwitch<Attribute, FailureOr<ElaboratorValue>>(attr)
745 .Case<IntegerAttr, SetAttr, TupleAttr>(
746 [&](
auto attr) {
return convert(attr); })
747 .Case<TypedAttr>([&](
auto typedAttr) -> FailureOr<ElaboratorValue> {
748 return ElaboratorValue(typedAttr);
751 [&](Attribute) -> FailureOr<ElaboratorValue> {
return failure(); });
755 FailureOr<ElaboratorValue>
convert(IntegerAttr attr) {
756 if (attr.getType().isSignlessInteger(1))
757 return ElaboratorValue(
bool(attr.getInt()));
758 if (isa<IndexType>(attr.getType()))
759 return ElaboratorValue(
size_t(attr.getInt()));
760 return ElaboratorValue(attr);
763 FailureOr<ElaboratorValue>
convert(SetAttr setAttr) {
764 SetVector<ElaboratorValue> set;
765 for (
auto element : *setAttr.getElements()) {
766 auto converted =
convert(element);
767 if (failed(converted))
769 set.insert(*converted);
772 internalizer.internalize<SetStorage>(std::move(set), setAttr.getType());
774 storage->attrCache = setAttr;
775 return ElaboratorValue(storage);
778 FailureOr<ElaboratorValue>
convert(TupleAttr tupleAttr) {
779 SmallVector<ElaboratorValue> values;
780 for (
auto element : tupleAttr.getElements()) {
781 auto converted =
convert(element);
782 if (failed(converted))
784 values.push_back(*converted);
786 auto *storage = internalizer.internalize<TupleStorage>(std::move(values));
788 storage->attrCache = tupleAttr;
789 return ElaboratorValue(storage);
792 Internalizer &internalizer;
798class ElaboratorValueToAttributeConverter {
800 ElaboratorValueToAttributeConverter(MLIRContext *context)
801 : context(context) {}
805 TypedAttr
convert(
const ElaboratorValue &value) {
807 [&](
auto val) -> TypedAttr {
808 if constexpr (std::is_base_of_v<CachableStorage,
809 std::remove_pointer_t<
810 std::decay_t<
decltype(value)>>>) {
812 return val->attrCache;
820 TypedAttr visit(TypedAttr val) {
return val; }
822 TypedAttr visit(
bool val) {
823 return IntegerAttr::get(IntegerType::get(context, 1), val);
826 TypedAttr visit(
size_t val) {
827 return IntegerAttr::get(IndexType::get(context), val);
830 TypedAttr visit(SetStorage *val) {
831 DenseSet<TypedAttr> elements;
832 for (
auto element : val->set) {
833 auto converted =
convert(element);
836 auto typedAttr = dyn_cast<TypedAttr>(converted);
839 elements.insert(typedAttr);
841 return SetAttr::get(cast<SetType>(val->type), &elements);
844 TypedAttr visit(TupleStorage *val) {
845 SmallVector<TypedAttr> elements;
846 for (
auto element : val->values) {
847 auto converted =
convert(element);
850 auto typedAttr = dyn_cast<TypedAttr>(converted);
853 elements.push_back(typedAttr);
855 return TupleAttr::get(context, elements);
861#define VISIT_UNSUPPORTED(STORAGETYPE) \
863 TypedAttr visit(STORAGETYPE *val) { return {}; }
878#undef VISIT_UNSUPPORTED
880 MLIRContext *context;
893 SharedState(MLIRContext *ctxt, SymbolTable &table)
894 : ctxt(ctxt), table(table) {}
899 Internalizer internalizer;
904 explicit TestState(
unsigned seed) : rng(RngScope(seed)) {}
911 std::pair<ContextResourceAttrInterface, ContextResourceAttrInterface>,
922 Materializer(OpBuilder builder, TestState &testState,
923 SharedState &sharedState,
924 SmallVector<ElaboratorValue> &blockArgs)
925 : builder(builder), testState(testState), sharedState(sharedState),
926 blockArgs(blockArgs), attrConverter(builder.getContext()) {}
930 Value materialize(ElaboratorValue val, Location loc,
931 function_ref<InFlightDiagnostic()> emitError) {
932 auto iter = materializedValues.find(val);
933 if (iter != materializedValues.end())
936 LLVM_DEBUG(llvm::dbgs() <<
"Materializing " << val);
938 if (
auto res = tryMaterializeAsConstant(val, loc))
943 Value res = std::visit(
945 if constexpr (std::is_base_of_v<IdentityValue,
946 std::remove_pointer_t<
947 std::decay_t<
decltype(value)>>>) {
948 if (identityValueRoot.contains(value)) {
951 static_cast<IdentityValue *
>(value)->alreadyMaterialized;
952 assert(!materialized &&
"must not already be materialized");
956 return visit(value, loc, emitError);
959 Value arg = builder.getBlock()->addArgument(value->type, loc);
960 blockArgs.push_back(val);
961 blockArgTypes.push_back(arg.getType());
962 materializedValues[val] = arg;
966 return visit(value, loc, emitError);
970 LLVM_DEBUG(llvm::dbgs() <<
" to\n" << res <<
"\n\n");
975 bool isInPlace(Operation *op)
const {
976 return builder.getBlock()->getParent() == op->getParentRegion();
985 LogicalResult materialize(Operation *op,
986 DenseMap<Value, ElaboratorValue> &state) {
987 if (op->getNumRegions() > 0)
988 return op->emitOpError(
"ops with nested regions must be elaborated away");
996 for (
auto res : op->getResults())
997 if (!res.use_empty())
998 return op->emitOpError(
999 "ops with results that have uses are not supported");
1001 if (isInPlace(op)) {
1004 deleteOpsUntil([&](
auto iter) {
return &*iter == op; });
1006 if (builder.getInsertionPoint() == builder.getBlock()->end())
1007 return op->emitError(
"operation did not occur after the current "
1008 "materializer insertion point");
1010 LLVM_DEBUG(llvm::dbgs() <<
"Modifying in-place: " << *op <<
"\n\n");
1012 LLVM_DEBUG(llvm::dbgs() <<
"Materializing a clone of " << *op <<
"\n\n");
1013 op = builder.clone(*op);
1014 builder.setInsertionPoint(op);
1017 for (
auto &operand : op->getOpOperands()) {
1018 auto emitError = [&]() {
1019 auto diag = op->emitError();
1020 diag.attachNote(op->getLoc())
1021 <<
"while materializing value for operand#"
1022 << operand.getOperandNumber();
1026 auto elabVal = state.at(operand.get());
1027 Value val = materialize(elabVal, op->getLoc(), emitError);
1031 state[val] = elabVal;
1035 builder.setInsertionPointAfter(op);
1042 deleteOpsUntil([](
auto iter) {
return false; });
1044 for (
auto *op :
llvm::reverse(toDelete))
1051 void registerIdentityValue(IdentityValue *val) {
1052 identityValueRoot.insert(val);
1055 ArrayRef<Type> getBlockArgTypes()
const {
return blockArgTypes; }
1057 void map(ElaboratorValue eval, Value val) { materializedValues[eval] = val; }
1059 template <
typename OpTy,
typename... Args>
1060 OpTy create(Location location, Args &&...args) {
1061 return OpTy::create(builder, location, std::forward<Args>(args)...);
1065 Value tryMaterializeAsConstant(ElaboratorValue val, Location loc) {
1066 if (
auto attr = attrConverter.convert(val)) {
1067 Value res = ConstantOp::create(builder, loc, attr);
1068 materializedValues[val] = res;
1075 SequenceOp elaborateSequence(
const RandomizedSequenceStorage *
seq,
1076 SmallVector<ElaboratorValue> &elabArgs);
1078 void deleteOpsUntil(function_ref<
bool(Block::iterator)> stop) {
1079 auto ip = builder.getInsertionPoint();
1080 while (ip != builder.getBlock()->end() && !stop(ip)) {
1081 LLVM_DEBUG(llvm::dbgs() <<
"Marking to be deleted: " << *ip <<
"\n\n");
1082 toDelete.push_back(&*ip);
1084 builder.setInsertionPointAfter(&*ip);
1085 ip = builder.getInsertionPoint();
1089 Value visit(TypedAttr val, Location loc,
1090 function_ref<InFlightDiagnostic()> emitError) {
1094 Value visit(
size_t val, Location loc,
1095 function_ref<InFlightDiagnostic()> emitError) {
1099 Value visit(
bool val, Location loc,
1100 function_ref<InFlightDiagnostic()> emitError) {
1104 Value visit(ArrayStorage *val, Location loc,
1105 function_ref<InFlightDiagnostic()> emitError) {
1106 SmallVector<Value> elements;
1107 elements.reserve(val->array.size());
1108 for (
auto el : val->array) {
1109 auto materialized = materialize(el, loc, emitError);
1113 elements.push_back(materialized);
1116 Value res = ArrayCreateOp::create(builder, loc, val->type, elements);
1117 materializedValues[val] = res;
1121 Value visit(SetStorage *val, Location loc,
1122 function_ref<InFlightDiagnostic()> emitError) {
1123 SmallVector<Value> elements;
1124 elements.reserve(val->set.size());
1125 for (
auto el : val->set) {
1126 auto materialized = materialize(el, loc, emitError);
1130 elements.push_back(materialized);
1133 auto res = SetCreateOp::create(builder, loc, val->type, elements);
1134 materializedValues[val] = res;
1138 Value visit(BagStorage *val, Location loc,
1139 function_ref<InFlightDiagnostic()> emitError) {
1140 SmallVector<Value> values, weights;
1141 values.reserve(val->bag.size());
1142 weights.reserve(val->bag.size());
1143 for (
auto [val, weight] : val->bag) {
1144 auto materializedVal = materialize(val, loc, emitError);
1145 auto materializedWeight = materialize(weight, loc, emitError);
1146 if (!materializedVal || !materializedWeight)
1149 values.push_back(materializedVal);
1150 weights.push_back(materializedWeight);
1153 auto res = BagCreateOp::create(builder, loc, val->type, values, weights);
1154 materializedValues[val] = res;
1158 Value visit(MemoryBlockStorage *val, Location loc,
1159 function_ref<InFlightDiagnostic()> emitError) {
1160 auto intType = builder.getIntegerType(val->baseAddress.getBitWidth());
1161 Value res = MemoryBlockDeclareOp::create(
1162 builder, val->loc, val->type,
1163 IntegerAttr::get(intType, val->baseAddress),
1164 IntegerAttr::get(intType, val->endAddress));
1165 materializedValues[val] = res;
1169 Value visit(MemoryStorage *val, Location loc,
1170 function_ref<InFlightDiagnostic()> emitError) {
1171 auto memBlock = materialize(val->memoryBlock, val->loc, emitError);
1172 auto memSize = materialize(val->size, val->loc, emitError);
1173 auto memAlign = materialize(val->alignment, val->loc, emitError);
1174 if (!(memBlock && memSize && memAlign))
1178 MemoryAllocOp::create(builder, val->loc, memBlock, memSize, memAlign);
1179 materializedValues[val] = res;
1183 Value visit(SequenceStorage *val, Location loc,
1184 function_ref<InFlightDiagnostic()> emitError) {
1185 emitError() <<
"materializing a non-randomized sequence not supported yet";
1189 Value visit(RandomizedSequenceStorage *val, Location loc,
1190 function_ref<InFlightDiagnostic()> emitError) {
1196 SmallVector<ElaboratorValue> elabArgs;
1199 SequenceOp seqOp = elaborateSequence(val, elabArgs);
1205 SmallVector<Value> args;
1206 SmallVector<Type> argTypes;
1207 for (
auto arg : elabArgs) {
1208 Value materialized = materialize(arg, val->loc, emitError);
1212 args.push_back(materialized);
1213 argTypes.push_back(materialized.getType());
1216 Value res = GetSequenceOp::create(
1217 builder, val->loc, SequenceType::get(builder.getContext(), argTypes),
1218 seqOp.getSymName());
1223 res = SubstituteSequenceOp::create(builder, val->loc, res, args);
1225 res = RandomizeSequenceOp::create(builder, val->loc, res);
1227 materializedValues[val] = res;
1231 Value visit(InterleavedSequenceStorage *val, Location loc,
1232 function_ref<InFlightDiagnostic()> emitError) {
1233 SmallVector<Value> sequences;
1234 for (
auto seqVal : val->sequences) {
1235 Value materialized = materialize(seqVal, loc, emitError);
1239 sequences.push_back(materialized);
1242 if (sequences.size() == 1)
1243 return sequences[0];
1246 InterleaveSequencesOp::create(builder, loc, sequences, val->batchSize);
1247 materializedValues[val] = res;
1251 Value visit(VirtualRegisterStorage *val, Location loc,
1252 function_ref<InFlightDiagnostic()> emitError) {
1253 Value res = VirtualRegisterOp::create(builder, val->loc, val->allowedRegs);
1254 materializedValues[val] = res;
1258 Value visit(UniqueLabelStorage *val, Location loc,
1259 function_ref<InFlightDiagnostic()> emitError) {
1260 auto materialized = materialize(val->name, val->loc, emitError);
1263 Value res = LabelUniqueDeclOp::create(builder, val->loc, materialized);
1264 materializedValues[val] = res;
1268 Value visit(TupleStorage *val, Location loc,
1269 function_ref<InFlightDiagnostic()> emitError) {
1270 SmallVector<Value> materialized;
1271 materialized.reserve(val->values.size());
1272 for (
auto v : val->values)
1273 materialized.push_back(materialize(v, loc, emitError));
1274 Value res = TupleCreateOp::create(builder, loc, materialized);
1275 materializedValues[val] = res;
1279 Value visit(SymbolicComputationWithIdentityValue *val, Location loc,
1280 function_ref<InFlightDiagnostic()> emitError) {
1281 auto *noConstStorage =
1282 const_cast<SymbolicComputationWithIdentityStorage *
>(val->storage);
1283 auto res0 = materialize(noConstStorage, loc, emitError);
1287 auto *op = res0.getDefiningOp();
1288 auto res = op->getResults()[val->idx];
1289 materializedValues[val] = res;
1293 Value visit(SymbolicComputationWithIdentityStorage *val, Location loc,
1294 function_ref<InFlightDiagnostic()> emitError) {
1295 SmallVector<Value> operands;
1296 for (
auto operand : val->operands) {
1297 auto materialized = materialize(operand, val->loc, emitError);
1301 operands.push_back(materialized);
1304 OperationState state(val->loc, val->name);
1305 state.addTypes(val->resultTypes);
1306 state.attributes = val->attributes;
1307 state.propertiesAttr = val->properties;
1308 state.addOperands(operands);
1309 auto *op = builder.create(state);
1311 materializedValues[val] = op->getResult(0);
1312 return op->getResult(0);
1315 Value visit(SymbolicComputationStorage *val, Location loc,
1316 function_ref<InFlightDiagnostic()> emitError) {
1317 SmallVector<Value> operands;
1318 for (
auto operand : val->operands) {
1319 auto materialized = materialize(operand, loc, emitError);
1323 operands.push_back(materialized);
1326 OperationState state(loc, val->name);
1327 state.addTypes(val->resultTypes);
1328 state.attributes = val->attributes;
1329 state.propertiesAttr = val->properties;
1330 state.addOperands(operands);
1331 auto *op = builder.create(state);
1333 for (
auto res : op->getResults())
1334 materializedValues[val] = res;
1336 return op->getResult(0);
1345 DenseMap<ElaboratorValue, Value> materializedValues;
1351 SmallVector<Operation *> toDelete;
1353 TestState &testState;
1354 SharedState &sharedState;
1359 SmallVector<ElaboratorValue> &blockArgs;
1360 SmallVector<Type> blockArgTypes;
1365 DenseSet<IdentityValue *> identityValueRoot;
1368 ElaboratorValueToAttributeConverter attrConverter;
1377enum class DeletionKind { Keep,
Delete };
1380class Elaborator :
public RTGOpVisitor<Elaborator, FailureOr<DeletionKind>> {
1383 using RTGBase::visitOp;
1385 Elaborator(SharedState &sharedState, TestState &testState,
1386 Materializer &materializer,
1387 ContextResourceAttrInterface currentContext = {})
1388 : sharedState(sharedState), testState(testState),
1389 materializer(materializer), currentContext(currentContext),
1390 attrConverter(sharedState.internalizer),
1391 elabValConverter(sharedState.ctxt) {}
1393 template <
typename ValueTy>
1394 inline ValueTy
get(Value val)
const {
1395 return std::get<ValueTy>(state.at(val));
1400 return visitOpGeneric(op);
1404 return visitOpGeneric(op);
1407 FailureOr<DeletionKind> visitOp(GetSequenceOp op) {
1408 SmallVector<ElaboratorValue> replacements;
1409 state[op.getResult()] =
1410 sharedState.internalizer.internalize<SequenceStorage>(
1411 op.getSequenceAttr().getAttr(), std::move(replacements));
1412 return DeletionKind::Delete;
1415 FailureOr<DeletionKind> visitOp(SubstituteSequenceOp op) {
1416 if (isSymbolic(state.at(op.getSequence())))
1417 return visitOpGeneric(op);
1419 auto *
seq = get<SequenceStorage *>(op.getSequence());
1421 SmallVector<ElaboratorValue> replacements(
seq->args);
1422 for (
auto replacement : op.getReplacements())
1423 replacements.push_back(state.at(replacement));
1425 state[op.getResult()] =
1426 sharedState.internalizer.internalize<SequenceStorage>(
1427 seq->familyName, std::move(replacements));
1429 return DeletionKind::Delete;
1432 FailureOr<DeletionKind> visitOp(RandomizeSequenceOp op) {
1433 auto *
seq = get<SequenceStorage *>(op.getSequence());
1434 auto *randomizedSeq =
1435 sharedState.internalizer.create<RandomizedSequenceStorage>(
1436 currentContext,
seq, op.getLoc());
1437 materializer.registerIdentityValue(randomizedSeq);
1438 state[op.getResult()] =
1439 sharedState.internalizer.internalize<InterleavedSequenceStorage>(
1441 return DeletionKind::Delete;
1444 FailureOr<DeletionKind> visitOp(InterleaveSequencesOp op) {
1445 SmallVector<ElaboratorValue> sequences;
1446 for (
auto seq : op.getSequences())
1447 sequences.push_back(state.at(
seq));
1449 state[op.getResult()] =
1450 sharedState.internalizer.internalize<InterleavedSequenceStorage>(
1451 std::move(sequences), op.getBatchSize());
1452 return DeletionKind::Delete;
1456 LogicalResult isValidContext(ElaboratorValue value, Operation *op)
const {
1457 if (std::holds_alternative<RandomizedSequenceStorage *>(value)) {
1458 auto *
seq = std::get<RandomizedSequenceStorage *>(value);
1459 if (
seq->context != currentContext) {
1460 auto err = op->emitError(
"attempting to place sequence derived from ")
1461 <<
seq->sequence->familyName.getValue() <<
" under context "
1463 <<
", but it was previously randomized for context ";
1465 err <<
seq->context;
1473 auto *interVal = std::get<InterleavedSequenceStorage *>(value);
1474 for (
auto val : interVal->sequences)
1475 if (failed(isValidContext(val, op)))
1480 FailureOr<DeletionKind> visitOp(EmbedSequenceOp op) {
1481 auto *seqVal = get<InterleavedSequenceStorage *>(op.getSequence());
1482 if (failed(isValidContext(seqVal, op)))
1485 return DeletionKind::Keep;
1488 FailureOr<DeletionKind> visitOp(SetCreateOp op) {
1489 SetVector<ElaboratorValue> set;
1490 for (
auto val : op.getElements())
1491 set.insert(state.at(val));
1493 state[op.getSet()] = sharedState.internalizer.internalize<SetStorage>(
1494 std::move(set), op.getSet().getType());
1495 return DeletionKind::Delete;
1498 FailureOr<DeletionKind> visitOp(SetSelectRandomOp op) {
1499 auto set = get<SetStorage *>(op.getSet())->set;
1502 return op->emitError(
"cannot select from an empty set");
1504 size_t selected = testState.rng.getUniformlyInRange(0, set.size() - 1);
1505 state[op.getResult()] = set[selected];
1506 return DeletionKind::Delete;
1509 FailureOr<DeletionKind> visitOp(SetDifferenceOp op) {
1510 auto original = get<SetStorage *>(op.getOriginal())->set;
1511 auto diff = get<SetStorage *>(op.getDiff())->set;
1513 SetVector<ElaboratorValue> result(original);
1514 result.set_subtract(diff);
1516 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1517 std::move(result), op.getResult().getType());
1518 return DeletionKind::Delete;
1521 FailureOr<DeletionKind> visitOp(SetUnionOp op) {
1522 SetVector<ElaboratorValue> result;
1523 for (
auto set : op.getSets())
1524 result.set_union(
get<SetStorage *>(set)->set);
1526 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1527 std::move(result), op.getType());
1528 return DeletionKind::Delete;
1531 FailureOr<DeletionKind> visitOp(SetSizeOp op) {
1532 auto size = get<SetStorage *>(op.getSet())->set.size();
1533 state[op.getResult()] = size;
1534 return DeletionKind::Delete;
1540 FailureOr<DeletionKind> visitOp(SetCartesianProductOp op) {
1541 SetVector<ElaboratorValue> result;
1542 SmallVector<SmallVector<ElaboratorValue>> tuples;
1543 tuples.push_back({});
1545 for (
auto input : op.getInputs()) {
1546 auto &set = get<SetStorage *>(input)->set;
1548 SetVector<ElaboratorValue>
empty;
1549 state[op.getResult()] =
1550 sharedState.internalizer.internalize<SetStorage>(std::move(
empty),
1552 return DeletionKind::Delete;
1555 for (
unsigned i = 0, e = tuples.size(); i < e; ++i) {
1556 for (
auto setEl : set.getArrayRef().drop_back()) {
1557 tuples.push_back(tuples[i]);
1558 tuples.back().push_back(setEl);
1560 tuples[i].push_back(set.back());
1564 for (
auto &tup : tuples)
1566 sharedState.internalizer.internalize<TupleStorage>(std::move(tup)));
1568 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1569 std::move(result), op.getType());
1570 return DeletionKind::Delete;
1573 FailureOr<DeletionKind> visitOp(SetConvertToBagOp op) {
1574 auto set = get<SetStorage *>(op.getInput())->set;
1576 for (
auto val : set)
1577 bag.insert({val, 1});
1578 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1579 std::move(bag), op.getType());
1580 return DeletionKind::Delete;
1583 FailureOr<DeletionKind> visitOp(BagCreateOp op) {
1585 for (
auto [val, multiple] :
1586 llvm::zip(op.getElements(), op.getMultiples())) {
1590 bag[state.at(val)] += get<size_t>(multiple);
1593 state[op.getBag()] = sharedState.internalizer.internalize<BagStorage>(
1594 std::move(bag), op.getType());
1595 return DeletionKind::Delete;
1598 FailureOr<DeletionKind> visitOp(BagSelectRandomOp op) {
1599 auto bag = get<BagStorage *>(op.getBag())->bag;
1602 return op->emitError(
"cannot select from an empty bag");
1604 SmallVector<std::pair<ElaboratorValue, uint32_t>> prefixSum;
1605 prefixSum.reserve(bag.size());
1606 uint32_t accumulator = 0;
1607 for (
auto [val, weight] : bag) {
1608 accumulator += weight;
1609 prefixSum.push_back({val, accumulator});
1612 auto idx = testState.rng.getUniformlyInRange(0, accumulator - 1);
1613 auto *iter = llvm::upper_bound(
1615 [](uint32_t a,
const std::pair<ElaboratorValue, uint32_t> &b) {
1616 return a < b.second;
1619 state[op.getResult()] = iter->first;
1620 return DeletionKind::Delete;
1623 FailureOr<DeletionKind> visitOp(BagDifferenceOp op) {
1624 auto original = get<BagStorage *>(op.getOriginal())->bag;
1625 auto diff = get<BagStorage *>(op.getDiff())->bag;
1628 for (
const auto &el : original) {
1629 if (!diff.contains(el.first)) {
1637 auto toDiff = diff.lookup(el.first);
1638 if (el.second <= toDiff)
1641 result.insert({el.first, el.second - toDiff});
1644 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1645 std::move(result), op.getType());
1646 return DeletionKind::Delete;
1649 FailureOr<DeletionKind> visitOp(BagUnionOp op) {
1651 for (
auto bag : op.getBags()) {
1652 auto val = get<BagStorage *>(bag)->bag;
1653 for (
auto [el, multiple] : val)
1654 result[el] += multiple;
1657 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1658 std::move(result), op.getType());
1659 return DeletionKind::Delete;
1662 FailureOr<DeletionKind> visitOp(BagUniqueSizeOp op) {
1663 auto size = get<BagStorage *>(op.getBag())->bag.size();
1664 state[op.getResult()] = size;
1665 return DeletionKind::Delete;
1668 FailureOr<DeletionKind> visitOp(BagConvertToSetOp op) {
1669 auto bag = get<BagStorage *>(op.getInput())->bag;
1670 SetVector<ElaboratorValue> set;
1671 for (
auto [k, v] : bag)
1673 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1674 std::move(set), op.getType());
1675 return DeletionKind::Delete;
1678 FailureOr<DeletionKind> visitOp(VirtualRegisterOp op) {
1679 auto *val = sharedState.internalizer.create<VirtualRegisterStorage>(
1680 op.getAllowedRegsAttr(), op.getType(), op.getLoc());
1681 state[op.getResult()] = val;
1682 materializer.registerIdentityValue(val);
1683 return DeletionKind::Delete;
1686 FailureOr<DeletionKind> visitOp(ArrayCreateOp op) {
1687 SmallVector<ElaboratorValue> array;
1688 array.reserve(op.getElements().size());
1689 for (
auto val : op.getElements())
1690 array.emplace_back(state.at(val));
1692 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1693 op.getResult().getType(), std::move(array));
1694 return DeletionKind::Delete;
1697 FailureOr<DeletionKind> visitOp(ArrayExtractOp op) {
1698 auto array = get<ArrayStorage *>(op.getArray())->array;
1699 size_t idx = get<size_t>(op.getIndex());
1701 if (array.size() <= idx)
1702 return op->emitError(
"invalid to access index ")
1703 << idx <<
" of an array with " << array.size() <<
" elements";
1705 state[op.getResult()] = array[idx];
1706 return DeletionKind::Delete;
1709 FailureOr<DeletionKind> visitOp(ArrayInjectOp op) {
1710 auto arrayOpaque = state.at(op.getArray());
1711 auto idxOpaque = state.at(op.getIndex());
1712 if (isSymbolic(arrayOpaque) || isSymbolic(idxOpaque))
1713 return visitOpGeneric(op);
1715 auto array = std::get<ArrayStorage *>(arrayOpaque)->array;
1716 size_t idx = std::get<size_t>(idxOpaque);
1718 if (array.size() <= idx)
1719 return op->emitError(
"invalid to access index ")
1720 << idx <<
" of an array with " << array.size() <<
" elements";
1722 array[idx] = state.at(op.getValue());
1723 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1724 op.getResult().getType(), std::move(array));
1725 return DeletionKind::Delete;
1728 FailureOr<DeletionKind> visitOp(ArrayAppendOp op) {
1729 auto array = std::get<ArrayStorage *>(state.at(op.getArray()))->array;
1730 array.push_back(state.at(op.getElement()));
1731 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1732 op.getResult().getType(), std::move(array));
1733 return DeletionKind::Delete;
1736 FailureOr<DeletionKind> visitOp(ArraySizeOp op) {
1737 auto array = get<ArrayStorage *>(op.getArray())->array;
1738 state[op.getResult()] = array.size();
1739 return DeletionKind::Delete;
1742 FailureOr<DeletionKind> visitOp(LabelUniqueDeclOp op) {
1743 auto *val = sharedState.internalizer.create<UniqueLabelStorage>(
1744 state.at(op.getNamePrefix()), op.getLoc());
1745 state[op.getLabel()] = val;
1746 materializer.registerIdentityValue(val);
1747 return DeletionKind::Delete;
1750 FailureOr<DeletionKind> visitOp(RandomScopeOp op) {
1751 auto getNestedRng = [&]() -> RngScope {
1753 return RngScope(op.getSeed()->getZExtValue());
1754 return testState.rng.getNested();
1757 RngScope nestedRng = getNestedRng();
1758 std::swap(testState.rng, nestedRng);
1766 SmallVector<ElaboratorValue> yieldedVals;
1767 if (failed(elaborate(op.getBodyRegion(), {},
false,
1772 std::swap(testState.rng, nestedRng);
1775 for (
auto [res, out] :
llvm::zip(op.getResults(), yieldedVals))
1778 return DeletionKind::Delete;
1781 FailureOr<DeletionKind> visitOp(RandomNumberInRangeOp op) {
1782 size_t lower = get<size_t>(op.getLowerBound());
1783 size_t upper = get<size_t>(op.getUpperBound());
1785 return op->emitError(
"cannot select a number from an empty range");
1787 state[op.getResult()] =
1788 size_t(testState.rng.getUniformlyInRange(lower, upper));
1789 return DeletionKind::Delete;
1792 FailureOr<DeletionKind> visitOp(IntToImmediateOp op) {
1793 size_t input = get<size_t>(op.getInput());
1794 auto width = op.getType().getWidth();
1795 auto emitError = [&]() {
return op->emitError(); };
1796 if (input > APInt::getAllOnes(width).getZExtValue())
1797 return emitError() <<
"cannot represent " << input <<
" with " << width
1800 state[op.getResult()] =
1801 ImmediateAttr::get(op.getContext(), APInt(width, input));
1802 return DeletionKind::Delete;
1805 FailureOr<DeletionKind> visitOp(OnContextOp op) {
1806 ContextResourceAttrInterface from = currentContext,
1807 to = cast<ContextResourceAttrInterface>(
1808 get<TypedAttr>(op.getContext()));
1809 if (!currentContext)
1810 from = DefaultContextAttr::get(op->getContext(), to.getType());
1812 auto emitError = [&]() {
1813 auto diag = op.emitError();
1814 diag.attachNote(op.getLoc())
1815 <<
"while materializing value for context switching for " << op;
1820 Value seqVal = materializer.materialize(
1821 get<SequenceStorage *>(op.getSequence()), op.getLoc(), emitError);
1826 materializer.create<RandomizeSequenceOp>(op.getLoc(), seqVal);
1827 materializer.create<EmbedSequenceOp>(op.getLoc(), randSeqVal);
1828 return DeletionKind::Delete;
1834 auto *iter = testState.contextSwitches.find({from, to});
1837 if (iter == testState.contextSwitches.end())
1838 iter = testState.contextSwitches.find(
1839 {from, AnyContextAttr::get(op->getContext(), to.getType())});
1842 if (iter == testState.contextSwitches.end())
1843 iter = testState.contextSwitches.find(
1844 {AnyContextAttr::get(op->getContext(), from.getType()), to});
1847 if (iter == testState.contextSwitches.end())
1848 iter = testState.contextSwitches.find(
1849 {AnyContextAttr::get(op->getContext(), from.getType()),
1850 AnyContextAttr::get(op->getContext(), to.getType())});
1856 if (iter == testState.contextSwitches.end())
1857 return op->emitError(
"no context transition registered to switch from ")
1858 << from <<
" to " << to;
1860 auto familyName = iter->second->familyName;
1861 SmallVector<ElaboratorValue> args{from, to,
1862 get<SequenceStorage *>(op.getSequence())};
1863 auto *
seq = sharedState.internalizer.internalize<SequenceStorage>(
1864 familyName, std::move(args));
1865 auto *randSeq = sharedState.internalizer.create<RandomizedSequenceStorage>(
1866 to,
seq, op.getLoc());
1867 materializer.registerIdentityValue(randSeq);
1868 Value seqVal = materializer.materialize(randSeq, op.getLoc(), emitError);
1872 materializer.create<EmbedSequenceOp>(op.getLoc(), seqVal);
1873 return DeletionKind::Delete;
1876 FailureOr<DeletionKind> visitOp(ContextSwitchOp op) {
1877 testState.contextSwitches[{op.getFromAttr(), op.getToAttr()}] =
1878 get<SequenceStorage *>(op.getSequence());
1879 return DeletionKind::Delete;
1882 FailureOr<DeletionKind> visitOp(MemoryBlockDeclareOp op) {
1883 auto *val = sharedState.internalizer.create<MemoryBlockStorage>(
1884 op.getBaseAddress(), op.getEndAddress(), op.getType(), op.getLoc());
1885 state[op.getResult()] = val;
1886 materializer.registerIdentityValue(val);
1887 return DeletionKind::Delete;
1890 FailureOr<DeletionKind> visitOp(MemoryAllocOp op) {
1891 size_t size = get<size_t>(op.getSize());
1892 size_t alignment = get<size_t>(op.getAlignment());
1893 auto *memBlock = get<MemoryBlockStorage *>(op.getMemoryBlock());
1894 auto *val = sharedState.internalizer.create<MemoryStorage>(
1895 memBlock, size, alignment, op.getLoc());
1896 state[op.getResult()] = val;
1897 materializer.registerIdentityValue(val);
1898 return DeletionKind::Delete;
1901 FailureOr<DeletionKind> visitOp(MemorySizeOp op) {
1902 auto *memory = get<MemoryStorage *>(op.getMemory());
1903 state[op.getResult()] = memory->size;
1904 return DeletionKind::Delete;
1907 FailureOr<DeletionKind> visitOp(TupleCreateOp op) {
1908 SmallVector<ElaboratorValue> values;
1909 values.reserve(op.getElements().size());
1910 for (
auto el : op.getElements())
1911 values.push_back(state.at(el));
1913 state[op.getResult()] =
1914 sharedState.internalizer.internalize<TupleStorage>(std::move(values));
1915 return DeletionKind::Delete;
1918 FailureOr<DeletionKind> visitOp(TupleExtractOp op) {
1919 auto *tuple = get<TupleStorage *>(op.getTuple());
1920 state[op.getResult()] = tuple->values[op.getIndex().getZExtValue()];
1921 return DeletionKind::Delete;
1924 FailureOr<DeletionKind> visitOp(scf::IfOp op) {
1925 bool cond = get<bool>(op.getCondition());
1926 auto &toElaborate = cond ? op.getThenRegion() : op.getElseRegion();
1927 if (toElaborate.empty())
1928 return DeletionKind::Delete;
1934 SmallVector<ElaboratorValue> yieldedVals;
1936 elaborate(toElaborate, {},
false, yieldedVals)))
1940 for (
auto [res, out] :
llvm::zip(op.getResults(), yieldedVals))
1943 return DeletionKind::Delete;
1946 FailureOr<DeletionKind> visitOp(scf::ForOp op) {
1947 if (!(std::holds_alternative<size_t>(state.at(op.getLowerBound())) &&
1948 std::holds_alternative<size_t>(state.at(op.getStep())) &&
1949 std::holds_alternative<size_t>(state.at(op.getUpperBound()))))
1950 return op->emitOpError(
"can only elaborate index type iterator");
1952 auto lowerBound = get<size_t>(op.getLowerBound());
1953 auto step = get<size_t>(op.getStep());
1954 auto upperBound = get<size_t>(op.getUpperBound());
1960 state[op.getInductionVar()] = lowerBound;
1961 for (
auto [iterArg, initArg] :
1962 llvm::zip(op.getRegionIterArgs(), op.getInitArgs()))
1963 state[iterArg] = state.at(initArg);
1966 SmallVector<ElaboratorValue> yieldedVals;
1967 for (
size_t i = lowerBound; i < upperBound; i += step) {
1968 yieldedVals.clear();
1969 if (failed(elaborate(op.getBodyRegion(), {},
false,
1975 state[op.getInductionVar()] = i + step;
1976 for (
auto [iterArg, prevIterArg] :
1977 llvm::zip(op.getRegionIterArgs(), yieldedVals))
1978 state[iterArg] = prevIterArg;
1982 for (
auto [res, iterArg] :
1983 llvm::zip(op->getResults(), op.getRegionIterArgs()))
1984 state[res] = state.at(iterArg);
1986 return DeletionKind::Delete;
1989 FailureOr<DeletionKind> visitOp(arith::AddIOp op) {
1990 if (!isa<IndexType>(op.getType()))
1991 return visitOpGeneric(op);
1993 size_t lhs = get<size_t>(op.getLhs());
1994 size_t rhs = get<size_t>(op.getRhs());
1995 state[op.getResult()] = lhs + rhs;
1996 return DeletionKind::Delete;
1999 FailureOr<DeletionKind> visitOp(arith::AndIOp op) {
2000 if (!op.getType().isSignlessInteger(1))
2001 return visitOpGeneric(op);
2003 bool lhs = get<bool>(op.getLhs());
2004 bool rhs = get<bool>(op.getRhs());
2005 state[op.getResult()] = lhs && rhs;
2006 return DeletionKind::Delete;
2009 FailureOr<DeletionKind> visitOp(arith::XOrIOp op) {
2010 if (!op.getType().isSignlessInteger(1))
2011 return visitOpGeneric(op);
2013 bool lhs = get<bool>(op.getLhs());
2014 bool rhs = get<bool>(op.getRhs());
2015 state[op.getResult()] = lhs != rhs;
2016 return DeletionKind::Delete;
2019 FailureOr<DeletionKind> visitOp(arith::OrIOp op) {
2020 if (!op.getType().isSignlessInteger(1))
2021 return visitOpGeneric(op);
2023 bool lhs = get<bool>(op.getLhs());
2024 bool rhs = get<bool>(op.getRhs());
2025 state[op.getResult()] = lhs || rhs;
2026 return DeletionKind::Delete;
2029 FailureOr<DeletionKind> visitOp(arith::SelectOp op) {
2030 auto condOpaque = state.at(op.getCondition());
2031 if (isSymbolic(condOpaque))
2032 return visitOpGeneric(op);
2034 bool cond = std::get<bool>(condOpaque);
2035 auto trueVal = state.at(op.getTrueValue());
2036 auto falseVal = state.at(op.getFalseValue());
2037 state[op.getResult()] = cond ? trueVal : falseVal;
2038 return DeletionKind::Delete;
2041 FailureOr<DeletionKind> visitOp(index::AddOp op) {
2042 size_t lhs = get<size_t>(op.getLhs());
2043 size_t rhs = get<size_t>(op.getRhs());
2044 state[op.getResult()] = lhs + rhs;
2045 return DeletionKind::Delete;
2048 FailureOr<DeletionKind> visitOp(index::SubOp op) {
2049 size_t lhs = get<size_t>(op.getLhs());
2050 size_t rhs = get<size_t>(op.getRhs());
2051 state[op.getResult()] = lhs - rhs;
2052 return DeletionKind::Delete;
2055 FailureOr<DeletionKind> visitOp(index::MulOp op) {
2056 size_t lhs = get<size_t>(op.getLhs());
2057 size_t rhs = get<size_t>(op.getRhs());
2058 state[op.getResult()] = lhs * rhs;
2059 return DeletionKind::Delete;
2062 FailureOr<DeletionKind> visitOp(index::DivUOp op) {
2063 size_t lhs = get<size_t>(op.getLhs());
2064 size_t rhs = get<size_t>(op.getRhs());
2067 return op->emitOpError(
"attempted division by zero");
2069 state[op.getResult()] = lhs / rhs;
2070 return DeletionKind::Delete;
2073 FailureOr<DeletionKind> visitOp(index::CeilDivUOp op) {
2074 size_t lhs = get<size_t>(op.getLhs());
2075 size_t rhs = get<size_t>(op.getRhs());
2078 return op->emitOpError(
"attempted division by zero");
2081 state[op.getResult()] = (lhs + rhs - 1) / rhs;
2083 state[op.getResult()] = 1 + ((lhs - 1) / rhs);
2085 return DeletionKind::Delete;
2088 FailureOr<DeletionKind> visitOp(index::RemUOp op) {
2089 size_t lhs = get<size_t>(op.getLhs());
2090 size_t rhs = get<size_t>(op.getRhs());
2093 return op->emitOpError(
"attempted division by zero");
2095 state[op.getResult()] = lhs % rhs;
2096 return DeletionKind::Delete;
2099 FailureOr<DeletionKind> visitOp(index::AndOp op) {
2100 size_t lhs = get<size_t>(op.getLhs());
2101 size_t rhs = get<size_t>(op.getRhs());
2102 state[op.getResult()] = lhs & rhs;
2103 return DeletionKind::Delete;
2106 FailureOr<DeletionKind> visitOp(index::OrOp op) {
2107 size_t lhs = get<size_t>(op.getLhs());
2108 size_t rhs = get<size_t>(op.getRhs());
2109 state[op.getResult()] = lhs | rhs;
2110 return DeletionKind::Delete;
2113 FailureOr<DeletionKind> visitOp(index::XOrOp op) {
2114 size_t lhs = get<size_t>(op.getLhs());
2115 size_t rhs = get<size_t>(op.getRhs());
2116 state[op.getResult()] = lhs ^ rhs;
2117 return DeletionKind::Delete;
2120 FailureOr<DeletionKind> visitOp(index::ShlOp op) {
2121 size_t lhs = get<size_t>(op.getLhs());
2122 size_t rhs = get<size_t>(op.getRhs());
2123 state[op.getResult()] = lhs << rhs;
2124 return DeletionKind::Delete;
2127 FailureOr<DeletionKind> visitOp(index::ShrUOp op) {
2128 size_t lhs = get<size_t>(op.getLhs());
2129 size_t rhs = get<size_t>(op.getRhs());
2130 state[op.getResult()] = lhs >> rhs;
2131 return DeletionKind::Delete;
2134 FailureOr<DeletionKind> visitOp(index::MaxUOp op) {
2135 size_t lhs = get<size_t>(op.getLhs());
2136 size_t rhs = get<size_t>(op.getRhs());
2137 state[op.getResult()] = std::max(lhs, rhs);
2138 return DeletionKind::Delete;
2141 FailureOr<DeletionKind> visitOp(index::MinUOp op) {
2142 size_t lhs = get<size_t>(op.getLhs());
2143 size_t rhs = get<size_t>(op.getRhs());
2144 state[op.getResult()] = std::min(lhs, rhs);
2145 return DeletionKind::Delete;
2148 FailureOr<DeletionKind> visitOp(index::CmpOp op) {
2149 size_t lhs = get<size_t>(op.getLhs());
2150 size_t rhs = get<size_t>(op.getRhs());
2152 switch (op.getPred()) {
2153 case index::IndexCmpPredicate::EQ:
2154 result = lhs == rhs;
2156 case index::IndexCmpPredicate::NE:
2157 result = lhs != rhs;
2159 case index::IndexCmpPredicate::ULT:
2162 case index::IndexCmpPredicate::ULE:
2163 result = lhs <= rhs;
2165 case index::IndexCmpPredicate::UGT:
2168 case index::IndexCmpPredicate::UGE:
2169 result = lhs >= rhs;
2172 return op->emitOpError(
"elaboration not supported");
2174 state[op.getResult()] = result;
2175 return DeletionKind::Delete;
2178 bool isSymbolic(ElaboratorValue val) {
2179 return std::holds_alternative<SymbolicComputationWithIdentityValue *>(
2181 std::holds_alternative<SymbolicComputationWithIdentityStorage *>(
2183 std::holds_alternative<SymbolicComputationStorage *>(val);
2186 bool isSymbolic(Operation *op) {
2187 return llvm::any_of(op->getOperands(), [&](
auto operand) {
2188 auto val = state.at(operand);
2189 return isSymbolic(val);
2196 bool attemptConcreteCase(Operation *op) {
2197 if (op->getNumResults() == 0)
2200 SmallVector<Attribute> operands;
2201 for (
auto operand : op->getOperands()) {
2202 auto evalValue = state[operand];
2203 auto attr = elabValConverter.convert(evalValue);
2204 operands.push_back(attr);
2207 SmallVector<OpFoldResult> results;
2208 if (failed(op->fold(operands, results)))
2211 if (results.size() != op->getNumResults())
2214 for (
auto [res, val] :
llvm::zip(results, op->getResults())) {
2215 auto attr = llvm::dyn_cast_or_null<TypedAttr>(res.dyn_cast<Attribute>());
2219 if (attr.getType() != val.getType())
2223 auto converted = attrConverter.convert(attr);
2224 if (succeeded(converted)) {
2225 state[val] = *converted;
2234 FailureOr<DeletionKind> visitOpGeneric(Operation *op) {
2235 if (op->getNumResults() == 0)
2236 return DeletionKind::Keep;
2238 if (attemptConcreteCase(op))
2239 return DeletionKind::Delete;
2241 if (mlir::isMemoryEffectFree(op)) {
2242 if (op->getNumResults() != 1)
2243 return op->emitOpError(
2244 "symbolic elaboration of memory-effect-free operations with "
2245 "multiple results not supported");
2247 state[op->getResult(0)] =
2248 sharedState.internalizer.internalize<SymbolicComputationStorage>(
2250 return DeletionKind::Delete;
2261 bool onlyAlloc = mlir::hasSingleEffect<mlir::MemoryEffects::Allocate>(op);
2262 onlyAlloc |= isa<ValidateOp>(op);
2264 auto *validationVal =
2265 sharedState.internalizer.create<SymbolicComputationWithIdentityStorage>(
2267 materializer.registerIdentityValue(validationVal);
2268 state[op->getResult(0)] = validationVal;
2270 for (
auto [i, res] :
llvm::enumerate(op->getResults())) {
2274 sharedState.internalizer.create<SymbolicComputationWithIdentityValue>(
2275 res.getType(), validationVal, i);
2277 materializer.registerIdentityValue(val);
2279 return onlyAlloc ? DeletionKind::Delete : DeletionKind::Keep;
2282 bool supportsSymbolicValuesNonGenerically(Operation *op) {
2283 return isa<SubstituteSequenceOp, ArrayCreateOp, ArrayInjectOp,
2284 TupleCreateOp, arith::SelectOp>(op);
2287 FailureOr<DeletionKind> dispatchOpVisitor(Operation *op) {
2288 if (isSymbolic(op) && !supportsSymbolicValuesNonGenerically(op))
2289 return visitOpGeneric(op);
2291 return TypeSwitch<Operation *, FailureOr<DeletionKind>>(op)
2294 arith::AddIOp, arith::XOrIOp, arith::AndIOp, arith::OrIOp,
2297 index::AddOp, index::SubOp, index::MulOp, index::DivUOp,
2298 index::CeilDivUOp, index::RemUOp, index::AndOp, index::OrOp,
2299 index::XOrOp, index::ShlOp, index::ShrUOp, index::MaxUOp,
2300 index::MinUOp, index::CmpOp,
2302 scf::IfOp, scf::ForOp>([&](
auto op) {
return visitOp(op); })
2303 .Default([&](Operation *op) {
return RTGBase::dispatchOpVisitor(op); });
2307 LogicalResult elaborate(Region ®ion,
2308 ArrayRef<ElaboratorValue> regionArguments,
2309 bool keepTerminator,
2310 SmallVector<ElaboratorValue> &terminatorOperands) {
2311 if (region.getBlocks().size() > 1)
2312 return region.getParentOp()->emitOpError(
2313 "regions with more than one block are not supported");
2315 for (
auto [arg, elabArg] :
2316 llvm::zip(region.getArguments(), regionArguments))
2317 state[arg] = elabArg;
2319 Block *block = ®ion.front();
2320 auto iter = keepTerminator ? *block : block->without_terminator();
2321 for (
auto &op : iter) {
2322 auto result = dispatchOpVisitor(&op);
2326 if (*result == DeletionKind::Keep)
2327 if (failed(materializer.materialize(&op, state)))
2331 llvm::dbgs() <<
"Elaborated " << op <<
" to\n[";
2333 llvm::interleaveComma(op.getResults(), llvm::dbgs(), [&](
auto res) {
2334 if (state.contains(res))
2335 llvm::dbgs() << state.at(res);
2337 llvm::dbgs() <<
"unknown";
2340 llvm::dbgs() <<
"]\n\n";
2344 if (!block->empty() && block->back().hasTrait<OpTrait::IsTerminator>()) {
2345 auto *terminator = block->getTerminator();
2346 for (
auto val : terminator->getOperands())
2347 terminatorOperands.push_back(state.at(val));
2349 if (!keepTerminator && materializer.isInPlace(terminator))
2350 terminator->erase();
2358 SharedState &sharedState;
2361 TestState &testState;
2365 Materializer &materializer;
2368 DenseMap<Value, ElaboratorValue> state;
2371 ContextResourceAttrInterface currentContext;
2374 AttributeToElaboratorValueConverter attrConverter;
2377 ElaboratorValueToAttributeConverter elabValConverter;
2382Materializer::elaborateSequence(
const RandomizedSequenceStorage *
seq,
2383 SmallVector<ElaboratorValue> &elabArgs) {
2385 sharedState.table.lookup<SequenceOp>(
seq->sequence->familyName);
2388 OpBuilder builder(familyOp);
2389 auto seqOp = builder.cloneWithoutRegions(familyOp);
2390 auto name = sharedState.names.newName(
seq->sequence->familyName.getValue());
2391 seqOp.setSymName(name);
2392 seqOp.getBodyRegion().emplaceBlock();
2393 sharedState.table.insert(seqOp);
2394 assert(seqOp.getSymName() == name &&
"should not have been renamed");
2396 LLVM_DEBUG(llvm::dbgs() <<
"\n=== Elaborating sequence family @"
2397 << familyOp.getSymName() <<
" into @"
2398 << seqOp.getSymName() <<
" under context "
2399 <<
seq->context <<
"\n\n");
2401 Materializer materializer(OpBuilder::atBlockBegin(seqOp.getBody()), testState,
2402 sharedState, elabArgs);
2403 Elaborator elaborator(sharedState, testState, materializer,
seq->context);
2404 SmallVector<ElaboratorValue> yieldedVals;
2405 if (failed(elaborator.elaborate(familyOp.getBodyRegion(),
seq->sequence->args,
2406 false, yieldedVals)))
2409 seqOp.setSequenceType(
2410 SequenceType::get(builder.getContext(), materializer.getBlockArgTypes()));
2411 materializer.finalize();
2421struct ElaborationPass
2422 :
public rtg::impl::ElaborationPassBase<ElaborationPass> {
2425 void runOnOperation()
override;
2426 void matchTestsAgainstTargets(SymbolTable &table);
2427 LogicalResult elaborateModule(ModuleOp moduleOp, SymbolTable &table);
2431void ElaborationPass::runOnOperation() {
2432 auto moduleOp = getOperation();
2433 SymbolTable table(moduleOp);
2435 matchTestsAgainstTargets(table);
2437 if (failed(elaborateModule(moduleOp, table)))
2438 return signalPassFailure();
2441void ElaborationPass::matchTestsAgainstTargets(SymbolTable &table) {
2442 auto moduleOp = getOperation();
2444 for (
auto test :
llvm::make_early_inc_range(moduleOp.getOps<TestOp>())) {
2445 if (test.getTargetAttr())
2448 bool matched =
false;
2450 for (
auto target : moduleOp.getOps<TargetOp>()) {
2454 bool isSubtype =
true;
2455 auto testEntries = test.getTargetType().getEntries();
2456 auto targetEntries = target.getTarget().getEntries();
2460 size_t targetIdx = 0;
2461 for (
auto testEntry : testEntries) {
2463 while (targetIdx < targetEntries.size() &&
2464 targetEntries[targetIdx].name.getValue() <
2465 testEntry.name.getValue())
2469 if (targetIdx >= targetEntries.size() ||
2470 targetEntries[targetIdx].name != testEntry.name ||
2471 targetEntries[targetIdx].type != testEntry.type) {
2480 IRRewriter rewriter(test);
2482 auto newTest = cast<TestOp>(test->clone());
2483 newTest.setSymName(test.getSymName().str() +
"_" +
2484 target.getSymName().str());
2488 newTest.setTargetAttr(target.getSymNameAttr());
2490 table.insert(newTest, rewriter.getInsertionPoint());
2494 if (matched || deleteUnmatchedTests)
2500 return isa<MemoryBlockType, ContextResourceTypeInterface>(type);
2503LogicalResult ElaborationPass::elaborateModule(ModuleOp moduleOp,
2504 SymbolTable &table) {
2505 SharedState state(moduleOp.getContext(), table);
2508 state.names.add(moduleOp);
2510 struct TargetElabResult {
2511 TargetElabResult(DictType targetType, uint32_t seed)
2512 : targetType(targetType), testState(seed) {}
2514 DictType targetType;
2515 SmallVector<ElaboratorValue> yields;
2516 TestState testState;
2520 DenseMap<StringAttr, TargetElabResult> targetMap;
2521 for (
auto targetOp : moduleOp.getOps<TargetOp>()) {
2522 LLVM_DEBUG(llvm::dbgs() <<
"=== Elaborating target @"
2523 << targetOp.getSymName() <<
"\n\n");
2525 auto [it, inserted] = targetMap.try_emplace(targetOp.getSymNameAttr(),
2526 targetOp.getTarget(), seed);
2527 auto &result = it->second;
2529 SmallVector<ElaboratorValue> blockArgs;
2530 Materializer targetMaterializer(OpBuilder::atBlockBegin(targetOp.getBody()),
2531 result.testState, state, blockArgs);
2532 Elaborator targetElaborator(state, result.testState, targetMaterializer);
2535 if (failed(targetElaborator.elaborate(targetOp.getBodyRegion(), {},
2540 targetMaterializer.finalize();
2545 for (
auto testOp : moduleOp.getOps<TestOp>()) {
2549 if (!testOp.getTargetAttr())
2552 LLVM_DEBUG(llvm::dbgs()
2553 <<
"\n=== Elaborating test @" << testOp.getTemplateName()
2554 <<
" for target @" << *testOp.getTarget() <<
"\n\n");
2557 auto &targetResult = targetMap.at(testOp.getTargetAttr());
2558 TestState testState(seed);
2559 testState.contextSwitches = targetResult.testState.contextSwitches;
2560 testState.name = testOp.getSymNameAttr();
2562 SmallVector<ElaboratorValue> filteredYields;
2564 for (
auto [entry, yield] :
2565 llvm::zip(targetResult.targetType.getEntries(), targetResult.yields)) {
2566 if (i >= testOp.getTargetType().getEntries().size())
2569 if (entry.name == testOp.getTargetType().getEntries()[i].name) {
2570 filteredYields.push_back(yield);
2577 SmallVector<ElaboratorValue> blockArgs;
2578 Materializer materializer(OpBuilder::atBlockBegin(testOp.getBody()),
2579 testState, state, blockArgs);
2581 for (
auto [arg, val] :
2582 llvm::zip(testOp.getBody()->getArguments(), filteredYields))
2584 materializer.map(val, arg);
2586 Elaborator elaborator(state, testState, materializer);
2587 SmallVector<ElaboratorValue> ignore;
2588 if (failed(elaborator.elaborate(testOp.getBodyRegion(), filteredYields,
2592 materializer.finalize();
assert(baseType &&"element must be base type")
static bool onlyLegalToMaterializeInTarget(Type type)
#define VISIT_UNSUPPORTED(STORAGETYPE)
static void print(TypedAttr val, llvm::raw_ostream &os)
static LogicalResult convert(arc::ExecuteOp op, arc::ExecuteOp::Adaptor adaptor, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
static Location getLoc(DefSlot slot)
static InstancePath empty
A namespace that is used to store existing names and generate new names in some scope within the IR.
This helps visit TypeOp nodes.
ResultType visitExternalOp(Operation *op, ExtraArgs... args)
ResultType visitUnhandledOp(Operation *op, ExtraArgs... args)
This callback is invoked on any operations that are not handled by the concrete visitor.
Direction get(bool isOutput)
Returns an output direction if isOutput is true, otherwise returns an input direction.
StringAttr getName(ArrayAttr names, size_t idx)
Return the name at the specified index of the ArrayAttr or null if it cannot be determined.
OS & operator<<(OS &os, const InnerSymTarget &target)
Printing InnerSymTarget's.
static llvm::hash_code hash_value(const ModulePort &port)
int64_t getBitWidth(mlir::Type type)
Return the hardware bit width of a type.
@ Delete
Erase the matched ops.
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
size_t hash_combine(size_t h1, size_t h2)
C++'s stdlib doesn't have a hash_combine function. This is a simple one.
static bool isEqual(const bool &lhs, const bool &rhs)
static unsigned getHashValue(const bool &val)