On 1/9/26 9:42 AM, Paolo Bonzini wrote:


Il ven 9 gen 2026, 17:21 Pierrick Bouvier <[email protected] <mailto:[email protected]>> ha scritto:

     > For them, the death of target_long/target_ulong is not really
    possible,
     > because they will have to reinvent include/exec/target_long.h for
    their
     > CPUStates.
     >

    At this time, I don't have a simple solution to provide to workaround
    this. As long as compilation units are duplicated, you will have
    duplicated symbols for any extern symbol, thus preventing to link the
    final qemu-system binary. So duplication has to be eliminated, one way
    or another. And multiple type definitions is a barrier for this.


Yes, the idea is that if the "single binary" will include both i386 and x86_64 targets, they will both use the TARGET_LONG_BITS==64 version (using it also for the 32-bit target) of CPUState, of the TCG frontend and helpers, etc.

IOW the single binary could build a third copy of target/i386, or reuse the x86_64-softmmu one.

    By making all files for an architecture "common", TARGET_LONG_BITS is
    eliminated by design, since it's a poisoned identifier.


Good point.

     > include/exec/target_long32.h
     > ----------------------------
     > #ifndef TARGET_LONG_BITS
     > #define TARGET_LONG_BITS 32
     > #endif
     > #define TARGET_ADDRESS_BITS 32
     > #define TARGET_LONG_SIZE 4
     > typedef int32_t target_long;
     > typedef uint32_t target_ulong;
     > #define TARGET_FMT_lx "%08x"
     > #define TARGET_FMT_ld "%d"
     > #define TARGET_FMT_lu "%u"
     > #define MO_TL MO_32
     >
     > include/exec/target_long64.h
     > ----------------------------
     > #ifndef TARGET_LONG_BITS
     > #define TARGET_LONG_BITS 64
     > #endif
     > #define TARGET_ADDRESS_BITS 64
     > #define TARGET_LONG_SIZE 8
     > typedef int64_t target_long;
     > typedef uint64_t target_ulong;
     > #define TARGET_FMT_lx "%016" PRIx64
     > #define TARGET_FMT_ld "%" PRId64
     > #define TARGET_FMT_lu "%" PRIu64
     > #define MO_TL MO_64
     >
     > ... and use them in include/exec/target_long.h:
     >
     > include/exec/target_long.h:
     > #ifndef TARGET_LONG_BITS
     > #error TARGET_LONG_BITS not defined
     > #elif TARGET_LONG_BITS == 32
     > #include "exec/target_long32.h"
     > #elif TARGET_LONG_BITS == 64
     > #include "exec/target_long64.h"
     > #endif
     >
     > Then the single-size targets can replace TARGET_LONG_BITS with:
     > - a "#define TCG_ADDRESS_BITS" in their translate.c
     > - a #include "exec/target_longNN.h" in their cpu.h.
     >
     > Dual-size targets, instead, can add to their cpu.h an initial stanza
     > like this:
     >
     > #ifdef TARGET_I386
     > #include "exec/target_long32.h"
     > #else
     > #include "exec/target_long64.h" // x86_64 or single binary
     > #endif
     >
     > and keep using target_long.
     >

    I'm not sure what we gain from this header mechanics, wouldn't that be
    better to eradicate TARGET_LONG_BITS completely instead?


The problem is that dropping target_long in CPUState would be inefficient. For example i386 registers occupy 32 bytes vs. 256 for x86_64. So I would like to keep 32-bit registers for the 32-bit single- target binary.


I agree, and that's why the current solution is not the final word on this question. My position on the single binary is that runtime compromises can be acceptable on our translation path (since it's not where we spend most of our time anyway), but definitely not on code generated by tcg, which has to be optimal.

In the first version of this series, I defined TCGv as an opaque type (i.e. not typedef to i32 or i64), and wrote wrappers for tcg_gen_* ops that were casting it to appropriate type and called the i32 or i64 variants based on current context. After talking with Richard, I understood it was not useful for target/arm, since code has been cleanly splitted between 32/64 bits, so dropped it for the TCG_ADDRESS_BITS approach.

That said, I still think the opaque type + wrapper approach has its place for some architectures. It could be used for code where rewriting is too complicated, and still allow to generate efficient code. The downside is that we need some boilerplate in headers to generate this, but it's not something a macro can't help to deal with.

Compared to the current mechanism, it decouples the choice of TARGET_LONG_BITS from configs/targets/ and makes it possible for target/ */ to pick its preferred length when built for the single binary.

But anyway this was just a brain dump—we are in sync for what is needed for this series.

    With Philippe, we introduced target-info.h, to precisely find this
    information at runtime, with target_long_bits().
    As well, as you can see in codebase, target_long_bits() is not used in
    many places, and especially, it's not needed anywhere in target/arm. So
    it does not seem needed to keep it alive.


I agree that target_long_bits() should be needed almost nowhere (maybe it's needed for VMSTATE_UINTTL migration but not much else) because ideally all use of target_long/ulong would really be confined to target/ and not be in common code.

It could be called like an x86_ulong, but it would have to be redone almost the same across all the dual-size targets, hence it's easier to keep the current name and provide a common mechanism.

Thanks,

Paolo

    Thanks for the feedback,
    Pierrick



Reply via email to