Skip to content

accel/tcg, target/arm: inline jump-cache probe for A64 indirect branches - #62

Merged
dylanneve1 merged 2 commits into
masterfrom
perf/inline-lookup
Oct 23, 2026
Merged

dylanneve1 merged 2 commits into
masterfrom
perf/inline-lookup

Conversation

@dylanneve1

@dylanneve1 dylanneve1 commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

A64 indirect branches (BR/BLR/RET and the PAC forms) and cross-page direct branches now do the jump-cache hit inline in the generated code:

  • hash the pc and load the cache entry;
  • check the entry's pc, and the target TB's cs_base, flags and cflags against those of the TB being translated, which is the CPU state at those exits;
  • check that there are no breakpoints;
  • jump to the target's code.

A hit costs about 33 host instructions with no call. Through helper_arm_lookup_tb_ptr it was about 69 plus a call.

Misses: the generated code calls a new helper_lookup_tb_ptr_miss() with the pc and the TB state it already has. That goes straight to the slow lookup, without recomputing the state or probing the jump cache a second time, so lookups that miss don't get slower.

Where it doesn't apply:

  • exits where the TB state may differ (DISAS_UPDATE_NOCHAIN, or BTI's BTYPE);
  • runs with exec logging on;
  • TBs whose cflags aren't the vCPU's current ones.

It's on by default; -accel tcg,x-inline-lookup=off keeps the helper for every lookup.

Numbers

Android emulator emulator-5554, a13-test microbenchmarks, the same binary with x-inline-lookup off → on, median of 5:

workload helper inline change
w1 hot BL/RET 1.24 s 0.67 s −46%
w2 BLR/RET, 4096 targets 2.07 s 1.48 s −29%
w2s BLR/RET, 16384 targets (mostly misses) 1.95 s 1.91 s −2%
u1 / u2 (the same at EL0) 1.23 / 2.10 s 0.67 / 1.46 s −46% / −30%
w4 ALU control 1.65 s 1.65 s 0%

Restore boot (iOS 14 restore, -smp 4, 8 interleaved runs per variant): median waiting_host went from 9.70 s to 9.45 s (−2.6%) and host CPU from 7.30 s to 7.25 s. All boots reached waiting_host with no panic.

Tests

  • run-a13-tests.py passes 41/41 on an M1 macOS build.
  • New native_int_check.S test 9 calls a function at EL1 first, so its EL1 TB is in the jump cache, and then from EL0, where its MRS TPIDR_EL1 must trap. With the probe's state checks removed, the test fails as intended.
  • native_int_gen.py now also emits BR and BLR/RET. Soaks with 0 mismatches against TCG and the host CPU: 300 seeds on this change alone, then 100 seeds after rebasing onto native-int: SIMD&FP loads/stores and literal loads stay inside regions #61.

#55 (32768-entry jump cache) on top

inline inline + #55
w2s 1.91 s 1.41 s (−26%)
w2 / u2 1.48 / 1.46 s 1.41 / 1.40 s
restore boot waiting_host (8 runs) 9.45 s 9.30 s
restore boot host CPU 7.25 s 7.00 s

The cost is 512 KiB of jump cache per vCPU instead of 256 KiB. The emulator runs on M1, so the open question from #55 (the Pixel's smaller L2) still needs a phone A/B.

🤖 Generated with Claude Code

dylanneve1 and others added 2 commits October 23, 2026 12:32
…(-accel tcg,x-inline-lookup)

A64 indirect branches (BR/BLR/RET and the PAC forms) and cross-page direct
branches now do the jump-cache hit in the generated code: hash the pc,
load the cache entry, check its pc and the TB's cs_base, flags and cflags
against those of the TB being translated (the CPU state at those exits),
check for breakpoints, and jump to its code. A miss or any mismatch calls
helper_arm_lookup_tb_ptr as before. Not used where the TB state may differ
at the exit (DISAS_UPDATE_NOCHAIN, BTI's BTYPE), with exec logging, or
when the TB's cflags are not the vCPU's current ones.
-accel tcg,x-inline-lookup=off keeps the helper for every lookup.

Android emulator, a13-test microbenchmarks, x-inline-lookup off -> on,
median of 5: w1/u1 (hot BL/RET) -46%, w2/u2 (BLR/RET over 4096
targets) -27/-28%, w2s (16384 targets, mostly jump-cache misses) +6%,
w4 control +1%.

Tests: native_int_check.S test 9 calls a function first at EL1 (its EL1 TB
goes into the jump cache) and then at EL0, where its MRS TPIDR_EL1 must
trap: removing the probe's state checks makes it fail. native_int_gen.py
adds BR and BLR/RET to the random programs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd the second jump-cache probe

On a miss, the generated code calls the new helper_lookup_tb_ptr_miss()
with the pc and the TB state it already has, which goes straight to the
slow lookup (breakpoints, hash table, jump-cache fill) instead of
helper_arm_lookup_tb_ptr(), which recomputed the state from the CPU and
probed the jump cache again. This takes back what the inline probe added
to lookups that miss (w2s, 16384 targets: +6% before).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dylanneve1 dylanneve1 changed the title WIP: inline jump-cache probe for A64 indirect branches (x-inline-lookup) accel/tcg, target/arm: inline jump-cache probe for A64 indirect branches Oct 23, 2026
@dylanneve1
dylanneve1 marked this pull request as ready for review October 23, 2026 11:34
@dylanneve1
dylanneve1 merged commit 259eb6a into master Oct 23, 2026
5 checks passed
@dylanneve1
dylanneve1 deleted the perf/inline-lookup branch November 2, 2026 01:56
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