All of lore.kernel.org
 help / color / mirror / Atom feed
* [BUG] x86_64 guest: vpmovmskb produces wrong mask on a riscv64 host (user-mode) -- root cause = TCG_REG_TMP0 clobber in tcg/riscv tcg_out_dup{m,i}_vec (10.0.0-10.0.7, 10.1.0-10.1.3, 10.2.0)
@ 2026-09-16  9:41 a vincent163
  2026-09-16 12:33 ` Markus Armbruster
  0 siblings, 1 reply; 2+ messages in thread
From: a vincent163 @ 2026-09-16  9:41 UTC (permalink / raw)
  To: qemu-devel@nongnu.org

STATUS (please read first)
--------------------------
This defect is ALREADY FIXED upstream.  It was reported by Zhijin Zeng
(SpacemiT) and fixed by Richard Henderson on master as

  af6db3b71310ea63a018d517ba7d79e4e014db62
  "tcg/riscv: Fix TCG_REG_TMP0 clobber in tcg_gen_dup{m,i}"
  Fixes: d4be6ee1111 ("tcg/riscv: Implement vector mov/dup{m/i}")

and cherry-picked to the stable branches (10.0 backport:

  8c3c8daaadcab875d40028f88c4d31e80fc47718, first released in v10.0.8).

There is therefore nothing new to fix.  This note is (a) an independent
reproduction/root-cause confirmation of that bug, and (b) a version
coverage check, because our project is based on QEMU 10.1.0, which
predates the fix.  No duplicate GitLab issue is needed unless the
maintainers want the extra data point; see "What we would like" below.

Environment
-----------
Host:    riscv64 (RVV 1.0, VLEN=256), 8 cores, Linux 6.6.63
         CPU: "Ky(R) X1" (rv64imafdcv + zve32x/zve64x/zve64f/zve64d)
Guest:   x86_64, static, freestanding (no libc), AVX2
QEMU:    10.1.0, built from the vanilla upstream v10.1.0 tag on the
         riscv64 host:
           ./configure --target-list=x86_64-linux-user \
               --disable-werror --disable-docs --disable-guest-agent
           make -j4
         (binary: build/qemu-x86_64, user-mode emulation only)
Also tested: 8.2.2 (Ubuntu package), 10.0.13 (Debian trixie package),
         the same test source built with the 10.1.0 base plus a local
         workaround, 10.1.0+ds-5ubuntu2 and 10.2.1+ds-1ubuntu3 (could
         not be executed on this host, see "Untested" below).

Symptom
-------
An x86_64 AVX2 guest that computes a byte mask with vpmovmskb gets a
wrong result on a riscv64 host.  Only the riscv64 host backend is
affected; the same guest binary is correct on an x86_64 host and under
any host backend that does not lower vpmovmskb through RVV (e.g.
QEMU 8.2.2 / 9.x, which have no riscv vector TCG backend).

Reproducer (self-contained, no libc, no guest rootfs needed)
-----------------------------------------------------------
    /* pmovmskb-min2.c
     * Build: x86_64-linux-gnu-gcc -m64 -nostdlib -static -no-pie \
     *            -mavx2 -O1 -o pmovmskb-min2 pmovmskb-min2.c
     * Run:   ./pmovmskb-min2 | od -An -tx4
     */
    #define NR 8

    unsigned char data[32] __attribute__((aligned(32)));
    unsigned out[NR];

    static unsigned do_mask(const unsigned char *p)
    {
        unsigned m;
        __asm__ __volatile__(
            "vpxor     %%ymm0, %%ymm0, %%ymm0\n\t"
            "vpcmpeqd  (%1), %%ymm0, %%ymm1\n\t"
            "vpmovmskb %%ymm1, %0\n\t"
            "vzeroupper\n\t"
            : "=r"(m) : "r"(p) : "ymm0", "ymm1", "memory");
        return m;
    }

    void _start(void)
    {
        unsigned i, j;

        for (i = 0; i < NR; i++) {
            for (j = 0; j < 32; j++) {
                data[j] = 0;
            }
            for (j = 0; j < i; j++) {
                data[4 * j] = 0x2f;      /* dword j != 0 */
            }
            out[i] = do_mask(data);
        }

        /* write(1, out, sizeof out); exit(0) */
        __asm__ __volatile__(
            "mov $1, %%eax\n\t"
            "mov $1, %%edi\n\t"
            "lea out(%%rip), %%rsi\n\t"
            "mov $32, %%edx\n\t"
            "syscall\n\t"
            "mov $231, %%eax\n\t"
            "xor %%edi, %%edi\n\t"
            "syscall\n\t"
            ::: "rax", "rdi", "rsi", "rdx", "memory");
    }

Iteration i builds a 32-byte vector in which dwords 0..i-1 are 0x2f and
the rest are zero, then compares it against zero with vpcmpeqd (so the
higher dwords become all-ones) and extracts the 32 mask bits.

Observed vs expected (od -An -tx4)
----------------------------------
Vanilla QEMU 10.1.0 on riscv64:

  ffffffff ffffff3c ffffff00 ffff3c00 ffff0000 ff3c0000 ff000000
  3c000000

Expected (native x86_64, QEMU 8.2.2 and 10.0.13 on the same host, and
the same 10.1.0 tree with the fix/workaround applied):

  ffffffff fffffff0 ffffff00 fffff000 ffff0000 fff00000 ff000000
  f0000000

Every miscomputed byte is 0x3c where 0xf0 is expected; 0x00 and 0xff
patterns come out unchanged.  Runs are deterministic.

Root cause
----------
vpmovmskb is lowered by target/i386/tcg/emit.c.inc:gen_pmovmskb_vec() to
a per-64-bit-lane SWAR sequence:

    d = s & 0x8080808080808080;         /* one sign bit per byte */
    d |= d << 7;  d |= d << 14;  d |= d << 28;
    /* then one byte is read out of each 64-bit lane */

The constant is materialised on the host through tcg_out_dupi_vec()
(vece = MO_8, arg >> 56 == -128), which in the affected QEMU versions
does:

    tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, arg);     /* TMP0 = -128 */
    tcg_out_dup_vec(s, type, vece, dst, TCG_REG_TMP0);

and tcg_out_dup_vec() calls set_vtype_len_sew() *before* using src:

    set_vtype_len_sew(s, type, vece);      /* may write TCG_REG_TMP0 */
    tcg_out_opc_vx(s, OPC_VMV_V_X, dst, 0, src);

set_vtype() emits a vsetvl whose destination register is TCG_REG_TMP0
(see probe_frac_lmul_1(): for avl >= 32 with lmul == avl it uses
"rd != 0 and rs1 == 0 uses vlmax", i.e. vsetvli TMP0, zero, <vtype>),
so TMP0 is overwritten with the resulting vl before the splat happens.
tcg_out_dupm_vec() has the same shape (load into TMP0, then delegate).
Every fixed version (10.0.8+, 10.1.4+, 10.2.1+, all 11.x) instead calls
set_vtype*() first and only then materialises into TMP0 -- the fix
commit restores exactly that, with the comment "Note set_vtype* may
clobber TMP0, so do that first."

Host code emitted for the guest TB (qemu-x86_64 -d in_asm,op,out_asm),
affected 10.1.0 vs correct 10.0.13 -- only the ordering differs:

  10.1.0 (wrong):
    addi    t6,zero,-128          # TMP0 = 0xffffffffffffff80
    vsetvli t6,zero,e8,m1,ta,ma   # TMP0 = vl   <-- clobbers the imm
    vmv.v.x v2,t6                 # splat vl instead of -128
    vand.vv v1,v1,v2

  10.0.13 (correct):
    vsetvli t6,zero,e8,m1,ta,ma
    addi    t6,zero,-128
    vmv.v.x v2,t6
    vand.vv v1,v1,v2

On this host (V256, e8, m1) vl is 32, so the vector AND mask becomes
0x20 per byte instead of 0x80.  Measured directly on the CPU with
"vsetvli zero, zero, e8, m1, ta, ma": vl = 32 (VLEN = 256; the same
instruction with e32/m1 gives 8 and with e64/m1 gives 4, matching the
vsetivli counts in the QEMU out_asm dump).  Simulating the SWAR
sequence above with 0x20 gives, bit for bit, all eight wrong 32-bit
masks listed above (0xf0 -> 0x3c, 0x00 and 0xff unchanged); with 0x80
it reproduces the expected masks.  That closes the loop on the whole
chain.

The three vsll.vi/vor.vv pairs in the same block are identical in both
versions (same encoding 0x961e3157 etc.), so the disassembler text
"vsll.vi v2,v1,60" printed by 10.1.0 in the out_asm dump is a text
rendering difference only, not the cause (the hardware executes the
same encoding in both builds).

Version coverage (verified by running the reproducer and/or by
inspecting tcg/riscv/tcg-target.c.inc at the release tag)
---------------------------------------------------------
  8.2.2, 9.x                unaffected (no RVV TCG backend)
  10.0.0 .. 10.0.7          AFFECTED (delegating form)
  10.0.8 .. 10.0.13         fixed (contains 8c3c8daa)
  10.1.0 .. 10.1.3          AFFECTED (delegating form)
  10.1.4, 10.1.5            fixed
  10.2.0                    AFFECTED (delegating form)
  10.2.1 .. 10.2.4          fixed
  11.0.0 .. 11.1.1          fixed (contains af6db3b7 on master/stable)

Direct measurements: 10.0.13 correct, 10.1.0 wrong (both on this
riscv64 host, the 10.1.0 binary built from the upstream v10.1.0 tag).

Fix
---
  master / 11.x:  af6db3b71310ea63a018d517ba7d79e4e014db62
                  8c3c8daaadcab875d40028f88c4d31e80fc47718 (10.0 stable)
  Order of operations: call set_vtype*() first, then use TCG_REG_TMP0
  (and stops routing dupm/dupi through tcg_out_dup_vec).

What we would like
------------------
1. Confirmation that no host-side regression remains in the affected
   stable branches -- from the version table above, 10.1.0/10.1.1/
   10.1.2/10.1.3 and 10.2.0 are affected and 10.1.4/10.2.1 are the
   first fixed patch releases.  If that matches your intent, nothing
   needs to change; we simply want to be sure we are not missing a
   branch that still ships the buggy order.
2. If a GitLab issue is preferred for the record (or if you want the
   riscv64 Ky(R) X1 / VLEN=256 data point attached to the existing
   thread), say so and we will file one; we did not want to open a
   duplicate issue without asking first.
3. No patch is offered, since the upstream fix already exists; happy to
   test a backport to any branch that still needs it.

Impact on our project
---------------------
We maintain a QEMU fork (COGBT, an AOT translating accelerator) based on
QEMU 10.1.0, so we are in the AFFECTED window.  We first hit this as a
wcslen()/glibc misbehaviour in x86_64 guest binaries and chased it down
to vpmovmskb.  Our interim workaround replaces the vector SWAR with a
scalar per-byte loop in gen_PMOVMSKB (target/i386/tcg/emit.c.inc), which
makes 10.1.0 correct on riscv64 but is not the right long-term answer;
we intend to drop it and carry af6db3b7/8c3c8daa instead.

The three functions the fix touches (tcg_out_dup_vec, tcg_out_dupm_vec,
tcg_out_dupi_vec) are textually identical to vanilla 10.1.0 in our tree,
and the patch applies to it without any conflict (verified with
"patch --dry-run -p1" against our current tcg/riscv/tcg-target.c.inc),
so this is a straight cherry-pick for us.

Untested / unknowns (stated honestly)
-------------------------------------
* Two Ubuntu riscv64 packages could not be executed on this host:
  qemu-user 10.1.0+ds-5ubuntu2 and qemu-user 10.2.1+ds-1ubuntu3 abort
  with SIGILL (their ELF Tag_RISCV_arch asks for a richer vector
  extension set than this CPU advertises).  10.1.0 is in the affected
  window and 10.2.1 in the fixed window per the source inspection
  above, but we could not confirm that by execution.
* qemu-user-static 9.2.1+ds-1ubuntu5 from Ubuntu is a transitional
  package with no guest binaries, so 9.x was not measured directly; it
  predates the riscv vector TCG backend and is therefore assumed
  unaffected.
* We only exercised a riscv64 host.  Any host whose set_vtype() picks
  the "rd = TCG_REG_TMP0" vsetvl form (avl >= 32 with lmul == avl)
  should be equally affected; hosts where only the VSETIVLI form is
  used (avl < 32) may accidentally not clobber TMP0 and could mask the
  bug depending on VLEN.

Reported-by: Vincent <vincent.163@outlook.com>
Submitted on behalf of the author by the AI agent deepseek-v4.1-flash;
the reproduction, the source analysis and the version matrix above were
produced by that agent and independently confirmed by re-running the
reproducer against each QEMU build named in the report.


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [BUG] x86_64 guest: vpmovmskb produces wrong mask on a riscv64 host (user-mode) -- root cause = TCG_REG_TMP0 clobber in tcg/riscv tcg_out_dup{m,i}_vec (10.0.0-10.0.7, 10.1.0-10.1.3, 10.2.0)
  2026-09-16  9:41 [BUG] x86_64 guest: vpmovmskb produces wrong mask on a riscv64 host (user-mode) -- root cause = TCG_REG_TMP0 clobber in tcg/riscv tcg_out_dup{m,i}_vec (10.0.0-10.0.7, 10.1.0-10.1.3, 10.2.0) a vincent163
@ 2026-09-16 12:33 ` Markus Armbruster
  0 siblings, 0 replies; 2+ messages in thread
From: Markus Armbruster @ 2026-09-16 12:33 UTC (permalink / raw)
  To: a vincent163; +Cc: qemu-devel@nongnu.org

Please read docs/devel/code-provenance.rst section "Use of AI-generated
content".



^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-16 12:35 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16  9:41 [BUG] x86_64 guest: vpmovmskb produces wrong mask on a riscv64 host (user-mode) -- root cause = TCG_REG_TMP0 clobber in tcg/riscv tcg_out_dup{m,i}_vec (10.0.0-10.0.7, 10.1.0-10.1.3, 10.2.0) a vincent163
2026-09-16 12:33 ` Markus Armbruster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.