Skip to content

GXRuntime: inline the hot paths of the FP gate and paired-single access - #9

Open
dougchansan wants to merge 1 commit into
ExpansionPak:moderngekko-vendorfrom
dougchansan:gxruntime-inline-hot-helpers
Open

GXRuntime: inline the hot paths of the FP gate and paired-single access#9
dougchansan wants to merge 1 commit into
ExpansionPak:moderngekko-vendorfrom
dougchansan:gxruntime-inline-hot-helpers

Conversation

@dougchansan

Copy link
Copy Markdown

Profiling a recompiled Mario Kart: Double Dash on Apple Silicon (sample, race scene) put two runtime helpers near the top of self time inside StaticRecompCore::Run:

symbol self samples share
ppc_fp_available 869 ~7.1%
psq_load_value + psq_store_value 1101 ~9.0%

Generated code calls them once per instruction site — 121,874 FP sites and 19,642 paired-single sites on this title.

What changed

ppc_fp_available_inline() puts the MSR[FP] test in the header, leaving only the exception raise out of line. A running game always has MSR[FP] set, so the cost was the call, not the test.

ppc_psq_load_inline() / ppc_psq_store_inline() handle GQR type 0 — plain IEEE singles, no quantisation, no scale, which is what games leave GQR0 at — as two 32-bit accesses and a conversion. Quantised types (4..7), invalid types, and the LSQE illegal-instruction check all fall through to the existing out-of-line functions, so behaviour is unchanged.

Nothing new had to be exposed: f64_value, convert_to_double, convert_to_single_ftz and f64_bits were already static inline in core/types.h, and the memory accessors and PPC_HID2_LSQE were already header-visible.

ppc_fp_available(), ppc_psq_load() and ppc_psq_store() all remain real symbols — the LLVM backend emits calls to them by name.

Measured

Idle Apple M5, alternating A/B with a reversed-order block, largest competing process under 70%:

change delta ranges
inline FP gate +11.8% do not overlap
inline paired-single +4.0% do not overlap

Both beat their profiled share. An out-of-line call is also an optimisation barrier: at every generated site w, gqr_index, indexed and cia are literals, so inlining lets the compiler fold the LSQE test and the w branch away per site. The paired-single module came out slightly smaller despite 19,642 inlined sites, which is that folding showing up.

With PGO on top (+7.5%), MKDD under static recompilation on Metal went 51.1 → 62.5 fps single-player and 40.6 → 50.8 fps split-screen.

Ordering

Pairs with a DolRecomp change that emits the _inline forms. This one should land first — with the emitter updated and these absent, a port fails to compile.

Profiling a recompiled Mario Kart: Double Dash on Apple Silicon (`sample`, race
scene) put these near the top of self time inside StaticRecompCore::Run:

    ppc_fp_available                    869 samples  ~7.1%
    psq_load_value + psq_store_value   1101 samples  ~9.0%

Generated code calls them once per instruction site -- 121,874 FP sites and
19,642 paired-single sites on this title.

ppc_fp_available_inline() puts the MSR[FP] test in the header and leaves only
the exception raise out of line. A running game always has MSR[FP] set, so the
cost was the call, not the test.

ppc_psq_load_inline()/ppc_psq_store_inline() handle GQR type 0 -- plain IEEE
singles, no quantisation, no scale, which is what games leave GQR0 at -- as two
32-bit accesses and a conversion. Quantised types (4..7), invalid types and the
LSQE illegal-instruction check all fall through to the existing out-of-line
functions, so behaviour is unchanged. Nothing new had to be exposed:
f64_value, convert_to_double, convert_to_single_ftz and f64_bits were already
static inline in core/types.h.

ppc_fp_available(), ppc_psq_load() and ppc_psq_store() all remain real symbols.
The LLVM backend emits calls to them by name.

Measured on an idle Apple M5, alternating A/B with a reversed-order block:

    inline FP gate         +11.8%   ranges do not overlap
    inline paired-single    +4.0%   ranges do not overlap

Both beat their profiled share, because an out-of-line call is also an
optimisation barrier: at every generated site w, gqr_index, indexed and cia are
literals, so inlining lets the compiler fold the LSQE test and the w branch away
per site. The paired-single module came out slightly SMALLER despite 19,642
inlined sites, which is that folding showing up.

Together with PGO on top (+7.5%), MKDD under static recompilation on Metal went
51.1 -> 62.5 fps single-player and 40.6 -> 50.8 fps split-screen.

Pairs with the DolRecomp change that emits the _inline forms. This one should
land first: with the emitter updated and these absent, a port fails to compile.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant