Files
psxrecomp/runtime/src/code_provider.c
T
Matthew Stanley 77d036bfb6 docs: production-friendly contributor docs + declutter root
Make the repo approachable for outside contributors and an IDE integrator:

- New contributor docs:
  - docs/EXECUTION_MODEL.md — semi-technical tour of the static / native-overlay
    / interpreter execution model and how coverage gaps get plugged.
  - docs/ARCHITECTURE.md — component-level view (recompiler, runtime, LLE+HLE
    BIOS, overlay backend static->gcc->tcc, renderers, oracle model).
  - docs/BUILDING.md — accurate dependency table, per-platform prereqs, build
    steps, and the submodule + local-junction framework-linking setup.
  - CONTRIBUTING.md — distilled core rules, verification expectations, PR flow.
- Declutter root: move ~14 internal dev-logs into docs/internal/ (history
  preserved via rename) with an index README; repoint CLAUDE.md references.
- README: correct build recipe, LLE+HLE + overlay-backend summary, links to the
  new docs.
- code_provider.c: update the stale backend comment to the real static->gcc->tcc
  resolution (dead SLJIT.md reference removed).
- config_schema.md: drop the obsolete "once Phase A2/A3 ships --config" note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:25:16 -07:00

92 lines
4.5 KiB
C

/* code_provider.c — the CodeProvider implementations + active selection.
* See code_provider.h for the abstraction.
*
* The "spawn compiler" provider is a thin pass-through over the validated
* autocompile_* path (compile emitted overlay C to a DLL and load it); it backs
* BOTH the gcc and tcc tiers, which differ only in which compiler the pipeline
* invokes. The in-process JIT provider (code_provider_sljit) is a separate,
* legacy path. Which provider is active is decided once at init by
* overlay_backend_resolve() in overlay_backend.c (env PSX_OVERLAY_BACKEND >
* [runtime] overlay_backend > auto). The auto tier order is:
* static -> gcc (if gcc on PATH) -> tcc (bundled toolchain-free fallback). */
#include <stddef.h>
#include "code_provider.h"
#include "autocompile.h"
#include "overlay_sljit.h"
#include "overlay_backend.h"
/* ---- gcc provider: pass-through to the validated spawn->DLL->rescan path --- */
/* The hook signatures match autocompile_* exactly, so we point straight at
* them — no wrappers, no behavior change. compile_fragment stays NULL: a
* compiler spawn is orders of magnitude too slow for the dispatch path, so gcc
* never produces a fragment synchronously (it self-improves via the batch path
* + autocapture instead). */
static const CodeProvider s_gcc = {
/* name */ "gcc",
/* available */ autocompile_configured,
/* request */ autocompile_request,
/* busy */ autocompile_busy,
/* poll_main */ autocompile_poll_main,
/* compile_fragment */ NULL,
};
/* ---- sljit provider: in-process JIT (sync on-miss only, v1) ---------------- */
/* No batch/async path: sljit produces synchronously at a dispatch miss, so
* request/busy/poll_main are inert. compile_fragment delegates to the MIPS->sljit
* emitter (overlay_sljit_try_compile), which compiles the supported instruction
* classes and declines the rest (fn=NULL) — safe by construction (precision over
* recall). The returned shard is validated via the same-state diff before live. */
static int sljit_request(void) { return 0; }
static int sljit_busy(void) { return 0; }
static void sljit_poll_main(void) { }
static void sljit_compile_fragment(uint32_t entry, const uint8_t *bytes,
uint32_t size, uint32_t image_base_vram,
CompiledFragment *out) {
OverlaySljitResult r = {0};
overlay_sljit_try_compile(entry, bytes, size, image_base_vram, &r);
/* OverlaySljitFn and CodeProviderFn are the same void fn(CPUState*) type. */
out->fn = (CodeProviderFn)r.fn;
out->code_lo = r.code_lo;
out->code_len = r.code_len;
out->serialized = r.serialized; /* for the persisted shard cache */
out->serialized_size = r.serialized_size;
}
static const CodeProvider s_sljit = {
/* name */ "sljit",
/* available */ overlay_sljit_available,
/* request */ sljit_request,
/* busy */ sljit_busy,
/* poll_main */ sljit_poll_main,
/* compile_fragment */ sljit_compile_fragment,
};
/* ---- active selection ------------------------------------------------------ */
/* Default to gcc so any caller before code_provider_init() (there should be
* none — init runs at overlay-cache setup) gets the proven path. */
static const CodeProvider *s_active = &s_gcc;
void code_provider_init(const char *cfg_backend, int gcc_configured) {
/* overlay_backend_resolve resolves AUTO to GCC or SLJIT and never returns
* AUTO, so this maps cleanly to one of the two providers. It also logs the
* decision (surfaced via the sljit_status debug command). */
OverlayBackend b = overlay_backend_resolve(cfg_backend, gcc_configured);
/* The "gcc" provider is really the spawn-a-compiler->DLL->rescan path; it is
* compiler-agnostic (the actual compiler is whatever overlay_autocompile_cmd
* runs — gcc OR tcc, selected in main.cpp by the resolved backend). So BOTH
* gcc and tcc map to it. Only the deprecated in-process sljit producer is
* separate. */
s_active = (b == OVERLAY_BACKEND_SLJIT) ? &s_sljit : &s_gcc;
}
const CodeProvider *code_provider_active(void) { return s_active; }
/* sljit provider for the synchronous JIT-on-miss gap-fill, regardless of which
* provider is "active". NULL when sljit isn't available so callers fall to interp. */
const CodeProvider *code_provider_sljit(void) {
return (s_sljit.available && s_sljit.available()) ? &s_sljit : NULL;
}