On Tue, Aug 25, 2026 at 6:52 PM Richard Henderson
<[email protected]> wrote:
>
> On 8/22/26 12:08, Matt Turner wrote:
> > RFC because:
> >
> > - Only wired up for x86_64, and only for qemu_ld and qemu_st; the i128
> >    qemu_ld2 and qemu_st2 pairs are left alone.
> > - Requiring that no slow path exists is stricter than necessary. Recording
> >    the displacement in TCGLabelQemuLdst and emitting one lea on the slow
> >    path would cover alignment-checked accesses too, at no fast path cost.
> > - Softmmu wants the displacement folded into the TLB comparison as well,
> >    which is a bigger change than this one.
> > - A one op window catches everything the frontends emit today but is
> >    trivially defeated by anything scheduled in between.
>
> Plausible.  Several times I've considered exposing complex addressing
> modes to the translators so that things that do LEA are kept intact for
> awhile.  There are plenty of host-specific code sequences for x << s + y
> + c, even before we fold that into the memory access.  Then expose the
> host memory path to tcg ops, somehow, and finally implement a simple CSE
> pass.  But yeah, hand waving is as far as I've ever gone.

That's the shape I kept wanting. A one-op peephole is a poor
substitute, and it loses the moment anything is scheduled between the
add and the access. I went this way because it needed no frontend
changes; happy to look at the op instead if you'd rather.

> On the second point, I guess you also assuming the offset is also
> aligned?  I.e. for X + 8*N, you can test X for 8-byte alignment without
> constructing the complete address.

Right, and that's what makes the second bullet worth doing. If disp is
a multiple of the required alignment, X + disp is aligned exactly when
X is, so the fast path test can stay on the base and never needs the
full address. That covers every displacement a frontend emits for a
struct or stack access.

What's left is the slow path, which hands addr_reg to the helper --
and addr_reg is now the base. Recording the displacement in
TCGLabelQemuLdst and emitting one lea there fixes it at no fast path
cost. v4 spells that out in the bullet but still refuses any access
needing a test.

> > +static bool tcg_target_ldst_disp_ok(TCGContext *s, MemOpIdx oi, int64_t 
> > disp)
> > +{
> > +#ifdef CONFIG_USER_ONLY
> > +    MemOp opc = get_memop(oi);
> > +    TCGAtomAlign aa;
> > +    int64_t ofs;
> > +
> > +    if (tcg_use_softmmu || s->addr_type != TCG_TYPE_I64) {
> > +        return false;
> > +    }
> > +    aa = atom_and_align_for_opc(s, opc, MO_ATOM_WITHIN16,
> > +                                (opc & MO_SIZE) == MO_128);
> > +    if (aa.align) {
> > +        return false;
> > +    }
> > +    ofs = (int64_t)x86_guest_base.ofs + disp;
> > +    return ofs == (int32_t)ofs;
> > +#else
> > +    return false;
> > +#endif
> > +}
> These compilation mode tests belong...
>
> > +static void __attribute__((noinline))
> > +fold_ldst_disp(TCGContext *s)
> > +{
> > +    TCGOp *op;
> > +
> > +    if (!TCG_TARGET_HAS_ldst_disp) {
> > +        return;
> > +    }
> ... here, before we step over the loop.  You might as well pass MemOp to
> the target function and not MemOpIdx -- nothing about the mmu_idx is
> relevant.
>
> Ideally, the atom_and_align test would also be done generically, not
> requiring each target to replicate that boilerplate.

All three done. x86_64 is now just:

    ofs = (int64_t)x86_guest_base.ofs + disp;
    return ofs == (int32_t)ofs;

The alignment one needed a compromise. atom_and_align_for_opc() takes
host_atom and allow_two_ops and both feed aa.align, so a generic
caller either gets them from the target -- the same boilerplate, moved
to tcg-target.h -- or answers without them. I answered without them: a
small ldst_disp_needs_align() that gives the answer for the most
restrictive
host.

Exact for MO_ATOM_NONE and the IFALIGN cases, i.e. everything
frontends emit by default, so the alpha numbers don't move.
Conservative for MO_ATOM_WITHIN16 and MO_ATOM_SUBALIGN: x86 could take
those folds and no longer does. One #define of the host atomicity next
to TCG_TARGET_HAS_ldst_disp gets that back if you'd rather have it
exact.

Reply via email to