mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
0c1f005c35
Three coupled landings on top of the sljit merge: 1. Shared widescreen render-cull helper. auto_screen_x now emits a call to one runtime helper psx_ws_cull_sltiu(sx,imm) (gpu.c) instead of an inline expr, and the sljit JIT (overlay_sljit.c) + dirty-RAM interpreter (dirty_ram_interp.c) route flagged render-funnel sltiu 0x140/0x141 through the SAME helper. So an overlay's GTE render funnel widens identically whether it runs as a gcc DLL, a sljit shard, or interpreted. Helper is byte-identical to vanilla at 4:3 (margin 0), so widescreen-off stays bit-exact. psx_ws_func_has_screen_cull gates the widen to real render funnels (sltiu 0x140 + sltiu 0xE0 signature). 2. Versioned overlay cache path. gcc cache moves to <game_id>/gcc/<arch-abi>/cg<N>/ where N = PSX_OVERLAY_CODEGEN_VER (overlay_api.h), parsed identically by the runtime (overlay_loader.c) and compile_overlays.py so they can't drift. A build whose emitter output changed (e.g. this auto_screen_x change) reads+writes a FRESH dir and never reuses a stale DLL; old versions coexist (no auto-delete). Pre-1.0: legacy flat/unversioned fallbacks dropped. 3. sljit default-on (validated). gcc and sljit are complementary, not exclusive: gcc fills the cache via prebuilt DLLs + async batch (compile_fragment NULL), sljit JITs gcc-absent regions synchronously on-miss. New code_provider_sljit() exposes sljit for the JIT-on-miss gap-fill independent of the "active" backend; the dispatch gate fires whenever sljit is available (head<0 == gcc-absent), and apply_live_policy defaults live ON when sljit is available. Priority gcc > sljit > interp; shards still route through the same-state diff gate (device-touch + diverging shards pinned to interp) so default-on does NOT reintroduce the save-load wedge; a declined/failing shard falls to interp (never a crash). Verified live: live=1, 62 shards compiled, 2 declines->interp, native:interp ratio ~31%->~87%, gcc still primary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
98 lines
5.0 KiB
C
98 lines
5.0 KiB
C
/* code_provider.h — the backend-agnostic code-production seam (SLJIT.md §3).
|
|
*
|
|
* One interface the overlay capture/dispatch spine talks to instead of calling
|
|
* a specific backend directly. The validated gcc spawn->DLL path becomes its
|
|
* first implementation; the in-process sljit JIT (SLJIT.md §7 step 4) becomes
|
|
* the second. Everything that makes overlays correct — content-keyed multi-
|
|
* candidate dispatch, per-call live-byte validation, the self-mod blacklist,
|
|
* the coverage manifest — is unchanged and backend-blind (SLJIT.md §2).
|
|
*
|
|
* Two production lifecycles, BOTH expressed here because the two backends model
|
|
* production differently and a provider implements whichever it supports:
|
|
*
|
|
* - BATCH / ASYNC (request / busy / poll_main): the gcc model. Kicked by the
|
|
* autocapture tick, spawns a compiler off-thread, applied later via a cache
|
|
* rescan on the emu thread. Seconds of latency; many fragments per run.
|
|
* - SYNC / ON-MISS (compile_fragment): the sljit model. JIT a single fragment
|
|
* in-process at a dispatch miss, sub-ms, return a native fn the caller
|
|
* registers immediately. Wired into the dispatch path (overlay_loader.c
|
|
* try_sljit_region); the sljit shard is then VALIDATED via the same-state
|
|
* diff before it runs live (see SLJIT.md §11 / overlay_loader_dispatch).
|
|
*
|
|
* A provider sets the hooks it supports and leaves the rest NULL; callers
|
|
* null-check before invoking. The gcc provider's batch hooks are thin pass-
|
|
* throughs to autocompile_* — behavior is byte-for-byte what it was before the
|
|
* abstraction (this is a pure refactor; selection defaults to gcc).
|
|
*/
|
|
#ifndef PSXRECOMP_CODE_PROVIDER_H
|
|
#define PSXRECOMP_CODE_PROVIDER_H
|
|
|
|
#include "cpu_state.h"
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* Native shard ABI — identical to overlay_loader's OverlayFn / OverlaySljitFn. */
|
|
typedef void (*CodeProviderFn)(CPUState *);
|
|
|
|
/* Result of a synchronous on-miss compile. fn == NULL means the provider
|
|
* declined (caller runs the interpreter). On success [code_lo, code_lo+code_len)
|
|
* is the PHYS byte-range the fragment was compiled from — the caller registers a
|
|
* candidate over it for per-call live-byte validation (the backend-neutral
|
|
* equivalent of the gcc .ranges manifest). */
|
|
typedef struct {
|
|
CodeProviderFn fn; /* NULL = declined */
|
|
uint32_t code_lo; /* phys start of compiled code range */
|
|
uint32_t code_len; /* byte length of compiled code range */
|
|
} CompiledFragment;
|
|
|
|
typedef struct CodeProvider {
|
|
const char *name; /* "gcc" | "sljit" */
|
|
int (*available)(void); /* gcc: a compile cmd is configured;
|
|
sljit: always (no external toolchain) */
|
|
|
|
/* Batch/async production (autocapture-driven). request() returns 1 if a
|
|
* compile was started. busy() is 1 while one is in flight. poll_main() runs
|
|
* on the emu thread to apply a finished compile (cache rescan). Any may be
|
|
* NULL on a provider that does not do batch production. */
|
|
int (*request)(void);
|
|
int (*busy)(void);
|
|
void (*poll_main)(void);
|
|
|
|
/* Synchronous on-miss production of one fragment. Fills *out (out->fn NULL =
|
|
* declined → caller runs the interpreter, the always-safe precision-over-
|
|
* recall floor). gcc leaves this hook NULL (a compiler spawn is far too slow
|
|
* for the dispatch path); sljit JITs in-process. A NULL hook == always
|
|
* declines. `bytes`/`size`/`image_base_vram` describe the image to decode
|
|
* from (typically live RAM: bytes = RAM base, image_base_vram = 0). */
|
|
void (*compile_fragment)(uint32_t entry, const uint8_t *bytes, uint32_t size,
|
|
uint32_t image_base_vram, CompiledFragment *out);
|
|
} CodeProvider;
|
|
|
|
/* Resolve the active backend (overlay_backend_resolve) and cache the matching
|
|
* provider. cfg_backend = [runtime] overlay_backend ("auto"|"gcc"|"sljit", may
|
|
* be NULL/empty for auto); gcc_configured = autocompile_configured(). Call once
|
|
* at overlay-cache init, on the emu thread, before the run loop starts. */
|
|
void code_provider_init(const char *cfg_backend, int gcc_configured);
|
|
|
|
/* The active/primary provider (owns the batch path + default selection). Never
|
|
* NULL — defaults to the gcc provider before code_provider_init() runs. */
|
|
const CodeProvider *code_provider_active(void);
|
|
|
|
/* The sljit provider IF it is available, else NULL. gcc and sljit are
|
|
* COMPLEMENTARY, not exclusive: gcc fills the cache via the async batch path +
|
|
* prebuilt DLLs and never JITs synchronously (compile_fragment == NULL), while
|
|
* sljit is the synchronous JIT-on-miss that fills the gaps gcc has not (yet)
|
|
* covered. The dispatch path uses this — independent of which provider is
|
|
* "active" — so on a gcc dev box sljit still JITs gcc-absent regions
|
|
* (priority gcc > sljit > interp). NULL => sljit unavailable, gaps fall to interp. */
|
|
const CodeProvider *code_provider_sljit(void);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* PSXRECOMP_CODE_PROVIDER_H */
|