diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index 7932e0d452c43f..68155021d8eb78 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -178,6 +178,7 @@ set( JIT_SOURCES ssabuilder.cpp ssarenamestate.cpp stacklevelsetter.cpp + structsegments.cpp switchrecognition.cpp treelifeupdater.cpp unwind.cpp @@ -379,6 +380,7 @@ set( JIT_HEADERS ssaconfig.h ssarenamestate.h stacklevelsetter.h + structsegments.h target.h targetx86.h targetamd64.h diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 299d65f4f33cda..f8080a73cc2119 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -1990,6 +1990,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, m_outlinedCompositeSsaNums = nullptr; m_nodeToLoopMemoryBlockMap = nullptr; m_signatureToLookupInfoMap = nullptr; + m_significantSegmentsMap = nullptr; fgSsaPassesCompleted = 0; fgSsaValid = false; fgVNPassesCompleted = 0; @@ -8373,6 +8374,67 @@ void Compiler::TransferTestDataToNode(GenTree* from, GenTree* to) #endif // DEBUG +//------------------------------------------------------------------------ +// GetSignificantSegments: +// Compute a segment tree containing all significant (non-padding) segments +// for the specified class layout. +// +// Parameters: +// layout - The layout +// +// Returns: +// Segment tree containing all significant parts of the layout. +// +const StructSegments& Compiler::GetSignificantSegments(ClassLayout* layout) +{ + StructSegments* cached; + if ((m_significantSegmentsMap != nullptr) && m_significantSegmentsMap->Lookup(layout, &cached)) + { + return *cached; + } + + COMP_HANDLE compHnd = info.compCompHnd; + + StructSegments* newSegments = new (this, CMK_Promotion) StructSegments(getAllocator(CMK_Promotion)); + + if (layout->IsBlockLayout()) + { + newSegments->Add(StructSegments::Segment(0, layout->GetSize())); + } + else + { + CORINFO_TYPE_LAYOUT_NODE nodes[256]; + size_t numNodes = ArrLen(nodes); + GetTypeLayoutResult result = compHnd->getTypeLayout(layout->GetClassHandle(), nodes, &numNodes); + + if (result != GetTypeLayoutResult::Success) + { + newSegments->Add(StructSegments::Segment(0, layout->GetSize())); + } + else + { + for (size_t i = 0; i < numNodes; i++) + { + const CORINFO_TYPE_LAYOUT_NODE& node = nodes[i]; + if ((node.type != CORINFO_TYPE_VALUECLASS) || (node.simdTypeHnd != NO_CLASS_HANDLE) || + node.hasSignificantPadding) + { + newSegments->Add(StructSegments::Segment(node.offset, node.offset + node.size)); + } + } + } + } + + if (m_significantSegmentsMap == nullptr) + { + m_significantSegmentsMap = new (this, CMK_Promotion) ClassLayoutStructSegmentsMap(getAllocator(CMK_Promotion)); + } + + m_significantSegmentsMap->Set(layout, newSegments); + + return *newSegments; +} + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 488d6a01de9e1e..fb5950694f5fd1 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -43,6 +43,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "valuenum.h" #include "scev.h" #include "namedintrinsiclist.h" +#include "structsegments.h" #ifdef LATE_DISASM #include "disasm.h" #endif @@ -5630,6 +5631,11 @@ class Compiler return m_signatureToLookupInfoMap; } + const StructSegments& GetSignificantSegments(ClassLayout* layout); + + typedef JitHashTable, class StructSegments*> ClassLayoutStructSegmentsMap; + ClassLayoutStructSegmentsMap* m_significantSegmentsMap; + #ifdef SWIFT_SUPPORT typedef JitHashTable, CORINFO_SWIFT_LOWERING*> SwiftLoweringMap; SwiftLoweringMap* m_swiftLoweringCache; diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index b656f1576d0d9d..c6a1eb0b3f4e6e 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -1245,7 +1245,7 @@ class LocalsUseVisitor : public GenTreeVisitor } #endif - agg->Unpromoted = m_prom->SignificantSegments(m_compiler->lvaGetDesc(agg->LclNum)->GetLayout()); + agg->Unpromoted = m_compiler->GetSignificantSegments(m_compiler->lvaGetDesc(agg->LclNum)->GetLayout()); for (Replacement& rep : reps) { agg->Unpromoted.Subtract(StructSegments::Segment(rep.Offset, rep.Offset + genTypeSize(rep.AccessType))); @@ -1484,365 +1484,6 @@ bool Replacement::Overlaps(unsigned otherStart, unsigned otherSize) const return true; } -//------------------------------------------------------------------------ -// IntersectsOrAdjacent: -// Check if this segment intersects or is adjacent to another segment. -// -// Parameters: -// other - The other segment. -// -// Returns: -// True if so. -// -bool StructSegments::Segment::IntersectsOrAdjacent(const Segment& other) const -{ - if (End < other.Start) - { - return false; - } - - if (other.End < Start) - { - return false; - } - - return true; -} - -//------------------------------------------------------------------------ -// Intersects: -// Check if this segment intersects another segment. -// -// Parameters: -// other - The other segment. -// -// Returns: -// True if so. -// -bool StructSegments::Segment::Intersects(const Segment& other) const -{ - if (End <= other.Start) - { - return false; - } - - if (other.End <= Start) - { - return false; - } - - return true; -} - -//------------------------------------------------------------------------ -// Contains: -// Check if this segment contains another segment. -// -// Parameters: -// other - The other segment. -// -// Returns: -// True if so. -// -bool StructSegments::Segment::Contains(const Segment& other) const -{ - return (other.Start >= Start) && (other.End <= End); -} - -//------------------------------------------------------------------------ -// Merge: -// Update this segment to also contain another segment. -// -// Parameters: -// other - The other segment. -// -void StructSegments::Segment::Merge(const Segment& other) -{ - Start = min(Start, other.Start); - End = max(End, other.End); -} - -//------------------------------------------------------------------------ -// Add: -// Add a segment to the data structure. -// -// Parameters: -// segment - The segment to add. -// -void StructSegments::Add(const Segment& segment) -{ - size_t index = Promotion::BinarySearch(m_segments, segment.Start); - - if ((ssize_t)index < 0) - { - index = ~index; - } - - m_segments.insert(m_segments.begin() + index, segment); - size_t endIndex; - for (endIndex = index + 1; endIndex < m_segments.size(); endIndex++) - { - if (!m_segments[index].IntersectsOrAdjacent(m_segments[endIndex])) - { - break; - } - - m_segments[index].Merge(m_segments[endIndex]); - } - - m_segments.erase(m_segments.begin() + index + 1, m_segments.begin() + endIndex); -} - -//------------------------------------------------------------------------ -// Subtract: -// Subtract a segment from the data structure. -// -// Parameters: -// segment - The segment to subtract. -// -void StructSegments::Subtract(const Segment& segment) -{ - size_t index = Promotion::BinarySearch(m_segments, segment.Start); - if ((ssize_t)index < 0) - { - index = ~index; - } - else - { - // Start == segment[index].End, which makes it non-interesting. - index++; - } - - if (index >= m_segments.size()) - { - return; - } - - // Here we know Start < segment[index].End. Do they not intersect at all? - if (m_segments[index].Start >= segment.End) - { - // Does not intersect any segment. - return; - } - - assert(m_segments[index].Intersects(segment)); - - if (m_segments[index].Contains(segment)) - { - if (segment.Start > m_segments[index].Start) - { - // New segment (existing.Start, segment.Start) - if (segment.End < m_segments[index].End) - { - m_segments.insert(m_segments.begin() + index, Segment(m_segments[index].Start, segment.Start)); - - // And new segment (segment.End, existing.End) - m_segments[index + 1].Start = segment.End; - return; - } - - m_segments[index].End = segment.Start; - return; - } - if (segment.End < m_segments[index].End) - { - // New segment (segment.End, existing.End) - m_segments[index].Start = segment.End; - return; - } - - // Full segment is being removed - m_segments.erase(m_segments.begin() + index); - return; - } - - if (segment.Start > m_segments[index].Start) - { - m_segments[index].End = segment.Start; - index++; - } - - size_t endIndex = Promotion::BinarySearch(m_segments, segment.End); - if ((ssize_t)endIndex >= 0) - { - m_segments.erase(m_segments.begin() + index, m_segments.begin() + endIndex + 1); - return; - } - - endIndex = ~endIndex; - if (endIndex == m_segments.size()) - { - m_segments.erase(m_segments.begin() + index, m_segments.end()); - return; - } - - if (segment.End > m_segments[endIndex].Start) - { - m_segments[endIndex].Start = segment.End; - } - - m_segments.erase(m_segments.begin() + index, m_segments.begin() + endIndex); -} - -//------------------------------------------------------------------------ -// IsEmpty: -// Check if the segment tree is empty. -// -// Returns: -// True if so. -// -bool StructSegments::IsEmpty() -{ - return m_segments.size() == 0; -} - -//------------------------------------------------------------------------ -// CoveringSegment: -// Compute a segment that covers all contained segments in this segment tree. -// -// Parameters: -// result - [out] The single segment. Only valid if the method returns true. -// -// Returns: -// True if this segment tree was non-empty; otherwise false. -// -bool StructSegments::CoveringSegment(Segment* result) -{ - if (m_segments.size() == 0) - { - return false; - } - - result->Start = m_segments[0].Start; - result->End = m_segments[m_segments.size() - 1].End; - return true; -} - -//------------------------------------------------------------------------ -// Intersects: -// Check if a segment intersects with any segment in this segment tree. -// -// Parameters: -// segment - The segment. -// -// Returns: -// True if the input segment intersects with any segment in the tree; -// otherwise false. -// -bool StructSegments::Intersects(const Segment& segment) -{ - size_t index = Promotion::BinarySearch(m_segments, segment.Start); - if ((ssize_t)index < 0) - { - index = ~index; - } - else - { - // Start == segment[index].End, which makes it non-interesting. - index++; - } - - if (index >= m_segments.size()) - { - return false; - } - - // Here we know Start < segment[index].End. Do they not intersect at all? - if (m_segments[index].Start >= segment.End) - { - // Does not intersect any segment. - return false; - } - - assert(m_segments[index].Intersects(segment)); - return true; -} - -#ifdef DEBUG -//------------------------------------------------------------------------ -// Dump: -// Dump a string representation of the segment tree to stdout. -// -void StructSegments::Dump() -{ - if (m_segments.size() == 0) - { - printf(""); - } - else - { - const char* sep = ""; - for (const Segment& segment : m_segments) - { - printf("%s[%03u..%03u)", sep, segment.Start, segment.End); - sep = " "; - } - } -} -#endif - -//------------------------------------------------------------------------ -// SignificantSegments: -// Compute a segment tree containing all significant (non-padding) segments -// for the specified class layout. -// -// Parameters: -// layout - The layout -// -// Returns: -// Segment tree containing all significant parts of the layout. -// -StructSegments Promotion::SignificantSegments(ClassLayout* layout) -{ - StructSegments* cached; - if ((m_significantSegmentsCache != nullptr) && m_significantSegmentsCache->Lookup(layout, &cached)) - { - return StructSegments(*cached); - } - - COMP_HANDLE compHnd = m_compiler->info.compCompHnd; - - StructSegments segments(m_compiler->getAllocator(CMK_Promotion)); - - if (layout->IsBlockLayout()) - { - segments.Add(StructSegments::Segment(0, layout->GetSize())); - } - else - { - CORINFO_TYPE_LAYOUT_NODE nodes[256]; - size_t numNodes = ArrLen(nodes); - GetTypeLayoutResult result = compHnd->getTypeLayout(layout->GetClassHandle(), nodes, &numNodes); - - if (result != GetTypeLayoutResult::Success) - { - segments.Add(StructSegments::Segment(0, layout->GetSize())); - } - else - { - for (size_t i = 0; i < numNodes; i++) - { - const CORINFO_TYPE_LAYOUT_NODE& node = nodes[i]; - if ((node.type != CORINFO_TYPE_VALUECLASS) || (node.simdTypeHnd != NO_CLASS_HANDLE) || - node.hasSignificantPadding) - { - segments.Add(StructSegments::Segment(node.offset, node.offset + node.size)); - } - } - } - } - - if (m_significantSegmentsCache == nullptr) - { - m_significantSegmentsCache = - new (m_compiler, CMK_Promotion) ClassLayoutStructSegmentsMap(m_compiler->getAllocator(CMK_Promotion)); - } - - m_significantSegmentsCache->Set(layout, new (m_compiler, CMK_Promotion) StructSegments(segments)); - - return segments; -} - //------------------------------------------------------------------------ // CreateWriteBack: // Create IR that writes a replacement local's value back to its struct local: diff --git a/src/coreclr/jit/promotion.h b/src/coreclr/jit/promotion.h index 89097d78cd1061..ba5f6fbfa9c67d 100644 --- a/src/coreclr/jit/promotion.h +++ b/src/coreclr/jit/promotion.h @@ -40,55 +40,6 @@ struct Replacement bool Overlaps(unsigned otherStart, unsigned otherSize) const; }; -// Represents significant segments of a struct operation. -// -// Essentially a segment tree (but not stored as a tree) that supports boolean -// Add/Subtract operations of segments. Used to compute the remainder after -// replacements have been handled as part of a decomposed block operation. -class StructSegments -{ -public: - struct Segment - { - unsigned Start = 0; - unsigned End = 0; - - Segment() - { - } - - Segment(unsigned start, unsigned end) - : Start(start) - , End(end) - { - } - - bool IntersectsOrAdjacent(const Segment& other) const; - bool Intersects(const Segment& other) const; - bool Contains(const Segment& other) const; - void Merge(const Segment& other); - }; - -private: - jitstd::vector m_segments; - -public: - explicit StructSegments(CompAllocator allocator) - : m_segments(allocator) - { - } - - void Add(const Segment& segment); - void Subtract(const Segment& segment); - bool IsEmpty(); - bool CoveringSegment(Segment* result); - bool Intersects(const Segment& segment); - -#ifdef DEBUG - void Dump(); -#endif -}; - // Represents information about an aggregate that now has replacements in it. struct AggregateInfo { @@ -137,12 +88,9 @@ class AggregateInfoMap } }; -typedef JitHashTable, class StructSegments*> ClassLayoutStructSegmentsMap; - class Promotion { - Compiler* m_compiler; - ClassLayoutStructSegmentsMap* m_significantSegmentsCache = nullptr; + Compiler* m_compiler; friend class LocalUses; friend class LocalsUseVisitor; @@ -152,8 +100,6 @@ class Promotion friend class DecompositionPlan; friend class StructSegments; - StructSegments SignificantSegments(ClassLayout* layout); - void ExplicitlyZeroInitReplacementLocals(unsigned lclNum, const jitstd::vector& replacements, Statement** prevStmt); diff --git a/src/coreclr/jit/promotiondecomposition.cpp b/src/coreclr/jit/promotiondecomposition.cpp index d4f71b99835208..bf89852547fd7b 100644 --- a/src/coreclr/jit/promotiondecomposition.cpp +++ b/src/coreclr/jit/promotiondecomposition.cpp @@ -238,7 +238,7 @@ class DecompositionPlan { ClassLayout* dstLayout = m_store->GetLayout(m_compiler); - StructSegments segments = m_promotion->SignificantSegments(dstLayout); + StructSegments segments = m_compiler->GetSignificantSegments(dstLayout); for (int i = 0; i < m_entries.Height(); i++) { diff --git a/src/coreclr/jit/structsegments.cpp b/src/coreclr/jit/structsegments.cpp new file mode 100644 index 00000000000000..bbd54af6500e63 --- /dev/null +++ b/src/coreclr/jit/structsegments.cpp @@ -0,0 +1,303 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "jitpch.h" +#include "structsegments.h" +#include "promotion.h" + +//------------------------------------------------------------------------ +// IntersectsOrAdjacent: +// Check if this segment intersects or is adjacent to another segment. +// +// Parameters: +// other - The other segment. +// +// Returns: +// True if so. +// +bool StructSegments::Segment::IntersectsOrAdjacent(const Segment& other) const +{ + if (End < other.Start) + { + return false; + } + + if (other.End < Start) + { + return false; + } + + return true; +} + +//------------------------------------------------------------------------ +// Intersects: +// Check if this segment intersects another segment. +// +// Parameters: +// other - The other segment. +// +// Returns: +// True if so. +// +bool StructSegments::Segment::Intersects(const Segment& other) const +{ + if (End <= other.Start) + { + return false; + } + + if (other.End <= Start) + { + return false; + } + + return true; +} + +//------------------------------------------------------------------------ +// Contains: +// Check if this segment contains another segment. +// +// Parameters: +// other - The other segment. +// +// Returns: +// True if so. +// +bool StructSegments::Segment::Contains(const Segment& other) const +{ + return (other.Start >= Start) && (other.End <= End); +} + +//------------------------------------------------------------------------ +// Merge: +// Update this segment to also contain another segment. +// +// Parameters: +// other - The other segment. +// +void StructSegments::Segment::Merge(const Segment& other) +{ + Start = min(Start, other.Start); + End = max(End, other.End); +} + +//------------------------------------------------------------------------ +// Add: +// Add a segment to the data structure. +// +// Parameters: +// segment - The segment to add. +// +void StructSegments::Add(const Segment& segment) +{ + size_t index = Promotion::BinarySearch(m_segments, segment.Start); + + if ((ssize_t)index < 0) + { + index = ~index; + } + + m_segments.insert(m_segments.begin() + index, segment); + size_t endIndex; + for (endIndex = index + 1; endIndex < m_segments.size(); endIndex++) + { + if (!m_segments[index].IntersectsOrAdjacent(m_segments[endIndex])) + { + break; + } + + m_segments[index].Merge(m_segments[endIndex]); + } + + m_segments.erase(m_segments.begin() + index + 1, m_segments.begin() + endIndex); +} + +//------------------------------------------------------------------------ +// Subtract: +// Subtract a segment from the data structure. +// +// Parameters: +// segment - The segment to subtract. +// +void StructSegments::Subtract(const Segment& segment) +{ + size_t index = Promotion::BinarySearch(m_segments, segment.Start); + if ((ssize_t)index < 0) + { + index = ~index; + } + else + { + // Start == segment[index].End, which makes it non-interesting. + index++; + } + + if (index >= m_segments.size()) + { + return; + } + + // Here we know Start < segment[index].End. Do they not intersect at all? + if (m_segments[index].Start >= segment.End) + { + // Does not intersect any segment. + return; + } + + assert(m_segments[index].Intersects(segment)); + + if (m_segments[index].Contains(segment)) + { + if (segment.Start > m_segments[index].Start) + { + // New segment (existing.Start, segment.Start) + if (segment.End < m_segments[index].End) + { + m_segments.insert(m_segments.begin() + index, Segment(m_segments[index].Start, segment.Start)); + + // And new segment (segment.End, existing.End) + m_segments[index + 1].Start = segment.End; + return; + } + + m_segments[index].End = segment.Start; + return; + } + if (segment.End < m_segments[index].End) + { + // New segment (segment.End, existing.End) + m_segments[index].Start = segment.End; + return; + } + + // Full segment is being removed + m_segments.erase(m_segments.begin() + index); + return; + } + + if (segment.Start > m_segments[index].Start) + { + m_segments[index].End = segment.Start; + index++; + } + + size_t endIndex = Promotion::BinarySearch(m_segments, segment.End); + if ((ssize_t)endIndex >= 0) + { + m_segments.erase(m_segments.begin() + index, m_segments.begin() + endIndex + 1); + return; + } + + endIndex = ~endIndex; + if (endIndex == m_segments.size()) + { + m_segments.erase(m_segments.begin() + index, m_segments.end()); + return; + } + + if (segment.End > m_segments[endIndex].Start) + { + m_segments[endIndex].Start = segment.End; + } + + m_segments.erase(m_segments.begin() + index, m_segments.begin() + endIndex); +} + +//------------------------------------------------------------------------ +// IsEmpty: +// Check if the segment tree is empty. +// +// Returns: +// True if so. +// +bool StructSegments::IsEmpty() const +{ + return m_segments.size() == 0; +} + +//------------------------------------------------------------------------ +// CoveringSegment: +// Compute a segment that covers all contained segments in this segment tree. +// +// Parameters: +// result - [out] The single segment. Only valid if the method returns true. +// +// Returns: +// True if this segment tree was non-empty; otherwise false. +// +bool StructSegments::CoveringSegment(Segment* result) const +{ + if (m_segments.size() == 0) + { + return false; + } + + result->Start = m_segments[0].Start; + result->End = m_segments[m_segments.size() - 1].End; + return true; +} + +//------------------------------------------------------------------------ +// Intersects: +// Check if a segment intersects with any segment in this segment tree. +// +// Parameters: +// segment - The segment. +// +// Returns: +// True if the input segment intersects with any segment in the tree; +// otherwise false. +// +bool StructSegments::Intersects(const Segment& segment) const +{ + size_t index = Promotion::BinarySearch(m_segments, segment.Start); + if ((ssize_t)index < 0) + { + index = ~index; + } + else + { + // Start == segment[index].End, which makes it non-interesting. + index++; + } + + if (index >= m_segments.size()) + { + return false; + } + + // Here we know Start < segment[index].End. Do they not intersect at all? + if (m_segments[index].Start >= segment.End) + { + // Does not intersect any segment. + return false; + } + + assert(m_segments[index].Intersects(segment)); + return true; +} + +#ifdef DEBUG +//------------------------------------------------------------------------ +// Dump: +// Dump a string representation of the segment tree to stdout. +// +void StructSegments::Dump() +{ + if (m_segments.size() == 0) + { + printf(""); + } + else + { + const char* sep = ""; + for (const Segment& segment : m_segments) + { + printf("%s[%03u..%03u)", sep, segment.Start, segment.End); + sep = " "; + } + } +} +#endif diff --git a/src/coreclr/jit/structsegments.h b/src/coreclr/jit/structsegments.h new file mode 100644 index 00000000000000..2a78a181a5b183 --- /dev/null +++ b/src/coreclr/jit/structsegments.h @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "alloc.h" +#include "jitstd/vector.h" + +// Represents significant segments of a struct operation. +// +// Essentially a segment tree (but not stored as a tree) that supports boolean +// Add/Subtract operations of segments. Used to compute the remainder after +// replacements have been handled as part of a decomposed block operation. +class StructSegments +{ +public: + struct Segment + { + unsigned Start = 0; + unsigned End = 0; + + Segment() + { + } + + Segment(unsigned start, unsigned end) + : Start(start) + , End(end) + { + } + + bool IntersectsOrAdjacent(const Segment& other) const; + bool Intersects(const Segment& other) const; + bool Contains(const Segment& other) const; + void Merge(const Segment& other); + }; + +private: + jitstd::vector m_segments; + +public: + explicit StructSegments(CompAllocator allocator) + : m_segments(allocator) + { + } + + void Add(const Segment& segment); + void Subtract(const Segment& segment); + bool IsEmpty() const; + bool CoveringSegment(Segment* result) const; + bool Intersects(const Segment& segment) const; + +#ifdef DEBUG + void Dump(); +#endif +};