| name | linx-qemu |
| description | Linx emulator development workflow for submodule `emulator/qemu`. Use when implementing or debugging decode/execute behavior, trap and interrupt handling, MMU and device interactions, or AVS runtime/system regressions where emulator behavior is the likely first divergence. |
Linx QEMU
Overview
Use this skill for emulator-focused work in emulator/qemu and for runtime failures where QEMU is the likely first divergence point.
Target lines
Keep the active QEMU line explicit before making changes:
- Current/modern line may expose
linx64-softmmu in the in-repo build tree.
- For current LinxISA superproject bring-up, prefer
/Users/zhoubot/linx-isa/emulator/qemu/build-linx/qemu-system-linx64 when
it exists. Use QEMU or QEMU_CLEAN_OUT_DIR only when intentionally testing
another matching build. The older emulator/qemu/build/qemu-system-linx64
path can be a stale legacy fallback.
- Recovered historical lines can instead expose:
linx-softmmu
linx-linux-user
linx_be-linux-user
Do not assume the target naming surface. Read configs/targets/ first and use
the names that actually exist in the checked-out branch.
For the merged current recovery lane, direct kernel/rootfs runs are
firmwareless by default. Preserve -bios none in local reproductions unless a
specific firmware blob is intentionally under test.
For direct-boot AVS and SuperNPUBench AI workload runs, set
LINX_VIRT_TEST_FINISHER=1 unless intentionally debugging guest-side spins.
That env makes test finisher MMIO writes report pass/fail to the host instead
of relying on timeout behavior.
The finisher is a post-store MMIO contract. Translation helpers must not stop
the CPU before the terminal store executes and retires: route the write through
the linx-test-finisher device, let the normal store/commit path record the
instruction, then use stop_after_commit to close commit/Minst traces. Minst
store records must carry the real store value in mem_wdata. A focused
regression must end with exactly one retired finisher store at 0x10009000
with the expected status value; never obtain trace parity by dropping or
normalizing away the terminal record.
QEMU linux-user mode is a separate process ABI lane. Use it only when the
checked-out or recovered QEMU tree actually provides a qemu-linx binary; the
current canonical checked-in target list may still expose only softmmu targets.
The reusable smoke form is:
python3 /Users/zhoubot/linx-isa/avs/qemu/run_musl_smoke.py \
--mode phase-b \
--link static \
--runner user \
--qemu-user /Users/zhoubot/linx-isa/emulator/qemu/build-user/qemu-linx
python3 /Users/zhoubot/linx-isa/avs/qemu/run_glibc_smoke.py \
--runner user \
--qemu-user /Users/zhoubot/linx-isa/emulator/qemu/build-user/qemu-linx
This invokes qemu-linx -L <sysroot> <elf> and should be treated as a fast
pre-rootfs check for ELF startup, libc syscalls, and linux-user dispatch. It
does not replace qemu-system-linx64 kernel/initramfs or full-OS gates.
Historical recovery lane
When the user provides a recovered full patch and says it is the authoritative
latest implementation, treat that as a separate recovery workflow rather than a
normal forward-port.
- Prefer finding an old upstream QEMU base that fits the patch over manually
replaying selected hunks onto a newer branch.
- In this workspace, the recovered
my.patch-style Linx tree matched best
against an upstream v7.0.0 base.
- If you must configure
linx-linux-user on a non-Linux host during recovery,
any host-policy relaxations in configure / meson.build are host bring-up
adaptations, not ISA or emulator semantic changes.
- If the recovered tree references
pc-bios/LinxInit.bin but the firmware blob
is unavailable, prefer making that blob optional for local configure/build
validation instead of pretending to have reconstructed it.
Required gates
bash /Users/zhoubot/linx-isa/avs/qemu/check_system_strict.sh
bash /Users/zhoubot/linx-isa/avs/qemu/run_tests.sh --all --timeout 10
python3 /Users/zhoubot/linx-isa/avs/qemu/run_callret_contract.py
python3 /Users/zhoubot/linx-isa/tools/bringup/check_qemu_opcode_meta_sync.py --qemu-root /Users/zhoubot/linx-isa/emulator/qemu --allowlist /Users/zhoubot/linx-isa/docs/bringup/qemu_opcode_sync_allowlist.json --report-out /Users/zhoubot/linx-isa/docs/bringup/gates/qemu_opcode_sync_latest.json --out-md /Users/zhoubot/linx-isa/docs/bringup/gates/qemu_opcode_sync_latest.md
python3 /Users/zhoubot/linx-isa/tools/bringup/report_qemu_isa_coverage.py --spec /Users/zhoubot/linx-isa/isa/v0.57/linxisa-v0.57.json --qemu-meta /Users/zhoubot/linx-isa/emulator/qemu/target/linx/linx_opcode_meta_gen.h --report-out /Users/zhoubot/linx-isa/docs/bringup/gates/qemu_isa_coverage_latest.json --out-md /Users/zhoubot/linx-isa/docs/bringup/gates/qemu_isa_coverage_latest.md
Coverage and opcode-sync gates must target the live standalone v0.57 catalog.
Treat any isa/v0.3, isa/v0.4, or other retired-profile coverage command as
archive-only unless explicitly running a historical comparison.
Incremental build policy
- Prefer a fresh out-of-tree local build when the checked-in
build/ tree
drifts across upstream Meson option schema changes.
- Current recovered QEMU uses
configure passthrough options rather than the
older --with-git-submodules=ignore form. A known-good local rebuild form is:
mkdir -p /tmp/linx-qemu-local-build
cd /tmp/linx-qemu-local-build
/Users/zhoubot/linx-isa/emulator/qemu/configure \
--target-list=linx64-softmmu \
--enable-plugins \
--disable-docs \
--disable-werror \
--disable-install-blobs
ninja qemu-system-linx64
- Reuse that build directory for incremental semantic/debug iterations once it
configures successfully.
Workflow
- Reproduce with the smallest AVS case.
- If the failure is in AVS compile or object generation, first decide whether
the blocker belongs to the compiler surface rather than QEMU semantics.
In particular, old Linx AVS cases that still use
%tpcrel_hi/%tpcrel_lo,
unfused symbolic BSTART CALL, or legacy block-attribute syntax should not
be treated as emulator regressions until the in-repo clang integrated
assembler accepts them.
- For Linux userspace/runtime failures, try the linux-user process smoke first
if
qemu-linx exists. A user-mode pass narrows later failures to
kernel/rootfs/system integration; a user-mode fail keeps the first
divergence in ELF startup, syscall, or linux-user QEMU dispatch.
- Capture the first wrong architectural event (
pc, opcode, trap/irq cause, memory side-effect).
- Only if needed, add targeted QEMU tracing around the suspicious PC or first wrong event; do not start tracing from reset/boot by default.
- Compare against ISA semantics and expected Linux/runtime behavior.
- If the first divergence only appears on positive direct-call or call/ret
runtime cases using 64-bit
L.BSTART.* headers, verify whether the raw
immediate decode is still in halfword units before linx_pcrel_target()
shifts it into a byte offset.
- Patch decode/execute or exception path and add a focused regression.
- Re-run runtime and system strict gates.
First-divergence rules:
- For current
ET_REL direct-boot relocation failures, first compare QEMU
loader relocation types with LLVM ELFRelocs/LinxISA.def. Enter opcode
dispatch only for an explicit whitelist of current relocation types; never
infer dynamic, TLS, GOT, or unsupported relocations from instruction bits.
- Do not close platform-defined
XB/CAC_TABLE coverage with decoder-only or
no-op handling. First freeze the table-entry layout plus the XBINFO,
permission, and E_INST ABI tracked by LinxISA/linx-isa#140.
- Treat
report_qemu_isa_coverage.py as L1 decoder/source-mapping evidence
only. Call coverage executable and semantic only when the same form has L2
runtime execution tied to a test ID and an L3 architectural result oracle;
report missing L2/L3 evidence as unavailable rather than zero.
- Keep tile hand allocation, source provenance, backing storage, ACR switching,
and VMState in one state-ownership contract. Shared backing requires shared
liveness; banked liveness requires equivalently banked backing.
- Interpret B.IOT tile sources as six-bit hand/rank operands, with rank 1 as
the newest live entry in that hand. Never derive an architectural rank from
a physical TILE id. Freeze each header's source and output bindings before
executing its body, reserve outputs in distinct physical slots, then consume
inputs and publish outputs in descriptor order at successful block commit.
- Stage tile queue metadata locally across TMA, CUBE, and vector execution.
Backing-memory beats may follow their documented restart rules, but live,
reserved, ordered, and ACR-pin metadata must become visible atomically. ACR
source pins and nonempty queue state are migration state: serialize them or
reject the unsupported migration shape explicitly.
- Normalize typed vector destinations to queue publication rather than direct
physical replacement. The currently executable LTAR ABI maps TA..TD to
indices 0..3 and TO/TS to 4/5; do not invent a wider mapping until the
assembler, compiler, and QEMU parser adopt it together.
- Keep vector LTAR lifetime tied to descriptor provenance. Source-less
MSEQ/MPAR B.IOT outputs are block-local scratch: reserve them for TO/TS while
the body runs, then discard them without publishing into a tile hand.
Input-bound VPAR/TEPL outputs are persistent tile dataflow and must consume
frozen inputs before publishing the independently reserved output. Until
general vector lowering becomes queue-relative as one cross-layer change,
do not apply the tile-body queue-head destination compatibility rule to
ordinary fixed-slot MSEQ/MPAR code.
- For faultable tile helpers, plan descriptor allocation and source consumption
without mutating live state, then publish them only after the operation
succeeds. TMA Normal-memory beats may remain externally non-atomic when the
ISA allows restartable partial completion, but stale backing is never a
general substitute for a live source; any compiler-compatibility exception
must be provenance-bound and operand-exact.
- For v0.57 PTO/tile decode, keep
TPREFETCH adjacent to the TLOAD/TSTORE
encoding family but destination-free. Execute it as a TLOAD-like prefetch of
addressing and attributes with no tile destination operand and no queue
publication.
- Decode TMA selectors as the dense v0.57
0..8 PTO tile-memory family. Reject
holes, aliases, and legacy selector spellings unless the v0.57 golden manifest
explicitly reserves them.
- Keep named
CUBE opcode/template identities unique across QEMU metadata,
golden decode names, and compiler block templates; never collapse distinct
CUBE forms into one handler name without an explicit sub-op identity.
- Add scalar CAS/DMA decode metadata and execution coverage as active v0.57
forms, not compatibility fallbacks.
- When PTOAS/QEMU bridge metadata is involved, validate against the v0.57
111-operation PTO map and reject legacy PTO spellings rather than normalizing
them silently.
For recovered historical lines, insert one extra step before implementation:
- Pick and validate the old base first.
- Apply the recovered patch whole if possible.
- Only then start conflict resolution or branch-local build adaptation.
Trace policy
- Do not generate full-run QEMU traces from the beginning of execution unless no narrower reproducer exists.
- First localize the suspicious
pc/opcode/window from AVS output, guest state, or a smaller repro, then enable trace only near that window.
- Keep QEMU trace/log output storage-bounded. Prefer
LINX_*_TRACE_LIMIT,
heartbeat/stat counters, and narrow PC/count windows over unbounded logs.
After a QEMU packet is summarized, preserve the report/manifest and the small
excerpt cited by docs, then delete bulky rerunnable artifacts such as
avs/qemu/out/**/qemu.log, generated *.trace, generated initramfs *.cpio,
and temporary .omx/tmp*.log files. Do not keep multi-hundred-MB QEMU logs
solely as historical context once their outcome is captured in JSON/Markdown.
- For hosted user-fault bring-up, prefer the opt-in
LINX_FAULT_TRACE=1
path before adding new trace code. Narrow it with
LINX_FAULT_TRACE_PC_LO/LINX_FAULT_TRACE_PC_HI or
LINX_FAULT_TRACE_COUNT_LO/LINX_FAULT_TRACE_COUNT_HI, and cap output with
LINX_FAULT_TRACE_LIMIT when possible.
- For wrong runtime instruction bytes, pair a narrow TLB-fill trace with
PC-watch virtual and physical byte dumps before changing decode. A reusable
shape is
LINX_TLB_FILL_TRACE_VA=<va>,
LINX_DEBUG_PC_WATCH=<pc>,
LINX_DEBUG_PC_WATCH_DUMP_CODE_BYTES=<n>, and
LINX_DEBUG_PC_WATCH_DUMP_PHYS=1. If virtual bytes and physical bytes agree
but disagree with the ELF file offset that should back that VA, treat QEMU as
following the installed PTE and route the blocker to Linux file
mapping/page-cache/VMA triage.
- For SPEC userspace-trap PC-watch packets, require the runner's
qemu_debug_env JSON field in the evidence so the exact heartbeat,
fault-trace, and PC-watch knobs are reproducible. Keep watch sets minimal:
broad multi-window PC-watch can perturb row timing enough to change a
userspace trap into a kernel live-timeout. LINX_DEBUG_PC_WATCH_RING=1 is
useful when the terminal fault is on a watched PC, but signal paths can still
end without LINX_PC_WATCH_RING output; in that case use printed
linx_pc_watch: / LINX_PC_WATCH_REGS hit lines as authoritative evidence
and narrow the next repro toward the producer/caller path instead of widening
the trace.
- For SPEC live-timeout throughput triage, use the SPEC runner switch
--qemu-tlb-stats or LINX_SPEC_QEMU_TLB_STATS=1 before enabling full
LINX_TLB_TRACE=1. The switch sets LINX_QEMU_TLB_STATS=1; QEMU appends
tlbi_iall, tlbi_ia, tlbi_iv, tlbi_iav, and last-invalidation
PC/BPC/operand/ACR fields to LINX_HEARTBEAT without enabling the
experimental MMU cache. The SPEC runner records these fields under
heartbeat_tlb_invalidation and prints compact tlbi= liveness tags. Use
this to separate startup/fault-path invalidation pressure from steady-state
benchmark execution before changing QEMU TLB behavior.
- For SPEC demand page-walk attribution, use
LINX_QEMU_TLB_FILL_STATS=1
before enabling full LINX_QEMU_TLB_FILL_TRACE=1. The fill-stats path adds
tlbf_total, fetch/load/store/probe/ok/fault counts, user/kernel/other
MMU-index splits, and last-fill PC/BPC/VA/PA/access/MMU/prot/cause/ACR
fields to LINX_HEARTBEAT; the SPEC runner records them under
heartbeat_tlb_fill and prints compact tlbf= liveness tags in matrix
markdown. Use full fill traces only after the aggregate counters identify a
row/window worth narrowing.
- For SPEC page-walk cache experiments, do not treat QEMU's
tlb_set_page
size argument as multi-page lookup coverage. Upstream cputlb still
materializes one TARGET_PAGE_SIZE entry and uses larger sizes only for
invalidation bookkeeping. If Linx large block mappings are the target, keep
the generic soft-TLB page-granular and test the opt-in block-aware page-walk
result cache via the SPEC runner switches --qemu-mmu-cache and
--qemu-mmu-cache-stats (or their LINX_QEMU_MMU_CACHE=1 /
LINX_QEMU_MMU_CACHE_STATS=1 env equivalents); use the runner's
heartbeat_mmu_cache / mmuc= fields to prove hit/miss/fill behavior before
promoting it. On 2026-07-05, focused 505.mcf_r train improved from
41000000005 to 45000000003 bounded instructions in 180s with
mmuc_hit=122234545, while strict 999.specrand_ir passed with the new
switches; keep the cache opt-in until all-row train evidence is green.
The 2026-07-06 clean train-all packet
workloads/generated/specint-train-all-mmuc-split-clean-qemu-20260706-r1/
adds size-class and collision counters. Read mmuc_col* before changing the
cache shape: high 4K collision ratios, as in 505.mcf_r and
531.deepsjeng_r, justify associativity or user-data fast-path prototypes;
low hit and low collision rows, as in 541.leela_r and 525.x264_r, should
stay in TB/template or transport lanes instead of MMU-cache resizing.
The 2026-07-06 same-head assoc2 controls reject the first adjacent 2-way
cache-shape prototype as a promotion candidate:
workloads/generated/specint-505-mmuc-direct-control-qemu-20260706-r1/
versus workloads/generated/specint-505-mmuc-assoc2-qemu-20260706-r1/
drops 505.mcf_r hits and raises fills/collisions, and
workloads/generated/specint-531-mmuc-direct-control-qemu-20260706-r1/
versus workloads/generated/specint-531-mmuc-assoc2-qemu-20260706-r1/
drops 531.deepsjeng_r hits while leaving fills/collisions flat. Keep
LINX_QEMU_MMU_CACHE_ASSOC2 / --qemu-mmu-cache-assoc2 default-off and do
not put it into all-row train gates unless a new same-head control beats the
direct-mapped cache. Future cache-shape experiments should avoid simply
pairing adjacent direct-map slots; preserve or improve direct-map locality
first, then rerun 999.specrand_ir plus focused 505/531 controls before
train-all promotion.
- For long live-timeout rows where aggregate fill volume is high but full fill
trace output would be too large, add
LINX_QEMU_TLB_FILL_HOT=1 alongside
LINX_QEMU_TLB_FILL_STATS=1. QEMU emits LINX_TLB_FILL_HOT heartbeat
companion lines with a small hot-page sketch; the SPEC runner records
heartbeat_tlb_fill_hot and prints compact tlbf-hot= tags with the
hottest page/access/MMU tuple, eviction pressure, and reuse/churn counters
(inserts, last_hits, slot_hits). If inserts and evictions track
total fills while last_hits/slot_hits stay near zero, route the row to
streaming page-walk or soft-MMU lookup cost rather than enlarging the small
hot-page sketch. The 2026-07-06 focused 505.mcf_r train packet
workloads/generated/specint-505-tlbf-hot-reuse-qemu-20260706-r1/ is the
current example: tlbf_total=89914727, inserts=89914720,
evictions=89914704, last_hits=5, and slot_hits=2.
The 2026-07-06 focused 505.mcf_r post-start host profile
workloads/generated/specint-505-poststart-profile-speedstack-qemu-20260706-r1/
showed linx_tlb_fill_stats_record in the top QEMU frames, but a same-head
75-second A/B rejected TLB-fill stats/hot instrumentation as the row-level
throughput limiter: workloads/generated/specint-505-speedstack-tlbfillstats-control-qemu-20260706-r1/
and workloads/generated/specint-505-speedstack-no-tlbfillstats-qemu-20260706-r1/
both reached 21B bounded instructions with effectively identical TB,
MMU-cache, TLBI, and frame counters. Do not spend a speed loop optimizing
TLB-fill stats overhead for 505 unless a new matched control shows a real
instruction-count delta; keep the next 505 work on frame helper exits,
generic soft-MMU lookup/probe cost, TB dispatch, and page-walk cache shape.
- For SPEC rows or post-start profiles where
helper_linx_tlb_iv is hot, add
LINX_QEMU_TLB_INV_HOT=1 or the SPEC runner switch --qemu-tlb-inv-hot
alongside --qemu-tlb-stats before changing QEMU cputlb behavior. QEMU emits
LINX_TLB_INV_HOT heartbeat companion lines keyed by invalidation op and
source PC, with last BPC/operand/page/ACR plus per-heartbeat deltas. The SPEC
runner records heartbeat_tlb_inv_hot and prints compact tlbi-hot= tags.
If the hot source is in Linx Linux update_mmu_cache_range() or
ptep_set_wrprotect(), route the next speed loop to Linux flush frequency or
batching before retrying QEMU MMU-index narrowing.
- For SPEC frame-template attribution, use
LINX_QEMU_FRAME_STATS=1 or the
SPEC runner's --qemu-frame-stats before enabling full LINX_FENTRY_TRACE
/ LINX_FRET_STK_TRACE. QEMU appends fr_ counters to LINX_HEARTBEAT,
and the SPEC runner records them under heartbeat_frame_stats. Use this to
rule frame fallback stores or return-cache misses in/out before reopening
frame-store, restore-load, or BSTART return-target experiments.
- When aggregate
fr_ counters show frame-template traffic but do not identify
the hot shape, add LINX_QEMU_FRAME_SHAPE_HOT=1 or the SPEC runner switch
--qemu-frame-shape-hot. QEMU emits LINX_FRAME_SHAPE_HOT companion lines
with the hottest frame kind, register range, register count, stack size,
count/delta, and eviction pressure; the SPEC runners record
heartbeat_frame_shape_hot and matrix markdown prints frame-hot= tags. On
2026-07-05, focused 541.leela_r train showed the dominant shape was a
one-register stack-32 FENTRY/FRET.STK pair, so future template-helper
speed prototypes should specialize concrete shapes like that before adding
broader frame-helper machinery.
- For one-register frame-template speed experiments, use
LINX_QEMU_FRAME_SINGLE_REG_FAST=1 or the SPEC runner switch
--qemu-frame-single-reg-fast. The path is default-off, applies only to
valid one-register FENTRY/FRET.STK shapes, and keeps frame trace modes on
the generic implementation. Pair it with --qemu-frame-stats so
fr_single_fast_fentry and fr_single_fast_fret_stk prove usage. On
2026-07-05, focused 541.leela_r train improved from 5500000007 to
5900000001 bounded instructions in the fine-heartbeat 45s shape while
strict 999.specrand_ir and call/ret contract gates passed. The same day,
clean-build train-all evidence under
workloads/generated/specint-train-all-frame-single-fast-clean-qemu-20260705-r1/
kept 999.specrand_ir passing and all other rows live-progressing, but the
timeout-normalized result was mixed: 520, 523, 525, and 541 improved,
531 was roughly flat, and 500, 505, and 557 regressed versus the
prior clean ledger. Keep it opt-in/default-off and use it only for focused
one-register frame-shape experiments when used by itself. A later same-head
180-second train-all run that stacked --qemu-frame-single-reg-fast on top
of --qemu-mmu-cache --qemu-mmu-cache-stats improved six measured rows
versus MMU-cache-only (500, 502, 505, 525, 541, and 557) and left
520, 523, and 531 flat while preserving the strict
999.specrand_ir train hash
(workloads/generated/specint-train-all-mmuc-single-fast-latest-qemu-20260705-r1/).
Treat that combined stack as the current best bounded all-row speed probe,
but keep it opt-in/default-off until at least one real SPEC row completes
with correct output. Feature-compatible post-start profiles for that stack
under
workloads/generated/specint-profile-suite-train-mmuc-single-fast-latest-qemu-20260705-r1/
split the next speed lanes into template/TB/MMU dispatch for 500, 502,
505, 520, 523, and 541; Linux TLBI source reduction for 531 and
557; and separate 9p/kernel transport profiling for 525.
- Never promote
--qemu-speed-stack without a same-manifest, default-off
999.specrand_ir train A/B first. On 2026-07-17 the default-off sentinel
completed with its strict hash in 108.581 seconds, while the speed-stack
lane timed out at 180 seconds; the same speed-stack run timed out all ten
train rows before hash checking. For timeout diagnosis, also set a nonzero
--qemu-heartbeat-interval: enabling extended heartbeat fields alone does
not emit LINX_HEARTBEAT, so running and site-progress remain
unobservable and must not be reported as proof of either liveness or stall.
- For SPEC frame restore-load experiments, keep
LINX_QEMU_FRAME_RESTORE_HOST_LOAD=1 / LINX_FRAME_RESTORE_HOST_LOAD=1
opt-in and normally drive it through the SPEC runner's
--qemu-frame-restore-host-load or
LINX_SPEC_QEMU_FRAME_RESTORE_HOST_LOAD=1. The fast path uses a
same-page, non-faulting host-pointer hit probe for restore slots and falls
back to the existing faulting load on misses. Use it only with
--qemu-frame-stats so fr_restore_host and fr_restore_fallback prove
whether the run actually used the path. As of 2026-07-04 it improved a
focused 531.deepsjeng_r train sample but did not close the all-row train
gate and does not replace the data-memory/TLB lane for 505. The 2026-07-06
same-head train-all comparison on QEMU head
4e9c0fcf35e80216ae46e407d97118ecd721618a rejected broad promotion:
--qemu-frame-restore-host-load preserved the strict 999.specrand_ir
sentinel and improved bounded counts for 502.gcc_r, 525.x264_r, and
557.xz_r, but regressed 500.perlbench_r, 505.mcf_r,
520.omnetpp_r, 523.xalancbmk_r, and 531.deepsjeng_r, with 541.leela_r
neutral. Keep it default-off for all-row gates; use it as a focused A/B
switch on rows where restore-side frame traffic is the current hypothesis.
- For SPEC frame-template dispatch experiments, keep
LINX_QEMU_TEMPLATE_CHAIN=1 opt-in until real SPEC rows complete correctly
under the speed stack. The 2026-07-04 focused 523.xalancbmk_r probe with
LINX_QEMU_MMU_CACHE=1 improved the 120-second count from 16B to 22B
instructions while preserving call/ret and 999 sentinels. The later
same-head 180-second train-all run stacked template chaining on
--qemu-mmu-cache, --qemu-mmu-cache-stats,
--qemu-frame-single-reg-fast, and --qemu-frame-stats, preserved the
strict 999.specrand_ir train hash, and
improved all nine measured train rows versus the same stack without template
chaining: 500 +18.00%, 502 +64.29%, 505 +27.08%, 520 +83.33%,
523 +69.57%, 525 +96.43%, 531 +31.25%, 541 +80.95%, and
557 +72.50%
(workloads/generated/specint-train-all-mmuc-single-fast-template-chain-latest-qemu-20260705-r1/).
Drive new fast-gate runs with --template-chain instead of relying only on
ambient env; the comparison/analyzer tools also infer older env-only runs
from linked stage-row qemu_debug_env. Treat this full stack as the current
best bounded all-row speed probe, but keep it default-off until at least one
real SPEC row completes with correct output. A feature-compatible post-start
profile for that full stack exists under
workloads/generated/specint-profile-suite-train-mmuc-single-fast-template-chain-latest-qemu-20260705-r1/
with the joined lane report under
workloads/generated/specint-qemu-progress-analysis-mmuc-single-fast-template-chain-latest-20260705-r1/.
Do not repeat that profile before the next implementation change. The lane
split is still template/TB/MMU dispatch for 500, 502, 505, 520,
523, and 541; Linux TLBI attribution for 531 and 557; and a separate
9p row for 525, whose latest active frames are also template/TB/MMU-heavy.
The next QEMU speed loop should target template return/entry helper exits,
TB lookup/dispatch, and soft-MMU lookup/probe for the six shared rows first,
then rerun the all-row gate with 999.specrand_ir as the strict sentinel.
- For SPEC TCG dispatch/cache-pressure attribution, use
LINX_QEMU_TB_STATS=1 or the SPEC runner's --qemu-tb-stats before changing
TCG tb-size, cache policy, or dispatch behavior. QEMU appends tbs_
counters to LINX_HEARTBEAT, and the SPEC runner records them under
heartbeat_tb_stats. Use this after host profiles implicate
cpu_tb_exec, pthread_jit_write_protect_np, or TB lookup/codegen pressure.
When aggregate counters show high lookup/dispatch pressure but do not identify
a source PC, add LINX_QEMU_TB_HOT=1 or the SPEC runner switch
--qemu-tb-hot on a focused row. QEMU emits LINX_TB_HOT companion lines
with a 256-slot hot-PC sketch; the SPEC runners record
heartbeat_tb_hot and matrix markdown prints compact tb-hot= tags with
lookup delta, total lookup count, jump-cache hits, hash hits, misses, and
eviction pressure. On 2026-07-06, focused 502.gcc_r train evidence in
workloads/generated/specint-502-tb-hot-256-qemu-20260706-r1/ identified
0xffffffff8006dbca as the all-run max-delta TB lookup PC
(125865/125865, all jump-cache hits) while the row stayed heartbeat-live.
That PC appeared before LINX_SPEC_START and resolves to
printk_ringbuffer.c; quiet/loglevel bootargs were neutral. When
LINX_SPEC_START is present, prefer heartbeat_tb_hot.post_start_* fields
or matrix tb-hot=post: markdown for benchmark-phase attribution. Symbolize
post-start user PCs against the matching benchmark ELF before changing QEMU
cache policy; current runner output records this under
heartbeat_tb_hot_user_symbols / heartbeat_tb_hot_user_symbol_evidence
for no-ASLR Linx static-PIE rows. The same 502 evidence maps the benchmark
hot PC to 0x403ec3aa=gimple_code gimple.c:0, with nearby gimplify.c and
tree-inline.c blocks, so route 502-style evidence to user tiny-helper
dispatch/template-entry-return/TB chaining work. Route all-run boot-phase
kernel PCs through the Linux TLBI/timer/transport lane first.
The 2026-07-03 focused 505.mcf_r probe had tbs_flush=0, stable
miss/generation counts, and only about 36 MiB of roughly 1 GiB code-buffer
use, so larger TB cache was rejected; route similar evidence toward per-TB
dispatch/JIT transition and soft-MMU lookup work instead. A simple
per-thread TLS guard around qemu_thread_jit_execute() /
qemu_thread_jit_write() was also rejected on 2026-07-03: it preserved 999
correctness but lowered the comparable no-stats 505 count to about 30B
instructions in 120s, below the clean 34B baseline.
- If a post-
LINX_SPEC_START SPEC profile shows helper_linx_tlb_iv hot,
first quantify Linux-side local_flush_tlb_page() sources before changing
QEMU cputlb semantics. In the 2026-07-04 531.deepsjeng_r profile,
helper_linx_tlb_iv was the second largest active QEMU frame after
probe_access_internal; a naive uncommitted experiment that narrowed
TLB.IV/TLB.IAV to an address-derived QEMU MMU-index map preserved the
999 sentinel but was neutral on focused 531. Do not retry address-derived
MMU-index narrowing without new evidence; inspect Linx Linux
update_mmu_cache_range, ptep_set_wrprotect, and fault/vmalloc
local_flush_tlb_page() call volume first.
- For host-side SPEC profiling, prefer
tools/spec2017/profile_qemu_after_spec_start.py over manual pgrep or
parent-process sampling. The wrapper waits for LINX_SPEC_START in the
generated qemu.log, then samples only a descendant whose executable
basename is qemu-system-linx64, avoiding false samples of Python runner
commands that merely contain a --qemu argument.
- Keep trace scope minimal: shortest repro, narrowest event set, shortest instruction window, and one lane at a time.
- Treat QEMU traces as disposable debugging artifacts. Remove large trace/log outputs immediately after extracting the needed evidence.
- Clean temporary QEMU traces from
/tmp, /private/tmp, and repo-local output trees before closeout when they are no longer needed.
Collaboration rules
- When QEMU diverges from LinxCore/pyCircuit traces, act as reference owner and publish first mismatch evidence.
- Keep timer IRQ policy explicit in strict runs (
LINX_EMU_DISABLE_TIMER_IRQ=0 by default).
- Coordinate trap/MMU semantic updates with
linx-isa before declaring closure.
- If a QEMU AVS case is blocked by current compiler asm-surface compatibility
rather than emulator semantics, mark or skip that case explicitly in the AVS
harness instead of counting it as a QEMU execution failure.
Skill evolve loop (mandatory closeout)
- At closeout, decide
skill-evolve: update or skill-evolve: no-update.
- Update this skill only for material reusable findings:
- new emulator semantic rule that must stay aligned with LinxArch,
- new required runtime gate/command/env for reproducibility,
- new recurring first-divergence triage sequence.
- Do not update for minor optimization, wording cleanup, or one-off local workaround.
- If update is needed, keep scope narrow and validate with:
python3 /Users/zhoubot/.codex/skills/.system/skill-creator/scripts/quick_validate.py /Users/zhoubot/linx-isa/skills/linx-skills/linx-qemu
python3 /Users/zhoubot/linx-isa/skills/linx-skills/scripts/check_skill_change_scope.py --repo-root /Users/zhoubot/linx-isa/skills/linx-skills --base origin/main
References
references/runtime_gates.md