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"
55 size_t n = w / 32 + (w % 32 != 0);
57 return w0 > 0 ? uint32_t(~0) >> (32 - w0) : 0;
62 const uint32_t diff = b - a + 1;
66 const uint32_t digits = std::numeric_limits<uint32_t>::digits;
70 uint32_t width = digits - llvm::countl_zero(diff) - 1;
71 if ((diff & (std::numeric_limits<uint32_t>::max() >> (digits - width))) != 0)
90struct SequenceStorage;
91struct RandomizedSequenceStorage;
92struct InterleavedSequenceStorage;
94struct VirtualRegisterStorage;
95struct UniqueLabelStorage;
98struct MemoryBlockStorage;
99struct SymbolicComputationWithIdentityStorage;
100struct SymbolicComputationWithIdentityValue;
101struct SymbolicComputationStorage;
104using ElaboratorValue =
105 std::variant<TypedAttr, BagStorage *, bool, size_t, SequenceStorage *,
106 RandomizedSequenceStorage *, InterleavedSequenceStorage *,
107 SetStorage *, VirtualRegisterStorage *, UniqueLabelStorage *,
108 ArrayStorage *, TupleStorage *, MemoryStorage *,
109 MemoryBlockStorage *, SymbolicComputationWithIdentityStorage *,
110 SymbolicComputationWithIdentityValue *,
111 SymbolicComputationStorage *>;
114llvm::hash_code
hash_value(
const ElaboratorValue &val) {
116 [&val](
const auto &alternative) {
119 return llvm::hash_combine(val.index(), alternative);
134 static bool isEqual(
const bool &lhs,
const bool &rhs) {
return lhs == rhs; }
149template <
typename StorageTy>
150struct HashedStorage {
151 HashedStorage(
unsigned hashcode = 0, StorageTy *storage =
nullptr)
152 : hashcode(hashcode), storage(storage) {}
162template <
typename StorageTy>
163struct StorageKeyInfo {
164 static inline HashedStorage<StorageTy> getEmptyKey() {
165 return HashedStorage<StorageTy>(0,
166 DenseMapInfo<StorageTy *>::getEmptyKey());
168 static inline HashedStorage<StorageTy> getTombstoneKey() {
169 return HashedStorage<StorageTy>(
170 0, DenseMapInfo<StorageTy *>::getTombstoneKey());
173 static inline unsigned getHashValue(
const HashedStorage<StorageTy> &key) {
176 static inline unsigned getHashValue(
const StorageTy &key) {
180 static inline bool isEqual(
const HashedStorage<StorageTy> &lhs,
181 const HashedStorage<StorageTy> &rhs) {
182 return lhs.storage == rhs.storage;
184 static inline bool isEqual(
const StorageTy &lhs,
185 const HashedStorage<StorageTy> &rhs) {
186 if (isEqual(rhs, getEmptyKey()) || isEqual(rhs, getTombstoneKey()))
189 return lhs.isEqual(rhs.storage);
199struct CachableStorage {
204struct SetStorage : CachableStorage {
205 static unsigned computeHash(
const SetVector<ElaboratorValue> &set,
207 llvm::hash_code setHash = 0;
208 for (
auto el : set) {
213 setHash = setHash ^ llvm::hash_combine(el);
215 return llvm::hash_combine(type, setHash);
218 SetStorage(SetVector<ElaboratorValue> &&set, Type type)
219 : hashcode(computeHash(set, type)), set(std::move(set)), type(type) {}
221 bool isEqual(
const SetStorage *other)
const {
226 bool allContained =
true;
228 allContained &= other->set.contains(el);
230 return hashcode == other->hashcode && set.size() == other->set.size() &&
231 allContained && type == other->type;
235 const unsigned hashcode;
238 const SetVector<ElaboratorValue> set;
247 BagStorage(MapVector<ElaboratorValue, uint64_t> &&bag, Type type)
249 type,
llvm::hash_combine_range(bag.begin(), bag.
end()))),
250 bag(std::move(bag)), type(type) {}
252 bool isEqual(
const BagStorage *other)
const {
253 return hashcode == other->hashcode && llvm::equal(bag, other->bag) &&
258 const unsigned hashcode;
262 const MapVector<ElaboratorValue, uint64_t> bag;
270struct SequenceStorage {
271 SequenceStorage(StringAttr familyName, SmallVector<ElaboratorValue> &&args)
273 familyName,
llvm::hash_combine_range(args.begin(), args.
end()))),
274 familyName(familyName), args(std::move(args)) {}
276 bool isEqual(
const SequenceStorage *other)
const {
277 return hashcode == other->hashcode && familyName == other->familyName &&
282 const unsigned hashcode;
285 const StringAttr familyName;
288 const SmallVector<ElaboratorValue> args;
292struct InterleavedSequenceStorage {
293 InterleavedSequenceStorage(SmallVector<ElaboratorValue> &&sequences,
295 : sequences(std::move(sequences)), batchSize(batchSize),
297 llvm::hash_combine_range(sequences.begin(), sequences.
end()),
300 explicit InterleavedSequenceStorage(RandomizedSequenceStorage *sequence)
301 : sequences(SmallVector<ElaboratorValue>(1, sequence)), batchSize(1),
303 llvm::hash_combine_range(sequences.begin(), sequences.
end()),
306 bool isEqual(
const InterleavedSequenceStorage *other)
const {
307 return hashcode == other->hashcode && sequences == other->sequences &&
308 batchSize == other->batchSize;
311 const SmallVector<ElaboratorValue> sequences;
313 const uint32_t batchSize;
316 const unsigned hashcode;
321 ArrayStorage(Type type, SmallVector<ElaboratorValue> &&array)
323 type,
llvm::hash_combine_range(array.begin(), array.
end()))),
324 type(type), array(array) {}
326 bool isEqual(
const ArrayStorage *other)
const {
327 return hashcode == other->hashcode && type == other->type &&
328 array == other->array;
332 const unsigned hashcode;
339 const SmallVector<ElaboratorValue> array;
343struct TupleStorage : CachableStorage {
344 TupleStorage(SmallVector<ElaboratorValue> &&values)
345 : hashcode(
llvm::hash_combine_range(values.begin(), values.
end())),
346 values(std::move(values)) {}
348 bool isEqual(
const TupleStorage *other)
const {
349 return hashcode == other->hashcode && values == other->values;
353 const unsigned hashcode;
355 const SmallVector<ElaboratorValue> values;
358struct SymbolicComputationStorage {
359 SymbolicComputationStorage(
const DenseMap<Value, ElaboratorValue> &state,
361 : name(op->
getName()), resultTypes(op->getResultTypes()),
362 operands(
llvm::map_range(op->getOperands(),
363 [&](Value v) {
return state.lookup(v); })),
364 attributes(op->getAttrDictionary()),
365 properties(op->getPropertiesAsAttribute()),
366 hashcode(llvm::hash_combine(name, llvm::hash_combine_range(resultTypes),
367 llvm::hash_combine_range(operands),
368 attributes, op->hashProperties())) {}
370 bool isEqual(
const SymbolicComputationStorage *other)
const {
371 return hashcode == other->hashcode && name == other->name &&
372 resultTypes == other->resultTypes && operands == other->operands &&
373 attributes == other->attributes && properties == other->properties;
376 const OperationName name;
377 const SmallVector<Type> resultTypes;
378 const SmallVector<ElaboratorValue> operands;
379 const DictionaryAttr attributes;
380 const Attribute properties;
381 const unsigned hashcode;
392struct IdentityValue {
394 IdentityValue(Type type, Location loc) : type(type), loc(loc) {}
407 bool alreadyMaterialized =
false;
416struct VirtualRegisterStorage : IdentityValue {
417 VirtualRegisterStorage(VirtualRegisterConfigAttr allowedRegs, Type type,
419 : IdentityValue(type, loc), allowedRegs(allowedRegs) {}
426 const VirtualRegisterConfigAttr allowedRegs;
429struct UniqueLabelStorage : IdentityValue {
430 UniqueLabelStorage(
const ElaboratorValue &name, Location loc)
431 : IdentityValue(LabelType::
get(loc->getContext()), loc), name(name) {}
437 const ElaboratorValue name;
441struct MemoryBlockStorage : IdentityValue {
442 MemoryBlockStorage(
const APInt &baseAddress,
const APInt &endAddress,
443 Type type, Location loc)
444 : IdentityValue(type, loc), baseAddress(baseAddress),
445 endAddress(endAddress) {}
450 const APInt baseAddress;
453 const APInt endAddress;
457struct MemoryStorage : IdentityValue {
458 MemoryStorage(MemoryBlockStorage *memoryBlock,
size_t size,
size_t alignment,
460 : IdentityValue(MemoryType::
get(memoryBlock->type.getContext(),
463 memoryBlock(memoryBlock), size(size), alignment(alignment) {}
465 MemoryBlockStorage *memoryBlock;
467 const size_t alignment;
471struct RandomizedSequenceStorage : IdentityValue {
472 RandomizedSequenceStorage(ContextResourceAttrInterface context,
473 SequenceStorage *sequence, Location loc)
475 RandomizedSequenceType::
get(sequence->familyName.getContext()),
477 context(context), sequence(sequence) {}
480 const ContextResourceAttrInterface context;
482 const SequenceStorage *sequence;
486struct SymbolicComputationWithIdentityStorage : IdentityValue {
487 SymbolicComputationWithIdentityStorage(
488 const DenseMap<Value, ElaboratorValue> &state, Operation *op)
489 : IdentityValue(op->getResult(0).getType(), op->
getLoc()),
490 name(op->
getName()), resultTypes(op->getResultTypes()),
491 operands(
llvm::map_range(op->getOperands(),
492 [&](Value v) {
return state.lookup(v); })),
493 attributes(op->getAttrDictionary()),
494 properties(op->getPropertiesAsAttribute()) {}
496 const OperationName name;
497 const SmallVector<Type> resultTypes;
498 const SmallVector<ElaboratorValue> operands;
499 const DictionaryAttr attributes;
500 const Attribute properties;
503struct SymbolicComputationWithIdentityValue : IdentityValue {
504 SymbolicComputationWithIdentityValue(
505 Type type,
const SymbolicComputationWithIdentityStorage *storage,
507 : IdentityValue(type, storage->loc), storage(storage), idx(idx) {
510 "Use SymbolicComputationWithIdentityStorage for result with index 0.");
513 const SymbolicComputationWithIdentityStorage *storage;
527 template <
typename StorageTy,
typename... Args>
528 StorageTy *internalize(Args &&...args) {
529 static_assert(!std::is_base_of_v<IdentityValue, StorageTy> &&
530 "values with identity must not be internalized");
532 StorageTy storage(std::forward<Args>(args)...);
534 auto existing = getInternSet<StorageTy>().insert_as(
535 HashedStorage<StorageTy>(storage.hashcode), storage);
536 StorageTy *&storagePtr = existing.first->storage;
539 new (allocator.Allocate<StorageTy>()) StorageTy(std::move(storage));
544 template <
typename StorageTy,
typename... Args>
545 StorageTy *create(Args &&...args) {
546 static_assert(std::is_base_of_v<IdentityValue, StorageTy> &&
547 "values with structural equivalence must be internalized");
549 return new (allocator.Allocate<StorageTy>())
550 StorageTy(std::forward<Args>(args)...);
554 template <
typename StorageTy>
555 DenseSet<HashedStorage<StorageTy>, StorageKeyInfo<StorageTy>> &
557 if constexpr (std::is_same_v<StorageTy, ArrayStorage>)
558 return internedArrays;
559 else if constexpr (std::is_same_v<StorageTy, SetStorage>)
561 else if constexpr (std::is_same_v<StorageTy, BagStorage>)
563 else if constexpr (std::is_same_v<StorageTy, SequenceStorage>)
564 return internedSequences;
565 else if constexpr (std::is_same_v<StorageTy, RandomizedSequenceStorage>)
566 return internedRandomizedSequences;
567 else if constexpr (std::is_same_v<StorageTy, InterleavedSequenceStorage>)
568 return internedInterleavedSequences;
569 else if constexpr (std::is_same_v<StorageTy, TupleStorage>)
570 return internedTuples;
571 else if constexpr (std::is_same_v<StorageTy, SymbolicComputationStorage>)
572 return internedSymbolicComputationWithIdentityValues;
574 static_assert(!
sizeof(StorageTy),
575 "no intern set available for this storage type.");
580 llvm::BumpPtrAllocator allocator;
585 DenseSet<HashedStorage<ArrayStorage>, StorageKeyInfo<ArrayStorage>>
587 DenseSet<HashedStorage<SetStorage>, StorageKeyInfo<SetStorage>> internedSets;
588 DenseSet<HashedStorage<BagStorage>, StorageKeyInfo<BagStorage>> internedBags;
589 DenseSet<HashedStorage<SequenceStorage>, StorageKeyInfo<SequenceStorage>>
591 DenseSet<HashedStorage<RandomizedSequenceStorage>,
592 StorageKeyInfo<RandomizedSequenceStorage>>
593 internedRandomizedSequences;
594 DenseSet<HashedStorage<InterleavedSequenceStorage>,
595 StorageKeyInfo<InterleavedSequenceStorage>>
596 internedInterleavedSequences;
597 DenseSet<HashedStorage<TupleStorage>, StorageKeyInfo<TupleStorage>>
599 DenseSet<HashedStorage<SymbolicComputationStorage>,
600 StorageKeyInfo<SymbolicComputationStorage>>
601 internedSymbolicComputationWithIdentityValues;
608static llvm::raw_ostream &
operator<<(llvm::raw_ostream &os,
609 const ElaboratorValue &value);
611static void print(TypedAttr val, llvm::raw_ostream &os) {
612 os <<
"<attr " << val <<
">";
615static void print(BagStorage *val, llvm::raw_ostream &os) {
617 llvm::interleaveComma(val->bag, os,
618 [&](
const std::pair<ElaboratorValue, uint64_t> &el) {
619 os << el.first <<
" -> " << el.second;
621 os <<
"} at " << val <<
">";
624static void print(
bool val, llvm::raw_ostream &os) {
625 os <<
"<bool " << (val ?
"true" :
"false") <<
">";
628static void print(
size_t val, llvm::raw_ostream &os) {
629 os <<
"<index " << val <<
">";
632static void print(SequenceStorage *val, llvm::raw_ostream &os) {
633 os <<
"<sequence @" << val->familyName.getValue() <<
"(";
634 llvm::interleaveComma(val->args, os,
635 [&](
const ElaboratorValue &val) { os << val; });
636 os <<
") at " << val <<
">";
639static void print(RandomizedSequenceStorage *val, llvm::raw_ostream &os) {
640 os <<
"<randomized-sequence derived from @"
641 << val->sequence->familyName.getValue() <<
" under context "
642 << val->context <<
"(";
643 llvm::interleaveComma(val->sequence->args, os,
644 [&](
const ElaboratorValue &val) { os << val; });
645 os <<
") at " << val <<
">";
648static void print(InterleavedSequenceStorage *val, llvm::raw_ostream &os) {
649 os <<
"<interleaved-sequence [";
650 llvm::interleaveComma(val->sequences, os,
651 [&](
const ElaboratorValue &val) { os << val; });
652 os <<
"] batch-size " << val->batchSize <<
" at " << val <<
">";
655static void print(ArrayStorage *val, llvm::raw_ostream &os) {
657 llvm::interleaveComma(val->array, os,
658 [&](
const ElaboratorValue &val) { os << val; });
659 os <<
"] at " << val <<
">";
662static void print(SetStorage *val, llvm::raw_ostream &os) {
664 llvm::interleaveComma(val->set, os,
665 [&](
const ElaboratorValue &val) { os << val; });
666 os <<
"} at " << val <<
">";
669static void print(
const VirtualRegisterStorage *val, llvm::raw_ostream &os) {
670 os <<
"<virtual-register " << val <<
" " << val->allowedRegs <<
">";
673static void print(
const UniqueLabelStorage *val, llvm::raw_ostream &os) {
674 os <<
"<unique-label " << val <<
" " << val->name <<
">";
677static void print(
const TupleStorage *val, llvm::raw_ostream &os) {
679 llvm::interleaveComma(val->values, os,
680 [&](
const ElaboratorValue &val) { os << val; });
684static void print(
const MemoryStorage *val, llvm::raw_ostream &os) {
685 os <<
"<memory {" << ElaboratorValue(val->memoryBlock)
686 <<
", size=" << val->size <<
", alignment=" << val->alignment <<
"}>";
689static void print(
const MemoryBlockStorage *val, llvm::raw_ostream &os) {
690 os <<
"<memory-block {"
691 <<
", address-width=" << val->baseAddress.getBitWidth()
692 <<
", base-address=" << val->baseAddress
693 <<
", end-address=" << val->endAddress <<
"}>";
696static void print(
const SymbolicComputationWithIdentityValue *val,
697 llvm::raw_ostream &os) {
698 os <<
"<symbolic-computation-with-identity-value (" << val->storage <<
") at "
702static void print(
const SymbolicComputationWithIdentityStorage *val,
703 llvm::raw_ostream &os) {
704 os <<
"<symbolic-computation-with-identity " << val->name <<
"(";
705 llvm::interleaveComma(val->operands, os,
706 [&](
const ElaboratorValue &val) { os << val; });
707 os <<
") -> " << val->resultTypes <<
" with attributes " << val->attributes
708 <<
" and properties " << val->properties;
712static void print(
const SymbolicComputationStorage *val,
713 llvm::raw_ostream &os) {
714 os <<
"<symbolic-computation " << val->name <<
"(";
715 llvm::interleaveComma(val->operands, os,
716 [&](
const ElaboratorValue &val) { os << val; });
717 os <<
") -> " << val->resultTypes <<
" with attributes " << val->attributes
718 <<
" and properties " << val->properties;
723 const ElaboratorValue &value) {
724 std::visit([&](
auto val) {
print(val, os); }, value);
739class AttributeToElaboratorValueConverter {
741 AttributeToElaboratorValueConverter(Internalizer &internalizer)
742 : internalizer(internalizer) {}
745 FailureOr<ElaboratorValue>
convert(Attribute attr) {
746 return llvm::TypeSwitch<Attribute, FailureOr<ElaboratorValue>>(attr)
747 .Case<IntegerAttr, SetAttr, TupleAttr>(
748 [&](
auto attr) {
return convert(attr); })
749 .Case<TypedAttr>([&](
auto typedAttr) -> FailureOr<ElaboratorValue> {
750 return ElaboratorValue(typedAttr);
753 [&](Attribute) -> FailureOr<ElaboratorValue> {
return failure(); });
757 FailureOr<ElaboratorValue>
convert(IntegerAttr attr) {
758 if (attr.getType().isSignlessInteger(1))
759 return ElaboratorValue(
bool(attr.getInt()));
760 if (isa<IndexType>(attr.getType()))
761 return ElaboratorValue(
size_t(attr.getInt()));
762 return ElaboratorValue(attr);
765 FailureOr<ElaboratorValue>
convert(SetAttr setAttr) {
766 SetVector<ElaboratorValue> set;
767 for (
auto element : *setAttr.getElements()) {
768 auto converted =
convert(element);
769 if (failed(converted))
771 set.insert(*converted);
774 internalizer.internalize<SetStorage>(std::move(set), setAttr.getType());
776 storage->attrCache = setAttr;
777 return ElaboratorValue(storage);
780 FailureOr<ElaboratorValue>
convert(TupleAttr tupleAttr) {
781 SmallVector<ElaboratorValue> values;
782 for (
auto element : tupleAttr.getElements()) {
783 auto converted =
convert(element);
784 if (failed(converted))
786 values.push_back(*converted);
788 auto *storage = internalizer.internalize<TupleStorage>(std::move(values));
790 storage->attrCache = tupleAttr;
791 return ElaboratorValue(storage);
794 Internalizer &internalizer;
800class ElaboratorValueToAttributeConverter {
802 ElaboratorValueToAttributeConverter(MLIRContext *context)
803 : context(context) {}
807 TypedAttr
convert(
const ElaboratorValue &value) {
809 [&](
auto val) -> TypedAttr {
810 if constexpr (std::is_base_of_v<CachableStorage,
811 std::remove_pointer_t<
812 std::decay_t<
decltype(value)>>>) {
814 return val->attrCache;
822 TypedAttr visit(TypedAttr val) {
return val; }
824 TypedAttr visit(
bool val) {
825 return IntegerAttr::get(IntegerType::get(context, 1), val);
828 TypedAttr visit(
size_t val) {
829 return IntegerAttr::get(IndexType::get(context), val);
832 TypedAttr visit(SetStorage *val) {
833 DenseSet<TypedAttr> elements;
834 for (
auto element : val->set) {
835 auto converted =
convert(element);
838 auto typedAttr = dyn_cast<TypedAttr>(converted);
841 elements.insert(typedAttr);
843 return SetAttr::get(cast<SetType>(val->type), &elements);
846 TypedAttr visit(TupleStorage *val) {
847 SmallVector<TypedAttr> elements;
848 for (
auto element : val->values) {
849 auto converted =
convert(element);
852 auto typedAttr = dyn_cast<TypedAttr>(converted);
855 elements.push_back(typedAttr);
857 return TupleAttr::get(context, elements);
863#define VISIT_UNSUPPORTED(STORAGETYPE) \
865 TypedAttr visit(STORAGETYPE *val) { return {}; }
880#undef VISIT_UNSUPPORTED
882 MLIRContext *context;
895 SharedState(MLIRContext *ctxt, SymbolTable &table,
unsigned seed)
896 : ctxt(ctxt), table(table), rng(seed) {}
902 Internalizer internalizer;
912 std::pair<ContextResourceAttrInterface, ContextResourceAttrInterface>,
920 Materializer(OpBuilder builder, TestState &testState,
921 SharedState &sharedState,
922 SmallVector<ElaboratorValue> &blockArgs)
923 : builder(builder), testState(testState), sharedState(sharedState),
924 blockArgs(blockArgs), attrConverter(builder.getContext()) {}
928 Value materialize(ElaboratorValue val, Location loc,
929 function_ref<InFlightDiagnostic()> emitError) {
930 auto iter = materializedValues.find(val);
931 if (iter != materializedValues.end())
934 LLVM_DEBUG(llvm::dbgs() <<
"Materializing " << val);
936 if (
auto res = tryMaterializeAsConstant(val, loc))
941 Value res = std::visit(
943 if constexpr (std::is_base_of_v<IdentityValue,
944 std::remove_pointer_t<
945 std::decay_t<
decltype(value)>>>) {
946 if (identityValueRoot.contains(value)) {
949 static_cast<IdentityValue *
>(value)->alreadyMaterialized;
950 assert(!materialized &&
"must not already be materialized");
954 return visit(value, loc, emitError);
957 Value arg = builder.getBlock()->addArgument(value->type, loc);
958 blockArgs.push_back(val);
959 blockArgTypes.push_back(arg.getType());
960 materializedValues[val] = arg;
964 return visit(value, loc, emitError);
968 LLVM_DEBUG(llvm::dbgs() <<
" to\n" << res <<
"\n\n");
979 LogicalResult materialize(Operation *op,
980 DenseMap<Value, ElaboratorValue> &state) {
981 if (op->getNumRegions() > 0)
982 return op->emitOpError(
"ops with nested regions must be elaborated away");
990 for (
auto res : op->getResults())
991 if (!res.use_empty())
992 return op->emitOpError(
993 "ops with results that have uses are not supported");
995 if (op->getParentRegion() == builder.getBlock()->getParent()) {
998 deleteOpsUntil([&](
auto iter) {
return &*iter == op; });
1000 if (builder.getInsertionPoint() == builder.getBlock()->end())
1001 return op->emitError(
"operation did not occur after the current "
1002 "materializer insertion point");
1004 LLVM_DEBUG(llvm::dbgs() <<
"Modifying in-place: " << *op <<
"\n\n");
1006 LLVM_DEBUG(llvm::dbgs() <<
"Materializing a clone of " << *op <<
"\n\n");
1007 op = builder.clone(*op);
1008 builder.setInsertionPoint(op);
1011 for (
auto &operand : op->getOpOperands()) {
1012 auto emitError = [&]() {
1013 auto diag = op->emitError();
1014 diag.attachNote(op->getLoc())
1015 <<
"while materializing value for operand#"
1016 << operand.getOperandNumber();
1020 auto elabVal = state.at(operand.get());
1021 Value val = materialize(elabVal, op->getLoc(), emitError);
1025 state[val] = elabVal;
1029 builder.setInsertionPointAfter(op);
1036 deleteOpsUntil([](
auto iter) {
return false; });
1038 for (
auto *op :
llvm::reverse(toDelete))
1045 void registerIdentityValue(IdentityValue *val) {
1046 identityValueRoot.insert(val);
1049 ArrayRef<Type> getBlockArgTypes()
const {
return blockArgTypes; }
1051 void map(ElaboratorValue eval, Value val) { materializedValues[eval] = val; }
1053 template <
typename OpTy,
typename... Args>
1054 OpTy create(Location location, Args &&...args) {
1055 return OpTy::create(builder, location, std::forward<Args>(args)...);
1059 Value tryMaterializeAsConstant(ElaboratorValue val, Location loc) {
1060 if (
auto attr = attrConverter.convert(val)) {
1061 Value res = ConstantOp::create(builder, loc, attr);
1062 materializedValues[val] = res;
1069 SequenceOp elaborateSequence(
const RandomizedSequenceStorage *
seq,
1070 SmallVector<ElaboratorValue> &elabArgs);
1072 void deleteOpsUntil(function_ref<
bool(Block::iterator)> stop) {
1073 auto ip = builder.getInsertionPoint();
1074 while (ip != builder.getBlock()->end() && !stop(ip)) {
1075 LLVM_DEBUG(llvm::dbgs() <<
"Marking to be deleted: " << *ip <<
"\n\n");
1076 toDelete.push_back(&*ip);
1078 builder.setInsertionPointAfter(&*ip);
1079 ip = builder.getInsertionPoint();
1083 Value visit(TypedAttr val, Location loc,
1084 function_ref<InFlightDiagnostic()> emitError) {
1088 Value visit(
size_t val, Location loc,
1089 function_ref<InFlightDiagnostic()> emitError) {
1093 Value visit(
bool val, Location loc,
1094 function_ref<InFlightDiagnostic()> emitError) {
1098 Value visit(ArrayStorage *val, Location loc,
1099 function_ref<InFlightDiagnostic()> emitError) {
1100 SmallVector<Value> elements;
1101 elements.reserve(val->array.size());
1102 for (
auto el : val->array) {
1103 auto materialized = materialize(el, loc, emitError);
1107 elements.push_back(materialized);
1110 Value res = ArrayCreateOp::create(builder, loc, val->type, elements);
1111 materializedValues[val] = res;
1115 Value visit(SetStorage *val, Location loc,
1116 function_ref<InFlightDiagnostic()> emitError) {
1117 SmallVector<Value> elements;
1118 elements.reserve(val->set.size());
1119 for (
auto el : val->set) {
1120 auto materialized = materialize(el, loc, emitError);
1124 elements.push_back(materialized);
1127 auto res = SetCreateOp::create(builder, loc, val->type, elements);
1128 materializedValues[val] = res;
1132 Value visit(BagStorage *val, Location loc,
1133 function_ref<InFlightDiagnostic()> emitError) {
1134 SmallVector<Value> values, weights;
1135 values.reserve(val->bag.size());
1136 weights.reserve(val->bag.size());
1137 for (
auto [val, weight] : val->bag) {
1138 auto materializedVal = materialize(val, loc, emitError);
1139 auto materializedWeight = materialize(weight, loc, emitError);
1140 if (!materializedVal || !materializedWeight)
1143 values.push_back(materializedVal);
1144 weights.push_back(materializedWeight);
1147 auto res = BagCreateOp::create(builder, loc, val->type, values, weights);
1148 materializedValues[val] = res;
1152 Value visit(MemoryBlockStorage *val, Location loc,
1153 function_ref<InFlightDiagnostic()> emitError) {
1154 auto intType = builder.getIntegerType(val->baseAddress.getBitWidth());
1155 Value res = MemoryBlockDeclareOp::create(
1156 builder, val->loc, val->type,
1157 IntegerAttr::get(intType, val->baseAddress),
1158 IntegerAttr::get(intType, val->endAddress));
1159 materializedValues[val] = res;
1163 Value visit(MemoryStorage *val, Location loc,
1164 function_ref<InFlightDiagnostic()> emitError) {
1165 auto memBlock = materialize(val->memoryBlock, val->loc, emitError);
1166 auto memSize = materialize(val->size, val->loc, emitError);
1167 auto memAlign = materialize(val->alignment, val->loc, emitError);
1168 if (!(memBlock && memSize && memAlign))
1172 MemoryAllocOp::create(builder, val->loc, memBlock, memSize, memAlign);
1173 materializedValues[val] = res;
1177 Value visit(SequenceStorage *val, Location loc,
1178 function_ref<InFlightDiagnostic()> emitError) {
1179 emitError() <<
"materializing a non-randomized sequence not supported yet";
1183 Value visit(RandomizedSequenceStorage *val, Location loc,
1184 function_ref<InFlightDiagnostic()> emitError) {
1190 SmallVector<ElaboratorValue> elabArgs;
1193 SequenceOp seqOp = elaborateSequence(val, elabArgs);
1199 SmallVector<Value> args;
1200 SmallVector<Type> argTypes;
1201 for (
auto arg : elabArgs) {
1202 Value materialized = materialize(arg, val->loc, emitError);
1206 args.push_back(materialized);
1207 argTypes.push_back(materialized.getType());
1210 Value res = GetSequenceOp::create(
1211 builder, val->loc, SequenceType::get(builder.getContext(), argTypes),
1212 seqOp.getSymName());
1217 res = SubstituteSequenceOp::create(builder, val->loc, res, args);
1219 res = RandomizeSequenceOp::create(builder, val->loc, res);
1221 materializedValues[val] = res;
1225 Value visit(InterleavedSequenceStorage *val, Location loc,
1226 function_ref<InFlightDiagnostic()> emitError) {
1227 SmallVector<Value> sequences;
1228 for (
auto seqVal : val->sequences) {
1229 Value materialized = materialize(seqVal, loc, emitError);
1233 sequences.push_back(materialized);
1236 if (sequences.size() == 1)
1237 return sequences[0];
1240 InterleaveSequencesOp::create(builder, loc, sequences, val->batchSize);
1241 materializedValues[val] = res;
1245 Value visit(VirtualRegisterStorage *val, Location loc,
1246 function_ref<InFlightDiagnostic()> emitError) {
1247 Value res = VirtualRegisterOp::create(builder, val->loc, val->allowedRegs);
1248 materializedValues[val] = res;
1252 Value visit(UniqueLabelStorage *val, Location loc,
1253 function_ref<InFlightDiagnostic()> emitError) {
1254 auto materialized = materialize(val->name, val->loc, emitError);
1257 Value res = LabelUniqueDeclOp::create(builder, val->loc, materialized);
1258 materializedValues[val] = res;
1262 Value visit(TupleStorage *val, Location loc,
1263 function_ref<InFlightDiagnostic()> emitError) {
1264 SmallVector<Value> materialized;
1265 materialized.reserve(val->values.size());
1266 for (
auto v : val->values)
1267 materialized.push_back(materialize(v, loc, emitError));
1268 Value res = TupleCreateOp::create(builder, loc, materialized);
1269 materializedValues[val] = res;
1273 Value visit(SymbolicComputationWithIdentityValue *val, Location loc,
1274 function_ref<InFlightDiagnostic()> emitError) {
1275 auto *noConstStorage =
1276 const_cast<SymbolicComputationWithIdentityStorage *
>(val->storage);
1277 auto res0 = materialize(noConstStorage, loc, emitError);
1281 auto *op = res0.getDefiningOp();
1282 auto res = op->getResults()[val->idx];
1283 materializedValues[val] = res;
1287 Value visit(SymbolicComputationWithIdentityStorage *val, Location loc,
1288 function_ref<InFlightDiagnostic()> emitError) {
1289 SmallVector<Value> operands;
1290 for (
auto operand : val->operands) {
1291 auto materialized = materialize(operand, val->loc, emitError);
1295 operands.push_back(materialized);
1298 OperationState state(val->loc, val->name);
1299 state.addTypes(val->resultTypes);
1300 state.attributes = val->attributes;
1301 state.propertiesAttr = val->properties;
1302 state.addOperands(operands);
1303 auto *op = builder.create(state);
1305 materializedValues[val] = op->getResult(0);
1306 return op->getResult(0);
1309 Value visit(SymbolicComputationStorage *val, Location loc,
1310 function_ref<InFlightDiagnostic()> emitError) {
1311 SmallVector<Value> operands;
1312 for (
auto operand : val->operands) {
1313 auto materialized = materialize(operand, loc, emitError);
1317 operands.push_back(materialized);
1320 OperationState state(loc, val->name);
1321 state.addTypes(val->resultTypes);
1322 state.attributes = val->attributes;
1323 state.propertiesAttr = val->properties;
1324 state.addOperands(operands);
1325 auto *op = builder.create(state);
1327 for (
auto res : op->getResults())
1328 materializedValues[val] = res;
1330 return op->getResult(0);
1339 DenseMap<ElaboratorValue, Value> materializedValues;
1345 SmallVector<Operation *> toDelete;
1347 TestState &testState;
1348 SharedState &sharedState;
1353 SmallVector<ElaboratorValue> &blockArgs;
1354 SmallVector<Type> blockArgTypes;
1359 DenseSet<IdentityValue *> identityValueRoot;
1362 ElaboratorValueToAttributeConverter attrConverter;
1371enum class DeletionKind { Keep, Delete };
1374class Elaborator :
public RTGOpVisitor<Elaborator, FailureOr<DeletionKind>> {
1377 using RTGBase::visitOp;
1379 Elaborator(SharedState &sharedState, TestState &testState,
1380 Materializer &materializer,
1381 ContextResourceAttrInterface currentContext = {})
1382 : sharedState(sharedState), testState(testState),
1383 materializer(materializer), currentContext(currentContext),
1384 attrConverter(sharedState.internalizer),
1385 elabValConverter(sharedState.ctxt) {}
1387 template <
typename ValueTy>
1388 inline ValueTy
get(Value val)
const {
1389 return std::get<ValueTy>(state.at(val));
1394 return visitOpGeneric(op);
1398 return visitOpGeneric(op);
1401 FailureOr<DeletionKind> visitOp(GetSequenceOp op) {
1402 SmallVector<ElaboratorValue> replacements;
1403 state[op.getResult()] =
1404 sharedState.internalizer.internalize<SequenceStorage>(
1405 op.getSequenceAttr().getAttr(), std::move(replacements));
1406 return DeletionKind::Delete;
1409 FailureOr<DeletionKind> visitOp(SubstituteSequenceOp op) {
1410 if (isSymbolic(state.at(op.getSequence())))
1411 return visitOpGeneric(op);
1413 auto *
seq = get<SequenceStorage *>(op.getSequence());
1415 SmallVector<ElaboratorValue> replacements(
seq->args);
1416 for (
auto replacement : op.getReplacements())
1417 replacements.push_back(state.at(replacement));
1419 state[op.getResult()] =
1420 sharedState.internalizer.internalize<SequenceStorage>(
1421 seq->familyName, std::move(replacements));
1423 return DeletionKind::Delete;
1426 FailureOr<DeletionKind> visitOp(RandomizeSequenceOp op) {
1427 auto *
seq = get<SequenceStorage *>(op.getSequence());
1428 auto *randomizedSeq =
1429 sharedState.internalizer.create<RandomizedSequenceStorage>(
1430 currentContext,
seq, op.getLoc());
1431 materializer.registerIdentityValue(randomizedSeq);
1432 state[op.getResult()] =
1433 sharedState.internalizer.internalize<InterleavedSequenceStorage>(
1435 return DeletionKind::Delete;
1438 FailureOr<DeletionKind> visitOp(InterleaveSequencesOp op) {
1439 SmallVector<ElaboratorValue> sequences;
1440 for (
auto seq : op.getSequences())
1441 sequences.push_back(state.at(
seq));
1443 state[op.getResult()] =
1444 sharedState.internalizer.internalize<InterleavedSequenceStorage>(
1445 std::move(sequences), op.getBatchSize());
1446 return DeletionKind::Delete;
1450 LogicalResult isValidContext(ElaboratorValue value, Operation *op)
const {
1451 if (std::holds_alternative<RandomizedSequenceStorage *>(value)) {
1452 auto *
seq = std::get<RandomizedSequenceStorage *>(value);
1453 if (
seq->context != currentContext) {
1454 auto err = op->emitError(
"attempting to place sequence derived from ")
1455 <<
seq->sequence->familyName.getValue() <<
" under context "
1457 <<
", but it was previously randomized for context ";
1459 err <<
seq->context;
1467 auto *interVal = std::get<InterleavedSequenceStorage *>(value);
1468 for (
auto val : interVal->sequences)
1469 if (failed(isValidContext(val, op)))
1474 FailureOr<DeletionKind> visitOp(EmbedSequenceOp op) {
1475 auto *seqVal = get<InterleavedSequenceStorage *>(op.getSequence());
1476 if (failed(isValidContext(seqVal, op)))
1479 return DeletionKind::Keep;
1482 FailureOr<DeletionKind> visitOp(SetCreateOp op) {
1483 SetVector<ElaboratorValue> set;
1484 for (
auto val : op.getElements())
1485 set.insert(state.at(val));
1487 state[op.getSet()] = sharedState.internalizer.internalize<SetStorage>(
1488 std::move(set), op.getSet().getType());
1489 return DeletionKind::Delete;
1492 FailureOr<DeletionKind> visitOp(SetSelectRandomOp op) {
1493 auto set = get<SetStorage *>(op.getSet())->set;
1496 return op->emitError(
"cannot select from an empty set");
1500 op->getAttrOfType<IntegerAttr>(
"rtg.elaboration_custom_seed")) {
1501 std::mt19937 customRng(intAttr.getInt());
1507 state[op.getResult()] = set[selected];
1508 return DeletionKind::Delete;
1511 FailureOr<DeletionKind> visitOp(SetDifferenceOp op) {
1512 auto original = get<SetStorage *>(op.getOriginal())->set;
1513 auto diff = get<SetStorage *>(op.getDiff())->set;
1515 SetVector<ElaboratorValue> result(original);
1516 result.set_subtract(diff);
1518 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1519 std::move(result), op.getResult().getType());
1520 return DeletionKind::Delete;
1523 FailureOr<DeletionKind> visitOp(SetUnionOp op) {
1524 SetVector<ElaboratorValue> result;
1525 for (
auto set : op.getSets())
1526 result.set_union(
get<SetStorage *>(set)->set);
1528 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1529 std::move(result), op.getType());
1530 return DeletionKind::Delete;
1533 FailureOr<DeletionKind> visitOp(SetSizeOp op) {
1534 auto size = get<SetStorage *>(op.getSet())->set.size();
1535 state[op.getResult()] = size;
1536 return DeletionKind::Delete;
1542 FailureOr<DeletionKind> visitOp(SetCartesianProductOp op) {
1543 SetVector<ElaboratorValue> result;
1544 SmallVector<SmallVector<ElaboratorValue>> tuples;
1545 tuples.push_back({});
1547 for (
auto input : op.getInputs()) {
1548 auto &set = get<SetStorage *>(input)->set;
1550 SetVector<ElaboratorValue>
empty;
1551 state[op.getResult()] =
1552 sharedState.internalizer.internalize<SetStorage>(std::move(
empty),
1554 return DeletionKind::Delete;
1557 for (
unsigned i = 0, e = tuples.size(); i < e; ++i) {
1558 for (
auto setEl : set.getArrayRef().drop_back()) {
1559 tuples.push_back(tuples[i]);
1560 tuples.back().push_back(setEl);
1562 tuples[i].push_back(set.back());
1566 for (
auto &tup : tuples)
1568 sharedState.internalizer.internalize<TupleStorage>(std::move(tup)));
1570 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1571 std::move(result), op.getType());
1572 return DeletionKind::Delete;
1575 FailureOr<DeletionKind> visitOp(SetConvertToBagOp op) {
1576 auto set = get<SetStorage *>(op.getInput())->set;
1577 MapVector<ElaboratorValue, uint64_t> bag;
1578 for (
auto val : set)
1579 bag.insert({val, 1});
1580 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1581 std::move(bag), op.getType());
1582 return DeletionKind::Delete;
1585 FailureOr<DeletionKind> visitOp(BagCreateOp op) {
1586 MapVector<ElaboratorValue, uint64_t> bag;
1587 for (
auto [val, multiple] :
1588 llvm::zip(op.getElements(), op.getMultiples())) {
1592 bag[state.at(val)] += get<size_t>(multiple);
1595 state[op.getBag()] = sharedState.internalizer.internalize<BagStorage>(
1596 std::move(bag), op.getType());
1597 return DeletionKind::Delete;
1600 FailureOr<DeletionKind> visitOp(BagSelectRandomOp op) {
1601 auto bag = get<BagStorage *>(op.getBag())->bag;
1604 return op->emitError(
"cannot select from an empty bag");
1606 SmallVector<std::pair<ElaboratorValue, uint32_t>> prefixSum;
1607 prefixSum.reserve(bag.size());
1608 uint32_t accumulator = 0;
1609 for (
auto [val, weight] : bag) {
1610 accumulator += weight;
1611 prefixSum.push_back({val, accumulator});
1614 auto customRng = sharedState.rng;
1616 op->getAttrOfType<IntegerAttr>(
"rtg.elaboration_custom_seed")) {
1617 customRng = std::mt19937(intAttr.getInt());
1621 auto *iter = llvm::upper_bound(
1623 [](uint32_t a,
const std::pair<ElaboratorValue, uint32_t> &b) {
1624 return a < b.second;
1627 state[op.getResult()] = iter->first;
1628 return DeletionKind::Delete;
1631 FailureOr<DeletionKind> visitOp(BagDifferenceOp op) {
1632 auto original = get<BagStorage *>(op.getOriginal())->bag;
1633 auto diff = get<BagStorage *>(op.getDiff())->bag;
1635 MapVector<ElaboratorValue, uint64_t> result;
1636 for (
const auto &el : original) {
1637 if (!diff.contains(el.first)) {
1645 auto toDiff = diff.lookup(el.first);
1646 if (el.second <= toDiff)
1649 result.insert({el.first, el.second - toDiff});
1652 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1653 std::move(result), op.getType());
1654 return DeletionKind::Delete;
1657 FailureOr<DeletionKind> visitOp(BagUnionOp op) {
1658 MapVector<ElaboratorValue, uint64_t> result;
1659 for (
auto bag : op.getBags()) {
1660 auto val = get<BagStorage *>(bag)->bag;
1661 for (
auto [el, multiple] : val)
1662 result[el] += multiple;
1665 state[op.getResult()] = sharedState.internalizer.internalize<BagStorage>(
1666 std::move(result), op.getType());
1667 return DeletionKind::Delete;
1670 FailureOr<DeletionKind> visitOp(BagUniqueSizeOp op) {
1671 auto size = get<BagStorage *>(op.getBag())->bag.size();
1672 state[op.getResult()] = size;
1673 return DeletionKind::Delete;
1676 FailureOr<DeletionKind> visitOp(BagConvertToSetOp op) {
1677 auto bag = get<BagStorage *>(op.getInput())->bag;
1678 SetVector<ElaboratorValue> set;
1679 for (
auto [k, v] : bag)
1681 state[op.getResult()] = sharedState.internalizer.internalize<SetStorage>(
1682 std::move(set), op.getType());
1683 return DeletionKind::Delete;
1686 FailureOr<DeletionKind> visitOp(VirtualRegisterOp op) {
1687 auto *val = sharedState.internalizer.create<VirtualRegisterStorage>(
1688 op.getAllowedRegsAttr(), op.getType(), op.getLoc());
1689 state[op.getResult()] = val;
1690 materializer.registerIdentityValue(val);
1691 return DeletionKind::Delete;
1694 FailureOr<DeletionKind> visitOp(ArrayCreateOp op) {
1695 SmallVector<ElaboratorValue> array;
1696 array.reserve(op.getElements().size());
1697 for (
auto val : op.getElements())
1698 array.emplace_back(state.at(val));
1700 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1701 op.getResult().getType(), std::move(array));
1702 return DeletionKind::Delete;
1705 FailureOr<DeletionKind> visitOp(ArrayExtractOp op) {
1706 auto array = get<ArrayStorage *>(op.getArray())->array;
1707 size_t idx = get<size_t>(op.getIndex());
1709 if (array.size() <= idx)
1710 return op->emitError(
"invalid to access index ")
1711 << idx <<
" of an array with " << array.size() <<
" elements";
1713 state[op.getResult()] = array[idx];
1714 return DeletionKind::Delete;
1717 FailureOr<DeletionKind> visitOp(ArrayInjectOp op) {
1718 auto arrayOpaque = state.at(op.getArray());
1719 auto idxOpaque = state.at(op.getIndex());
1720 if (isSymbolic(arrayOpaque) || isSymbolic(idxOpaque))
1721 return visitOpGeneric(op);
1723 auto array = std::get<ArrayStorage *>(arrayOpaque)->array;
1724 size_t idx = std::get<size_t>(idxOpaque);
1726 if (array.size() <= idx)
1727 return op->emitError(
"invalid to access index ")
1728 << idx <<
" of an array with " << array.size() <<
" elements";
1730 array[idx] = state.at(op.getValue());
1731 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1732 op.getResult().getType(), std::move(array));
1733 return DeletionKind::Delete;
1736 FailureOr<DeletionKind> visitOp(ArrayAppendOp op) {
1737 auto array = std::get<ArrayStorage *>(state.at(op.getArray()))->array;
1738 array.push_back(state.at(op.getElement()));
1739 state[op.getResult()] = sharedState.internalizer.internalize<ArrayStorage>(
1740 op.getResult().getType(), std::move(array));
1741 return DeletionKind::Delete;
1744 FailureOr<DeletionKind> visitOp(ArraySizeOp op) {
1745 auto array = get<ArrayStorage *>(op.getArray())->array;
1746 state[op.getResult()] = array.size();
1747 return DeletionKind::Delete;
1750 FailureOr<DeletionKind> visitOp(LabelUniqueDeclOp op) {
1751 auto *val = sharedState.internalizer.create<UniqueLabelStorage>(
1752 state.at(op.getNamePrefix()), op.getLoc());
1753 state[op.getLabel()] = val;
1754 materializer.registerIdentityValue(val);
1755 return DeletionKind::Delete;
1758 FailureOr<DeletionKind> visitOp(RandomNumberInRangeOp op) {
1759 size_t lower = get<size_t>(op.getLowerBound());
1760 size_t upper = get<size_t>(op.getUpperBound());
1762 return op->emitError(
"cannot select a number from an empty range");
1765 op->getAttrOfType<IntegerAttr>(
"rtg.elaboration_custom_seed")) {
1766 std::mt19937 customRng(intAttr.getInt());
1767 state[op.getResult()] =
1770 state[op.getResult()] =
1774 return DeletionKind::Delete;
1777 FailureOr<DeletionKind> visitOp(IntToImmediateOp op) {
1778 size_t input = get<size_t>(op.getInput());
1779 auto width = op.getType().getWidth();
1780 auto emitError = [&]() {
return op->emitError(); };
1781 if (input > APInt::getAllOnes(width).getZExtValue())
1782 return emitError() <<
"cannot represent " << input <<
" with " << width
1785 state[op.getResult()] =
1786 ImmediateAttr::get(op.getContext(), APInt(width, input));
1787 return DeletionKind::Delete;
1790 FailureOr<DeletionKind> visitOp(OnContextOp op) {
1791 ContextResourceAttrInterface from = currentContext,
1792 to = cast<ContextResourceAttrInterface>(
1793 get<TypedAttr>(op.getContext()));
1794 if (!currentContext)
1795 from = DefaultContextAttr::get(op->getContext(), to.getType());
1797 auto emitError = [&]() {
1798 auto diag = op.emitError();
1799 diag.attachNote(op.getLoc())
1800 <<
"while materializing value for context switching for " << op;
1805 Value seqVal = materializer.materialize(
1806 get<SequenceStorage *>(op.getSequence()), op.getLoc(), emitError);
1811 materializer.create<RandomizeSequenceOp>(op.getLoc(), seqVal);
1812 materializer.create<EmbedSequenceOp>(op.getLoc(), randSeqVal);
1813 return DeletionKind::Delete;
1819 auto *iter = testState.contextSwitches.find({from, to});
1822 if (iter == testState.contextSwitches.end())
1823 iter = testState.contextSwitches.find(
1824 {from, AnyContextAttr::get(op->getContext(), to.getType())});
1827 if (iter == testState.contextSwitches.end())
1828 iter = testState.contextSwitches.find(
1829 {AnyContextAttr::get(op->getContext(), from.getType()), to});
1832 if (iter == testState.contextSwitches.end())
1833 iter = testState.contextSwitches.find(
1834 {AnyContextAttr::get(op->getContext(), from.getType()),
1835 AnyContextAttr::get(op->getContext(), to.getType())});
1841 if (iter == testState.contextSwitches.end())
1842 return op->emitError(
"no context transition registered to switch from ")
1843 << from <<
" to " << to;
1845 auto familyName = iter->second->familyName;
1846 SmallVector<ElaboratorValue> args{from, to,
1847 get<SequenceStorage *>(op.getSequence())};
1848 auto *
seq = sharedState.internalizer.internalize<SequenceStorage>(
1849 familyName, std::move(args));
1850 auto *randSeq = sharedState.internalizer.create<RandomizedSequenceStorage>(
1851 to,
seq, op.getLoc());
1852 materializer.registerIdentityValue(randSeq);
1853 Value seqVal = materializer.materialize(randSeq, op.getLoc(), emitError);
1857 materializer.create<EmbedSequenceOp>(op.getLoc(), seqVal);
1858 return DeletionKind::Delete;
1861 FailureOr<DeletionKind> visitOp(ContextSwitchOp op) {
1862 testState.contextSwitches[{op.getFromAttr(), op.getToAttr()}] =
1863 get<SequenceStorage *>(op.getSequence());
1864 return DeletionKind::Delete;
1867 FailureOr<DeletionKind> visitOp(MemoryBlockDeclareOp op) {
1868 auto *val = sharedState.internalizer.create<MemoryBlockStorage>(
1869 op.getBaseAddress(), op.getEndAddress(), op.getType(), op.getLoc());
1870 state[op.getResult()] = val;
1871 materializer.registerIdentityValue(val);
1872 return DeletionKind::Delete;
1875 FailureOr<DeletionKind> visitOp(MemoryAllocOp op) {
1876 size_t size = get<size_t>(op.getSize());
1877 size_t alignment = get<size_t>(op.getAlignment());
1878 auto *memBlock = get<MemoryBlockStorage *>(op.getMemoryBlock());
1879 auto *val = sharedState.internalizer.create<MemoryStorage>(
1880 memBlock, size, alignment, op.getLoc());
1881 state[op.getResult()] = val;
1882 materializer.registerIdentityValue(val);
1883 return DeletionKind::Delete;
1886 FailureOr<DeletionKind> visitOp(MemorySizeOp op) {
1887 auto *memory = get<MemoryStorage *>(op.getMemory());
1888 state[op.getResult()] = memory->size;
1889 return DeletionKind::Delete;
1892 FailureOr<DeletionKind> visitOp(TupleCreateOp op) {
1893 SmallVector<ElaboratorValue> values;
1894 values.reserve(op.getElements().size());
1895 for (
auto el : op.getElements())
1896 values.push_back(state.at(el));
1898 state[op.getResult()] =
1899 sharedState.internalizer.internalize<TupleStorage>(std::move(values));
1900 return DeletionKind::Delete;
1903 FailureOr<DeletionKind> visitOp(TupleExtractOp op) {
1904 auto *tuple = get<TupleStorage *>(op.getTuple());
1905 state[op.getResult()] = tuple->values[op.getIndex().getZExtValue()];
1906 return DeletionKind::Delete;
1909 FailureOr<DeletionKind> visitOp(scf::IfOp op) {
1910 bool cond = get<bool>(op.getCondition());
1911 auto &toElaborate = cond ? op.getThenRegion() : op.getElseRegion();
1912 if (toElaborate.empty())
1913 return DeletionKind::Delete;
1919 SmallVector<ElaboratorValue> yieldedVals;
1920 if (failed(elaborate(toElaborate, {}, yieldedVals)))
1924 for (
auto [res, out] :
llvm::zip(op.getResults(), yieldedVals))
1927 return DeletionKind::Delete;
1930 FailureOr<DeletionKind> visitOp(scf::ForOp op) {
1931 if (!(std::holds_alternative<size_t>(state.at(op.getLowerBound())) &&
1932 std::holds_alternative<size_t>(state.at(op.getStep())) &&
1933 std::holds_alternative<size_t>(state.at(op.getUpperBound()))))
1934 return op->emitOpError(
"can only elaborate index type iterator");
1936 auto lowerBound = get<size_t>(op.getLowerBound());
1937 auto step = get<size_t>(op.getStep());
1938 auto upperBound = get<size_t>(op.getUpperBound());
1944 state[op.getInductionVar()] = lowerBound;
1945 for (
auto [iterArg, initArg] :
1946 llvm::zip(op.getRegionIterArgs(), op.getInitArgs()))
1947 state[iterArg] = state.at(initArg);
1950 SmallVector<ElaboratorValue> yieldedVals;
1951 for (
size_t i = lowerBound; i < upperBound; i += step) {
1952 yieldedVals.clear();
1953 if (failed(elaborate(op.getBodyRegion(), {}, yieldedVals)))
1958 state[op.getInductionVar()] = i + step;
1959 for (
auto [iterArg, prevIterArg] :
1960 llvm::zip(op.getRegionIterArgs(), yieldedVals))
1961 state[iterArg] = prevIterArg;
1965 for (
auto [res, iterArg] :
1966 llvm::zip(op->getResults(), op.getRegionIterArgs()))
1967 state[res] = state.at(iterArg);
1969 return DeletionKind::Delete;
1972 FailureOr<DeletionKind> visitOp(scf::YieldOp op) {
1973 return DeletionKind::Delete;
1976 FailureOr<DeletionKind> visitOp(arith::AddIOp op) {
1977 if (!isa<IndexType>(op.getType()))
1978 return visitOpGeneric(op);
1980 size_t lhs = get<size_t>(op.getLhs());
1981 size_t rhs = get<size_t>(op.getRhs());
1982 state[op.getResult()] = lhs + rhs;
1983 return DeletionKind::Delete;
1986 FailureOr<DeletionKind> visitOp(arith::AndIOp op) {
1987 if (!op.getType().isSignlessInteger(1))
1988 return visitOpGeneric(op);
1990 bool lhs = get<bool>(op.getLhs());
1991 bool rhs = get<bool>(op.getRhs());
1992 state[op.getResult()] = lhs && rhs;
1993 return DeletionKind::Delete;
1996 FailureOr<DeletionKind> visitOp(arith::XOrIOp op) {
1997 if (!op.getType().isSignlessInteger(1))
1998 return visitOpGeneric(op);
2000 bool lhs = get<bool>(op.getLhs());
2001 bool rhs = get<bool>(op.getRhs());
2002 state[op.getResult()] = lhs != rhs;
2003 return DeletionKind::Delete;
2006 FailureOr<DeletionKind> visitOp(arith::OrIOp op) {
2007 if (!op.getType().isSignlessInteger(1))
2008 return visitOpGeneric(op);
2010 bool lhs = get<bool>(op.getLhs());
2011 bool rhs = get<bool>(op.getRhs());
2012 state[op.getResult()] = lhs || rhs;
2013 return DeletionKind::Delete;
2016 FailureOr<DeletionKind> visitOp(arith::SelectOp op) {
2017 auto condOpaque = state.at(op.getCondition());
2018 if (isSymbolic(condOpaque))
2019 return visitOpGeneric(op);
2021 bool cond = std::get<bool>(condOpaque);
2022 auto trueVal = state.at(op.getTrueValue());
2023 auto falseVal = state.at(op.getFalseValue());
2024 state[op.getResult()] = cond ? trueVal : falseVal;
2025 return DeletionKind::Delete;
2028 FailureOr<DeletionKind> visitOp(index::AddOp op) {
2029 size_t lhs = get<size_t>(op.getLhs());
2030 size_t rhs = get<size_t>(op.getRhs());
2031 state[op.getResult()] = lhs + rhs;
2032 return DeletionKind::Delete;
2035 FailureOr<DeletionKind> visitOp(index::SubOp op) {
2036 size_t lhs = get<size_t>(op.getLhs());
2037 size_t rhs = get<size_t>(op.getRhs());
2038 state[op.getResult()] = lhs - rhs;
2039 return DeletionKind::Delete;
2042 FailureOr<DeletionKind> visitOp(index::MulOp op) {
2043 size_t lhs = get<size_t>(op.getLhs());
2044 size_t rhs = get<size_t>(op.getRhs());
2045 state[op.getResult()] = lhs * rhs;
2046 return DeletionKind::Delete;
2049 FailureOr<DeletionKind> visitOp(index::DivUOp op) {
2050 size_t lhs = get<size_t>(op.getLhs());
2051 size_t rhs = get<size_t>(op.getRhs());
2054 return op->emitOpError(
"attempted division by zero");
2056 state[op.getResult()] = lhs / rhs;
2057 return DeletionKind::Delete;
2060 FailureOr<DeletionKind> visitOp(index::CeilDivUOp op) {
2061 size_t lhs = get<size_t>(op.getLhs());
2062 size_t rhs = get<size_t>(op.getRhs());
2065 return op->emitOpError(
"attempted division by zero");
2068 state[op.getResult()] = (lhs + rhs - 1) / rhs;
2070 state[op.getResult()] = 1 + ((lhs - 1) / rhs);
2072 return DeletionKind::Delete;
2075 FailureOr<DeletionKind> visitOp(index::RemUOp op) {
2076 size_t lhs = get<size_t>(op.getLhs());
2077 size_t rhs = get<size_t>(op.getRhs());
2080 return op->emitOpError(
"attempted division by zero");
2082 state[op.getResult()] = lhs % rhs;
2083 return DeletionKind::Delete;
2086 FailureOr<DeletionKind> visitOp(index::AndOp op) {
2087 size_t lhs = get<size_t>(op.getLhs());
2088 size_t rhs = get<size_t>(op.getRhs());
2089 state[op.getResult()] = lhs & rhs;
2090 return DeletionKind::Delete;
2093 FailureOr<DeletionKind> visitOp(index::OrOp op) {
2094 size_t lhs = get<size_t>(op.getLhs());
2095 size_t rhs = get<size_t>(op.getRhs());
2096 state[op.getResult()] = lhs | rhs;
2097 return DeletionKind::Delete;
2100 FailureOr<DeletionKind> visitOp(index::XOrOp op) {
2101 size_t lhs = get<size_t>(op.getLhs());
2102 size_t rhs = get<size_t>(op.getRhs());
2103 state[op.getResult()] = lhs ^ rhs;
2104 return DeletionKind::Delete;
2107 FailureOr<DeletionKind> visitOp(index::ShlOp op) {
2108 size_t lhs = get<size_t>(op.getLhs());
2109 size_t rhs = get<size_t>(op.getRhs());
2110 state[op.getResult()] = lhs << rhs;
2111 return DeletionKind::Delete;
2114 FailureOr<DeletionKind> visitOp(index::ShrUOp op) {
2115 size_t lhs = get<size_t>(op.getLhs());
2116 size_t rhs = get<size_t>(op.getRhs());
2117 state[op.getResult()] = lhs >> rhs;
2118 return DeletionKind::Delete;
2121 FailureOr<DeletionKind> visitOp(index::MaxUOp op) {
2122 size_t lhs = get<size_t>(op.getLhs());
2123 size_t rhs = get<size_t>(op.getRhs());
2124 state[op.getResult()] = std::max(lhs, rhs);
2125 return DeletionKind::Delete;
2128 FailureOr<DeletionKind> visitOp(index::MinUOp op) {
2129 size_t lhs = get<size_t>(op.getLhs());
2130 size_t rhs = get<size_t>(op.getRhs());
2131 state[op.getResult()] = std::min(lhs, rhs);
2132 return DeletionKind::Delete;
2135 FailureOr<DeletionKind> visitOp(index::CmpOp op) {
2136 size_t lhs = get<size_t>(op.getLhs());
2137 size_t rhs = get<size_t>(op.getRhs());
2139 switch (op.getPred()) {
2140 case index::IndexCmpPredicate::EQ:
2141 result = lhs == rhs;
2143 case index::IndexCmpPredicate::NE:
2144 result = lhs != rhs;
2146 case index::IndexCmpPredicate::ULT:
2149 case index::IndexCmpPredicate::ULE:
2150 result = lhs <= rhs;
2152 case index::IndexCmpPredicate::UGT:
2155 case index::IndexCmpPredicate::UGE:
2156 result = lhs >= rhs;
2159 return op->emitOpError(
"elaboration not supported");
2161 state[op.getResult()] = result;
2162 return DeletionKind::Delete;
2165 bool isSymbolic(ElaboratorValue val) {
2166 return std::holds_alternative<SymbolicComputationWithIdentityValue *>(
2168 std::holds_alternative<SymbolicComputationWithIdentityStorage *>(
2170 std::holds_alternative<SymbolicComputationStorage *>(val);
2173 bool isSymbolic(Operation *op) {
2174 return llvm::any_of(op->getOperands(), [&](
auto operand) {
2175 auto val = state.at(operand);
2176 return isSymbolic(val);
2183 bool attemptConcreteCase(Operation *op) {
2184 if (op->getNumResults() == 0)
2187 SmallVector<Attribute> operands;
2188 for (
auto operand : op->getOperands()) {
2189 auto evalValue = state[operand];
2190 auto attr = elabValConverter.convert(evalValue);
2191 operands.push_back(attr);
2194 SmallVector<OpFoldResult> results;
2195 if (failed(op->fold(operands, results)))
2198 if (results.size() != op->getNumResults())
2201 for (
auto [res, val] :
llvm::zip(results, op->getResults())) {
2202 auto attr = llvm::dyn_cast_or_null<TypedAttr>(res.dyn_cast<Attribute>());
2206 if (attr.getType() != val.getType())
2210 auto converted = attrConverter.convert(attr);
2211 if (succeeded(converted)) {
2212 state[val] = *converted;
2221 FailureOr<DeletionKind> visitOpGeneric(Operation *op) {
2222 if (op->getNumResults() == 0)
2223 return DeletionKind::Keep;
2225 if (attemptConcreteCase(op))
2226 return DeletionKind::Delete;
2228 if (mlir::isMemoryEffectFree(op)) {
2229 if (op->getNumResults() != 1)
2230 return op->emitOpError(
2231 "symbolic elaboration of memory-effect-free operations with "
2232 "multiple results not supported");
2234 state[op->getResult(0)] =
2235 sharedState.internalizer.internalize<SymbolicComputationStorage>(
2237 return DeletionKind::Delete;
2248 bool onlyAlloc = mlir::hasSingleEffect<mlir::MemoryEffects::Allocate>(op);
2249 onlyAlloc |= isa<ValidateOp>(op);
2251 auto *validationVal =
2252 sharedState.internalizer.create<SymbolicComputationWithIdentityStorage>(
2254 materializer.registerIdentityValue(validationVal);
2255 state[op->getResult(0)] = validationVal;
2257 for (
auto [i, res] :
llvm::enumerate(op->getResults())) {
2261 sharedState.internalizer.create<SymbolicComputationWithIdentityValue>(
2262 res.getType(), validationVal, i);
2264 materializer.registerIdentityValue(val);
2266 return onlyAlloc ? DeletionKind::Delete : DeletionKind::Keep;
2269 bool supportsSymbolicValuesNonGenerically(Operation *op) {
2270 return isa<SubstituteSequenceOp, ArrayCreateOp, ArrayInjectOp,
2271 TupleCreateOp, arith::SelectOp>(op);
2274 FailureOr<DeletionKind> dispatchOpVisitor(Operation *op) {
2275 if (isSymbolic(op) && !supportsSymbolicValuesNonGenerically(op))
2276 return visitOpGeneric(op);
2278 return TypeSwitch<Operation *, FailureOr<DeletionKind>>(op)
2281 arith::AddIOp, arith::XOrIOp, arith::AndIOp, arith::OrIOp,
2284 index::AddOp, index::SubOp, index::MulOp, index::DivUOp,
2285 index::CeilDivUOp, index::RemUOp, index::AndOp, index::OrOp,
2286 index::XOrOp, index::ShlOp, index::ShrUOp, index::MaxUOp,
2287 index::MinUOp, index::CmpOp,
2289 scf::IfOp, scf::ForOp, scf::YieldOp>(
2290 [&](
auto op) {
return visitOp(op); })
2291 .Default([&](Operation *op) {
return RTGBase::dispatchOpVisitor(op); });
2295 LogicalResult elaborate(Region ®ion,
2296 ArrayRef<ElaboratorValue> regionArguments,
2297 SmallVector<ElaboratorValue> &terminatorOperands) {
2298 if (region.getBlocks().size() > 1)
2299 return region.getParentOp()->emitOpError(
2300 "regions with more than one block are not supported");
2302 for (
auto [arg, elabArg] :
2303 llvm::zip(region.getArguments(), regionArguments))
2304 state[arg] = elabArg;
2306 Block *block = ®ion.front();
2307 for (
auto &op : *block) {
2308 auto result = dispatchOpVisitor(&op);
2312 if (*result == DeletionKind::Keep)
2313 if (failed(materializer.materialize(&op, state)))
2317 llvm::dbgs() <<
"Elaborated " << op <<
" to\n[";
2319 llvm::interleaveComma(op.getResults(), llvm::dbgs(), [&](
auto res) {
2320 if (state.contains(res))
2321 llvm::dbgs() << state.at(res);
2323 llvm::dbgs() <<
"unknown";
2326 llvm::dbgs() <<
"]\n\n";
2330 if (region.front().mightHaveTerminator())
2331 for (
auto val : region.front().getTerminator()->getOperands())
2332 terminatorOperands.push_back(state.at(val));
2339 SharedState &sharedState;
2342 TestState &testState;
2346 Materializer &materializer;
2349 DenseMap<Value, ElaboratorValue> state;
2352 ContextResourceAttrInterface currentContext;
2355 AttributeToElaboratorValueConverter attrConverter;
2358 ElaboratorValueToAttributeConverter elabValConverter;
2363Materializer::elaborateSequence(
const RandomizedSequenceStorage *
seq,
2364 SmallVector<ElaboratorValue> &elabArgs) {
2366 sharedState.table.lookup<SequenceOp>(
seq->sequence->familyName);
2369 OpBuilder builder(familyOp);
2370 auto seqOp = builder.cloneWithoutRegions(familyOp);
2371 auto name = sharedState.names.newName(
seq->sequence->familyName.getValue());
2372 seqOp.setSymName(name);
2373 seqOp.getBodyRegion().emplaceBlock();
2374 sharedState.table.insert(seqOp);
2375 assert(seqOp.getSymName() == name &&
"should not have been renamed");
2377 LLVM_DEBUG(llvm::dbgs() <<
"\n=== Elaborating sequence family @"
2378 << familyOp.getSymName() <<
" into @"
2379 << seqOp.getSymName() <<
" under context "
2380 <<
seq->context <<
"\n\n");
2382 Materializer materializer(OpBuilder::atBlockBegin(seqOp.getBody()), testState,
2383 sharedState, elabArgs);
2384 Elaborator elaborator(sharedState, testState, materializer,
seq->context);
2385 SmallVector<ElaboratorValue> yieldedVals;
2386 if (failed(elaborator.elaborate(familyOp.getBodyRegion(),
seq->sequence->args,
2390 seqOp.setSequenceType(
2391 SequenceType::get(builder.getContext(), materializer.getBlockArgTypes()));
2392 materializer.finalize();
2402struct ElaborationPass
2403 :
public rtg::impl::ElaborationPassBase<ElaborationPass> {
2406 void runOnOperation()
override;
2407 void matchTestsAgainstTargets(SymbolTable &table);
2408 LogicalResult elaborateModule(ModuleOp moduleOp, SymbolTable &table);
2412void ElaborationPass::runOnOperation() {
2413 auto moduleOp = getOperation();
2414 SymbolTable table(moduleOp);
2416 matchTestsAgainstTargets(table);
2418 if (failed(elaborateModule(moduleOp, table)))
2419 return signalPassFailure();
2422void ElaborationPass::matchTestsAgainstTargets(SymbolTable &table) {
2423 auto moduleOp = getOperation();
2425 for (
auto test :
llvm::make_early_inc_range(moduleOp.getOps<TestOp>())) {
2426 if (test.getTargetAttr())
2429 bool matched =
false;
2431 for (
auto target : moduleOp.getOps<TargetOp>()) {
2435 bool isSubtype =
true;
2436 auto testEntries = test.getTargetType().getEntries();
2437 auto targetEntries = target.getTarget().getEntries();
2441 size_t targetIdx = 0;
2442 for (
auto testEntry : testEntries) {
2444 while (targetIdx < targetEntries.size() &&
2445 targetEntries[targetIdx].name.getValue() <
2446 testEntry.name.getValue())
2450 if (targetIdx >= targetEntries.size() ||
2451 targetEntries[targetIdx].name != testEntry.name ||
2452 targetEntries[targetIdx].type != testEntry.type) {
2461 IRRewriter rewriter(test);
2463 auto newTest = cast<TestOp>(test->clone());
2464 newTest.setSymName(test.getSymName().str() +
"_" +
2465 target.getSymName().str());
2469 newTest.setTargetAttr(target.getSymNameAttr());
2471 table.insert(newTest, rewriter.getInsertionPoint());
2475 if (matched || deleteUnmatchedTests)
2481 return isa<MemoryBlockType, ContextResourceTypeInterface>(type);
2484LogicalResult ElaborationPass::elaborateModule(ModuleOp moduleOp,
2485 SymbolTable &table) {
2486 SharedState state(moduleOp.getContext(), table, seed);
2489 state.names.add(moduleOp);
2491 struct TargetElabResult {
2492 DictType targetType;
2493 SmallVector<ElaboratorValue> yields;
2494 TestState testState;
2498 DenseMap<StringAttr, TargetElabResult> targetMap;
2499 for (
auto targetOp : moduleOp.getOps<TargetOp>()) {
2500 LLVM_DEBUG(llvm::dbgs() <<
"=== Elaborating target @"
2501 << targetOp.getSymName() <<
"\n\n");
2503 auto &result = targetMap[targetOp.getSymNameAttr()];
2504 result.targetType = targetOp.getTarget();
2506 SmallVector<ElaboratorValue> blockArgs;
2507 Materializer targetMaterializer(OpBuilder::atBlockBegin(targetOp.getBody()),
2508 result.testState, state, blockArgs);
2509 Elaborator targetElaborator(state, result.testState, targetMaterializer);
2512 if (failed(targetElaborator.elaborate(targetOp.getBodyRegion(), {},
2516 targetMaterializer.finalize();
2521 for (
auto testOp : moduleOp.getOps<TestOp>()) {
2525 if (!testOp.getTargetAttr())
2528 LLVM_DEBUG(llvm::dbgs()
2529 <<
"\n=== Elaborating test @" << testOp.getTemplateName()
2530 <<
" for target @" << *testOp.getTarget() <<
"\n\n");
2533 auto targetResult = targetMap[testOp.getTargetAttr()];
2534 TestState testState = targetResult.testState;
2535 testState.name = testOp.getSymNameAttr();
2537 SmallVector<ElaboratorValue> filteredYields;
2539 for (
auto [entry, yield] :
2540 llvm::zip(targetResult.targetType.getEntries(), targetResult.yields)) {
2541 if (i >= testOp.getTargetType().getEntries().size())
2544 if (entry.name == testOp.getTargetType().getEntries()[i].name) {
2545 filteredYields.push_back(yield);
2552 SmallVector<ElaboratorValue> blockArgs;
2553 Materializer materializer(OpBuilder::atBlockBegin(testOp.getBody()),
2554 testState, state, blockArgs);
2556 for (
auto [arg, val] :
2557 llvm::zip(testOp.getBody()->getArguments(), filteredYields))
2559 materializer.map(val, arg);
2561 Elaborator elaborator(state, testState, materializer);
2562 SmallVector<ElaboratorValue> ignore;
2563 if (failed(elaborator.elaborate(testOp.getBodyRegion(), filteredYields,
2567 materializer.finalize();
assert(baseType &&"element must be base type")
static uint32_t computeMask(size_t w)
static uint32_t getUniformlyInRange(std::mt19937 &rng, uint32_t a, uint32_t b)
Get a number uniformly at random in the in specified range.
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.
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 getTombstoneKey()
static unsigned getHashValue(const bool &val)
static unsigned getEmptyKey()