From 0906f72ed0de6260ceb177e0f318b7e260e596b0 Mon Sep 17 00:00:00 2001 From: Martin Erhart Date: Wed, 5 Feb 2025 12:37:11 +0000 Subject: [PATCH] [RTG][Elaboration] Support interleave_sequences, factor our sequence inlining and label resolution --- include/circt/Dialect/RTG/IR/RTGVisitors.h | 3 +- .../circt/Dialect/RTG/Transforms/RTGPasses.td | 30 +++ lib/Dialect/RTG/Transforms/CMakeLists.txt | 2 + .../RTG/Transforms/ElaborationPass.cpp | 188 ++++++++++-------- .../RTG/Transforms/InlineSequencesPass.cpp | 96 +++++++++ .../RTG/Transforms/LowerUniqueLabelsPass.cpp | 65 ++++++ lib/Tools/rtgtool/RtgToolOptions.cpp | 16 +- test/Dialect/RTG/Transform/elaboration.mlir | 179 ++++++++++------- .../RTG/Transform/inline-sequences.mlir | 35 ++++ .../RTG/Transform/lower-unique-labels.mlir | 19 ++ 10 files changed, 478 insertions(+), 155 deletions(-) create mode 100644 lib/Dialect/RTG/Transforms/InlineSequencesPass.cpp create mode 100644 lib/Dialect/RTG/Transforms/LowerUniqueLabelsPass.cpp create mode 100644 test/Dialect/RTG/Transform/inline-sequences.mlir create mode 100644 test/Dialect/RTG/Transform/lower-unique-labels.mlir diff --git a/include/circt/Dialect/RTG/IR/RTGVisitors.h b/include/circt/Dialect/RTG/IR/RTGVisitors.h index 194561350ad2..01c1cc68a1d0 100644 --- a/include/circt/Dialect/RTG/IR/RTGVisitors.h +++ b/include/circt/Dialect/RTG/IR/RTGVisitors.h @@ -47,7 +47,7 @@ class RTGOpVisitor { RandomNumberInRangeOp, // Sequences SequenceOp, GetSequenceOp, SubstituteSequenceOp, - RandomizeSequenceOp, EmbedSequenceOp, + RandomizeSequenceOp, EmbedSequenceOp, InterleaveSequencesOp, // Sets SetCreateOp, SetSelectRandomOp, SetDifferenceOp, SetUnionOp, SetSizeOp>([&](auto expr) -> ResultType { @@ -86,6 +86,7 @@ class RTGOpVisitor { HANDLE(GetSequenceOp, Unhandled); HANDLE(SubstituteSequenceOp, Unhandled); HANDLE(RandomizeSequenceOp, Unhandled); + HANDLE(InterleaveSequencesOp, Unhandled); HANDLE(EmbedSequenceOp, Unhandled); HANDLE(RandomNumberInRangeOp, Unhandled); HANDLE(OnContextOp, Unhandled); diff --git a/include/circt/Dialect/RTG/Transforms/RTGPasses.td b/include/circt/Dialect/RTG/Transforms/RTGPasses.td index 945db4cf3b31..44fc5d227c7a 100644 --- a/include/circt/Dialect/RTG/Transforms/RTGPasses.td +++ b/include/circt/Dialect/RTG/Transforms/RTGPasses.td @@ -64,6 +64,22 @@ def EmitRTGISAAssemblyPass : Pass<"rtg-emit-isa-assembly", "mlir::ModuleOp"> { ]; } +def InlineSequencesPass : Pass<"rtg-inline-sequences", "mlir::ModuleOp"> { + let summary = "inline and interleave sequences"; + let description = [{ + Inline all sequences into tests and remove the 'rtg.sequence' operations. + Also computes and materializes all interleaved sequences + ('interleave_sequences' operation). + }]; + + let statistics = [ + Statistic<"numSequencesInlined", "num-sequences-inlined", + "Number of sequences inlined into another sequence or test.">, + Statistic<"numSequencesInterleaved", "num-sequences-interleaved", + "Number of sequences interleaved with another sequence.">, + ]; +} + def LinearScanRegisterAllocationPass : Pass< "rtg-linear-scan-register-allocation", "rtg::TestOp"> { @@ -81,4 +97,18 @@ def LinearScanRegisterAllocationPass : Pass< ]; } +def LowerUniqueLabelsPass : Pass<"rtg-lower-unique-labels", "mlir::ModuleOp"> { + let summary = "lower label_unique_decl to label_decl operations"; + let description = [{ + This pass lowers label_unique_decl operations to label_decl operations by + creating a unique label string based on all the existing unique and + non-unique label declarations in the module. + }]; + + let statistics = [ + Statistic<"numLabelsLowered", "num-labels-lowered", + "Number of unique labels lowered to regular label declarations.">, + ]; +} + #endif // CIRCT_DIALECT_RTG_TRANSFORMS_RTGPASSES_TD diff --git a/lib/Dialect/RTG/Transforms/CMakeLists.txt b/lib/Dialect/RTG/Transforms/CMakeLists.txt index c0aadb60ced8..a8b85b3ce741 100644 --- a/lib/Dialect/RTG/Transforms/CMakeLists.txt +++ b/lib/Dialect/RTG/Transforms/CMakeLists.txt @@ -1,7 +1,9 @@ add_circt_dialect_library(CIRCTRTGTransforms ElaborationPass.cpp EmitRTGISAAssemblyPass.cpp + InlineSequencesPass.cpp LinearScanRegisterAllocationPass.cpp + LowerUniqueLabelsPass.cpp DEPENDS CIRCTRTGTransformsIncGen diff --git a/lib/Dialect/RTG/Transforms/ElaborationPass.cpp b/lib/Dialect/RTG/Transforms/ElaborationPass.cpp index b3a105057ce5..771ef37ae44e 100644 --- a/lib/Dialect/RTG/Transforms/ElaborationPass.cpp +++ b/lib/Dialect/RTG/Transforms/ElaborationPass.cpp @@ -88,6 +88,7 @@ namespace { struct BagStorage; struct SequenceStorage; struct RandomizedSequenceStorage; +struct InterleavedSequenceStorage; struct SetStorage; struct VirtualRegisterStorage; struct UniqueLabelStorage; @@ -107,8 +108,9 @@ struct LabelValue { /// The abstract base class for elaborated values. using ElaboratorValue = std::variant; + RandomizedSequenceStorage *, InterleavedSequenceStorage *, + SetStorage *, VirtualRegisterStorage *, UniqueLabelStorage *, + LabelValue>; // NOLINTNEXTLINE(readability-identifier-naming) llvm::hash_code hash_value(const LabelValue &val) { @@ -309,6 +311,34 @@ struct RandomizedSequenceStorage { const SequenceStorage *sequence; }; +/// Storage object for interleaved '!rtg.randomized_sequence'es. +struct InterleavedSequenceStorage { + InterleavedSequenceStorage(SmallVector &&sequences, + uint32_t batchSize) + : sequences(std::move(sequences)), batchSize(batchSize), + hashcode(llvm::hash_combine( + llvm::hash_combine_range(sequences.begin(), sequences.end()), + batchSize)) {} + + explicit InterleavedSequenceStorage(RandomizedSequenceStorage *sequence) + : sequences(SmallVector(1, sequence)), batchSize(1), + hashcode(llvm::hash_combine( + llvm::hash_combine_range(sequences.begin(), sequences.end()), + batchSize)) {} + + bool isEqual(const InterleavedSequenceStorage *other) const { + return hashcode == other->hashcode && sequences == other->sequences && + batchSize == other->batchSize; + } + + const SmallVector sequences; + + const uint32_t batchSize; + + // The cached hashcode to avoid repeated computations. + const unsigned hashcode; +}; + /// Represents a unique virtual register. struct VirtualRegisterStorage { VirtualRegisterStorage(ArrayAttr allowedRegs) : allowedRegs(allowedRegs) {} @@ -373,6 +403,8 @@ class Internalizer { return internedSequences; else if constexpr (std::is_same_v) return internedRandomizedSequences; + else if constexpr (std::is_same_v) + return internedInterleavedSequences; else static_assert(!sizeof(StorageTy), "no intern set available for this storage type."); @@ -392,6 +424,9 @@ class Internalizer { DenseSet, StorageKeyInfo> internedRandomizedSequences; + DenseSet, + StorageKeyInfo> + internedInterleavedSequences; }; } // namespace @@ -438,6 +473,13 @@ static void print(RandomizedSequenceStorage *val, llvm::raw_ostream &os) { os << ") at " << val << ">"; } +static void print(InterleavedSequenceStorage *val, llvm::raw_ostream &os) { + os << "sequences, os, + [&](const ElaboratorValue &val) { os << val; }); + os << "] batch-size " << val->batchSize << " at " << val << ">"; +} + static void print(SetStorage *val, llvm::raw_ostream &os) { os << "set, os, @@ -677,7 +719,25 @@ class Materializer { elabRequests.push(val); Value seq = builder.create( loc, SequenceType::get(builder.getContext(), {}), val->name); - return builder.create(loc, seq); + Value res = builder.create(loc, seq); + materializedValues[val] = res; + return res; + } + + Value visit(InterleavedSequenceStorage *val, Location loc, + std::queue &elabRequests, + function_ref emitError) { + SmallVector sequences; + for (auto seqVal : val->sequences) + sequences.push_back(materialize(seqVal, loc, elabRequests, emitError)); + + if (sequences.size() == 1) + return sequences[0]; + + Value res = + builder.create(loc, sequences, val->batchSize); + materializedValues[val] = res; + return res; } Value visit(VirtualRegisterStorage *val, Location loc, @@ -735,7 +795,6 @@ struct ElaboratorSharedState { SymbolTable &table; std::mt19937 rng; Namespace names; - Namespace labelNames; Internalizer internalizer; /// The worklist used to keep track of the test and sequence operations to @@ -841,27 +900,57 @@ class Elaborator : public RTGOpVisitor> { auto *seq = get(op.getSequence()); auto name = sharedState.names.newName(seq->familyName.getValue()); - state[op.getResult()] = + auto *randomizedSeq = sharedState.internalizer.internalize( name, currentContext, testState.name, seq); + state[op.getResult()] = + sharedState.internalizer.internalize( + randomizedSeq); return DeletionKind::Delete; } - FailureOr visitOp(EmbedSequenceOp op) { - auto *seq = get(op.getSequence()); - if (seq->context != currentContext) { - auto err = op->emitError("attempting to place sequence ") - << seq->name << " derived from " - << seq->sequence->familyName.getValue() << " under context " - << currentContext - << ", but it was previously randomized for context "; - if (seq->context) - err << seq->context; - else - err << "'default'"; - return err; + FailureOr visitOp(InterleaveSequencesOp op) { + SmallVector sequences; + for (auto seq : op.getSequences()) + sequences.push_back(get(seq)); + + state[op.getResult()] = + sharedState.internalizer.internalize( + std::move(sequences), op.getBatchSize()); + return DeletionKind::Delete; + } + + // NOLINTNEXTLINE(misc-no-recursion) + LogicalResult isValidContext(ElaboratorValue value, Operation *op) const { + if (std::holds_alternative(value)) { + auto *seq = std::get(value); + if (seq->context != currentContext) { + auto err = op->emitError("attempting to place sequence ") + << seq->name << " derived from " + << seq->sequence->familyName.getValue() << " under context " + << currentContext + << ", but it was previously randomized for context "; + if (seq->context) + err << seq->context; + else + err << "'default'"; + return err; + } + return success(); } + auto *interVal = std::get(value); + for (auto val : interVal->sequences) + if (failed(isValidContext(val, op))) + return failure(); + return success(); + } + + FailureOr visitOp(EmbedSequenceOp op) { + auto *seqVal = get(op.getSequence()); + if (failed(isValidContext(seqVal, op))) + return failure(); + return DeletionKind::Keep; } @@ -1039,7 +1128,6 @@ class Elaborator : public RTGOpVisitor> { FailureOr visitOp(LabelDeclOp op) { auto substituted = substituteFormatString(op.getFormatStringAttr(), op.getArgs()); - sharedState.labelNames.add(substituted.getValue()); state[op.getLabel()] = LabelValue(substituted); return DeletionKind::Delete; } @@ -1309,7 +1397,6 @@ struct ElaborationPass void runOnOperation() override; void cloneTargetsIntoTests(SymbolTable &table); LogicalResult elaborateModule(ModuleOp moduleOp, SymbolTable &table); - LogicalResult inlineSequences(TestOp testOp, SymbolTable &table); }; } // namespace @@ -1407,6 +1494,8 @@ LogicalResult ElaborationPass::elaborateModule(ModuleOp moduleOp, auto seqOp = builder.cloneWithoutRegions(familyOp); seqOp.getBodyRegion().emplaceBlock(); seqOp.setSymName(curr->name); + seqOp.setSequenceType( + SequenceType::get(builder.getContext(), ArrayRef{})); table.insert(seqOp); assert(seqOp.getSymName() == curr->name && "should not have been renamed"); @@ -1425,64 +1514,5 @@ LogicalResult ElaborationPass::elaborateModule(ModuleOp moduleOp, materializer.finalize(); } - for (auto testOp : moduleOp.getOps()) { - // Inline all sequences and remove the operations that place the sequences. - if (failed(inlineSequences(testOp, table))) - return failure(); - - // Convert 'rtg.label_unique_decl' to 'rtg.label_decl' by choosing a unique - // name based on the set of names we collected during elaboration. - for (auto labelOp : - llvm::make_early_inc_range(testOp.getOps())) { - IRRewriter rewriter(labelOp); - auto newName = state.labelNames.newName(labelOp.getFormatString()); - rewriter.replaceOpWithNewOp(labelOp, newName, ValueRange()); - } - } - - // Remove all sequences since they are not accessible from the outside and - // are not needed anymore since we fully inlined them. - for (auto seqOp : llvm::make_early_inc_range(moduleOp.getOps())) - seqOp->erase(); - - return success(); -} - -LogicalResult ElaborationPass::inlineSequences(TestOp testOp, - SymbolTable &table) { - OpBuilder builder(testOp); - for (auto iter = testOp.getBody()->begin(); - iter != testOp.getBody()->end();) { - auto embedOp = dyn_cast(&*iter); - if (!embedOp) { - ++iter; - continue; - } - - auto randSeqOp = embedOp.getSequence().getDefiningOp(); - if (!randSeqOp) - return embedOp->emitError("sequence operand not directly defined by " - "'rtg.randomize_sequence' op"); - auto getSeqOp = randSeqOp.getSequence().getDefiningOp(); - if (!getSeqOp) - return randSeqOp->emitError( - "sequence operand not directly defined by 'rtg.get_sequence' op"); - - auto seqOp = table.lookup(getSeqOp.getSequenceAttr()); - - builder.setInsertionPointAfter(embedOp); - IRMapping mapping; - for (auto &op : *seqOp.getBody()) - builder.clone(op, mapping); - - (iter++)->erase(); - - if (randSeqOp->use_empty()) - randSeqOp->erase(); - - if (getSeqOp->use_empty()) - getSeqOp->erase(); - } - return success(); } diff --git a/lib/Dialect/RTG/Transforms/InlineSequencesPass.cpp b/lib/Dialect/RTG/Transforms/InlineSequencesPass.cpp new file mode 100644 index 000000000000..b63fde02a302 --- /dev/null +++ b/lib/Dialect/RTG/Transforms/InlineSequencesPass.cpp @@ -0,0 +1,96 @@ +//===- InlineSequencesPass.cpp - RTG InlineSequencesPass implementation ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass inlines sequences and computes sequence interleavings. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/RTG/IR/RTGOps.h" +#include "circt/Dialect/RTG/Transforms/RTGPasses.h" +#include "mlir/IR/IRMapping.h" + +namespace circt { +namespace rtg { +#define GEN_PASS_DEF_INLINESEQUENCESPASS +#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc" +} // namespace rtg +} // namespace circt + +using namespace mlir; +using namespace circt; +using namespace circt::rtg; + +//===----------------------------------------------------------------------===// +// Inline Sequences Pass +//===----------------------------------------------------------------------===// + +namespace { +struct InlineSequencesPass + : public rtg::impl::InlineSequencesPassBase { + using Base::Base; + + void runOnOperation() override; + LogicalResult inlineSequences(TestOp testOp, SymbolTable &table); +}; +} // namespace + +LogicalResult InlineSequencesPass::inlineSequences(TestOp testOp, + SymbolTable &table) { + OpBuilder builder(testOp); + for (auto iter = testOp.getBody()->begin(); + iter != testOp.getBody()->end();) { + auto embedOp = dyn_cast(&*iter); + if (!embedOp) { + ++iter; + continue; + } + + auto randSeqOp = embedOp.getSequence().getDefiningOp(); + if (!randSeqOp) + return embedOp->emitError("sequence operand not directly defined by " + "'rtg.randomize_sequence' op"); + auto getSeqOp = randSeqOp.getSequence().getDefiningOp(); + if (!getSeqOp) + return randSeqOp->emitError( + "sequence operand not directly defined by 'rtg.get_sequence' op"); + + auto seqOp = table.lookup(getSeqOp.getSequenceAttr()); + + builder.setInsertionPointAfter(embedOp); + IRMapping mapping; + for (auto &op : *seqOp.getBody()) + builder.clone(op, mapping); + + (iter++)->erase(); + + if (randSeqOp->use_empty()) + randSeqOp->erase(); + + if (getSeqOp->use_empty()) + getSeqOp->erase(); + + ++numSequencesInlined; + } + + return success(); +} + +void InlineSequencesPass::runOnOperation() { + auto moduleOp = getOperation(); + SymbolTable table(moduleOp); + + // Inline all sequences and remove the operations that place the sequences. + for (auto testOp : moduleOp.getOps()) + if (failed(inlineSequences(testOp, table))) + return; + + // Remove all sequences since they are not accessible from the outside and + // are not needed anymore since we fully inlined them. + for (auto seqOp : llvm::make_early_inc_range(moduleOp.getOps())) + seqOp->erase(); +} diff --git a/lib/Dialect/RTG/Transforms/LowerUniqueLabelsPass.cpp b/lib/Dialect/RTG/Transforms/LowerUniqueLabelsPass.cpp new file mode 100644 index 000000000000..58bd7f97dc70 --- /dev/null +++ b/lib/Dialect/RTG/Transforms/LowerUniqueLabelsPass.cpp @@ -0,0 +1,65 @@ +//===- LowerUniqueLabelsPass.cpp - RTG LowerUniqueLabelsPass impl. --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass lowers label_unique_decl operations to label_decl operations by +// creating a unique label string based on all the existing unique and +// non-unique label declarations in the module. +// +//===----------------------------------------------------------------------===// + +#include "circt/Dialect/RTG/IR/RTGOps.h" +#include "circt/Dialect/RTG/Transforms/RTGPasses.h" +#include "circt/Support/Namespace.h" +#include "mlir/IR/PatternMatch.h" + +namespace circt { +namespace rtg { +#define GEN_PASS_DEF_LOWERUNIQUELABELSPASS +#include "circt/Dialect/RTG/Transforms/RTGPasses.h.inc" +} // namespace rtg +} // namespace circt + +using namespace mlir; +using namespace circt; +using namespace circt::rtg; + +//===----------------------------------------------------------------------===// +// Lower Unique Labels Pass +//===----------------------------------------------------------------------===// + +namespace { +struct LowerUniqueLabelsPass + : public rtg::impl::LowerUniqueLabelsPassBase { + using Base::Base; + + void runOnOperation() override; +}; +} // namespace + +void LowerUniqueLabelsPass::runOnOperation() { + auto moduleOp = getOperation(); + Namespace labelNames; + + // Collect all the label names in a first iteration. + moduleOp.walk([&](Operation *op) { + if (auto labelDecl = dyn_cast(op)) + labelNames.add(labelDecl.getFormatString()); + else if (auto labelDecl = dyn_cast(op)) + labelNames.add(labelDecl.getFormatString()); + }); + + // Lower the unique labels in a second iteration. + moduleOp.walk([&](LabelUniqueDeclOp op) { + // Convert 'rtg.label_unique_decl' to 'rtg.label_decl' by choosing a unique + // name based on the set of names we collected during elaboration. + IRRewriter rewriter(op); + auto newName = labelNames.newName(op.getFormatString()); + rewriter.replaceOpWithNewOp(op, newName, ValueRange()); + ++numLabelsLowered; + }); +} diff --git a/lib/Tools/rtgtool/RtgToolOptions.cpp b/lib/Tools/rtgtool/RtgToolOptions.cpp index 0f80634fa392..40662accd404 100644 --- a/lib/Tools/rtgtool/RtgToolOptions.cpp +++ b/lib/Tools/rtgtool/RtgToolOptions.cpp @@ -29,7 +29,12 @@ void rtg::populateRandomizerPipeline(mlir::PassManager &pm, std::make_unique>( "rtgtool")); - pm.addPass(createSimpleCanonicalizerPass()); + { + // Initial canonicalization of the input IR. + auto &anyPm = pm.nestAny(); + anyPm.addPass(mlir::createCSEPass()); + anyPm.addPass(createSimpleCanonicalizerPass()); + } if (options.getOutputFormat() == RtgToolOptions::OutputFormat::MLIR) return; @@ -39,9 +44,14 @@ void rtg::populateRandomizerPipeline(mlir::PassManager &pm, passOptions.seed = options.getSeed(); pm.addPass(rtg::createElaborationPass(passOptions)); } + pm.addPass(rtg::createInlineSequencesPass()); + pm.addPass(rtg::createLowerUniqueLabelsPass()); pm.addNestedPass(rtg::createLinearScanRegisterAllocationPass()); - pm.addPass(mlir::createCSEPass()); - pm.addPass(createSimpleCanonicalizerPass()); + { + auto &anyPm = pm.nestAny(); + anyPm.addPass(mlir::createCSEPass()); + anyPm.addPass(createSimpleCanonicalizerPass()); + } if (options.getOutputFormat() == RtgToolOptions::OutputFormat::ElaboratedMLIR) return; diff --git a/test/Dialect/RTG/Transform/elaboration.mlir b/test/Dialect/RTG/Transform/elaboration.mlir index d61d4d6e41ae..7a1d0b8d661c 100644 --- a/test/Dialect/RTG/Transform/elaboration.mlir +++ b/test/Dialect/RTG/Transform/elaboration.mlir @@ -104,70 +104,51 @@ rtg.target @target1 : !rtg.dict { rtg.yield %0 : index } -// Unused sequences are removed -// CHECK-NOT: rtg.sequence @unused -rtg.sequence @unused() {} - rtg.sequence @seq0(%arg0: index) { func.call @dummy2(%arg0) : (index) -> () } -rtg.sequence @seq1(%arg0: index) { - %0 = rtg.get_sequence @seq0 : !rtg.sequence - %1 = rtg.substitute_sequence %0(%arg0) : !rtg.sequence - %2 = rtg.randomize_sequence %1 - func.call @dummy2(%arg0) : (index) -> () - rtg.embed_sequence %2 - func.call @dummy2(%arg0) : (index) -> () -} - -// CHECK-LABEL: rtg.test @nestedSequences -rtg.test @nestedSequences() { - // CHECK: index.constant 0 - // CHECK: func.call @dummy2 - // CHECK: func.call @dummy2 - // CHECK: func.call @dummy2 +// CHECK-LABEL: rtg.test @sequenceSubstitution +rtg.test @sequenceSubstitution() { + // CHECK-NEXT: [[V0:%.+]] = rtg.get_sequence @seq0{{.*}} : !rtg.sequence{{$}} + // CHECK-NEXT: [[V1:%.+]] = rtg.randomize_sequence [[V0]] + // CHECK-NEXT: rtg.embed_sequence [[V1]] %0 = index.constant 0 - %1 = rtg.get_sequence @seq1 : !rtg.sequence + %1 = rtg.get_sequence @seq0 : !rtg.sequence %2 = rtg.substitute_sequence %1(%0) : !rtg.sequence %3 = rtg.randomize_sequence %2 rtg.embed_sequence %3 } -rtg.sequence @seq2(%arg0: index) { - func.call @dummy2(%arg0) : (index) -> () -} - // CHECK-LABEL: rtg.test @sameSequenceDifferentArgs rtg.test @sameSequenceDifferentArgs() { - // CHECK: [[C0:%.+]] = index.constant 0 - // CHECK: func.call @dummy2([[C0]]) - // CHECK: [[C1:%.+]] = index.constant 1 - // CHECK: func.call @dummy2([[C1]]) + // CHECK-NEXT: [[V0:%.*]] = rtg.get_sequence @seq0_1 : !rtg.sequence + // CHECK-NEXT: [[V1:%.*]] = rtg.randomize_sequence [[V0]] + // CHECK-NEXT: rtg.embed_sequence [[V1]] + // CHECK-NEXT: [[V2:%.*]] = rtg.get_sequence @seq0_2 : !rtg.sequence + // CHECK-NEXT: [[V3:%.*]] = rtg.randomize_sequence [[V2]] + // CHECK-NEXT: rtg.embed_sequence [[V3]] %0 = index.constant 0 %1 = index.constant 1 - %2 = rtg.get_sequence @seq2 : !rtg.sequence + %2 = rtg.get_sequence @seq0 : !rtg.sequence %3 = rtg.substitute_sequence %2(%0) : !rtg.sequence %4 = rtg.randomize_sequence %3 - %5 = rtg.get_sequence @seq2 : !rtg.sequence + %5 = rtg.get_sequence @seq0 : !rtg.sequence %6 = rtg.substitute_sequence %5(%1) : !rtg.sequence %7 = rtg.randomize_sequence %6 rtg.embed_sequence %4 rtg.embed_sequence %7 } -rtg.sequence @seq3(%arg0: !rtg.set) { - %0 = rtg.set_select_random %arg0 : !rtg.set // we can't use a custom seed here because it would render the test useless - func.call @dummy2(%0) : (index) -> () -} - // CHECK-LABEL: rtg.test @sequenceClosureFixesRandomization rtg.test @sequenceClosureFixesRandomization() { - // CHECK: %idx0 = index.constant 0 - // CHECK: func.call @dummy2(%idx0 - // CHECK: %idx1 = index.constant 1 - // CHECK: func.call @dummy2(%idx1 - // CHECK: func.call @dummy2(%idx0 + // CHECK-NEXT: [[V0:%.+]] = rtg.get_sequence @seq3_0 : !rtg.sequence + // CHECK-NEXT: [[V1:%.+]] = rtg.randomize_sequence [[V0]] + // CHECK-NEXT: rtg.embed_sequence [[V1]] + // CHECK-NEXT: [[V2:%.+]] = rtg.get_sequence @seq3_1 : !rtg.sequence + // CHECK-NEXT: [[V3:%.+]] = rtg.randomize_sequence [[V2]] + // CHECK-NEXT: rtg.embed_sequence [[V3]] + // CHECK-NEXT: rtg.embed_sequence [[V1]] %0 = index.constant 0 %1 = index.constant 1 %2 = rtg.set_create %0, %1 : index @@ -182,6 +163,20 @@ rtg.test @sequenceClosureFixesRandomization() { rtg.embed_sequence %5 } +// CHECK: rtg.sequence @seq3_0() { +// CHECK-NEXT: %idx0 = index.constant 0 +// CHECK-NEXT: func.call @dummy2(%idx0) : (index) -> () +// CHECK-NEXT: } +// CHECK: rtg.sequence @seq3_1() { +// CHECK-NEXT: %idx1 = index.constant 1 +// CHECK-NEXT: func.call @dummy2(%idx1) : (index) -> () +// CHECK-NEXT: } + +rtg.sequence @seq3(%arg0: !rtg.set) { + %0 = rtg.set_select_random %arg0 : !rtg.set // we can't use a custom seed here because it would render the test useless + func.call @dummy2(%0) : (index) -> () +} + // CHECK-LABEL: @indexOps rtg.test @indexOps() { // CHECK: [[C:%.+]] = index.constant 2 @@ -346,30 +341,20 @@ rtg.test @virtualRegisters() { // CHECK-LABEL: @labels rtg.test @labels() { - // CHECK-NEXT: [[L0:%.+]] = rtg.label_decl "label0" - // CHECK-NEXT: rtg.label local [[L0]] + // CHECK-NEXT: [[L0:%.+]] = rtg.label_unique_decl "label0" // CHECK-NEXT: rtg.label local [[L0]] - // CHECK-NEXT: [[L1:%.+]] = rtg.label_decl "label1_0_1" + // CHECK-NEXT: [[L1:%.+]] = rtg.label_decl "label0" // CHECK-NEXT: rtg.label local [[L1]] - // CHECK-NEXT: [[L2:%.+]] = rtg.label_decl "label0_0" - // CHECK-NEXT: rtg.label local [[L2]] + // CHECK-NEXT: [[L2:%.+]] = rtg.label_decl "label1_0_1" // CHECK-NEXT: rtg.label local [[L2]] - // CHECK-NEXT: [[L3:%.+]] = rtg.label_decl "label0_1" - // CHECK-NEXT: rtg.label local [[L3]] - %0 = index.constant 0 %1 = index.constant 1 - %l0 = rtg.label_decl "label0" + %l0 = rtg.label_unique_decl "label{{0}}", %0 %l1 = rtg.label_decl "label{{0}}", %0 %l2 = rtg.label_decl "label{{0}}_{{1}}_{{0}}", %1, %0 - %l3 = rtg.label_unique_decl "label0" - %l4 = rtg.label_unique_decl "label0" rtg.label local %l0 rtg.label local %l1 rtg.label local %l2 - rtg.label local %l3 - rtg.label local %l3 - rtg.label local %l4 } // CHECK-LABEL: rtg.test @randomIntegers @@ -389,24 +374,13 @@ rtg.test @randomIntegers() { // CHECK-LABEL: rtg.test @contexts_contextCpu rtg.test @contexts(%cpu0: !rtgtest.cpu, %cpu1: !rtgtest.cpu) { - // CHECK-NEXT: rtg.label_decl "label0" - // CHECK-NEXT: rtg.label - // CHECK-NEXT: rtg.label_decl "label5" - // CHECK-NEXT: rtg.label - // CHECK-NEXT: rtg.label_decl "label2" - // CHECK-NEXT: rtg.label - // CHECK-NEXT: rtg.label_decl "label7" - // CHECK-NEXT: rtg.label - // CHECK-NEXT: rtg.label_decl "label4" - // CHECK-NEXT: rtg.label - // CHECK-NEXT: rtg.label_decl "label8" - // CHECK-NEXT: rtg.label - // CHECK-NEXT: rtg.label_decl "label3" - // CHECK-NEXT: rtg.label - // CHECK-NEXT: rtg.label_decl "label6" - // CHECK-NEXT: rtg.label - // CHECK-NEXT: rtg.label_decl "label1" - // CHECK-NEXT: rtg.label + // CHECK-NEXT: [[L0:%.+]] = rtg.label_decl "label0" + // CHECK-NEXT: rtg.label local [[L0]] + // CHECK-NEXT: [[SEQ0:%.+]] = rtg.get_sequence @switchCpuSeq_0 : !rtg.sequence + // CHECK-NEXT: [[SEQ1:%.+]] = rtg.randomize_sequence [[SEQ0]] + // CHECK-NEXT: rtg.embed_sequence [[SEQ1]] + // CHECK-NEXT: [[L1:%.+]] = rtg.label_decl "label1" + // CHECK-NEXT: rtg.label local [[L1]] %0 = rtg.get_sequence @cpuSeq : !rtg.sequence %1 = rtg.substitute_sequence %0(%cpu1) : !rtg.sequence %l0 = rtg.label_decl "label0" @@ -426,6 +400,15 @@ rtg.target @contextCpu : !rtg.dict { rtg.yield %cpu0, %cpu1 : !rtgtest.cpu, !rtgtest.cpu } +// CHECK: rtg.sequence @cpuSeq_0() { +// CHECK-NEXT: [[L2:%.+]] = rtg.label_decl "label2" +// CHECK-NEXT: rtg.label local [[L2]] +// CHECK-NEXT: [[SEQ2:%.+]] = rtg.get_sequence @switchNestedCpuSeq_0 : !rtg.sequence +// CHECK-NEXT: [[SEQ3:%.+]] = rtg.randomize_sequence [[SEQ2]] +// CHECK-NEXT: rtg.embed_sequence [[SEQ3]] +// CHECK-NEXT: [[L3:%.+]] = rtg.label_decl "label3" +// CHECK-NEXT: rtg.label local [[L3]] +// CHECK-NEXT: } rtg.sequence @cpuSeq(%cpu: !rtgtest.cpu) { %l2 = rtg.label_decl "label2" rtg.label local %l2 @@ -435,11 +418,24 @@ rtg.sequence @cpuSeq(%cpu: !rtgtest.cpu) { rtg.label local %l3 } +// CHECK: rtg.sequence @nestedCpuSeq_0() { +// CHECK-NEXT: [[L6:%.+]] = rtg.label_decl "label4" +// CHECK-NEXT: rtg.label local [[L6]] +// CHECK-NEXT: } rtg.sequence @nestedCpuSeq() { %l4 = rtg.label_decl "label4" rtg.label local %l4 } +// CHECK: rtg.sequence @switchCpuSeq_0() { +// CHECK-NEXT: [[L8:%.+]] = rtg.label_decl "label5" +// CHECK-NEXT: rtg.label local [[L8]] +// CHECK-NEXT: [[SEQ5:%.+]] = rtg.get_sequence @cpuSeq_0 : !rtg.sequence +// CHECK-NEXT: [[SEQ6:%.+]] = rtg.randomize_sequence [[SEQ5]] +// CHECK-NEXT: rtg.embed_sequence [[SEQ6]] +// CHECK-NEXT: [[L9:%.+]] = rtg.label_decl "label6" +// CHECK-NEXT: rtg.label local [[L9]] +// CHECK-NEXT: } rtg.sequence @switchCpuSeq(%parent: !rtgtest.cpu, %child: !rtgtest.cpu, %seq: !rtg.sequence) { %l5 = rtg.label_decl "label5" rtg.label local %l5 @@ -449,6 +445,15 @@ rtg.sequence @switchCpuSeq(%parent: !rtgtest.cpu, %child: !rtgtest.cpu, %seq: !r rtg.label local %l6 } +// CHECK: rtg.sequence @switchNestedCpuSeq_0() { +// CHECK-NEXT: [[L12:%.+]] = rtg.label_decl "label7" +// CHECK-NEXT: rtg.label local [[L12]] +// CHECK-NEXT: [[SEQ8:%.+]] = rtg.get_sequence @nestedCpuSeq_0 : !rtg.sequence +// CHECK-NEXT: [[SEQ9:%.+]] = rtg.randomize_sequence [[SEQ8]] +// CHECK-NEXT: rtg.embed_sequence [[SEQ9]] +// CHECK-NEXT: [[L13:%.+]] = rtg.label_decl "label8" +// CHECK-NEXT: rtg.label local [[L13]] +// CHECK-NEXT: } rtg.sequence @switchNestedCpuSeq(%parent: !rtgtest.cpu, %child: !rtgtest.cpu, %seq: !rtg.sequence) { %l7 = rtg.label_decl "label7" rtg.label local %l7 @@ -458,6 +463,36 @@ rtg.sequence @switchNestedCpuSeq(%parent: !rtgtest.cpu, %child: !rtgtest.cpu, %s rtg.label local %l8 } +rtg.sequence @interleaveSequencesSeq0() { + rtgtest.rv32i.ebreak + rtgtest.rv32i.ebreak +} + +rtg.sequence @interleaveSequencesSeq1() { + rtgtest.rv32i.ecall + rtgtest.rv32i.ecall +} + +// CHECK-LABEL: @interleaveSequences() +rtg.test @interleaveSequences() { + // CHECK-NEXT: [[V0:%.+]] = rtg.get_sequence @interleaveSequencesSeq0_0 : !rtg.sequence + // CHECK-NEXT: [[V2:%.+]] = rtg.randomize_sequence [[V0]] + // CHECK-NEXT: [[V1:%.+]] = rtg.get_sequence @interleaveSequencesSeq1_0 : !rtg.sequence + // CHECK-NEXT: [[V3:%.+]] = rtg.randomize_sequence [[V1]] + // CHECK-NEXT: [[V4:%.+]] = rtg.interleave_sequences [[V2]], [[V3]] batch 2 + // CHECK-NEXT: [[V5:%.+]] = rtg.interleave_sequences [[V4]], [[V3]] + // CHECK-NEXT: [[V6:%.+]] = rtg.interleave_sequences [[V5]], [[V2]] + // CHECK-NEXT: rtg.embed_sequence [[V6]] + %0 = rtg.get_sequence @interleaveSequencesSeq0 : !rtg.sequence + %1 = rtg.get_sequence @interleaveSequencesSeq1 : !rtg.sequence + %2 = rtg.randomize_sequence %0 + %3 = rtg.randomize_sequence %1 + %4 = rtg.interleave_sequences %2, %3 batch 2 + %5 = rtg.interleave_sequences %4, %3 + %6 = rtg.interleave_sequences %5, %2 + rtg.embed_sequence %6 +} + // ----- rtg.test @nestedRegionsNotSupported() { diff --git a/test/Dialect/RTG/Transform/inline-sequences.mlir b/test/Dialect/RTG/Transform/inline-sequences.mlir new file mode 100644 index 000000000000..02ab24c89c6c --- /dev/null +++ b/test/Dialect/RTG/Transform/inline-sequences.mlir @@ -0,0 +1,35 @@ +// RUN: circt-opt --rtg-inline-sequences --split-input-file --verify-diagnostics %s | FileCheck %s + +// CHECK-NOT: rtg.sequence +rtg.sequence @seq0() { + rtgtest.rv32i.ebreak +} + +// CHECK-LABEL: @interleaveSequences +rtg.test @interleaveSequences() { + // CHECK-NEXT: rtgtest.rv32i.ecall + // CHECK-NEXT: rtgtest.rv32i.ebreak + // CHECK-NEXT: rtgtest.rv32i.ecall + // CHECK-NEXT: } + + %0 = rtg.get_sequence @seq0 : !rtg.sequence + %1 = rtg.randomize_sequence %0 + rtgtest.rv32i.ecall + rtg.embed_sequence %1 + rtgtest.rv32i.ecall +} + +// ----- + +rtg.test @test0(%seq : !rtg.randomized_sequence) { + // expected-error @below {{sequence operand not directly defined by 'rtg.randomize_sequence' op}} + rtg.embed_sequence %seq +} + +// ----- + +rtg.test @test0(%seq : !rtg.sequence) { + // expected-error @below {{sequence operand not directly defined by 'rtg.get_sequence' op}} + %0 = rtg.randomize_sequence %seq + rtg.embed_sequence %0 +} diff --git a/test/Dialect/RTG/Transform/lower-unique-labels.mlir b/test/Dialect/RTG/Transform/lower-unique-labels.mlir new file mode 100644 index 000000000000..ff4a759b14d6 --- /dev/null +++ b/test/Dialect/RTG/Transform/lower-unique-labels.mlir @@ -0,0 +1,19 @@ +// RUN: circt-opt --rtg-lower-unique-labels --split-input-file --verify-diagnostics %s | FileCheck %s + +// CHECK-LABEL: @labels +rtg.test @labels() { + // CHECK-NEXT: [[L0:%.+]] = rtg.label_decl "label" + // CHECK-NEXT: [[L1:%.+]] = rtg.label_decl "label_0" + // CHECK-NEXT: [[L2:%.+]] = rtg.label_decl "label_1" + // CHECK-NEXT: rtg.label local [[L0]] + // CHECK-NEXT: rtg.label local [[L1]] + // CHECK-NEXT: rtg.label local [[L2]] + // CHECK-NEXT: rtg.label local [[L2]] + %l0 = rtg.label_decl "label" + %l1 = rtg.label_unique_decl "label" + %l2 = rtg.label_unique_decl "label" + rtg.label local %l0 + rtg.label local %l1 + rtg.label local %l2 + rtg.label local %l2 +}