RFD: HotSpot CONSTANT_REGISTER_DECLARATION and extern const

Vladimir Kozlov vladimir.kozlov at oracle.com
Mon Jun 21 19:31:08 UTC 2021


Hi Andrew,

I think your suggestion is reasonable. I just did your experiment on linux-x64 and got:

22657816 Jun 21 12:27 libjvm.so

vs before change:

22745936 Jun 21 12:09 libjvm.so

HotSpot library size is decreased with change.


Thanks,
Vladimir K

On 6/21/21 10:01 AM, Andrew Haley wrote:
> I've been looking at the quality of the code GCC generates for
> HotSpot's assembler, and the code now is way suboptimal. There's a
> number of reasons for that, but one of the most important is the way
> that Registers are defined.
> 
> TL/DR: register definitions in HotSpot are declared as "extern const"
> for ancient-historical reasons. We should stop doing that: it would
> make the assembler significantly faster and smaller, improving both
> bootstrap time and compilation speed.
> 
> register.hpp contains these definitions:
> 
>    #define CONSTANT_REGISTER_DECLARATION(type, name, value)        \
>    extern const type name;                                         \
>    enum { name##_##type##EnumValue = (value) };
> 
>    #define REGISTER_DEFINITION(type, name)                 \
>    const type name = ((type)name##_##type##EnumValue)
> 
> a register declaration like this
> 
>    CONSTANT_REGISTER_DECLARATION(Register, r0,    (0));
> 
> expands to this in the header file:
> 
>    extern const Register r0; \
>    enum { r0_RegisterEnumValue = ((0)) };;
> 
> and this in the register_aarch64.cpp file:
> 
>    const Register r0 = ((Register)r0_RegisterEnumValue);
> 
> So, the constants which define each register appear only in one
> compilation unit (register_aarch64.o) and every user loads the
> constants from there.
> 
> The result (at least on AArch64) is tragic. Every reference to r0 has
> to do a load from the slot in memory that contains the constant r0,
> and what's worse all of those loads have to go via the GOT.
> 
> So the simple constant r0 requires two loads from memory, once to get
> the GOT entry and once to load the constant. This results in GCC
> generating awful code, 142 instructions (!) to generate
> 
>    add(r0, r0, zr);
> 
> (A lot of this generated code is because on AArch64 we check that every
> operand is in range, even in product builds. This has saved our
> backsides several times now, so it's not something I want to change.)
> 
> If I change the way register definitions are done to the obvious
> 
>    #define CONSTANT_REGISTER_DECLARATION(type, name, value)        \
>    const type name = ((type)value)
> 
>    #define REGISTER_DEFINITION(type, name)
> 
> I get this for  add(r0, r0, zr)  :
> 
>      ldr     x1, [x0, #8]
>      mov     w2, #0x8b1f0000
>      ldr     x0, [x1, #16]
>      str     w2, [x0], #4
>      str     x0, [x1, #16]
>      ret
> 
> which is near-enough optimal. GCC can do constant- and copy-
> propagation to remove all the range checks for the operands.
> 
> A comment in register.hpp explains why all this "extern const" stuff
> was done:
> 
> // We'd like to be able to simply define const instances of the
> // RegisterImpl* for each of the registers needed on a system in a
> // header file.  However many compilers don't handle this very well
> // and end up producing a private definition in every file which
> // includes the header file.  Along with the static constructors
> // necessary for initialization it can consume a significant amount of
> // space in the result library.
> 
> I believe that this is probably ancient history, way back when in
> early HotSpot days, and we probably don't need to do it any more. I
> could simply fix this in the AArch64 back end after checking that it
> works on Windows, which it almost certainly does: it's fine on Linux
> and MacOS, and we don't care about ancient compilers any more because
> we've moved to C++14.
> 
> Getting rid of "extern const" is better on x86 too, although the
> improvement isn't as dramatic as on AArch64, reducing 20 instructions
> to generate
> 
>     addl(rdx, 0);
> 
> to 8:
> 
>        mov    0x8(%rdi),%rdx
>        mov    $0xffffc283,%ecx
>        mov    0x10(%rdx),%rax
>        mov    %cx,(%rax)
>        add    $0x3,%rax
>        movb   $0x0,-0x1(%rax)
>        mov    %rax,0x10(%rdx)
>        retq
> 
> So, can we just stop using "extern const"? At least on Linux?
> 
> Thanks for reading this far,
> 
> --
> Andrew Haley  (he/him)
> Java Platform Lead Engineer
> Red Hat UK Ltd. <https://www.redhat.com>
> https://keybase.io/andrewhaley
> EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
> 


More information about the hotspot-compiler-dev mailing list