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.
SCHED_FIFO requires CAP_SYS_NICE or root, try SCHED_FIFO first for real-time
priority control, then fall back to setpriority() nice values if permission
is denied.
The 4KB-page virtual heap (v00000000, 0x00000000-0x3FFFFFFF) had 256MB
reserved at its top end for thread stacks, but thread stacks only use
the 64KB-page heap (v40000000) at 0x70000000-0x7F000000. This wasted
256MB of allocatable address space.
Keep the carve-out only on the v40000000 heap where stacks actually reside.
If the guest-supplied thread pointer doesn't resolve to an XThread,
we previously returned STATUS_SUCCESS without setting the affinity
or writing previous_affinity_ptr, leaving callers believing the call
succeeded. Real NT kernel would crash on a bad pointer, but that's not
easy for us to replicate so return STATUS_INVALID_HANDLE instead and
log the pointer value so the condition is visible rather than silent.
Previous global TLS bitmap was shared across all processes. Now
per-process TLS bitmaps stored X_KPROCESS structures which should
better match Xbox360 architecture.
Reset locked_achievement_icon_ alongside font_texture_ and
notification_icon_textures_ when the immediate drawer is cleared,
preventing a debug assertion in D3D12ImmediateDrawer's destructor.
BaseHeap::AllocRange was rounding the inclusive high_address UP via
xe::align(), which could push the search range one alignment stride
past the requested end. The new free-block-tracker top-down search
treats high_page_number as an inclusive max (usable_end =
high_page_number + 1), so the round-up caused it to return a base
page one stride beyond the caller's bound — most visibly when
PhysicalHeap::Alloc passed parent_heap_end = GetPhysicalAddress(...)
just below a page boundary, producing a translated address one page
past the child heap and tripping "passed out of range address range"
in BaseHeap::AllocFixed.
Drop the xe::align on the high side so high_address stays a true
inclusive bound. The low_address round-up is still correct since
allocations must START at or above the aligned low.
Replace linear page_table_ scans in AllocRange with a std::map-based
free block index that tracks contiguous free regions.
Insertions now coalesce with adjacent blocks on release and AllocFixed
uses the targeted tracker for pure reserves and falls back to a full rebuild
for mixed-state commits.
Also fixing PhysicalHeap leaking parent memory on child allocation failure,
Reset() not restoring unreserved_page_count_ and some incorrect method names
in PhysicalHeap error messages
When a thread wakes from a kernel wait, the Xenon scheduler boosts its
effective priority by the increment passed to the signaling call
(KeSetEvent, KeReleaseSemaphore, KeReleaseMutant). The boost is clamped
to the per-thread max_dynamic_priority cap, respects the guest
boost_disabled flag, and is drained on the next quantum expiry.
Guest KTHREAD priority fields are now initialized from parent process
defaults, and the previously unknown fields involved have been renamed
to match their identified purpose.
And default ignore_thread_priorities to false.
Map Xenon's 0-31 priority range across all 5 host priority levels
instead of collapsing 0-17 into kNormal.
Use timer-driven quantum decay (~20ms period) matching Xenon's
60-quantum / 3-per-tick cycle to prevent starvation by gradually lowering
effective priority for non-real-time threads (< 18), piggybacking on the
existing 1ms timestamp timer.
Spinlock acquire now checks if the lock holder shares the same guest
CPU and yields more aggressively (Sleep(0)) when contending on the
same Xenon HW thread, which should better approximate real kernel's
implicit serialization.
Child threads without an explicit affinity mask now inherit the
parent's guest CPU assignment instead of round-robining, so the
spinlock check correctly identifies parent-child co-location.
Stall detection was triggering during multi-pass subframe consumption
(e.g. stereo with subframe_decode_count < total subframes), breaking
audio looping in games like Tomb Raider. Now only detects a stall when
no subframes were pending, so Consume-only iterations aren't mistaken
for no-progress cycles. Fixes Halo 4 without regressing Tomb Raider.
Detect dependent `AND` and `NOT` IR sequences and combine them into a
singular `AND_NOT` opcode. The later dead-code-elimination-pass will
get rid of the left-over `NOT` opcode if nothing else uses it.
This gets quite a good amount of hits in some of the titles I've tested.
Also updates unit tests with additional data-types and ensures that
`And(..., Not())` returns the same result as `AndNot(...)`
- DIV_I32/I64 and MUL_HI_I64: use source registers directly in
sdiv/udiv/umulh/smulh instead of copying to scratch first
- SWIZZLE: fast paths for identity (no-op), broadcast (dup), and
pair-swap (rev64) before falling back to general TBL
- LOAD_VECTOR_SHL/SHR: build base index vector with fmov+ins
instead of stp+ldr stack round-trip
Replace per-op save/restore of FPCR in EmitWithVmxFpcr with tracked
lazy switching via ChangeFpcrMode. Consecutive VMX float ops in the
same basic block now emit a single FPCR switch instead of one per op.
ChangeFpcrMode loads pre-computed values from the backend context
(fpcr_fpu/fpcr_vmx) instead of MRS + read-modify-write, eliminating
an expensive system register read per switch.
FPU mode is restored at block boundaries and calls via ForgetFpcrMode.
Scalar FP sequences guard with ChangeFpcrMode(Fpu) which is a no-op
when already in FPU mode.
The LSE paths in RESERVED_STORE_I32/I64 returned early from Emit,
leaving forward-referenced no_reserve/done labels unbound. Xbyak
encodes these as offset-zero branches (branch-to-self), causing
infinite loops when the reservation check fails on LSE hardware.
Replace early return with branch-to-done inside an if/else so
labels are always bound regardless of the LSE path taken.
Adds tests for RESERVED_STORE I64 success path and I32/I64
no-reservation failure path with timeout-based hang detection.
* Add unit tests
* Use `dc zva` to zero out entire cache-lines worth of data, when possible
* Special instruction that zaps an entire cache-line into being zero-values, typically 64-bytes at a time
* Statically read and initialize the `DCZID_EL0` register value since reading registers with `mrs` can be kinda slow and isn't worth doing redundantly at JIT-time when it never changes.
* Update store-instructions to use inline post-increments to avoid an additional `add` instruction and keep encoded instruction immediates small
Pretty much every arm processor has a cache line size of 64 bytes, so a typical `dcbz128` to clear 128-bytes of data now results in:
```
dc zva, x0
add x0, x0, 64
dc zva, x0
```
rather than a sequence of 8 `stp xzr, xzr, ...` instructions