Skip to content

Hot Path vCPU FP Control State Retention

github-actions[bot] edited this page Sep 14, 2026 · 3 revisions

Floating-Point Control-State Retention

Lifecycle: Performance - Unproven · Type: State-setup shortcut
State: Source-backed; runtime value is not established.

Status: Source-validated exact-shortcut candidate; not implemented or benchmarked.

Mechanism class: Guarded trust of already-prepared host floating-point control state.

Reviewed source: 322986f4c5502b3fa887a9605ac8cd88e9b2c579

Primary files: target/i386/tcg/translate.c, tcg/i386/tcg-target.c.inc, target/i386/ops_fpu.h.

Summary

The hard-x87 translator tracks whether it has already prepared the host floating-point control environment through DisasContext::flcr_set.

gen_flcr() is explicitly marked:

/* TODO: Oversynchronized */

When control state is not considered valid, it:

  1. loads the guest x87 control word;
  2. extracts guest rounding bits;
  3. constructs an MXCSR value;
  4. stores that value to scratch memory;
  5. emits LDMXCSR.

gen_flush_fp() always clears flcr_set, coupling two different questions:

Must cached guest x87 values be materialized?

Must the translator forget that host FP control remains suitable?

The proposed shortcut keeps those decisions separate and bypasses redundant control-state reloads at audited boundaries.

Source-confirmed current route

gen_flush_fp() currently performs:

flush cached x87 values
reset fpstt_delta
set flcr_set = false

The backend emits INDEX_op_flcr as:

store generated MXCSR value to scratch
ldmxcsr [scratch]

Therefore a helper or boundary that triggers gen_flush_fp() may cause a later x87 operation to rebuild/reload control state even if nothing actually changed it.

flowchart TD
    A[Hard-x87 operation] --> B[gen_flcr]
    B --> C{flcr_set?}
    C -->|No| D[Load guest fpuc and build MXCSR]
    D --> E[Scratch store + LDMXCSR]
    E --> F[flcr_set = true]
    C -->|Yes| G[Reuse current host control]
    F --> H[Execute native FP operation]
    G --> H
    H --> I[Helper or generic flush boundary]
    I --> J[gen_flush_fp clears flcr_set]
    J --> A
Loading

Proposed shortcut

Introduce an explicit control-state validity contract independent of cached-value materialization:

flowchart TD
    A[Boundary or helper requested] --> B{Must x87 values be materialized?}
    B -->|Yes| C[Flush values]
    B -->|No| D[Retain values]
    C --> E{Can boundary alter required host FP control?}
    D --> E
    E -->|Yes or unknown| F[Invalidate control-state knowledge]
    E -->|No, audited| G[Retain active FP-control generation]
    F --> H[Next FP operation reloads control]
    G --> I[Next FP operation bypasses LDMXCSR]
Loading

An implementation can track a small generation or enum rather than only a boolean:

/* Illustrative translator state. */
struct FPControlKnowledge {
    bool valid;
    uint32_t guest_control_generation;
    uint32_t active_policy;
};

A hit is valid only when the active host control policy still corresponds to the current guest control generation and no intervening operation may have changed it.

Why it may help

The local cost includes:

  • loading guest fpuc from CPU state;
  • bit extraction and shifts;
  • constructing the MXCSR value;
  • a store to scratch memory;
  • LDMXCSR serialization/control-state cost;
  • possible surrounding register pressure.

The useful opportunity is:

executed FP-control loads
that follow boundaries which did not change control state

Count executed LDMXCSR operations in hot translated blocks. Counting gen_flcr() calls during translation alone does not establish runtime cost.

Exactness and ownership

The candidate is equivalent only if it preserves all required boundaries:

  • guest x87 control-word writes;
  • guest MXCSR writes where relevant to the hard-FPU route;
  • helper calls that may execute floating-point code or change host control;
  • exceptions and exits into C code;
  • transitions between translated code and the main loop;
  • signal/exception handling;
  • snapshot save/restore and CPU reset;
  • changes between hard- and soft-FPU policies;
  • mixed x87 and SSE execution;
  • host code requiring its own floating-point environment.

A pure integer helper is a plausible retained-control boundary, but only after its implementation and ABI behavior are audited. Unknown helpers invalidate.

Relationship to helper-aware x87 cache retention

Question x87 cache retention FP control retention
Values Can live x87 values remain in TCG temporaries? N/A
Canonical guest state May skip st80f/ld80f around helper May still materialize values if required
Host control May or may not remain valid Central concern
Main avoided work Value conversion/storage/reload Guest control decode + scratch store + LDMXCSR

Test four configurations where possible:

reference values + reference control
retained values + reference control
reference values + retained control
retained values + retained control

This prevents double-counting savings.

Code and ownership map

File/area Role
target/i386/tcg/translate.c::gen_flcr() Builds/loads host control state and sets flcr_set
target/i386/tcg/translate.c::gen_flush_fp() Currently invalidates control knowledge unconditionally
target/i386/ops_fpu.h::get_stn()/get_ft0() Requests control setup before using cached hard-FPU values
tcg/i386/tcg-target.c.inc::INDEX_op_flcr Scratch store and LDMXCSR emission
guest fpuc / MXCSR state Architectural source of numerical policy
C-to-JIT boundary Host-environment ownership and restoration

Evidence counters

Counter Unit Meaning
fp_control_requests requests FP operations asking for prepared control
fp_control_loads_emitted operations Generated LDMXCSR paths
fp_control_loads_executed operations Dynamic cost
fp_control_reuse_hits requests Candidate bypass used
fp_control_invalidations events Control knowledge discarded
fp_control_invalidation_reason_* events Guest write, helper, exit, restore, policy, unknown
redundant_loads_avoided operations Local shortcut benefit
fp_result_or_status_mismatches cases Differential failures

Required negative controls

  1. Change guest x87 rounding mode between operations.
  2. Write guest control state through every supported instruction/path.
  3. Insert an audited integer helper; control knowledge should remain valid.
  4. Insert a helper that uses or changes floating-point state; it must invalidate.
  5. Raise a guest exception and exit translated code.
  6. Switch between x87 and SSE operations with differing requirements.
  7. Save and restore a snapshot while control knowledge is valid.
  8. Re-enter translated code after surrounding host C code intentionally changes MXCSR.
  9. Deliberately omit one invalidation in a test build; the oracle must fail.

Host-environment test

The harness must verify both sides of the boundary:

Before entering JIT:
record host MXCSR/control

Inside translated execution:
apply required guest policy
run candidate sequence

After leaving JIT:
host environment matches its required restored value
guest results/status match reference

Improving guest execution while contaminating surrounding host code is not acceptable.

Expected profile signature

hot hard-x87 blocks execute
control reuse hits are nonzero
executed LDMXCSR operations fall
st80f/ld80f may remain unchanged in the isolated control experiment
x87 results, status, exceptions and host environment remain equal
vCPU time per fixed guest work improves or remains neutral

Failure modes

  • Assuming value flush and control invalidation are the same event.
  • Retaining control knowledge across arbitrary helpers.
  • Ignoring host C code's required environment.
  • Failing to invalidate after guest control writes or restore/reset.
  • Counting emitted operations without dynamic execution weighting.
  • Combining with helper-aware cache retention before isolated measurement.
  • Introducing a fixed rounding policy without labeling it an approximation.
  • Measuring only average FPS instead of vCPU time and frame tails.

Qualification plan

  1. Instrument executed control loads and invalidation reasons.
  2. Identify hot blocks with repeated LDMXCSR after unchanged control state.
  3. Introduce explicit control-validity state separate from value cache state.
  4. Admit only one audited pure-helper boundary initially.
  5. Build exact rounding/status/exception and host-environment differential tests.
  6. Inspect optimized Release disassembly and dynamic block execution counts.
  7. Test the candidate alone, then alongside x87 cache retention.
  8. Run CPU-heavy, PGR2, Morrowind, and applicable XISO coverage.
  9. Preserve conservative invalidation for every unknown boundary.

Related pages

Full technical reference

Primary reference: This subject page is the design and research reference for FP Control-State Retention. Pull requests are implementation/test history only.
Lifecycle: Performance - Unproven · Mechanism: Guarded host floating-point environment shortcut
Reviewed source: 322986f4c5502b3fa887a9605ac8cd88e9b2c579 · Acceptance class: intended exact or behavior-preserving for its admitted scope

1. Scope

Keep known-valid host FP control state across audited boundaries instead of broadly clearing the translator's knowledge and re-emitting MXCSR setup.

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.

2. Source seams

File / function Role
target/i386/tcg/translate.c gen_flcr(), gen_flush_fp() and flcr_set tracking
tcg/i386/tcg-target.c.inc LDMXCSR emission and host backend state

3. Current execution path

flowchart TD
    N0["FP operation needs control"]
    N1["If flcr_set false, load guest control word"]
    N0 --> N1
    N2["Construct MXCSR"]
    N1 --> N2
    N3["Store scratch"]
    N2 --> N3
    N4["LDMXCSR"]
    N3 --> N4
    N5["Broad helper/flush clears flcr_set"]
    N4 --> N5
    N6["Repeat later"]
    N5 --> N6
Loading

4. Candidate execution path

flowchart TD
    N0["Classify boundary FP-control effects"]
    N1["Retain control generation across audited preserving boundary"]
    N0 --> N1
    N2["Reload only on guest control change or clobbering boundary"]
    N1 --> N2
    N3["Restore host C environment on exit"]
    N2 --> N3
Loading

5. Why it may help

The code itself labels control setup as oversynchronized. Materializing cached x87 values and invalidating host control knowledge are separate concerns. This candidate targets repeated setup, but only after identifying helpers/boundaries that cannot alter the environment.

6. Work removed

  • Redundant guest-control extraction
  • Scratch store and LDMXCSR
  • Repeated setup after unrelated integer helpers

7. Work that remains mandatory

  • Reload after guest control change
  • Reload after unknown/clobbering helper
  • Guest exception/status behavior
  • Host environment restoration
  • Reference mode

8. Implementation sketch

if (boundary_preserves_host_fp_control(info)) {
    /* retain flcr_generation */
} else {
    s->flcr_valid = false;
}

if (!s->flcr_valid || s->flcr_generation != env->fpuc_generation)
    gen_flcr(s);

The sketch defines the intended boundary; it is not a complete patch and does not replace source review, ownership design, or focused tests.

9. Correctness contract

  • Audited preserving boundaries cannot change MXCSR/x87 control.
  • Guest control-word changes invalidate immediately.
  • Surrounding C code observes the expected host environment after JIT exit.
  • Exception and rounding semantics match the stated exact/approximate policy.

10. State, ownership, invalidation, and fallback

Translator tracks a control generation/valid bit separately from cached values. JIT entry/exit owns saving/restoring the host environment. Unknown helpers, signals/exceptions, mode changes, and unsupported operations invalidate conservatively.

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.

11. Advantages

  • Removes expensive control-register loads
  • Independent from value-cache retention
  • Clear generated-code counters
  • Can benefit mixed integer/FP blocks

12. Costs and failure modes

  • Host FP environment is process/thread-sensitive
  • One misclassified helper can corrupt numerical behavior
  • Savings may overlap with other patches
  • Platform/backend-specific implementation

13. Evidence counters

Counters must be cheap, subsystem-owned, and snapshotted outside the hot loop. Record at least:

  • executed_LDMXCSR
  • gen_flcr_emissions_weighted
  • preserving_boundaries
  • invalidations_by_reason
  • FP_blocks_executed
  • host_env_restore_checks
  • 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.

14. Qualification matrix

  • All guest rounding modes
  • x87/SSE transitions
  • Preserving and clobbering helpers
  • Exceptions/signals
  • JIT exit/reentry
  • Host sentinel MXCSR around execution
  • Independent A/B with value-cache retention disabled

Correctness runs may use validation layers, detailed comparisons, and fault injection. Performance acceptance must use optimized Release builds with matching symbols and minimal instrumentation.

15. Acceptance rule

Promote when boundary classification is proven, host/guest FP state remains correct, and executed control-load counts plus Release performance improve.

16. Rejection rule

Reject if host-environment restoration is unreliable, helper effects cannot be audited, or savings disappear when measured independently.

17. Related architecture and history

CPU-and-vCPU-Execution · Performance-Unproven · Hot-Path-vCPU-Helper-Aware-x87-Cache-Retention · Hot-Path-vCPU-Native-SSE-Arithmetic

PR pages may be linked as supporting evidence, but they are not substitutes for this reference.

Clone this wiki locally