...
Improve the implementation of JVM command-line flags (such as -XX:+UseCompressedOops)
Break up monolithic globals.hpp
- Before:
- #include "runtime/globals.hpp" + globals_shared.hpp = declare all possible flags
- After:
- #include "gc/z/z_globals.hpp" (see also gc/z/z_globals.flags.hpp)
- Future: subdivide globals.hpp into individual components:
- (Not in this patch, but possible)
- #include "memory/metaspaceShared_globals.hpp" (just flags related to CDS).
- Remove complex macros for flags manipulation
- Hotspot command line switches should have multiple type attributes
- See JDK-7123237
E.g., one can have a "manageable" switch and an "experimental" switch, but not a "manageable_experimental" switch.
- See JDK-7123237
- Templatize duplicated flag processing code:
- E.g., .(link to attachment)jvmFlags.cpp
- Speed up range/constraint checking for flags
- Avoid linear search for every -XX:NumericFlag=value argument in the command-line (jvmFlagRangeList.cpp)
...
- Most of the current patch is generated by a script, so it's easy to change the design
- Summary of changes: 14748 14333 lines changed: 6837 6443 ins; 6204 5024 del; 1707 2866 mod; 33867 unchg
Before
- 34730 unchg
Before
- Flags are specified via macros like
product()
,develop()
,diagnostic()
, etc.
Code Block |
---|
product(intx, MaxLoopPad, (OptoLoopAlignment-1), |
...
\
"Align a loop if padding size in bytes is less or equal to this " \
"value") \
range(0, max_jint) \
\
develop(intx, OptoPrologueNops, 0, \
"Insert this many extra nop instructions " \
"in the prologue of every nmethod") \
range(0, 128) \
\
notproduct(bool, VerifyGraphEdges , false, \
"Verify Bi-directional Edges") \
\
product_pd(intx, InteriorEntryAlignment, \
"Code alignment for interior entry points " \
"in generated code (in bytes)") \
constraint(InteriorEntryAlignmentConstraintFunc, AfterErgo) \
\
diagnostic(bool, StressLCM, false, \
"Randomize instruction scheduling in LCM") \ |
After
- Flags are specified using a pair of files xxx_globals.hpp, xxx_globals.cpp
- The HPP file is the "declaration" of the flags. It has a description of all the features for each flag.
HotSpot developers should consult the HPP for information about the flags (same as before).
- Example: c2_globals.hpp:
- Syntax of the
XXX_FLAG
macros: see the top of globals.hpp. - Implementation of these macros: see jvmFlags.hpp (around line 537)
- Syntax of the
Code Block |
---|
// PRODUCT_FLAG -- always settable
// DEVELOP_FLAG -- settable only during development and are constant in the PRODUCT version
// NOTPROD_FLAG -- settable only during development and are *not* declared in the PRODUCT version
PRODUCT_FLAG(intx, MaxLoopPad, (OptoLoopAlignment-1), JVMFlag::RANGE,
"Align a loop if padding size in bytes is less or equal to this "
"value");
FLAG_RANGE( MaxLoopPad, 0, max_jint);
DEVELOP_FLAG(intx, OptoPrologueNops, 0, JVMFlag::RANGE,
"Insert this many extra nop instructions "
"in the prologue of every nmethod");
FLAG_RANGE( OptoPrologueNops, 0, 128);
NOTPROD_FLAG(bool, VerifyGraphEdges, false, JVMFlag::DEFAULT,
"Verify Bi-directional Edges");
PRODUCT_FLAG_PD(intx, InteriorEntryAlignment, JVMFlag::CONSTRAINT,
"Code alignment for interior entry points "
"in generated code (in bytes)");
FLAG_CONSTRAINT( InteriorEntryAlignment, (void*)InteriorEntryAlignmentConstraintFunc, JVMFlag::AfterErgo);
// NOTE: diagnostic macros are specified by setting the JVMFlag::DIAGNOSTIC bit in the
// flag's attr (see globals.hpp for details).
//
// This allows more flexible specification of attributes. E.g., manageable-experimental flags
// in JDK-7123237 can be specified using JVMFlag::MANAGEABLE | JVMFlag::EXPERIMENTAL,
PRODUCT_FLAG(bool, StressLCM, false, JVMFlag::DIAGNOSTIC,
"Randomize instruction scheduling in LCM"); |
Macros in the HPP files are expanded to
Code Block |
---|
// The is the main API of this flag for HotSpot code. E.g.,
// if (StressLCM) {.....}
extern "C" bool StressLCM;
// This structure has the meta-information about this flag. E.g., HotSpot code can use it
// to dynamically query the type of this flag.
extern ProductFlag<FLAG_TYPE_StressLCM> FLAG_StressLCM;
// The following inline functions pass meta-information about this flag to the CPP file
inline bool FLAG_DEFVAL_StressLCM() { return false; }
typedef bool FLAG_TYPE_StressLCM;
inline JVMFlag::FlagType FLAG_TYPE_NAME_StressLCM() { return JVMFlag::TYPE_bool; }
inline int FLAG_ATTR_StressLCM() { return JVMFlag::DIAGNOSTIC; }
inline const char* FLAG_DOCS_StressLCM() { return "Randomize instruction scheduling in LCM"; };
<... the expansion of some macros are omitted for clarity>
// Range information are expanded like this:
inline int FLAG_ATTR_MaxLoopPad() { return JVMFlag::RANGE; }
inline FLAG_TYPE_MaxLoopPad FLAG_MIN_MaxLoopPad() { return 0; }
inline FLAG_TYPE_MaxLoopPad FLAG_MAX_MaxLoopPad() { return max_jint; } |
The main goal of the inline functions is to avoid duplicating the information between the HPP and CPP files, so the CPP files contains just boilerplate code:
Code Block |
---|
DEFN_PRODUCT_FLAG(StressLCM);
DEFN_PRODUCT_FLAG(StressGCM);
DEFN_PRODUCT_FLAG(MaxLoopPad); DEFN_PRODUCT_RANGE(MaxLoopPad);
DEFN_PRODUCT_FLAG_PD(InteriorEntryAlignment); DEFN_PRODUCT_CONSTRAINT(InteriorEntryAlignment); |
Macros in the CPP files are expanded to
Code Block |
---|
FLAG_TYPE_StressLCM StressLCM = FLAG_DEFVAL_StressLCM();
ProductFlag<FLAG_TYPE_StressLCM> FLAG_StressLCM(
FLAG_TYPE_NAME_StressLCM(),
"StressLCM",
(FLAG_ATTR_StressLCM() | JVMFlag::C2),
&StressLCM, FLAG_DOCS_StressLCM());
// Range check is implemented like this
JVMFlagRange<FLAG_TYPE_MaxLoopPad> FLAG_RANGE_MaxLoopPad(
&FLAG_MaxLoopPad, FLAG_MIN_MaxLoopPad(), FLAG_MAX_MaxLoopPad());
// Inside the constructor of FLAG_RANGE_MaxLoopPad, it essentially registers
// itself as the range spec for FLAG_MaxLoopPad,
FLAG_MaxLoopPad._range = &FLAG_RANGE_MaxLoopPad; |
The consistency between the .hpp/.cpp files are mostly statically enforced at compile time. E.g.,
- It's impossible to set a different default or range than as specified in the HPP file.
- It's impossible to define StressLCM as a different type than declared in the HPP file.
However, it's possible to forget to put DEFN_PRODUCT_RANGE(MaxLoopPad)
in c2_globals.cpp. I can't think of a way to statically check for that, so I added a runtime check (debug builds only). See JVMFlag::validate_flags().
Meta-information of the flags
As seen above, meta-information about each flag XYZ
is stored in the structure FLAG_XYZ
. This structure can be used directly to access the flag. E.g.,
Code Block |
---|
intx new_val = .....;
FLAG_MaxLoopPad.check_range(&new_val, verbose); |
As a result, we no longer need to do a linear search for range/constraint checking.
Iterating over all flags:
The FLAG_XXXs are constructed using C++ global constructors, and are appended to a global list. This list can be used for iterating over all the flags (e.g., for implementing -XX:+PrintFlagsFinal
).
Code Block |
---|
struct JVMFlag {
static JVMFlag* _head;
JVMFlag* _next;
...
};
template <typename T>
class ProductFlag : /* a subclass of JVMFlag */ {
ProductFlagNone(...) {
/*effectively*/
this->_next = _head; // executed as C++ global constructor
_head = this;
}};
#define JVMFLAG_FOR_EACH(f) \
for (f = JVMFlag::head(); f != NULL; f = |
Declaration Example: c2_globals.hpp (macros such as PRODUCT_FLAG are defined in jvmFlag.hpp)
|
Macros are expanded to
|
Definition Example: c2_globals.cpp (macros such as DEFN_PRODUCT_FLAG are defined jvmFlag.inline.hpp)
|
Macros are expanded to
|
Iterating over all flags:
...
f->next()) |
Alternatives
- Declare flags in template files with custom syntax
flag type = bool, name = UseCompressedOops, default = false, help = "Use 32-bit object references in 64-bit VM.", type = product | lp64;
Or XML, or ....
Auto-generation of header files from templates
- Pros:
- Can generate more compact code that current proposal (see below)
- Flags can be pre-sorted, hashed, etc
- Cons:
- More complex build (nowhere as bad as .ad files, but affects all developers, not just JIT compiler geeks)
- Not understood by IDE tools