Repository navigation
Hot Path vCPU Helper Aware x87 Cache Retention
Lifecycle: Performance - Unproven · Type: State-materialization shortcut
State: Source-backed; runtime value is not established.
Status: Source-validated exact-shortcut candidate; not implemented or benchmarked.
Mechanism class: Audited helper-call bypass of canonical x87 state materialization.
Reviewed source:
322986f4c5502b3fa887a9605ac8cd88e9b2c579Primary files:
tcg/tcg.c,target/i386/tcg/translate.c,target/i386/helper.h,target/i386/ops_fpu.h.
The fork's hard-x87 translator keeps guest stack values in TCG floating-point temporaries. Before every generated helper call, generic TCG call generation invokes gen_bb_epilogue(). The x86 translator implements that hook by calling gen_flush_fp().
Consequently, an unrelated integer helper can force xemu to:
- convert and store live cached x87 values into canonical 80-bit guest state;
- discard the translator's cached references;
- forget that host floating-point control was already prepared;
- call the helper;
- reload/convert the x87 values and reload control state when later x87 code resumes.
The proposed shortcut permits a very small, explicitly audited helper class to bypass that x87 flush.
This is a real shortcut: the maintained general route materializes all cached state before a helper; the candidate skips it only when the helper cannot observe or invalidate that state.
tcg_gen_callN() in tcg/tcg.c invokes:
gen_bb_epilogue();before allocating the helper-call operation.
The x86 translator hook is:
void gen_bb_epilogue(void)
{
gen_flush_fp((DisasContext *)tcg_ctx->disas_ctx);
}gen_flush_fp() flushes all cached x87 temporaries, resets the stack delta, and clears flcr_set.
target/i386/helper.h simultaneously marks helpers such as:
cc_compute_all
cc_compute_c
cc_compute_nzwith TCG_CALL_NO_RWG_SE; they take their condition-code inputs by value instead of receiving CPUX86State *env.
That does not automatically prove every helper with that metadata is safe for cached-FP retention. It does provide concrete candidates for an initial whitelist.
flowchart TD
A[Live translated x87 values] --> B[Translator emits unrelated helper call]
B --> C[Generic tcg_gen_callN invokes gen_bb_epilogue]
C --> D[gen_flush_fp]
D --> E[st80f cached stack values and FT0]
E --> F[Clear cached references, fpstt delta and flcr_set]
F --> G[Call helper]
G --> H[Later x87 instruction]
H --> I[gen_flcr and ld80f reloads]
I --> J[Continue arithmetic]
st80f/ld80f are not ordinary same-width spills. The hard-FPU path converts between cached host single/double temporaries and the canonical guest 80-bit representation.
The helper call needs a translator-specific effect decision before the x87 epilogue runs:
/* Illustrative interface, not an existing API. */
if (!x86_helper_preserves_cached_fp(helper_info)) {
gen_flush_fp(s);
}
emit_helper_call(helper_info, args);A conservative first version should use an explicit whitelist layered on top of helper metadata:
Helper must:
- take no CPU environment pointer;
- read no canonical guest globals;
- write no guest globals;
- have no side effects relevant to architectural FP state;
- not raise a guest exception or force a dispatcher boundary;
- not depend on or modify host FP control/status state;
- be reviewed for the exact compiled implementation.
flowchart TD
A[Helper emission requested] --> B{Explicitly audited FP-independent helper?}
B -->|No| R[Reference: flush cached x87 state]
R --> C[Emit helper call]
B -->|Yes| D[Retain logical x87 TCG temporaries and flcr knowledge]
D --> C
C --> E[TCG register allocator handles ABI spills as needed]
E --> F[Continue translated block]
The shortcut preserves the translator's logical cache. It does not assume physical host registers survive a C call. The TCG register allocator remains responsible for normal call-clobber spills.
The opportunity is not the total number of pure helpers. It is:
helper executions
where cached x87 values are live
× number and precision of live values
× avoided st80f/ld80f/control setup
A helper called millions of times while the x87 cache is empty does not justify this work. Conversely, one helper inside a hot x87 chain may repeatedly expand a small guest sequence into conversions and control-state reloads.
The candidate can be equivalent only when all of these remain true:
- the helper cannot read canonical x87 stack contents, tags, status, or control state;
- the helper cannot write or invalidate them;
- the helper cannot raise an exception that requires complete architectural FP state;
- guest-visible x87 state is materialized at every real architectural observation or exit boundary;
- TCG's call-clobber/liveness rules preserve temporary values correctly;
- host floating-point environment assumptions remain valid across the helper;
- debugging, plugins, or instrumentation do not introduce an observation omitted by the admission rule.
The helper metadata is an input to this proof, not the complete proof.
| File/area | Role |
|---|---|
tcg/tcg.c::tcg_gen_callN() |
Unconditional call to gen_bb_epilogue()
|
target/i386/tcg/translate.c::gen_bb_epilogue() |
Maps generic call boundary to x87 flush |
target/i386/tcg/translate.c::gen_flush_fp() |
Flushes values and clears flcr_set
|
target/i386/ops_fpu.h::flush_fp_regs_*() |
Emits canonical st80f stores and clears cached temporaries |
target/i386/helper.h |
Helper effect declarations and initial whitelist candidates |
| TCG call/liveness machinery | Preserves temporaries across call-clobber boundaries |
Record helper calls grouped by:
- helper identity;
- guest PC / translated block;
- dynamic block execution count;
- number of live cached x87 stack values;
- whether FT0 is live;
- cached precision (
f32orf64path); - whether
flcr_setwas true; - emitted
st80f,ld80f, andLDMXCSRoperations attributable to the boundary.
Suggested counters:
| Counter | Unit | Meaning |
|---|---|---|
helper_calls_total |
calls | All dynamic helper calls |
helper_calls_with_live_fp |
calls | Helper encountered with retained x87 values |
helper_calls_whitelist_eligible |
calls | Candidate admission |
live_fp_values_at_eligible_call |
values | Conversion pressure |
fp_flushes_bypassed |
boundaries | Shortcut entries |
st80f_avoided / ld80f_avoided
|
operations | Generated work removed |
fp_flush_fallbacks |
boundaries | Conservative route retained |
Translation-time counts must be weighted by block execution count. One emitted flush in a hot loop may execute millions of times.
Construct a block that:
- loads several live x87 values;
- performs arithmetic;
- invokes an audited by-value integer helper;
- performs more x87 arithmetic;
- stores the final guest result and status.
The candidate-generated code should omit the intermediate canonical st80f/ld80f sequence while matching the reference output and stack state.
Substitute helpers that:
- take
CPUX86State *envand read x87 state; - write x87 control/status or stack state;
- raise a guest exception;
- end the translated block;
- change host FP control state;
- invoke code paths not present in the original whitelist.
All must take the reference flush route.
Also deliberately misclassify one helper in a test build. The differential harness must detect stale or unmaterialized state. If it cannot, the test does not qualify the assumption.
This candidate and Floating-Point Control-State Retention overlap but are not identical:
Value materialization question:
Must cached guest x87 values be written to canonical storage?
Control-state question:
Must xemu forget that the current host MXCSR/control setup is valid?
Benchmark them independently:
- retain values only;
- retain control-state knowledge only where possible;
- combine only after each contribution is understood.
Otherwise the same eliminated LDMXCSR can be counted twice.
A successful candidate should show:
eligible helper calls > 0
intermediate st80f/ld80f operations fall
LDMXCSR may fall in the combined follow-up
helper work itself remains unchanged
host samples in conversion/load-store code fall
fixed guest output and x87 status remain equal
- Treating
TCG_CALL_NO_RWG_SEas a universal FP-safety certificate. - Preserving cached values across a helper that observes canonical state.
- Forgetting guest exception and dispatcher-exit boundaries.
- Assuming host registers survive instead of relying on TCG call-clobber handling.
- Retaining
flcr_setacross code that changed MXCSR. - Changing generic TCG semantics for every target instead of introducing a narrow x86 contract.
- Measuring helper counts without live-FP context.
- Combining this with multiple backend changes before attribution.
- Add translation/dynamic evidence for helper identity and live x87 state.
- Select one or two by-value condition-code helpers with high measured reachability.
- Introduce the narrowest x86-specific effect interface or whitelist.
- Verify generated code before and after with matching Release + DWARF builds.
- Run positive and deliberately broken negative controls.
- Compare exact result bits, x87 stack/tag/status/control state, exceptions, and exits.
- Exercise mixed x87, SSE, integer-helper, interrupt, snapshot, and self-modifying-code flows.
- Run uninstrumented Release ABBA/BAAB on workloads that actually reach the candidate.
- Keep the reference flush route permanent for all unaudited helpers.
- vCPU Shortcut Candidates
- Floating-Point Control-State Retention
- Native SSE Arithmetic
- CPU & vCPU Execution
- Debugging Tools & Commands
Primary reference: This subject page is the design and research reference for Helper-Aware x87 Cache Retention. Pull requests are implementation/test history only.
Lifecycle: Performance - Unproven · Mechanism: State-materialization bypass
Reviewed source:322986f4c5502b3fa887a9605ac8cd88e9b2c579· Acceptance class: intended exact or behavior-preserving for its admitted scope
Keep cached x87 temporaries live across explicitly audited FP-independent helpers instead of unconditionally converting them to canonical 80-bit guest state before every helper call.
This entry remains Unproven: the source path and opportunity are concrete, but no accepted xemu runtime result has established that the candidate improves representative workloads. The page defines what must be bypassed, what remains mandatory, and how the hypothesis can be rejected.
| File / function | Role |
|---|---|
tcg/tcg.c |
helper-call generation and gen_bb_epilogue() |
target/i386/tcg/translate.c |
gen_flush_fp(), cached x87 temporaries and flcr_set |
target/i386/ops_fpu.h |
helper metadata and FP consumers |
flowchart TD
N0["Live cached x87 values"]
N1["Every helper call triggers block epilogue"]
N0 --> N1
N2["st80f materialization"]
N1 --> N2
N3["Clear cached references/control knowledge"]
N2 --> N3
N4["Call helper"]
N3 --> N4
N5["ld80f/control reload later"]
N4 --> N5
flowchart TD
N0["Inspect audited helper effect class at translation"]
N1["FP-independent helper: emit call without canonical x87 flush"]
N0 --> N1
N2["TCG spills registers as ABI requires"]
N1 --> N2
N3["FP-touching/unknown helper: reference flush"]
N2 --> N3
This is not about assuming host registers survive a C call. TCG's allocator/call-clobber handling remains active. The avoided work is conversion to and from canonical guest 80-bit storage plus translator cache invalidation around a helper that cannot observe it.
- Unnecessary st80f/ld80f conversions
- Unnecessary cache invalidation
- Some repeated FP-control setup
- Normal ABI spills
- Flush for helpers that inspect/modify FP state or raise guest exceptions
- Canonical state at real architectural exits
- Reference route
static bool helper_preserves_cached_fp(const HelperInfo *info)
{
return info->explicit_fp_effect == HELPER_FP_NONE &&
!info->takes_cpu_env &&
!info->may_raise_guest_exception;
}
if (!helper_preserves_cached_fp(info))
gen_flush_fp(s);
emit_helper_call(info, args);The sketch defines the intended boundary; it is not a complete patch and does not replace source review, ownership design, or focused tests.
- Whitelist begins with by-value helpers proven not to access CPU/FP state.
- Unknown/pointer-taking helpers flush conservatively.
- Architectural exceptions/exits see canonical state.
- Host register clobbers are handled by TCG independently.
The translator owns logical cache validity. Helper metadata must represent FP effects explicitly rather than infer from generic purity alone. Any helper behavior change invalidates its audit/whitelist status.
The maintained reference path remains the fallback whenever admission, capability, lifetime, or validation is uncertain. A fast result with an unproven dependency is not acceptable as an exact optimization.
- Targets an unconditional fork interaction
- Exact reference path
- Potentially removes expensive 80-bit conversions
- Can be proven in generated code
- Helper audits can become stale
- Live x87 values may be uncommon at pure-helper calls
- Interaction with exceptions/control state
- Backend-specific spill behavior may limit gain
Counters must be cheap, subsystem-owned, and snapshotted outside the hot loop. Record at least:
- helper_calls_with_live_x87
- live_values_per_helper
- helpers_by_identity
- st80f/ld80f_executed
- cache_retained_calls
- fallback_flushes
- generated_code_bytes
- vCPU samples
A local reduction is not enough. Evidence must connect route reachability and removed work to fixed guest progress, CPU/GPU time where applicable, and p95/p99 behavior.
- Translation fixture with live x87 values around audited integer helper
- Exact x87 values/stack tags
- Helper that reads FP state must flush
- Helper that changes control/raises exception
- Register pressure/spills
- Long gameplay block counts weighted by execution
Correctness runs may use validation layers, detailed comparisons, and fault injection. Performance acceptance must use optimized Release builds with matching symbols and minimal instrumentation.
Promote one helper class at a time when generated conversions disappear, differential state tests pass, and weighted runtime evidence is meaningful.
Reject broad metadata inference or global flush removal; keep Unproven if pure helpers rarely occur with live cached values.
CPU-and-vCPU-Execution · Performance-Unproven · Hot-Path-vCPU-FP-Control-State-Retention · Debugging-Tools-and-Commands
PR pages may be linked as supporting evidence, but they are not substitutes for this reference.
DSP tooling guide and navigation updated November 2, 2026 for experimental PR #384, source 8f51d237. Capture/replay usage and limits.
The earlier wiki-wide review was published November 1, 2026. Emulator source: b7140848. Reviewed wiki content: 1d2b671. Current status · Release availability · Publication process.
- Home
- Current status
- Stable versus main
- Implemented mechanisms
- Controls and defaults
- Measured results and limits
- Complete change register
- System
- CPU and vCPU
- Memory and DMA
- Graphics / NV2A
- APU and audio
- Timing and timers
- Platform and I/O
- Hardware observations
- Fidelity boundaries
- Methodology
- PGR2 routes
- Debugging lag / hot paths
- Tools and commands
- DSP capture, replay and fuzzing
- HLE readiness and public XISO proof
- Concurrent DSP shadow and OpenAL proof
- DSP HLE operation inventory
- DSP HLE fix examples and testing
- Development
- Upstream shortlist
- Wiki maintenance
- Research / retired work
- Optimization catalog
- Hot-path shortcuts
- Researched experiments
- Unproven proposal directory
- PR lessons
- September26 snapshot
Repository · PRs · Issues