- Loading...
...
The new code design (see here for webrev):
The range/constraint information for a flag of type T is described by a JVMTypedFlagLimit<T>:
Code Block |
---|
class JVMFlagLimit {
enum {
HAS_RANGE = 1,
HAS_CONSTRAINT = 2
};
short _constraint_func;
char _phase;
char _kind; ...};
template <typename T>
class JVMTypedFlagLimit : public JVMFlagLimit {
const T _min;
const T _max; ...}; |
Each flag is given a unique enum that starts from 0 to NUM_JVMFlagsEnum-1. We use this enum to find the JVMTypedFlagLimit<T> of this flag from an array:
Code Block |
---|
static constexpr const JVMFlagLimit* const flagLimitTable[1 + NUM_JVMFlagsEnum] = { .... }
const JVMFlagLimit* const* JVMFlagLimit::flagLimits = &flagLimitTable[1]; // excludes dummy
/* E.g., to get the limit of this flag:
product(intx, ContendedPaddingWidth, 128, \
"How many bytes to pad the fields/classes marked @Contended with")\
range(0, 8192) \
constraint(ContendedPaddingWidthConstraintFunc,AfterErgo) \
*/
const JVMTypedFlagLimit<intx>* limit = JVMFlagLimit::flagLimits[Flag_ContendedPaddingWidth_Enum];
// We will see these fields:
// limit->_constraint_func ==> constraint_enum_ContendedPaddingWidthConstraintFunc (more on this below)
// limit->_phase ==> AfterErgo
// limit->_kind ==> HAS_RANGE | HAS_CONSTRAINT
// limit->_min ==> 0
// limit->_max ==> 8192 |
Most flags have neither range nor constraint. For those flags, we want its flagLimits[Flag_flagname_Enum] to be NULL.
To do this, we first define a JVMTypedFlagLimit<T> variable for each flag (including the ones that don't have range/constraint). It's done by this macro:
Code Block |
---|
// macro body starts here -------------------+
// |
// v
#define FLAG_LIMIT_DEFINE( type, name, ...) ); constexpr JVMTypedFlagLimit<type> limit_##name(0
#define FLAG_LIMIT_DEFINE_DUMMY(type, name, ...) ); constexpr DummyLimit nolimit_##name(0
#define FLAG_LIMIT_PTR( type, name, ...) ), LimitGetter<type>::get_limit(&limit_##name, 0
#define FLAG_LIMIT_PTR_NONE( type, name, ...) ), LimitGetter<type>::no_limit(0
#define APPLY_FLAG_RANGE(...) , __VA_ARGS__
#define APPLY_FLAG_CONSTRAINT(func, phase) , next_two_args_are_constraint, (short)CONSTRAINT_ENUM(func), int(JVMFlagConstraint::phase)
constexpr JVMTypedFlagLimit<int> limit_dummy
(
#ifdef PRODUCT
ALL_FLAGS(FLAG_LIMIT_DEFINE_DUMMY,
FLAG_LIMIT_DEFINE_DUMMY,
FLAG_LIMIT_DEFINE,
FLAG_LIMIT_DEFINE,
FLAG_LIMIT_DEFINE_DUMMY,
APPLY_FLAG_RANGE,
APPLY_FLAG_CONSTRAINT)
#else
ALL_FLAGS(FLAG_LIMIT_DEFINE,
FLAG_LIMIT_DEFINE,
FLAG_LIMIT_DEFINE,
FLAG_LIMIT_DEFINE,
FLAG_LIMIT_DEFINE,
APPLY_FLAG_RANGE,
APPLY_FLAG_CONSTRAINT)
#endif
); |
To understand how the macros work, it's best to compile jvmFlagLimit.c with gcc -save-temps. and look at the generated jvmFlagLimit.ii with the macros expanded. Here's an example:
Code Block |
---|
// code excerpt prettified manually
constexpr JVMTypedFlagLimit<int> limit_dummy();
constexpr JVMTypedFlagLimit<bool> limit_UseCompressedOops(0);
....
constexpr JVMTypedFlagLimit<intx> limit_ObjectAlignmentInBytes(0, 8, 256,
next_two_args_are_constraint,
(short)constraint_enum_ObjectAlignmentInBytesConstraintFunc, int(JVMFlagConstraint::AtParse));
....
constexpr JVMTypedFlagLimit<intx> limit_JVMCIThreads(0, 1, max_jint); |
We use overloaded constructors to fill out the necessarily fields of the JVMTypedFlagLimit<T> variables. Note that the min/max parameters, as well as the constraint_func/phase parameters, can both be integer values. For disambiguation, we pass in a dummy next_two_args_are_constraint for the constraint_func/phase.
We also need to always pass in an initial dummy 0 parameter so that the macros can safely add a comma before passing the min/max or constraint_func/phase.
These dummy parameters are evaluated at compile time so they can be safely optimized away.
The next step is to fill out the flagLimitTable[] array:
Code Block |
---|
static constexpr const JVMFlagLimit* const flagLimitTable[1 + NUM_JVMFlagsEnum] = {
// Because FLAG_LIMIT_PTR must start with an "),", we have to place a dummy element here.
LimitGetter<int>::get_limit(NULL, 0
#ifdef PRODUCT
ALL_FLAGS(FLAG_LIMIT_PTR_NONE,
FLAG_LIMIT_PTR_NONE,
FLAG_LIMIT_PTR,
FLAG_LIMIT_PTR,
FLAG_LIMIT_PTR_NONE,
APPLY_FLAG_RANGE,
APPLY_FLAG_CONSTRAINT)
#else
ALL_FLAGS(FLAG_LIMIT_PTR,
FLAG_LIMIT_PTR,
FLAG_LIMIT_PTR,
FLAG_LIMIT_PTR,
FLAG_LIMIT_PTR,
APPLY_FLAG_RANGE,
APPLY_FLAG_CONSTRAINT)
#endif
)
}; |
For the flags shown in the example above, the following code is generated by the macros:
Code Block |
---|
static constexpr const JVMFlagLimit* const flagLimitTable[1 + NUM_JVMFlagsEnum] = {
LimitGetter<int>::get_limit(NULL, 0),
LimitGetter<bool>::get_limit(&limit_UseCompressedOops, 0),
....
LimitGetter<intx>::get_limit(&limit_ObjectAlignmentInBytes, 0,
8, 256,
next_two_args_are_constraint,
(short)constraint_enum_ObjectAlignmentInBytesConstraintFunc, int(JVMFlagConstraint::AtParse)),
....
LimitGetter<intx>::get_limit(&limit_JVMCIThreads, 0, 1, max_jint), |
As a result, we will end up with this in the final output of the C++ compiler:
Code Block |
---|
static constexpr const JVMFlagLimit* const flagLimitTable[1 + NUM_JVMFlagsEnum] = {
NULL, // dummy
NULL, // UseCompressedOops has no range/constraint
....
&limit_ObjectAlignmentInBytes
....
&limit_JVMCIThreads |
All the flag limits are defined with the constexpr keyword, which has internal linkage by default. If a flag has no range/constraint, its flag limit (e.g., limit_UseCompressedOops in the example above) will be unused, and will be eliminated by the C++ compiler from the object file. So we don't waste any space.
This is a small optimization: There are 120 flags that use a constraint function, but there are only 65 total constraint functions. By using a short index, we can:
The savings are not a big deal, but since we can do it, why not?
...