# How SharpEmu's Direct CPU Execution Backend Runs PS5 x86-64 Instructions at Native Speed

> Discover how SharpEmu's Direct Execution Backend runs PS5 x86-64 instructions at native speed by feeding binaries directly to the host CPU and bypassing software interpretation.

- Repository: [Berk/sharpemu](https://github.com/par274/sharpemu)
- Tags: internals
- Published: 2026-07-16

---

**SharpEmu's `DirectExecutionBackend` feeds PS5 x86-64 guest binaries directly to the host CPU by mapping virtual memory, patching import stubs to native trampolines, and invoking the entry point with a direct native call, bypassing software interpretation entirely.**

The par274/sharpemu project implements a high-performance PlayStation 5 emulator that leverages the architectural similarity between the PS5 and modern PCs. By utilizing a **direct CPU execution backend**, SharpEmu eliminates the overhead of software instruction emulation, allowing guest code to run at near-native speeds while maintaining a robust HLE layer for system services.

## Architecture Overview of the Direct Execution Backend

At the heart of this mechanism lies the **`DirectExecutionBackend`** class located in [`src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs). This component orchestrates the transition from managed runtime to raw native execution.

When the runtime initiates a binary via `SharpEmuRuntime.Run`, the flow proceeds through `CpuDispatcher.DispatchEntry` to lazily instantiate the backend:

```csharp
// CpuDispatcher.cs - lazily creates the native backend
_nativeCpuBackend ??= new DirectExecutionBackend(_moduleManager);

```

Unlike traditional emulators that fetch-decode-execute in software, this backend prepares the host environment to execute guest instructions directly on the physical CPU.

## Memory Mapping and TLS Initialization

Before jumping into guest code, the backend establishes the guest's execution environment. This involves mapping critical memory regions including the stack, thread-local storage (TLS), and bootstrap areas.

The initialization sequence calls `TryMapStackRegion`, `TryMapTlsRegion`, and `SeedTlsLayout` to satisfy the PS5's memory layout expectations:

- **Stack mapping**: Allocates and protects the guest stack region
- **TLS setup**: Configures the static TLS layout defined in [`src/SharpEmu.HLE/GuestTlsTemplate.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.HLE/GuestTlsTemplate.cs)
- **Bootstrap regions**: Maps low-level memory required for PS5 process initialization

These steps ensure that when the CPU enters x86-64 mode, the `FS` and `GS` segment registers point to valid TLS data, and the stack pointer (`RSP`) references mapped host memory.

## Import Stub Patching and Native Intrinsics

PS5 binaries rely on NID (name identifier) imports to access system services. The backend handles these through `SetupImportStubs`, which iterates over every import address in the ELF and patches them to either direct native handlers or managed trampolines.

For each import, the backend either:

1. **Resolves a direct bridge** via `TryResolveDirectImportTarget` to a native address, or
2. **Emits a trampoline** using `CreateImportHandlerTrampoline` that forwards to the managed `ModuleManager` dispatch layer

Native intrinsics provide optimized paths for well-known functions. For example, the NID `fgxnMeTNUtY` (QueryPerformanceCounter) is replaced with hand-written machine code allocated via `VirtualAlloc` and marked executable with `VirtualProtect`:

```csharp
// Conceptual example of native intrinsic injection
backend.TryCreateNativeImportIntrinsic("fgxnMeTNUtY", out var nativeAddr);
backend.PatchImportStub((nint)importAddress, nativeAddr);

```

This patching strategy ensures that HLE calls incur minimal overhead, often executing as direct native jumps rather than expensive context switches.

## Native Entry and Context Transfer

Once memory is mapped and imports are patched, the backend prepares the initial CPU state. The `CpuDispatcher` populates a `CpuContext` structure with register values (RIP, RSP, flags, FS/GS base) and constructs the initial stack frame via `InitializeProcessEntryFrame`.

Execution commences when `DirectExecutionBackend.TryExecute` invokes `CallNativeEntry`:

```csharp
// DirectExecutionBackend.cs - final native transition
backend.TryExecute(context, entryPoint, Generation.Gen5,
                  importStubs, runtimeSymbols, new CpuExecutionOptions(),
                  out var execResult);

```

At this point, the host CPU begins executing the PS5 binary's x86-64 instructions directly. The guest runs natively until it hits a patched import stub or triggers a hardware exception.

### Threading Support

Multi-threaded PS5 applications are supported through `GuestThreadState` objects. The backend creates native worker threads via `GuestExecutionRunner` and performs context transfers using `GetOrCreateGuestContextTransferStub`. Each guest thread executes on its own host thread, maintaining the direct execution model across the entire process.

## Exception Handling and Diagnostics

Since the guest runs directly on the CPU, faults must be intercepted and translated. The backend registers a vectored exception handler named `RawVectoredHandlerManaged` that catches hardware exceptions (access violations, illegal instructions, etc.) and converts them into `CpuTrapInfo` or `CpuMemoryFaultInfo` structures for the runtime to handle.

To prevent HLE import loops from hanging the emulator, the backend maintains an import loop guard using `_recentImportTrace` with a configurable `ImportLoopHistoryLength`. This detects tight loops where guest code repeatedly calls imported functions without making progress, allowing the runtime to break out or log diagnostics.

## Summary

- **Direct execution** bypasses software interpretation by running PS5 x86-64 code directly on the host CPU through `DirectExecutionBackend`.
- **Memory mapping** initializes guest stacks, TLS regions, and bootstrap areas before entry, ensuring the PS5 memory model is satisfied.
- **Import stub patching** redirects NID calls to native handlers or managed trampolines, with optimized intrinsics for performance-critical functions.
- **Native context transfer** occurs via `CallNativeEntry` after `CpuDispatcher` prepares the initial register state and stack frame.
- **Exception handling** uses a vectored handler to translate hardware faults into managed trap information, while import loop guards prevent infinite recursion in HLE code.

## Frequently Asked Questions

### How does SharpEmu handle PS5 system calls without interpreting every instruction?

SharpEmu patches the PS5 binary's import stubs during load time. When the guest code calls a system function (e.g., `sceKernel*`), it jumps to a trampoline that forwards to the managed `ModuleManager` via `CreateImportHandlerTrampoline`. For performance-critical functions, it injects native intrinsics that execute directly without leaving the native context.

### What happens when the guest code triggers a memory fault or exception?

The `RawVectoredHandlerManaged` in [`DirectExecutionBackend.cs`](https://github.com/par274/sharpemu/blob/main/DirectExecutionBackend.cs) intercepts all hardware exceptions. It translates the fault into a `CpuMemoryFaultInfo` structure, unwinds the execution context, and returns control to the runtime. This allows the emulator to handle PS5-specific memory behaviors or deliver signals without crashing the host process.

### Can SharpEmu run multiple PS5 threads simultaneously using direct execution?

Yes. The backend supports multi-threading through `GuestThreadState` and `GuestExecutionRunner`. Each guest thread runs on a dedicated host native thread, maintains its own register context, and uses `GetOrCreateGuestContextTransferStub` for context switches. All threads execute PS5 x86-64 instructions directly on the host CPU cores.

### How does the import loop guard prevent infinite loops in HLE functions?

The backend tracks recent import calls using `_recentImportTrace` and monitors the sequence length against `ImportLoopHistoryLength`. If the guest enters a tight loop calling imported functions repeatedly without advancing the program counter, the guard detects this pattern and can break the loop or trigger diagnostic logging to prevent the emulator from hanging.