[Memory] Treat AllocRange high_page_number as exclusive

The free-block tracker search in BaseHeap::AllocRange treats
high_page_number as inclusive, shifting allocator-returned addresses
by one stride relative to the old loop-based search. Some titles
encode allocator-returned addresses in PPC code and break when that
layout shifts (Far Cry 3, Far Cry 4, Watchdogs).

In addition, reapply xe::align on the high side of AllocRange
(essentially reverting c28019e33). Without the round-up, a caller
passing a min/max window exactly the size of its request loses a
stride at the top and fails the early page_count size check.
This commit is contained in:
Herman S.
2026-04-14 13:59:45 +09:00
parent 763b160c7a
commit 4fcb8e4498
3 changed files with 33 additions and 19 deletions

View File

@@ -1177,7 +1177,8 @@ bool BaseHeap::AllocRange(uint32_t low_address, uint32_t high_address,
alignment = xe::round_up(alignment, page_size_);
uint32_t page_count = get_page_count(size, page_size_);
low_address = std::max(heap_base_, xe::align(low_address, alignment));
high_address = std::min(heap_base_ + (heap_size_ - 1), high_address);
high_address = std::min(heap_base_ + (heap_size_ - 1),
xe::align(high_address, alignment));
uint32_t low_page_number = (low_address - heap_base_) >> page_size_shift_;
uint32_t high_page_number = (high_address - heap_base_) >> page_size_shift_;
@@ -1219,7 +1220,11 @@ bool BaseHeap::AllocRange(uint32_t low_address, uint32_t high_address,
}
// Compute the highest aligned start within this block and range.
uint32_t usable_end = std::min(block_end, high_page_number + 1);
// high_page_number is exclusive and rounded down to the stride, so
// the top stride of pages is never returned.
uint32_t high_aligned =
high_page_number - QuickMod(high_page_number, page_scan_stride);
uint32_t usable_end = std::min(block_end, high_aligned);
if (usable_end < page_count) {
continue;
}
@@ -1260,10 +1265,12 @@ bool BaseHeap::AllocRange(uint32_t low_address, uint32_t high_address,
}
// Compute the lowest aligned start within this block and range.
// high_page_number is treated as exclusive — the page at
// high_page_number itself is never returned.
uint32_t earliest = std::max(block_start, low_page_number);
uint32_t aligned_start = xe::round_up(earliest, page_scan_stride, false);
if (aligned_start + page_count <= block_end &&
aligned_start + page_count - 1 <= high_page_number) {
aligned_start + page_count <= high_page_number) {
start_page_number = aligned_start;
end_page_number = aligned_start + page_count - 1;
break;