For guests running large amounts of code, most of what TCG executes is not
translated guest work but the fixed overhead around it. Blocks are short and
there are a great many of them, so the constant cost at each end of a block
(the interrupt poll and the can_do_io stores on entry, the dispatch on exit)
ends up dominating everything else.

The workload throughout is qemu-alpha running an emulated alpha gcc 16.2.0
compiling the SQLite 3.45.1 amalgamation (255k lines, -O2) on an x86-64
host, in a --static --enable-lto --target-list=alpha-linux-user build. It
executes 34.2 billion TBs at 6.04 guest instructions each, and 24.6% of its
TB exits cannot use goto_tb. That is a representative shape for any guest
whose text is much larger than a page: indirect calls and returns
everywhere, plus direct branches that merely crossed a page boundary.

The first three patches are ordinary cleanups that stand on their own; patch
3 picked up review tags in v2. Patches 4 and 5 are preparation split out of
v3's patch 4 at Richard's request and move nothing on their own. The
remaining four are marked RFC individually and are where the interesting
questions are.

  1  accel/tcg: fold the dynamic cflags into CPUState::tcg_cflags

     curr_cflags() recomputed three unlikely tests on every one of the run's
     8.4 billion dispatches, from state that changes only when gdb enables
     single-step, when one-insn-per-tb is toggled, or when the log mask
     moves. Fold each into tcg_cflags where it changes.          -5.15%

  2  accel/tcg: enlarge the TB jump cache to 64K entries

     4096 entries is too small for a guest running a large program;
     tb_htable_lookup() is 6.10% of samples. 16 bits is the knee of the
     sizing curve, at 1 MiB per CPUState.               -5.92%, -8.71% wall

  3  accel/tcg: skip the can_do_io stores in user-only builds

     Two stores per TB that nothing in a user-only build reads: 68 billion
     of them over the run.                              -4.57%, -4.53% wall

  4  tcg: pass the destination to tcg_gen_lookup_and_goto_ptr()

     Preparation. The destination PC is already in a TCG temp at every one
     of the 38 call sites; give the helper wrapper the option of taking it
     rather than discarding it. Five targets pass it; the rest pass NULL
     and keep today's behavior.

  5  accel/tcg: give the TB jump cache a second base pointer for generated
     code

     Preparation. CPUState::tb_jmp_cache_probe is a base pointer only
     generated code reads. Pointing it at a shared zero-filled cache makes
     every lookup through it miss, which is how the conditions an inline
     probe cannot check force it back into the helper. Nothing reads it
     yet.

  6  RFC: tcg: probe the TB jump cache inline instead of calling a helper

     95.8% of those 8.4 billion helper_lookup_tb_ptr() calls hit the jump
     cache. Emit the probe inline (hash, four guarded loads, goto_ptr) and
     call the helper only on a miss.                   -34.67%, -25.94% wall

  7  RFC: accel/tcg: allow cross-page goto_tb chaining in user-only builds

     translator_use_goto_tb() refuses to chain across a page. In user-only
     builds the invalidation path already covers what that was protecting
     against: every mmap/mprotect/munmap reaches page_set_flags(), which
     invalidates and unlinks. Lift it there for runs that can never acquire
     a breakpoint, keep it for system mode.             -2.75%, -4.84% wall

  8  RFC: accel/tcg: poison the jump cache instead of polling for indirect
     exits

     A block only needs the icount_decr poll if it can leave by goto_tb;
     every other exit already passes through a dispatch. Poison the probe
     pointer from patch 5 when an exit is requested, so every dispatch
     misses into the helper, which returns the epilogue. Emit the poll only
     in blocks that emitted a goto_tb.                  -2.52%, -1.96% wall

  9  RFC: tcg: fold a guest displacement into the host addressing mode

     tcg_gen_qemu_ld/st cannot express a based access, so a target with a
     displacement in its encodings materializes the address with an lea
     that the host addressing mode would have done for free. Fold a
     preceding constant add into a new argument on the op, opt-in per
     backend, wired up for x86_64 user-only.            -5.70%, -3.20% wall

Each percentage is against the patch before it. Every stage was measured in
one session on the same host, so end to end, from an unmodified LTO build of
the same base to the full series:

    instructions retired: 1,646,994,254,249 -> 819,262,147,022   -50.26%
    wall clock:                     133.19s ->          77.30s   -41.96%

Those are the v3 measurements. The machine they were taken on is busy, so v4
has not been re-measured. Nothing in the v4 changes is expected to move them
-- the splits are pure reorganization, the new alignment test in patch 9
reaches the same answer for everything the alpha frontend emits, and the
emulated compiler's output is unchanged -- but they are not a measurement of
this posting and should not be read as one.

The two figures do not track each other, and that is the interesting part:
what the series removes is cheap, well-predicted, highly pipelined work, so
it retires far more instructions than it saves time. Patch 6 also cuts L1
icache load misses by 39.0%, because a dispatch no longer jumps into qemu's
.text and evicts translated code; qemu's own .text falls from 38.8% to 5.3%
of profile samples.

Every step builds and runs on its own, so the series bisects, and the
emulated compiler produces byte-identical assembly output at every step,
which is the correctness check these patches most need. Three new tests
cover the hazards the series creates, all under tests/tcg/multiarch as of
this revision: test-xpage-chain.c and gdbstub/xpage-bp.py (patch 7) and
test-indirect-irq.c (patch 8). Each fails or hangs if the mechanism it
covers is removed, which is what makes them tests of the new behavior rather
than of the old.

Changes since v3
================

The biggest change is that v3's patch 4 is split three ways, as Richard
asked: the tcg_gen_lookup_and_goto_ptr() API change is now patch 4, the
tb_jmp_cache_probe base pointer and its poison are patch 5, and the inline
probe itself is patch 6. The other structural change is that both guest
tests move from tests/tcg/alpha to tests/tcg/multiarch, as Alex asked, so
every *-user target runs them. tests/tcg/alpha is now byte-identical to
master again.

  1  Update the cflags from the HMP handlers for 'log' and
     'one-insn-per-tb' rather than from qemu_set_log_internal() and the
     accelerator property setter; those are the paths that reach a running
     vCPU, and the monitor is the only thing that does (Richard). Queue the
     per-CPU update with async_run_on_cpu() rather than
     async_safe_run_on_cpu(): halting the other vCPUs buys nothing, since
     the queued work already runs on the owning CPU's own thread (Alex).
     Alex also asked whether there are cross-vCPU updates of tcg_cflags at
     all; with this change the monitor path has none, and the only
     remaining writer from another thread is cpu_single_step(), which is
     neither new nor made worse here. Stub moved to accel/stubs/, where the
     other accelerator stubs live (Philippe).

  2  Commit message only: a linux-user process is not a single vCPU. The
     cache is per CPUState and linux-user creates one per guest thread, so
     a threaded guest pays the 1 MiB per thread, exactly as system
     emulation pays it per vCPU (Richard).

  3  Unchanged.

  4  New, split out of v3's patch 4. No functional change from v3. Its
     commit message no longer claims most targets can simply pass a PC:
     five do, six cannot because their TB pc is derived (avr doubles it,
     i386's is pre-segmentation, riscv masks it, hppa derives it from the
     IAQ, hexagon adjusts it in hardware loops, sparc puts npc in cs_base),
     and seven look like they could but are untested.

  5  Also new, split out of v3's patch 4. The poison cache is a static
     object rather than one allocated on first use (Richard, who asked for
     const; the commit message says why it is plain static and lands in
     .bss).

  6  What remains of v3's patch 4. Emit the softmmu form of
     tb_jmp_cache_hash_func() under CONFIG_SOFTMMU rather than the
     user-only form everywhere: v3 was wrong for system mode, and only not
     a correctness bug because a wrong index simply misses (Richard).
     Compare the pc before testing tb for NULL, assert that
     TranslationBlock::flags is 8-byte aligned since folding the two guards
     into one 64-bit load relies on it, zero-extend a 32-bit guest PC
     instead of falling back to the helper, and describe cs_base in the
     probe as a second word of target-specific flags rather than by name
     (all Richard).

  7  Test moved to tests/tcg/multiarch (Alex). The hand-written branch went
     with it: falling off the end of a page is a cross-page goto_tb just
     the same, and needs no per-architecture branch encoding or
     displacement arithmetic, only "set the return value" and "return".
     Built and run under qemu-user on aarch64, alpha, arm, hppa,
     loongarch64, m68k, mips, ppc, ppc64le, riscv64, s390x, sh4, sparc64
     and x86_64; ppc64 ELFv1 skips, because a function pointer there is a
     descriptor rather than a code address.

  8  Test likewise moved to tests/tcg/multiarch (Alex). Nothing in it is
     architecture specific: the loop is a computed goto, which every
     target's compiler supports, so it covers whichever targets go on to
     use the inline probe.

  9  Hoist the compilation mode tests -- tcg_use_softmmu and the 64-bit
     address type -- out of the backend hook into fold_ldst_disp(), so the
     loop is not entered at all when the mode rules the fold out. Pass
     MemOp rather than MemOpIdx to the hook; nothing about the mmu_idx is
     relevant to it. Move the alignment test into generic code as
     ldst_disp_needs_align(), so a backend need not repeat the
     atom_and_align_for_opc() call; the exact answer depends on the host's
     atomicity capabilities, which the generic pass does not know, so it
     answers for the most restrictive host. That is the same answer for
     everything the frontends actually emit, and conservative for the
     handful of MO_ATOM_WITHIN16 and MO_ATOM_SUBALIGN accesses. (All
     Richard.) What is left of the x86_64 hook is the guest_base test, so
     it now lives beside x86_guest_base under the CONFIG_USER_ONLY that
     declares it. Also refuse a displacement that does not fit the int32_t
     out_disp() takes, which is unreachable with any real guest_base but
     which the interface could not have carried.

What I would most like reviewed
===============================

  - Patch 7 reverses a deliberate decision made in d3a2a1d803 on the
    strength of an argument about the user-only invalidation paths, plus a
    gate on whether gdb can ever attach.

  - Patch 8's un-poison in the main loop races a concurrent poison from
    another thread. I believe the existing barrier around
    icount_decr.u16.high covers it, but my testing was single-threaded user
    mode.

  - Patch 6 treats cpu flags, cflags and cs_base as translation-time
    constants in its guards, reads a jump cache entry without qatomic_read(),
    and leaves one_insn_per_tb and -d nochain toggles visible only at the
    next non-inline exit.

  - Patch 9 only examines the immediately preceding op, refuses any access
    with a slow path (so user-only, and no alignment check), and leaves the
    i128 pairs alone. Richard asked whether the alignment test could stay on
    the base register when the displacement is itself aligned; it can, and
    the reason the fold is still refused there is the slow path handing
    addr_reg to the helper. Recording the displacement in TCGLabelQemuLdst
    and emitting one lea on the slow path would cover alignment-checked
    accesses too, at no fast path cost. Not attempted here.

  - Patch 2's 1 MiB per CPUState is easy to justify for a single-threaded
    linux-user process and less obvious for system emulation with many
    vCPUs, or for a heavily threaded guest. It may want to be sized per
    target or made tunable rather than raised unconditionally.

Patches 6 and 9 are wired up for alpha and x86_64 respectively; everything
else is target-independent, and no other backend changes behavior or needs
touching.

v3: 
https://lore.kernel.org/qemu-devel/[email protected]/
v2: 
https://lore.kernel.org/qemu-devel/[email protected]/

Matt Turner (9):
  accel/tcg: fold the dynamic cflags into CPUState::tcg_cflags
  accel/tcg: enlarge the TB jump cache to 64K entries
  accel/tcg: skip the can_do_io stores in user-only builds
  tcg: pass the destination to tcg_gen_lookup_and_goto_ptr()
  accel/tcg: give the TB jump cache a second base pointer for generated
    code
  RFC: tcg: probe the TB jump cache inline instead of calling a helper
  RFC: accel/tcg: allow cross-page goto_tb chaining in user-only builds
  RFC: accel/tcg: poison the jump cache instead of polling for indirect
    exits
  RFC: tcg: fold a guest displacement into the host addressing mode

 accel/stubs/meson.build                       |   1 +
 accel/stubs/tcg-stub.c                        |  20 ++
 accel/tcg/cpu-exec-common.c                   |  33 +-
 accel/tcg/cpu-exec.c                          | 116 ++++++
 accel/tcg/internal-common.h                   |  13 +-
 accel/tcg/tb-jmp-cache.h                      |   2 +-
 accel/tcg/tcg-accel-ops.c                     |   1 +
 accel/tcg/translator.c                        |  94 ++++-
 cpu-common.c                                  |  11 +
 cpu-target.c                                  |   3 +
 gdbstub/user.c                                |  14 +
 include/gdbstub/user.h                        |  11 +
 include/hw/core/cpu.h                         |  11 +
 include/system/tcg.h                          |  21 ++
 include/tcg/tcg-op-common.h                   |  15 +-
 include/tcg/tcg-op.h                          |  12 +
 include/tcg/tcg-opc.h                         |   9 +-
 include/tcg/tcg.h                             |   2 +
 monitor/hmp-cmds.c                            |   5 +
 system/runstate-hmp-cmds.c                    |   4 +
 target/alpha/translate.c                      |   4 +-
 target/arm/tcg/translate-a64.c                |   4 +-
 target/arm/tcg/translate.c                    |  10 +-
 target/avr/translate.c                        |   4 +-
 target/hexagon/translate.c                    |   4 +-
 target/hppa/translate.c                       |   6 +-
 target/i386/tcg/translate.c                   |   2 +-
 .../tcg/insn_trans/trans_branch.c.inc         |   2 +-
 target/loongarch/tcg/translate.c              |   4 +-
 target/m68k/translate.c                       |   2 +-
 target/microblaze/translate.c                 |   4 +-
 target/mips/tcg/nanomips_translate.c.inc      |   2 +-
 target/mips/tcg/translate.c                   |   6 +-
 target/or1k/translate.c                       |   4 +-
 target/ppc/translate.c                        |   4 +-
 target/riscv/tcg/insn_trans/trans_rvzce.c.inc |   4 +-
 target/riscv/tcg/translate.c                  |   2 +-
 target/rx/translate.c                         |   4 +-
 target/s390x/tcg/translate.c                  |   5 +-
 target/sh4/translate.c                        |   4 +-
 target/sparc/translate.c                      |   4 +-
 target/tricore/translate.c                    |   4 +-
 tcg/tcg-op-ldst.c                             |   3 +-
 tcg/tcg-op.c                                  | 142 +++++++-
 tcg/tcg.c                                     | 132 ++++++-
 tcg/x86_64/tcg-target.c.inc                   |  41 +++
 tcg/x86_64/tcg-target.h                       |   3 +
 tests/tcg/multiarch/Makefile.target           |  12 +-
 tests/tcg/multiarch/gdbstub/xpage-bp.py       |  37 ++
 tests/tcg/multiarch/test-indirect-irq.c       |  62 ++++
 tests/tcg/multiarch/test-xpage-chain.c        | 336 ++++++++++++++++++
 51 files changed, 1192 insertions(+), 63 deletions(-)
 create mode 100644 accel/stubs/tcg-stub.c
 create mode 100644 tests/tcg/multiarch/gdbstub/xpage-bp.py
 create mode 100644 tests/tcg/multiarch/test-indirect-irq.c
 create mode 100644 tests/tcg/multiarch/test-xpage-chain.c


base-commit: eea8fe61b8be8f3016e522e6af24924a0266ca95
-- 
2.54.0


Reply via email to