[IRSim] Check largest sections first when analyzing similarity
When we check for similarity, right now there is no order to how it is checked, except for via the suffix tree ordering. We can reduce how much structural analysis we perform by checking the the regions in decreasing size. In doing so, we know that if two large sections match, each of their contained regions also match. This allows us to skip the structural checking for each smaller section. IT does require that we use the large regions as a "bridge" to create the canonical mapping between the two regions. This reduces compile time significantly for some benchmarks. It will not perform as well for programs with many small items. Recommit fixes the IRSimilarity tests. Recommit of:805ec19d7d
Recommit fixes llvm-sim tests Recommit of:082ec26758
Reviewer: paquette Differential Revision: https://reviews.llvm.org/D139338
This commit is contained in:
parent
ee7b6fd4c1
commit
e240e6b8b7
|
@ -850,6 +850,49 @@ public:
|
|||
IRSimilarityCandidate &SourceCand,
|
||||
DenseMap<unsigned, DenseSet<unsigned>> &ToSourceMapping,
|
||||
DenseMap<unsigned, DenseSet<unsigned>> &FromSourceMapping);
|
||||
|
||||
/// Create a mapping for the value numbering of the calling
|
||||
/// IRSimilarityCandidate, to a different separate set of numbers, based on
|
||||
/// the canonical ordering in \p SourceCand. These are defined based on the
|
||||
/// found mappings in \p ToSourceMapping and \p FromSourceMapping. Both of
|
||||
/// these relationships should have the same information, just in opposite
|
||||
/// directions. Uses the \p OneToOne mapping from target candidate to \p
|
||||
/// SourceCand GVNs to determine the mapping first for values with multiple
|
||||
/// mappings. This mapping is created by the ordering of operands in the
|
||||
/// instruction they are first seen in the candidates.
|
||||
///
|
||||
/// \param [in, out] SourceCand - The IRSimilarityCandidate to create a
|
||||
/// canonical numbering from.
|
||||
/// \param [in,out] OneToOne - A mapping of value numbers from candidate
|
||||
/// \p A to candidate \B using the structure of the original instructions.
|
||||
/// \param ToSourceMapping - The mapping of value numbers from this candidate
|
||||
/// to \p SourceCand.
|
||||
/// \param FromSourceMapping - The mapping of value numbers from \p SoureCand
|
||||
/// to this candidate.
|
||||
void createCanonicalRelationFrom(
|
||||
IRSimilarityCandidate &SourceCand,
|
||||
DenseMap<unsigned, unsigned> &OneToOne,
|
||||
DenseMap<unsigned, DenseSet<unsigned>> &ToSourceMapping,
|
||||
DenseMap<unsigned, DenseSet<unsigned>> &FromSourceMapping);
|
||||
|
||||
/// Create a mapping for the value numbering of the calling
|
||||
/// IRSimilarityCandidate, to a different separate set of numbers, based on
|
||||
/// the canonical ordering in \p SourceCand. These are defined based on the
|
||||
/// canonical mapping defined between \p SoureCandLarge and
|
||||
/// \p TargetCandLarge. These IRSimilarityCandidates are already structurally
|
||||
/// similar, and fully encapsulate the IRSimilarityCandidates in question.
|
||||
/// These are used as a "bridge" from the \p SourceCand to the target.
|
||||
///
|
||||
/// \param [in, out] SourceCand - The IRSimilarityCandidate to create a
|
||||
/// canonical numbering from.
|
||||
/// \param SoureCandLarge - The IRSimilarityCandidate fully containing
|
||||
/// \p SourceCand.
|
||||
/// \param TargetCandLarge - The IRSimilarityCandidate fully containing
|
||||
/// this Candidate.
|
||||
void createCanonicalRelationFrom(
|
||||
IRSimilarityCandidate &SourceCand,
|
||||
IRSimilarityCandidate &SourceCandLarge,
|
||||
IRSimilarityCandidate &TargetCandLarge);
|
||||
|
||||
/// \param [in,out] BBSet - The set to track the basic blocks.
|
||||
void getBasicBlocks(DenseSet<BasicBlock *> &BBSet) const {
|
||||
|
|
|
@ -1101,6 +1101,76 @@ void IRSimilarityCandidate::createCanonicalRelationFrom(
|
|||
}
|
||||
}
|
||||
|
||||
void IRSimilarityCandidate::createCanonicalRelationFrom(
|
||||
IRSimilarityCandidate &SourceCand, IRSimilarityCandidate &SourceCandLarge,
|
||||
IRSimilarityCandidate &TargetCandLarge) {
|
||||
assert(!SourceCand.CanonNumToNumber.empty() &&
|
||||
"Canonical Relationship is non-empty");
|
||||
assert(!SourceCand.NumberToCanonNum.empty() &&
|
||||
"Canonical Relationship is non-empty");
|
||||
|
||||
assert(!SourceCandLarge.CanonNumToNumber.empty() &&
|
||||
"Canonical Relationship is non-empty");
|
||||
assert(!SourceCandLarge.NumberToCanonNum.empty() &&
|
||||
"Canonical Relationship is non-empty");
|
||||
|
||||
assert(!TargetCandLarge.CanonNumToNumber.empty() &&
|
||||
"Canonical Relationship is non-empty");
|
||||
assert(!TargetCandLarge.NumberToCanonNum.empty() &&
|
||||
"Canonical Relationship is non-empty");
|
||||
|
||||
assert(CanonNumToNumber.empty() && "Canonical Relationship is non-empty");
|
||||
assert(NumberToCanonNum.empty() && "Canonical Relationship is non-empty");
|
||||
|
||||
// We're going to use the larger candidates as a "bridge" to create the
|
||||
// canonical number for the target candidate since we have idetified two
|
||||
// candidates as subsequences of larger sequences, and therefore must be
|
||||
// structurally similar.
|
||||
for (std::pair<Value *, unsigned> &ValueNumPair : ValueToNumber) {
|
||||
Value *CurrVal = ValueNumPair.first;
|
||||
unsigned TargetCandGVN = ValueNumPair.second;
|
||||
|
||||
// Find the numbering in the large candidate that surrounds the
|
||||
// current candidate.
|
||||
std::optional<unsigned> OLargeTargetGVN = TargetCandLarge.getGVN(CurrVal);
|
||||
assert(OLargeTargetGVN.has_value() && "GVN not found for Value");
|
||||
|
||||
// Get the canonical numbering in the large target candidate.
|
||||
std::optional<unsigned> OTargetCandCanon =
|
||||
TargetCandLarge.getCanonicalNum(OLargeTargetGVN.value());
|
||||
assert(OTargetCandCanon.has_value() &&
|
||||
"Canononical Number not found for GVN");
|
||||
|
||||
// Get the GVN in the large source candidate from the canonical numbering.
|
||||
std::optional<unsigned> OLargeSourceGVN =
|
||||
SourceCandLarge.fromCanonicalNum(OTargetCandCanon.value());
|
||||
assert(OLargeSourceGVN.has_value() &&
|
||||
"GVN Number not found for Canonical Number");
|
||||
|
||||
// Get the Value from the GVN in the large source candidate.
|
||||
std::optional<Value *> OLargeSourceV =
|
||||
SourceCandLarge.fromGVN(OLargeSourceGVN.value());
|
||||
assert(OLargeSourceV.has_value() && "Value not found for GVN");
|
||||
|
||||
// Get the GVN number for the Value in the source candidate.
|
||||
std::optional<unsigned> OSourceGVN =
|
||||
SourceCand.getGVN(OLargeSourceV.value());
|
||||
assert(OSourceGVN.has_value() && "GVN Number not found for Value");
|
||||
|
||||
// Get the canonical numbering from the GVN/
|
||||
std::optional<unsigned> OSourceCanon =
|
||||
SourceCand.getCanonicalNum(OSourceGVN.value());
|
||||
assert(OSourceCanon.has_value() && "Canon Number not found for GVN");
|
||||
|
||||
// Insert the canonical numbering and GVN pair into their respective
|
||||
// mappings.
|
||||
CanonNumToNumber.insert(
|
||||
std::make_pair(OSourceCanon.value(), TargetCandGVN));
|
||||
NumberToCanonNum.insert(
|
||||
std::make_pair(TargetCandGVN, OSourceCanon.value()));
|
||||
}
|
||||
}
|
||||
|
||||
void IRSimilarityCandidate::createCanonicalMappingFor(
|
||||
IRSimilarityCandidate &CurrCand) {
|
||||
assert(CurrCand.CanonNumToNumber.size() == 0 &&
|
||||
|
@ -1118,6 +1188,81 @@ void IRSimilarityCandidate::createCanonicalMappingFor(
|
|||
}
|
||||
}
|
||||
|
||||
/// Look for larger IRSimilarityCandidates From the previously matched
|
||||
/// IRSimilarityCandidates that fully contain \p CandA or \p CandB. If there is
|
||||
/// an overlap, return a pair of structurally similar, larger
|
||||
/// IRSimilarityCandidates.
|
||||
///
|
||||
/// \param [in] CandA - The first candidate we are trying to determine the
|
||||
/// structure of.
|
||||
/// \param [in] CandB - The second candidate we are trying to determine the
|
||||
/// structure of.
|
||||
/// \param [in] IndexToIncludedCand - Mapping of index of the an instruction in
|
||||
/// a circuit to the IRSimilarityCandidates that include this instruction.
|
||||
/// \param [in] CandToOverallGroup - Mapping of IRSimilarityCandidate to a
|
||||
/// number representing the structural group assigned to it.
|
||||
static std::optional<
|
||||
std::pair<IRSimilarityCandidate *, IRSimilarityCandidate *>>
|
||||
CheckLargerCands(
|
||||
IRSimilarityCandidate &CandA, IRSimilarityCandidate &CandB,
|
||||
DenseMap<unsigned, DenseSet<IRSimilarityCandidate *>> &IndexToIncludedCand,
|
||||
DenseMap<IRSimilarityCandidate *, unsigned> &CandToGroup) {
|
||||
DenseMap<unsigned, IRSimilarityCandidate *> IncludedGroupAndCandA;
|
||||
DenseMap<unsigned, IRSimilarityCandidate *> IncludedGroupAndCandB;
|
||||
DenseSet<unsigned> IncludedGroupsA;
|
||||
DenseSet<unsigned> IncludedGroupsB;
|
||||
|
||||
// Find the overall similarity group numbers that fully contain the candidate,
|
||||
// and record the larger candidate for each group.
|
||||
auto IdxToCandidateIt = IndexToIncludedCand.find(CandA.getStartIdx());
|
||||
std::optional<std::pair<IRSimilarityCandidate *, IRSimilarityCandidate *>>
|
||||
Result;
|
||||
|
||||
unsigned CandAStart = CandA.getStartIdx();
|
||||
unsigned CandAEnd = CandA.getEndIdx();
|
||||
unsigned CandBStart = CandB.getStartIdx();
|
||||
unsigned CandBEnd = CandB.getEndIdx();
|
||||
if (IdxToCandidateIt == IndexToIncludedCand.end())
|
||||
return Result;
|
||||
for (IRSimilarityCandidate *MatchedCand : IdxToCandidateIt->second) {
|
||||
if (MatchedCand->getStartIdx() > CandAStart ||
|
||||
(MatchedCand->getEndIdx() < CandAEnd))
|
||||
continue;
|
||||
unsigned GroupNum = CandToGroup.find(MatchedCand)->second;
|
||||
IncludedGroupAndCandA.insert(std::make_pair(GroupNum, MatchedCand));
|
||||
IncludedGroupsA.insert(GroupNum);
|
||||
}
|
||||
|
||||
// Find the overall similarity group numbers that fully contain the next
|
||||
// candidate, and record the larger candidate for each group.
|
||||
IdxToCandidateIt = IndexToIncludedCand.find(CandBStart);
|
||||
if (IdxToCandidateIt == IndexToIncludedCand.end())
|
||||
return Result;
|
||||
for (IRSimilarityCandidate *MatchedCand : IdxToCandidateIt->second) {
|
||||
if (MatchedCand->getStartIdx() > CandBStart ||
|
||||
MatchedCand->getEndIdx() < CandBEnd)
|
||||
continue;
|
||||
unsigned GroupNum = CandToGroup.find(MatchedCand)->second;
|
||||
IncludedGroupAndCandB.insert(std::make_pair(GroupNum, MatchedCand));
|
||||
IncludedGroupsB.insert(GroupNum);
|
||||
}
|
||||
|
||||
// Find the intersection between the two groups, these are the groups where
|
||||
// the larger candidates exist.
|
||||
set_intersect(IncludedGroupsA, IncludedGroupsB);
|
||||
|
||||
// If there is no intersection between the sets, then we cannot determine
|
||||
// whether or not there is a match.
|
||||
if (IncludedGroupsA.empty())
|
||||
return Result;
|
||||
|
||||
// Create a pair that contains the larger candidates.
|
||||
auto ItA = IncludedGroupAndCandA.find(*IncludedGroupsA.begin());
|
||||
auto ItB = IncludedGroupAndCandB.find(*IncludedGroupsA.begin());
|
||||
Result = std::make_pair(ItA->second, ItB->second);
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// From the list of IRSimilarityCandidates, perform a comparison between each
|
||||
/// IRSimilarityCandidate to determine if there are overlapping
|
||||
/// IRInstructionData, or if they do not have the same structure.
|
||||
|
@ -1127,9 +1272,16 @@ void IRSimilarityCandidate::createCanonicalMappingFor(
|
|||
/// \param [out] StructuralGroups - the mapping of unsigned integers to vector
|
||||
/// of IRSimilarityCandidates where each of the IRSimilarityCandidates in the
|
||||
/// vector are structurally similar to one another.
|
||||
/// \param [in] IndexToIncludedCand - Mapping of index of the an instruction in
|
||||
/// a circuit to the IRSimilarityCandidates that include this instruction.
|
||||
/// \param [in] CandToOverallGroup - Mapping of IRSimilarityCandidate to a
|
||||
/// number representing the structural group assigned to it.
|
||||
static void findCandidateStructures(
|
||||
std::vector<IRSimilarityCandidate> &CandsForRepSubstring,
|
||||
DenseMap<unsigned, SimilarityGroup> &StructuralGroups) {
|
||||
DenseMap<unsigned, SimilarityGroup> &StructuralGroups,
|
||||
DenseMap<unsigned, DenseSet<IRSimilarityCandidate *>> &IndexToIncludedCand,
|
||||
DenseMap<IRSimilarityCandidate *, unsigned> &CandToOverallGroup
|
||||
) {
|
||||
std::vector<IRSimilarityCandidate>::iterator CandIt, CandEndIt, InnerCandIt,
|
||||
InnerCandEndIt;
|
||||
|
||||
|
@ -1192,6 +1344,24 @@ static void findCandidateStructures(
|
|||
if (CandToGroupItInner != CandToGroup.end())
|
||||
continue;
|
||||
|
||||
// Check if we have found structural similarity between two candidates
|
||||
// that fully contains the first and second candidates.
|
||||
std::optional<std::pair<IRSimilarityCandidate *, IRSimilarityCandidate *>>
|
||||
LargerPair = CheckLargerCands(
|
||||
*CandIt, *InnerCandIt, IndexToIncludedCand, CandToOverallGroup);
|
||||
|
||||
// If a pair was found, it means that we can assume that these smaller
|
||||
// substrings are also structurally similar. Use the larger candidates to
|
||||
// determine the canonical mapping between the two sections.
|
||||
if (LargerPair.has_value()) {
|
||||
SameStructure = true;
|
||||
InnerCandIt->createCanonicalRelationFrom(
|
||||
*CandIt, *LargerPair.value().first, *LargerPair.value().second);
|
||||
CandToGroup.insert(std::make_pair(&*InnerCandIt, OuterGroupNum));
|
||||
CurrentGroupPair->second.push_back(*InnerCandIt);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise we determine if they have the same structure and add it to
|
||||
// vector if they match.
|
||||
ValueNumberMappingA.clear();
|
||||
|
@ -1218,24 +1388,58 @@ void IRSimilarityIdentifier::findCandidates(
|
|||
std::vector<SimilarityGroup> NewCandidateGroups;
|
||||
|
||||
DenseMap<unsigned, SimilarityGroup> StructuralGroups;
|
||||
DenseMap<unsigned, DenseSet<IRSimilarityCandidate *>> IndexToIncludedCand;
|
||||
DenseMap<IRSimilarityCandidate *, unsigned> CandToGroup;
|
||||
|
||||
// Iterate over the subsequences found by the Suffix Tree to create
|
||||
// IRSimilarityCandidates for each repeated subsequence and determine which
|
||||
// instances are structurally similar to one another.
|
||||
for (SuffixTree::RepeatedSubstring &RS : ST) {
|
||||
|
||||
// Sort the suffix tree from longest substring to shortest.
|
||||
std::vector<SuffixTree::RepeatedSubstring> RSes;
|
||||
for (SuffixTree::RepeatedSubstring &RS : ST)
|
||||
RSes.push_back(RS);
|
||||
|
||||
llvm::stable_sort(RSes, [](const SuffixTree::RepeatedSubstring &LHS,
|
||||
const SuffixTree::RepeatedSubstring &RHS) {
|
||||
return LHS.Length > RHS.Length;
|
||||
});
|
||||
for (SuffixTree::RepeatedSubstring &RS : RSes) {
|
||||
createCandidatesFromSuffixTree(Mapper, InstrList, IntegerMapping, RS,
|
||||
CandsForRepSubstring);
|
||||
|
||||
if (CandsForRepSubstring.size() < 2)
|
||||
continue;
|
||||
|
||||
findCandidateStructures(CandsForRepSubstring, StructuralGroups);
|
||||
for (std::pair<unsigned, SimilarityGroup> &Group : StructuralGroups)
|
||||
findCandidateStructures(CandsForRepSubstring, StructuralGroups,
|
||||
IndexToIncludedCand, CandToGroup);
|
||||
for (std::pair<unsigned, SimilarityGroup> &Group : StructuralGroups) {
|
||||
// We only add the group if it contains more than one
|
||||
// IRSimilarityCandidate. If there is only one, that means there is no
|
||||
// other repeated subsequence with the same structure.
|
||||
if (Group.second.size() > 1)
|
||||
if (Group.second.size() > 1) {
|
||||
SimilarityCandidates->push_back(Group.second);
|
||||
// Iterate over each candidate in the group, and add an entry for each
|
||||
// instruction included with a mapping to a set of
|
||||
// IRSimilarityCandidates that include that instruction.
|
||||
for (IRSimilarityCandidate &IRCand : SimilarityCandidates->back()) {
|
||||
for (unsigned Idx = IRCand.getStartIdx(), Edx = IRCand.getEndIdx();
|
||||
Idx <= Edx; ++Idx) {
|
||||
DenseMap<unsigned, DenseSet<IRSimilarityCandidate *>>::iterator
|
||||
IdIt;
|
||||
IdIt = IndexToIncludedCand.find(Idx);
|
||||
bool Inserted = false;
|
||||
if (IdIt == IndexToIncludedCand.end())
|
||||
std::tie(IdIt, Inserted) = IndexToIncludedCand.insert(
|
||||
std::make_pair(Idx, DenseSet<IRSimilarityCandidate *>()));
|
||||
IdIt->second.insert(&IRCand);
|
||||
}
|
||||
// Add mapping of candidate to the overall similarity group number.
|
||||
CandToGroup.insert(
|
||||
std::make_pair(&IRCand, SimilarityCandidates->size() - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CandsForRepSubstring.clear();
|
||||
StructuralGroups.clear();
|
||||
|
|
|
@ -4,7 +4,59 @@
|
|||
; This is a simple test to make sure the IRSimilarityIdentifier and
|
||||
; IRSimilarityPrinterPass is working.
|
||||
|
||||
; CHECK: 4 candidates of length 2. Found in:
|
||||
; CHECK: 4 candidates of length 6. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
; CHECK-NEXT: Function: cat, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 6, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 6, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 6, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT:4 candidates of length 5. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 2, ptr %2, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
; CHECK-NEXT: Function: cat, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT:4 candidates of length 4. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 3, ptr %3, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
; CHECK-NEXT: Function: cat, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 2, ptr %2, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 2, ptr %2, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 2, ptr %2, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT:4 candidates of length 3. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 4, ptr %4, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
; CHECK-NEXT: Function: cat, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 3, ptr %3, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 3, ptr %3, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 3, ptr %3, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT:4 candidates of length 2. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
|
@ -17,58 +69,6 @@
|
|||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 4, ptr %4, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT:4 candidates of length 3. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 4, ptr %4, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
; CHECK-NEXT: Function: cat, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 3, ptr %3, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 3, ptr %3, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 3, ptr %3, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT:4 candidates of length 4. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 3, ptr %3, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
; CHECK-NEXT: Function: cat, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 2, ptr %2, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 2, ptr %2, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 2, ptr %2, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT:4 candidates of length 5. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 2, ptr %2, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
; CHECK-NEXT: Function: cat, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT:4 candidates of length 6. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 6, ptr %6, align 4
|
||||
; CHECK-NEXT: Function: cat, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 6, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 6, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: Function: dog, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 6, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
|
||||
define linkonce_odr void @fish() {
|
||||
entry:
|
||||
|
@ -136,3 +136,5 @@ entry:
|
|||
store i32 5, ptr %5, align 4
|
||||
ret void
|
||||
}
|
||||
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
|
||||
; CHECK: {{.*}}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
|
||||
; RUN: opt -disable-output -S -passes=print-ir-similarity < %s 2>&1 | FileCheck %s
|
||||
|
||||
; When a debug instruction is the first instruction in a block, when that block
|
||||
|
@ -5,27 +6,27 @@
|
|||
; counted in similarity matching they must be ignored when creating canonical
|
||||
; relations from one region to another. This checks that this is enforced.
|
||||
|
||||
; CHECK: 2 candidates of length 3. Found in:
|
||||
; CHECK: 2 candidates of length 4. Found in:
|
||||
; CHECK-NEXT: Function: main, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: %0 = add i32 1, 4
|
||||
; CHECK-NEXT: End Instruction: %1 = sub i32 1, 4
|
||||
; CHECK-NEXT: Function: main, Basic Block: for.body169
|
||||
; CHECK-NEXT: Start Instruction: %2 = add i32 1, 4
|
||||
; CHECK-NEXT: End Instruction: %3 = sub i32 1, 4
|
||||
; CHECK-NEXT: 2 candidates of length 3. Found in:
|
||||
; CHECK-NEXT: Function: main, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: br label %for.body169
|
||||
; CHECK-NEXT: End Instruction: %1 = sub i32 1, 4
|
||||
; CHECK-NEXT: Function: main, Basic Block: for.body169
|
||||
; CHECK-NEXT: Start Instruction: br label %for.end122
|
||||
; CHECK-NEXT: End Instruction: %3 = sub i32 1, 4
|
||||
; CHECK-NEXT: 2 candidates of length 2. Found in:
|
||||
; CHECK-NEXT: 2 candidates of length 2. Found in:
|
||||
; CHECK-NEXT: Function: main, Basic Block: for.end122
|
||||
; CHECK-NEXT: Start Instruction: store i32 30, ptr undef, align 1
|
||||
; CHECK-NEXT: End Instruction: %1 = sub i32 1, 4
|
||||
; CHECK-NEXT: Function: main, Basic Block: for.end246
|
||||
; CHECK-NEXT: Start Instruction: store i32 0, ptr undef, align 1
|
||||
; CHECK-NEXT: End Instruction: %3 = sub i32 1, 4
|
||||
; CHECK-NEXT: 2 candidates of length 4. Found in:
|
||||
; CHECK-NEXT: Function: main, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: %0 = add i32 1, 4
|
||||
; CHECK-NEXT: End Instruction: %1 = sub i32 1, 4
|
||||
; CHECK-NEXT: Function: main, Basic Block: for.body169
|
||||
; CHECK-NEXT: Start Instruction: %2 = add i32 1, 4
|
||||
; CHECK-NEXT: End Instruction: %3 = sub i32 1, 4
|
||||
|
||||
source_filename = "irsimilarity_crash.ll"
|
||||
|
||||
|
@ -72,3 +73,5 @@ attributes #0 = { nocallback nofree nosync nounwind readnone speculatable willre
|
|||
!9 = !{}
|
||||
!10 = !DIBasicType(name: "long", size: 32, encoding: DW_ATE_signed)
|
||||
!11 = !DILocation(line: 522, column: 23, scope: !2)
|
||||
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
|
||||
; CHECK: {{.*}}
|
||||
|
|
|
@ -5,20 +5,20 @@
|
|||
; return items only within the same function when there are different sets of
|
||||
; instructions in functions.
|
||||
|
||||
; CHECK: 2 candidates of length 3. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: %b = load i32, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: %d = load i32, ptr %3, align 4
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: %a = load i32, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: %c = load i32, ptr %2, align 4
|
||||
; CHECK-NEXT: 2 candidates of length 5. Found in:
|
||||
; CHECK: 2 candidates of length 5. Found in:
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 6, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 4, ptr %4, align 4
|
||||
; CHECK-NEXT: Function: fish, Basic Block: entry
|
||||
; CHECK-NEXT: Start Instruction: store i32 1, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: store i32 5, ptr %5, align 4
|
||||
; CHECK-NEXT: 2 candidates of length 3. Found in:
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: %b = load i32, ptr %1, align 4
|
||||
; CHECK-NEXT: End Instruction: %d = load i32, ptr %3, align 4
|
||||
; CHECK-NEXT: Function: turtle, Basic Block: (unnamed)
|
||||
; CHECK-NEXT: Start Instruction: %a = load i32, ptr %0, align 4
|
||||
; CHECK-NEXT: End Instruction: %c = load i32, ptr %2, align 4
|
||||
|
||||
define linkonce_odr void @fish() {
|
||||
entry:
|
||||
|
@ -44,3 +44,5 @@ define void @turtle(ptr %0, ptr %1, ptr %2, ptr %3) {
|
|||
%d = load i32, ptr %3
|
||||
ret void
|
||||
}
|
||||
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
|
||||
; CHECK: {{.*}}
|
||||
|
|
|
@ -10,3 +10,5 @@ define linkonce_odr void @fish() {
|
|||
entry:
|
||||
ret void
|
||||
}
|
||||
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
|
||||
; CHECK: {{.*}}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -S -passes=verify,iroutliner -ir-outlining-no-cost < %s | FileCheck %s
|
||||
; RUN: opt -S -p iroutliner,verify -ir-outlining-no-cost < %s | FileCheck %s
|
||||
|
||||
; This test ensures that we do not include llvm.assumes. There are exceptions
|
||||
; in the CodeExtractor's algorithm for llvm.assumes, so we ignore it for now.
|
||||
|
@ -13,13 +13,13 @@ define void @outline_assumes() {
|
|||
; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: [[D:%.*]] = alloca i1, align 4
|
||||
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 -1, ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_3(i1 true, ptr [[D]], ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_4(i1 true, ptr [[D]], ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: [[DL_RELOAD:%.*]] = load i1, ptr [[DL_LOC]], align 1
|
||||
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 -1, ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: [[SPLIT_INST:%.*]] = sub i1 [[DL_RELOAD]], [[DL_RELOAD]]
|
||||
; CHECK-NEXT: call void @outlined_ir_func_0(ptr [[A]], ptr [[B]], ptr [[C]])
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[DL_RELOAD]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_1(ptr [[A]], ptr [[B]], ptr [[C]])
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[DL_RELOAD]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_2(ptr [[A]], ptr [[B]], ptr [[C]])
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
entry:
|
||||
|
@ -49,12 +49,12 @@ define void @outline_assumes2() {
|
|||
; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: [[D:%.*]] = alloca i1, align 4
|
||||
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 -1, ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_3(i1 false, ptr [[D]], ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_4(i1 false, ptr [[D]], ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: [[DL_RELOAD:%.*]] = load i1, ptr [[DL_LOC]], align 1
|
||||
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 -1, ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_0(ptr [[A]], ptr [[B]], ptr [[C]])
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[DL_RELOAD]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_1(ptr [[A]], ptr [[B]], ptr [[C]])
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[DL_RELOAD]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_2(ptr [[A]], ptr [[B]], ptr [[C]])
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
entry:
|
||||
|
@ -77,16 +77,17 @@ entry:
|
|||
define void @outline_assumes3() {
|
||||
; CHECK-LABEL: @outline_assumes3(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[DL_LOC:%.*]] = alloca i1, align 1
|
||||
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: [[D:%.*]] = alloca i1, align 4
|
||||
; CHECK-NEXT: store i1 true, ptr [[D]], align 4
|
||||
; CHECK-NEXT: [[DL:%.*]] = load i1, ptr [[D]], align 1
|
||||
; CHECK-NEXT: [[SPLIT_INST:%.*]] = add i1 [[DL]], [[DL]]
|
||||
; CHECK-NEXT: call void @outlined_ir_func_0(ptr [[A]], ptr [[B]], ptr [[C]])
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[DL]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_2(ptr [[A]])
|
||||
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 -1, ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_0(i1 true, ptr [[D]], ptr [[A]], ptr [[B]], ptr [[C]], ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: [[DL_RELOAD:%.*]] = load i1, ptr [[DL_LOC]], align 1
|
||||
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 -1, ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[DL_RELOAD]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_3(ptr [[A]])
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
entry:
|
||||
|
@ -109,16 +110,17 @@ entry:
|
|||
define void @outline_assumes4() {
|
||||
; CHECK-LABEL: @outline_assumes4(
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[DL_LOC:%.*]] = alloca i1, align 1
|
||||
; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: [[B:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: [[C:%.*]] = alloca i32, align 4
|
||||
; CHECK-NEXT: [[D:%.*]] = alloca i1, align 4
|
||||
; CHECK-NEXT: store i1 false, ptr [[D]], align 4
|
||||
; CHECK-NEXT: [[DL:%.*]] = load i1, ptr [[D]], align 1
|
||||
; CHECK-NEXT: [[SPLIT_INST:%.*]] = add i1 [[DL]], [[DL]]
|
||||
; CHECK-NEXT: call void @outlined_ir_func_0(ptr [[A]], ptr [[B]], ptr [[C]])
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[DL]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_2(ptr [[A]])
|
||||
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 -1, ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_0(i1 false, ptr [[D]], ptr [[A]], ptr [[B]], ptr [[C]], ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: [[DL_RELOAD:%.*]] = load i1, ptr [[DL_LOC]], align 1
|
||||
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 -1, ptr [[DL_LOC]])
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[DL_RELOAD]])
|
||||
; CHECK-NEXT: call void @outlined_ir_func_3(ptr [[A]])
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
entry:
|
||||
|
|
|
@ -6,21 +6,21 @@
|
|||
# CHECK: {
|
||||
# CHECK-NEXT: "1": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 18,
|
||||
# CHECK-NEXT: "start": 14,
|
||||
# CHECK-NEXT: "end": 19
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 8,
|
||||
# CHECK-NEXT: "start": 4,
|
||||
# CHECK-NEXT: "end": 9
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "2": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 17,
|
||||
# CHECK-NEXT: "start": 15,
|
||||
# CHECK-NEXT: "end": 19
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 7,
|
||||
# CHECK-NEXT: "start": 5,
|
||||
# CHECK-NEXT: "end": 9
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
|
@ -36,21 +36,21 @@
|
|||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "4": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 15,
|
||||
# CHECK-NEXT: "start": 17,
|
||||
# CHECK-NEXT: "end": 19
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 5,
|
||||
# CHECK-NEXT: "start": 7,
|
||||
# CHECK-NEXT: "end": 9
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "5": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 14,
|
||||
# CHECK-NEXT: "start": 18,
|
||||
# CHECK-NEXT: "end": 19
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 4,
|
||||
# CHECK-NEXT: "start": 8,
|
||||
# CHECK-NEXT: "end": 9
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ]
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
# CHECK: {
|
||||
# CHECK-NEXT: "1": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 18,
|
||||
# CHECK-NEXT: "start": 14,
|
||||
# CHECK-NEXT: "end": 19
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 8,
|
||||
# CHECK-NEXT: "start": 4,
|
||||
# CHECK-NEXT: "end": 9
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "2": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 17,
|
||||
# CHECK-NEXT: "start": 15,
|
||||
# CHECK-NEXT: "end": 19
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 7,
|
||||
# CHECK-NEXT: "start": 5,
|
||||
# CHECK-NEXT: "end": 9
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
|
@ -35,21 +35,21 @@
|
|||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "4": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 15,
|
||||
# CHECK-NEXT: "start": 17,
|
||||
# CHECK-NEXT: "end": 19
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 5,
|
||||
# CHECK-NEXT: "start": 7,
|
||||
# CHECK-NEXT: "end": 9
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ],
|
||||
# CHECK-NEXT: "5": [
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 14,
|
||||
# CHECK-NEXT: "start": 18,
|
||||
# CHECK-NEXT: "end": 19
|
||||
# CHECK-NEXT: },
|
||||
# CHECK-NEXT: {
|
||||
# CHECK-NEXT: "start": 4,
|
||||
# CHECK-NEXT: "start": 8,
|
||||
# CHECK-NEXT: "end": 9
|
||||
# CHECK-NEXT: }
|
||||
# CHECK-NEXT: ]
|
||||
|
|
Loading…
Reference in New Issue
Block a user