Skip to content

Hot Path vCPU Native SSE Arithmetic

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

Native SSE Floating-Point Arithmetic

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

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

Mechanism class: Direct host instruction emission that bypasses general SSE helpers.

Reviewed source: 322986f4c5502b3fa887a9605ac8cd88e9b2c579

Primary files: target/i386/tcg/emit.c.inc, target/i386/ops_sse.h, target/i386/tcg/ops_sse_header.h.inc, tcg/i386/tcg-target.c.inc.

Summary

The fork already emits many x87 operations through native hard-FPU TCG operations. That does not mean guest SSE floating-point arithmetic is native.

Current helper declarations cover packed and scalar:

  • add;
  • subtract;
  • multiply;
  • divide;
  • minimum/maximum;
  • square root;
  • comparisons and conversions.

The corresponding implementations operate through xemu/QEMU floating-point helper machinery, generally per element. The proposal emits a small supported subset as host SSE instructions and bypasses the helper ABI and per-lane dispatch.

This is a real shortcut because the current helper remains the general reference route. It is only equivalent if the candidate reproduces the required guest numerical environment, result bits, status flags, and untouched lanes. Otherwise it must remain an explicitly approximate mode.

Scope the first experiment narrowly

Do not begin with the complete SSE floating-point instruction family.

A useful first candidate is:

Host: x86-64 with required SSE instruction
Guest form: register-to-register
Operations: packed/scalar ADD, MUL and SUB
Guest exceptions: masked
Rounding/denormal policy: explicitly admitted and recorded
Memory operands: reference route initially
MIN/MAX, DIV, SQRT, conversions and comparisons: reference route initially

This isolates helper elimination from memory faults, complex NaN rules, conversion saturation, and comparison-status semantics.

Current route

flowchart TD
    A[Guest SSE FP instruction] --> B[Decode source and destination]
    B --> C[Build pointers to guest XMM storage]
    C --> D[Emit helper call]
    D --> E[Helper loads elements]
    E --> F[SoftFloat/x86 FP operation per active lane]
    F --> G[Update result and guest FP status]
    G --> H[Return through helper ABI]
    H --> I[Continue translated execution]
Loading

The helper route is broad and portable. It is the maintained fallback for unsupported operations, hosts, numerical modes, and any condition whose exact behavior is not yet proven.

Candidate route: direct operation per instruction

The first stage should still load and store guest XMM state around each supported operation:

flowchart TD
    A[Guest register-register ADD/MUL/SUB] --> B{Host and guest FP policy admitted?}
    B -->|No| R[Current helper reference]
    B -->|Yes| C[Load source/destination guest XMM values]
    C --> D[Emit corresponding packed/scalar host SSE opcode]
    D --> E[Preserve scalar untouched lanes]
    E --> F[Capture/merge required guest status]
    F --> G[Store guest destination]
Loading

Illustrative packed chain:

movups xmm0, [guest_xmm0]
movups xmm1, [guest_xmm1]
mulps  xmm0, xmm1
movups [guest_xmm0], xmm0

Only after the per-instruction experiment is understood should a second experiment retain guest SSE values across a short supported run:

flowchart LR
    A[Load guest XMM values] --> B[Native MULPS]
    B --> C[Native ADDPS]
    C --> D{Unsupported op, observation or exit?}
    D -->|No| B
    D -->|Yes| E[Materialize guest XMM state]
Loading

That second stage changes register lifetime and exit handling; it must not be bundled into the first patch.

Why it may help

Potentially removed work includes:

  • helper call setup and ABI transitions;
  • pointer argument construction;
  • per-lane helper dispatch;
  • repeated guest XMM load/store inside helpers;
  • helper-induced x87 cache flushes in mixed code;
  • larger generated-call sequences;
  • eventually, intermediate architectural XMM stores between supported operations.

The actual value depends on dynamic instruction mix. Count supported guest operations by hot block before implementing broad backend support.

Numerical and architectural compromise

“Both guest and host support SSE” is not a sufficient equivalence proof.

The candidate must account for:

Area Required question
Rounding Does active host MXCSR match guest rounding mode?
Exception flags How are host status bits merged into guest MXCSR?
Exception masks Can the host instruction trap when the guest operation should remain masked?
Denormals Do host DAZ/FTZ settings match guest behavior?
NaNs Are payload, quieting and operand-selection rules compatible?
Signed zero Do add/sub/mul edge cases match?
Scalar forms Are upper destination lanes preserved exactly?
Aliasing Is destination/source overlap handled identically?
Host contamination Is surrounding C code's FP environment restored?

Do not introduce FMA. Replacing guest multiply followed by add with a fused operation changes rounding and destroys attribution.

Exact and approximate modes

A development implementation may expose two dispositions:

  • Equivalent candidate: admitted only when host control/status handling reproduces tested guest semantics.
  • Approximate candidate: uses a documented fixed host FP policy and accepts bounded output differences.

The Wiki and evidence must never describe the second as exact merely because games appear to run.

Operations to defer

MIN/MAX

x86 minimum/maximum instructions have non-obvious NaN, signed-zero, and operand-selection behavior. Direct emission may be correct, but it needs its own oracle.

DIV/SQRT

These may have higher local cost, but also bring additional special-case and exception behavior. Add them after the basic framework is proven.

Comparisons and conversions

They update flags or convert with saturation/indefinite results. Keep them on the helper route until exact guest-status behavior is demonstrated.

Memory operands

A direct arithmetic opcode with a host memory operand must not bypass guest address translation, alignment faults, page faults, watchpoints, or MMIO. The initial route should use register-register guest forms only.

Code and ownership map

File/area Role
target/i386/tcg/ops_sse_header.h.inc Confirms current helper families
target/i386/ops_sse.h Current helper semantics and per-lane operations
target/i386/tcg/emit.c.inc Decode and helper-selection seam
tcg/i386/tcg-target.c.inc Host x86 opcode emission and register constraints
Guest CPUX86State::xmm_regs Architectural XMM storage
Guest sse_status / MXCSR Numerical policy and exception status

Evidence counters

Counter Unit Meaning
sse_fp_add_exec, mul_exec, sub_exec instructions Dynamic candidate frequency
native_sse_entries instructions Direct route reached
sse_helper_entries instructions Reference route reached
native_sse_reject_* instructions Rejection by operation, operand, host, or FP policy
helper_calls_avoided calls Local transition removal
guest_xmm_loads_stores_emitted operations State traffic before/after
fp_environment_switches operations Control-state cost
host_code_bytes_per_guest_op bytes Expansion
result_or_status_mismatches cases Correctness oracle failures

Differential test matrix

For each admitted instruction, test:

  • packed and scalar forms;
  • every destination/source alias pattern;
  • normals, zeros, subnormals, infinities, and NaNs;
  • all guest rounding modes;
  • masked and unmasked exception configurations;
  • DAZ/FTZ combinations supported by the guest model;
  • upper-lane preservation for scalar operations;
  • mixed native-candidate and helper-reference sequences;
  • mixed x87 and SSE execution;
  • interrupt, exception, snapshot, reset, and TB-exit boundaries.

Use exact result and guest-status comparisons for an equivalence claim. For an approximate mode, record the declared numerical differences and compatibility scope.

Generated-code proof

A source change is not enough. For hot blocks, capture:

guest PC and instruction
reference host disassembly
candidate host disassembly
helper calls
architectural loads/stores
FP control operations
block execution count
host sample weight

The direct route should visibly remove the helper and emit the intended host operation. A compiler inlining the helper differently than expected can change the hypothesis.

Expected profile signature

candidate operations execute frequently
SSE helper calls fall
host samples in helper/SoftFloat paths fall
code expansion and XMM memory traffic fall
vCPU time per fixed guest work improves
result/status oracle remains within declared contract

If helper calls fall but MXCSR setup, spills, or architectural XMM traffic becomes the new dominant cost, the next step is not automatically a larger instruction set. Measure the new path first.

Failure modes

  • Assuming host SSE semantics automatically equal guest Pentium III semantics.
  • Ignoring guest exception status because exceptions are masked.
  • Leaking guest MXCSR policy into host C code.
  • Mishandling scalar upper lanes.
  • Directly using guest memory without MMU/fault handling.
  • Combining register retention with helper elimination in one un-attributable patch.
  • Adding FMA or other algebraic rewrites.
  • Expanding to MIN/MAX/conversions before basic operations are proven.
  • Reporting synthetic throughput without retail reachability.

Qualification plan

  1. Count candidate operations dynamically by guest PC and workload.
  2. Implement direct emission for one operation and register-register form.
  3. Keep same-binary helper reference routing.
  4. Build exact result/status differential tests and broken-policy controls.
  5. Inspect optimized Release host disassembly with matching DWARF.
  6. Add operations one at a time: ADD, MUL, SUB.
  7. Measure local helper elimination and total vCPU progress.
  8. Run CPU-focused tests, PGR2, Morrowind, and applicable XISO coverage.
  9. Only then evaluate retaining values across short instruction runs.
  10. Keep unsupported operations and numerical modes on the helper route.

Related pages

Full technical reference

Primary reference: This subject page is the design and research reference for Native SSE Arithmetic. Pull requests are implementation/test history only.
Lifecycle: Performance - Unproven · Mechanism: Host-ISA direct-emission shortcut
Reviewed source: 322986f4c5502b3fa887a9605ac8cd88e9b2c579 · Acceptance class: intended exact or behavior-preserving for its admitted scope

1. Scope

Emit a narrowly admitted subset of guest SSE floating-point arithmetic directly instead of per-lane helper execution, under an explicit host FP-environment contract.

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/emit.c.inc SSE arithmetic translation/helper selection
target/i386/ops_sse.h per-element floating-point helper semantics
tcg/i386/tcg-target.c.inc host vector register and instruction emission

3. Current execution path

flowchart TD
    N0["Guest packed/scalar add/mul/sub"]
    N1["Helper call"]
    N0 --> N1
    N2["Per-lane softfloat operation/status"]
    N1 --> N2
    N3["Return result"]
    N2 --> N3
    N4["Materialize guest XMM"]
    N3 --> N4
Loading

4. Candidate execution path

flowchart TD
    N0["Admit operation/FP policy"]
    N1["Load guest XMM"]
    N0 --> N1
    N2["Emit native addps/mulps/subps or scalar form"]
    N1 --> N2
    N3["Merge required status"]
    N2 --> N3
    N4["Store guest XMM"]
    N3 --> N4
    N5["Fallback otherwise"]
    N4 --> N5
Loading

5. Why it may help

Sharing an instruction name does not prove identical behavior. The first experiment should isolate helper elimination for register-register ADD/MUL/SUB with masked exceptions and defined rounding/denormal policy. Retaining XMM values across multiple guest instructions is a later separate optimization.

6. Work removed

  • Helper ABI transitions
  • Per-lane dispatch
  • Eventually some intermediate guest-register loads/stores

7. Work that remains mandatory

  • Guest memory/fault handling
  • Unsupported operations
  • Status/exception policy
  • MIN/MAX/conversion quirks
  • Reference helper

8. Implementation sketch

; Desired first-stage generated shape
movups xmm0, [guest_xmm0]
movups xmm1, [guest_xmm1]
mulps  xmm0, xmm1
movups [guest_xmm0], xmm0

; A later experiment may keep xmm0 live across several admitted ops.

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

  • Rounding, exception masks/status, DAZ/FTZ, NaN, signed zero and scalar upper-lane behavior follow the declared contract.
  • No FMA contraction is introduced.
  • Unsupported or environment-mismatched cases use helpers.
  • Host environment is restored on exit.

10. State, ownership, invalidation, and fallback

Initial codegen loads/stores guest register storage per instruction. A later register-resident region requires TCG allocator integration, call/exit materialization, and backend-specific register availability.

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

  • Can turn several helpers into a short native sequence
  • High potential in vector-heavy guest code
  • Instruction-level differential tests are possible
  • Foundation for register-resident SSE regions

12. Costs and failure modes

  • Numerical/exception mismatches
  • Host environment setup may reduce savings
  • Win64 register pressure
  • Backend portability
  • Broader operation set has many edge semantics

13. Evidence counters

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

  • supported_SSE_ops_executed
  • helpers_avoided
  • guest_XMM_loads/stores
  • FP_environment_switches
  • host_code_bytes
  • spills/reloads
  • 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

  • Exact bits/status for normal/special values
  • Destination/source aliasing
  • Scalar upper lanes
  • Mixed x87/SSE
  • Rounding and denormal modes
  • Exceptions masked/unmasked
  • Generated-code inspection
  • Short chains and games

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 instruction families incrementally when their contract is proven and weighted execution plus Release measurements justify direct emission.

16. Rejection rule

Reject a blanket 'host supports SSE' implementation or any patch that silently changes FP policy, contracts operations, or leaves host state contaminated.

17. Related architecture and history

CPU-and-vCPU-Execution · Performance-Unproven · Hot-Path-vCPU-FP-Control-State-Retention · vCPU-Windows-XMM-Register-Budget

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

Clone this wiki locally