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