using Ryujinx.Common; using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace Ryujinx.Cpu.LightningJit.Cache { class PageAlignedRangeList { private readonly struct Range : IComparable { public int Offset { get; } public int Size { get; } public Range(int offset, int size) { Offset = offset; Size = size; } public int CompareTo([AllowNull] Range other) { return Offset.CompareTo(other.Offset); } } private readonly Action _alignedRangeAction; private readonly Action _alignedFunctionAction; private readonly List<(Range, ulong, TranslatedFunction)> _pendingFunctions; private readonly List _ranges; public PageAlignedRangeList(Action alignedRangeAction, Action alignedFunctionAction) { _alignedRangeAction = alignedRangeAction; _alignedFunctionAction = alignedFunctionAction; _pendingFunctions = new(); _ranges = new(); } public bool Has(ulong address) { foreach ((_, ulong guestAddress, _) in _pendingFunctions) { if (guestAddress == address) { return true; } } return false; } public void Add(int offset, int size, ulong address, TranslatedFunction function) { Range range = new(offset, size); Insert(range); _pendingFunctions.Add((range, address, function)); ProcessAlignedRanges(); } public void Pad(CacheMemoryAllocator allocator) { int pageSize = (int)MemoryBlock.GetPageSize(); for (int index = 0; index < _ranges.Count; index++) { Range range = _ranges[index]; int endOffset = range.Offset + range.Size; int alignedStart = BitUtils.AlignDown(range.Offset, pageSize); int alignedEnd = BitUtils.AlignUp(endOffset, pageSize); int alignedSize = alignedEnd - alignedStart; if (alignedStart < range.Offset) { allocator.ForceAllocation(alignedStart, range.Offset - alignedStart); } if (alignedEnd > endOffset) { allocator.ForceAllocation(endOffset, alignedEnd - endOffset); } _alignedRangeAction(alignedStart, alignedSize); _ranges.RemoveAt(index--); ProcessPendingFunctions(index, alignedEnd); } } private void ProcessAlignedRanges() { int pageSize = (int)MemoryBlock.GetPageSize(); for (int index = 0; index < _ranges.Count; index++) { Range range = _ranges[index]; int alignedStart = BitUtils.AlignUp(range.Offset, pageSize); int alignedEnd = BitUtils.AlignDown(range.Offset + range.Size, pageSize); int alignedSize = alignedEnd - alignedStart; if (alignedSize <= 0) { continue; } _alignedRangeAction(alignedStart, alignedSize); SplitAt(ref index, alignedStart, alignedEnd); ProcessPendingFunctions(index, alignedEnd); } } private void ProcessPendingFunctions(int rangeIndex, int alignedEnd) { if ((rangeIndex > 0 && rangeIndex == _ranges.Count) || (rangeIndex >= 0 && rangeIndex < _ranges.Count && _ranges[rangeIndex].Offset >= alignedEnd)) { rangeIndex--; } int alignedStart; if (rangeIndex >= 0) { alignedStart = _ranges[rangeIndex].Offset + _ranges[rangeIndex].Size; } else { alignedStart = 0; } if (rangeIndex < _ranges.Count - 1) { alignedEnd = _ranges[rangeIndex + 1].Offset; } else { alignedEnd = int.MaxValue; } for (int index = 0; index < _pendingFunctions.Count; index++) { (Range range, ulong address, TranslatedFunction function) = _pendingFunctions[index]; if (range.Offset >= alignedStart && range.Offset + range.Size <= alignedEnd) { _alignedFunctionAction(address, function); _pendingFunctions.RemoveAt(index--); } } } private void Insert(Range range) { int index = _ranges.BinarySearch(range); if (index < 0) { index = ~index; } if (index < _ranges.Count) { Range next = _ranges[index]; int endOffs = range.Offset + range.Size; if (next.Offset == endOffs) { range = new Range(range.Offset, range.Size + next.Size); _ranges.RemoveAt(index); } } if (index > 0) { Range prev = _ranges[index - 1]; if (prev.Offset + prev.Size == range.Offset) { range = new Range(range.Offset - prev.Size, range.Size + prev.Size); _ranges.RemoveAt(--index); } } _ranges.Insert(index, range); } private void SplitAt(ref int index, int alignedStart, int alignedEnd) { Range range = _ranges[index]; if (range.Offset < alignedStart) { _ranges[index++] = new(range.Offset, alignedStart - range.Offset); if (range.Offset + range.Size > alignedEnd) { _ranges.Insert(index, new(alignedEnd, (range.Offset + range.Size) - alignedEnd)); } } else if (range.Offset + range.Size > alignedEnd) { _ranges[index] = new(alignedEnd, (range.Offset + range.Size) - alignedEnd); } else if (range.Offset == alignedStart && range.Offset + range.Size == alignedEnd) { Debug.Assert(range.Offset == alignedStart && range.Offset + range.Size == alignedEnd); _ranges.RemoveAt(index--); } } } }