This document describes ....
Goals
- Reduce the complexity of macros related to command-line flags (JDK-8243209)
- Avoid including large globals.hpp in every file. Just include the <mod>_globals.hpp file of the module you need (JDK-8243205)
- Improvement footprint/performance
Requirements for Command-line Flags
The flags implementation in HotSpot has evolved over 20 years without much documentation.
By reading the JDK 15 code, I can gather the following requirements – which lead to the complex implementation.
(These requirements cannot be implemented elegantly/efficiently using older C++ compilers, leading to the current messy code)
- [REQ1] Flags have 3 types
- PRODUCT - readable/writable in all builds
- DEVELOP - readable in all builds, writable only in debug builds
- NOTPRODUCT - readable/writable only in debug builds
- Using a flag of this type in a product build will result in C compiler error.
- [REQ2] Flags can have optional attributes
- MANAGEABLE, DIAGNOSTIC, EXPERIMENTAL
- MANAGEABLE, DIAGNOSTIC, EXPERIMENTAL
[REQ3] Each flag can be in at most one of the following groups.
C1, C2, JVMCI, ARCH, LP64
(many flags aren't in any of these groups)
- [REQ4] Each flag must have a default value
- Platform-defined default values are specified by XXX_PD macros
- [REQ5] Flag declarations should be concise (default flag attributes shouldn't need to be specified)
- [REQ6] Metadata for flags must be compile-time generated
- Good: stored in .bss section
- Bad: initialized with global constructors
Problems with command-line flags in JDK 15
First, although types, attributes and groups are orthogonal, JDK 15 allows you to only specify a limited number of combinations. For example, all experimental flags must be of the PRODUCT type:
develop(bool, CleanChunkPoolAsync, true, \ "Clean the chunk pool asynchronously") \ experimental(bool, AlwaysSafeConstructors, false, \ "Force safe construction, as if all fields are final.") \
In a way, the [REQ5] conciseness requirement is the root cause of the messy implementation. If we add an extra "kind" parameter to the macros, we can convert the above to the following, which would allow the second flag to be both EXPERIMENTAL and DEVELOP
develop(bool, CleanChunkPoolAsync, true, /*kind=*/ 0\ "Clean the chunk pool asynchronously") \ develop(bool, AlwaysSafeConstructors, false, /*kind=*/ FLAG_KIND_EXPERIMENTAL \ "Force safe construction, as if all fields are final.") \
Similarly, groups could be implemented as something like the following (instead of the messy macros in here).
/*kind=*/ FLAG_KIND_EXPERIMENTAL|FLAG_GROUP_C1
Another way to keep the code concise is to use constructor overloading in the flags definition, something like (simplified)
#ifndef CONSTEXPR #define CONSTEXPR #endif #define FLAG_KIND_EXPERIMENTAL 1 struct Flag { const char* name; int kind; CONSTEXPR Flag(const char* n): name(n), kind(0) {} CONSTEXPR Flag(const char* n, int k): name(n), kind(k) {} }; Flag flags[] = { Flag("CleanChunkPoolAsync"), Flag("AlwaysSafeConstructors", FLAG_KIND_EXPERIMENTAL), };
However, this runs into problem with [REQ6]. Even the effect of the above code can be completely decided at compilation time, without constexpr, GCC stubbornly insists to initialize the array using global constructors
$ /home/iklam/devkit/latest/bin/gcc -O3 -o - -S test.cpp .... _GLOBAL__sub_I_flags: .LFB7: .cfi_startproc movq $.LC0, flags(%rip) movl $0, flags+8(%rip) movq $.LC1, flags+16(%rip) movl $1, flags+24(%rip) ret
Another option is to use clever macros. However, there's no vararg macro that I can think of that can satisfy all of the above macros
Proposal - Use constexpr!
C++ constexpr makes things much easier:
$ /home/iklam/devkit/latest/bin/gcc -O3 -o - -S -DCONSTEXPR=constexpr test.cpp .... flags: .quad .LC0 // name .long 0 // kind .quad .LC1 // name .long 1 // kind
New way of declaring flags - with optional argument for attributes:
develop(bool, CleanChunkPoolAsync, true, /* no attr */ \ "Clean the chunk pool asynchronously") \ \ product(uint, HandshakeTimeout, 0, /* attr= */DIAGNOSTIC, \ "If nonzero set a timeout in milliseconds for handshakes") \ \ product(bool, AlwaysSafeConstructors, false, /* attr= */ EXPERIMENTAL, \ "Force safe construction, as if all fields are final.") \
Flag declaration macros now all take only 7 arguments: (compared to old version in globals.hpp)
#define RUNTIME_FLAGS(develop, \ develop_pd, \ product, \ product_pd, \ /* REMOVED diagnostic, */\ /* REMOVED diagnostic_pd, */ \ /* REMOVED experimental, */\ notproduct, \ /* REMOVED manageable, */ \ /* REMOVED product_rw, */\ /* REMOVED lp64_product, */\ range, \ constraint) \