Commit e99b43f8 authored by Clemens Backes's avatar Clemens Backes Committed by V8 LUCI CQ

[flags] Store all flag values in a single struct

Instead of defining one global (FLAG_foo) per flag, define all flag
values as fields in a global {v8_flags} struct. This guarantees that the
memory is contiguous, and together with proper alignment allows us to
later memory-protect that memory space.

In order to avoid rewriting all existing code that uses the {FLAG_foo}
syntax, we define global aliases: {FLAG_foo} is a reference to
{v8_flags.foo}.

After the next branch cut (v10.6), follow-up CLs will rewrite all
existing code to use the {v8_flags.foo} syntax, and after another branch
cut (v10.7) the aliases will be removed.
This should allow us to merge back most fixes to the previous branch
(N-1). Merges to stable (N-2) might still require resolving merge
conflicts manually, if they modify code that reads flags.

R=cbruni@chromium.org
CC=​sroettger@chromium.org

Bug: v8:12887
Change-Id: I8bc44429767f611484fe345d7268af1d55c98124
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3810187
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82423}
parent fffa8873
...@@ -52,19 +52,33 @@ constexpr int kReturnAddressStackSlotCount = ...@@ -52,19 +52,33 @@ constexpr int kReturnAddressStackSlotCount =
#if (defined(V8_HOST_ARCH_PPC) || defined(V8_HOST_ARCH_PPC64)) && !defined(_AIX) #if (defined(V8_HOST_ARCH_PPC) || defined(V8_HOST_ARCH_PPC64)) && !defined(_AIX)
// Native PPC linux has large (64KB) physical pages. // Native PPC linux has large (64KB) physical pages.
// Simulator (and Aix) need to use the same value as x64. // Simulator (and Aix) need to use the same value as x64.
const int kPageSizeBits = 19; constexpr int kPageSizeBits = 19;
#elif defined(ENABLE_HUGEPAGE) #elif defined(ENABLE_HUGEPAGE)
// When enabling huge pages, adjust V8 page size to take up exactly one huge // When enabling huge pages, adjust V8 page size to take up exactly one huge
// page. This avoids huge-page-internal fragmentation for unused address ranges. // page. This avoids huge-page-internal fragmentation for unused address ranges.
const int kHugePageBits = 21; constexpr int kHugePageBits = 21;
const int kHugePageSize = (1U) << kHugePageBits; constexpr int kHugePageSize = 1 << kHugePageBits;
const int kPageSizeBits = kHugePageBits; constexpr int kPageSizeBits = kHugePageBits;
#else #else
// Arm64 supports up to 64k OS pages on Linux, however 4k pages are more common // Arm64 supports up to 64k OS pages on Linux, however 4k pages are more common
// so we keep the V8 page size at 256k. Nonetheless, we need to make sure we // so we keep the V8 page size at 256k. Nonetheless, we need to make sure we
// don't decrease it further in the future due to reserving 3 OS pages for every // don't decrease it further in the future due to reserving 3 OS pages for every
// executable V8 page. // executable V8 page.
const int kPageSizeBits = 18; constexpr int kPageSizeBits = 18;
#endif
// The minimal supported page size by the operation system. Any region aligned
// to that size needs to be individually protectable via
// {base::OS::SetPermission} and friends.
#if defined(V8_OS_MACOS) && defined(V8_HOST_ARCH_ARM64)
// MacOS on arm64 uses 16kB pages.
constexpr int kMinimumOSPageSize = 16 * 1024;
#elif defined(V8_OS_LINUX) && defined(V8_HOST_ARCH_ARM64)
// Linux on arm64 can be configured for up to 64kB pages.
constexpr int kMinimumOSPageSize = 64 * 1024;
#else
// Everything else uses 4kB pages.
constexpr int kMinimumOSPageSize = 4 * 1024;
#endif #endif
#endif // V8_BASE_BUILD_CONFIG_H_ #endif // V8_BASE_BUILD_CONFIG_H_
...@@ -29,25 +29,22 @@ ...@@ -29,25 +29,22 @@
#define DEFINE_NEG_NEG_IMPLICATION(whenflag, thenflag) \ #define DEFINE_NEG_NEG_IMPLICATION(whenflag, thenflag) \
DEFINE_NEG_VALUE_IMPLICATION(whenflag, thenflag, false) DEFINE_NEG_VALUE_IMPLICATION(whenflag, thenflag, false)
// We want to declare the names of the variables for the header file. Normally // With FLAG_MODE_DECLARE we declare the fields in the {FlagValues} struct.
// this will just be an extern declaration, but for a readonly flag we let the // Read-only flags are static constants instead of fields.
// compiler make better optimizations by giving it the value.
#if defined(FLAG_MODE_DECLARE) #if defined(FLAG_MODE_DECLARE)
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \ #define FLAG_FULL(ftype, ctype, nam, def, cmt) FlagValue<ctype> nam{def};
V8_EXPORT_PRIVATE extern FlagValue<ctype> FLAG_##nam;
#define FLAG_READONLY(ftype, ctype, nam, def, cmt) \ #define FLAG_READONLY(ftype, ctype, nam, def, cmt) \
static constexpr FlagValue<ctype> FLAG_##nam{def}; static constexpr FlagValue<ctype> nam{def};
// We want to supply the actual storage and value for the flag variable in the // Define a {FLAG_foo} alias per flag, pointing to {v8_flags.foo}.
// .cc file. We only do this for writable flags. // This allows to still use the old and deprecated syntax for accessing flag
#elif defined(FLAG_MODE_DEFINE) // values. This will be removed after v10.7.
#ifdef USING_V8_SHARED // TODO(clemensb): Remove this after v10.7.
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \ #elif defined(FLAG_MODE_DEFINE_GLOBAL_ALIASES)
V8_EXPORT_PRIVATE extern FlagValue<ctype> FLAG_##nam;
#else
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \ #define FLAG_FULL(ftype, ctype, nam, def, cmt) \
V8_EXPORT_PRIVATE FlagValue<ctype> FLAG_##nam{def}; inline auto& FLAG_##nam = v8_flags.nam;
#endif #define FLAG_READONLY(ftype, ctype, nam, def, cmt) \
inline auto constexpr& FLAG_##nam = v8_flags.nam;
// We need to define all of our default values so that the Flag structure can // We need to define all of our default values so that the Flag structure can
// access them by pointer. These are just used internally inside of one .cc, // access them by pointer. These are just used internally inside of one .cc,
...@@ -60,29 +57,29 @@ ...@@ -60,29 +57,29 @@
// printing / etc in the flag parser code. We only do this for writable flags. // printing / etc in the flag parser code. We only do this for writable flags.
#elif defined(FLAG_MODE_META) #elif defined(FLAG_MODE_META)
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \ #define FLAG_FULL(ftype, ctype, nam, def, cmt) \
{Flag::TYPE_##ftype, #nam, &FLAG_##nam, &FLAGDEFAULT_##nam, cmt, false}, {Flag::TYPE_##ftype, #nam, &v8_flags.nam, &FLAGDEFAULT_##nam, cmt, false},
#define FLAG_ALIAS(ftype, ctype, alias, nam) \ #define FLAG_ALIAS(ftype, ctype, alias, nam) \
{Flag::TYPE_##ftype, #alias, &FLAG_##nam, &FLAGDEFAULT_##nam, \ {Flag::TYPE_##ftype, #alias, &v8_flags.nam, &FLAGDEFAULT_##nam, \
"alias for --" #nam, false}, "alias for --" #nam, false}, // NOLINT(whitespace/indent)
// We produce the code to set flags when it is implied by another flag. // We produce the code to set flags when it is implied by another flag.
#elif defined(FLAG_MODE_DEFINE_IMPLICATIONS) #elif defined(FLAG_MODE_DEFINE_IMPLICATIONS)
#define DEFINE_VALUE_IMPLICATION(whenflag, thenflag, value) \ #define DEFINE_VALUE_IMPLICATION(whenflag, thenflag, value) \
changed |= TriggerImplication(FLAG_##whenflag, #whenflag, &FLAG_##thenflag, \ changed |= TriggerImplication(v8_flags.whenflag, #whenflag, \
value, false); &v8_flags.thenflag, value, false);
// A weak implication will be overwritten by a normal implication or by an // A weak implication will be overwritten by a normal implication or by an
// explicit flag. // explicit flag.
#define DEFINE_WEAK_VALUE_IMPLICATION(whenflag, thenflag, value) \ #define DEFINE_WEAK_VALUE_IMPLICATION(whenflag, thenflag, value) \
changed |= TriggerImplication(FLAG_##whenflag, #whenflag, &FLAG_##thenflag, \ changed |= TriggerImplication(v8_flags.whenflag, #whenflag, \
value, true); &v8_flags.thenflag, value, true);
#define DEFINE_GENERIC_IMPLICATION(whenflag, statement) \ #define DEFINE_GENERIC_IMPLICATION(whenflag, statement) \
if (FLAG_##whenflag) statement; if (v8_flags.whenflag) statement;
#define DEFINE_NEG_VALUE_IMPLICATION(whenflag, thenflag, value) \ #define DEFINE_NEG_VALUE_IMPLICATION(whenflag, thenflag, value) \
changed |= TriggerImplication(!FLAG_##whenflag, "!" #whenflag, \ changed |= TriggerImplication(!v8_flags.whenflag, "!" #whenflag, \
&FLAG_##thenflag, value, false); &v8_flags.thenflag, value, false);
// We apply a generic macro to the flags. // We apply a generic macro to the flags.
#elif defined(FLAG_MODE_APPLY) #elif defined(FLAG_MODE_APPLY)
...@@ -2349,7 +2346,7 @@ DEFINE_BOOL(enable_embedded_constant_pool, V8_EMBEDDED_CONSTANT_POOL, ...@@ -2349,7 +2346,7 @@ DEFINE_BOOL(enable_embedded_constant_pool, V8_EMBEDDED_CONSTANT_POOL,
#undef DEFINE_ALIAS_FLOAT #undef DEFINE_ALIAS_FLOAT
#undef FLAG_MODE_DECLARE #undef FLAG_MODE_DECLARE
#undef FLAG_MODE_DEFINE #undef FLAG_MODE_DEFINE_GLOBAL_ALIASES
#undef FLAG_MODE_DEFINE_DEFAULTS #undef FLAG_MODE_DEFINE_DEFAULTS
#undef FLAG_MODE_META #undef FLAG_MODE_META
#undef FLAG_MODE_DEFINE_IMPLICATIONS #undef FLAG_MODE_DEFINE_IMPLICATIONS
......
...@@ -28,12 +28,18 @@ ...@@ -28,12 +28,18 @@
#include "src/wasm/wasm-limits.h" #include "src/wasm/wasm-limits.h"
#endif // V8_ENABLE_WEBASSEMBLY #endif // V8_ENABLE_WEBASSEMBLY
namespace v8 { namespace v8::internal {
namespace internal {
// Define all of our flags. // Define {v8_flags}, declared in flags.h.
#define FLAG_MODE_DEFINE FlagValues v8_flags;
#include "src/flags/flag-definitions.h" // NOLINT(build/include)
// {v8_flags} needs to be aligned to a memory page, and the size needs to be a
// multiple of a page size. This is required for memory-protection of the memory
// holding the {v8_flags} struct.
// Both is guaranteed by the {alignas(kMinimumOSPageSize)} annotation on
// {FlagValues}.
static_assert(alignof(FlagValues) == kMinimumOSPageSize);
static_assert(sizeof(FlagValues) % kMinimumOSPageSize == 0);
// Define all of our flags default values. // Define all of our flags default values.
#define FLAG_MODE_DEFINE_DEFAULTS #define FLAG_MODE_DEFINE_DEFAULTS
...@@ -199,14 +205,14 @@ struct Flag { ...@@ -199,14 +205,14 @@ struct Flag {
} }
static bool ShouldCheckFlagContradictions() { static bool ShouldCheckFlagContradictions() {
if (FLAG_allow_overwriting_for_next_flag) { if (v8_flags.allow_overwriting_for_next_flag) {
// Setting the flag manually to false before calling Reset() avoids this // Setting the flag manually to false before calling Reset() avoids this
// becoming re-entrant. // becoming re-entrant.
FLAG_allow_overwriting_for_next_flag = false; v8_flags.allow_overwriting_for_next_flag = false;
FindFlagByPointer(&FLAG_allow_overwriting_for_next_flag)->Reset(); FindFlagByPointer(&v8_flags.allow_overwriting_for_next_flag)->Reset();
return false; return false;
} }
return FLAG_abort_on_contradictory_flags && !FLAG_fuzzing; return v8_flags.abort_on_contradictory_flags && !v8_flags.fuzzing;
} }
// {change_flag} indicates if we're going to change the flag value. // {change_flag} indicates if we're going to change the flag value.
...@@ -261,7 +267,7 @@ struct Flag { ...@@ -261,7 +267,7 @@ struct Flag {
case SetBy::kCommandLine: case SetBy::kCommandLine:
if (new_set_by == SetBy::kImplication && check_command_line_flags) { if (new_set_by == SetBy::kImplication && check_command_line_flags) {
// Exit instead of abort for certain testing situations. // Exit instead of abort for certain testing situations.
if (FLAG_exit_on_contradictory_flags) base::OS::ExitProcess(0); if (v8_flags.exit_on_contradictory_flags) base::OS::ExitProcess(0);
if (is_bool_flag) { if (is_bool_flag) {
FatalError{} << "Flag " << FlagName{name()} FatalError{} << "Flag " << FlagName{name()}
<< ": value implied by " << FlagName{implied_by} << ": value implied by " << FlagName{implied_by}
...@@ -274,7 +280,7 @@ struct Flag { ...@@ -274,7 +280,7 @@ struct Flag {
} else if (new_set_by == SetBy::kCommandLine && } else if (new_set_by == SetBy::kCommandLine &&
check_command_line_flags) { check_command_line_flags) {
// Exit instead of abort for certain testing situations. // Exit instead of abort for certain testing situations.
if (FLAG_exit_on_contradictory_flags) base::OS::ExitProcess(0); if (v8_flags.exit_on_contradictory_flags) base::OS::ExitProcess(0);
if (is_bool_flag) { if (is_bool_flag) {
FatalError{} << "Command-line provided flag " << FlagName{name()} FatalError{} << "Command-line provided flag " << FlagName{name()}
<< " specified as both true and false"; << " specified as both true and false";
...@@ -487,9 +493,9 @@ uint32_t ComputeFlagListHash() { ...@@ -487,9 +493,9 @@ uint32_t ComputeFlagListHash() {
if (flag.IsDefault()) continue; if (flag.IsDefault()) continue;
// We want to be able to flip --profile-deserialization without // We want to be able to flip --profile-deserialization without
// causing the code cache to get invalidated by this hash. // causing the code cache to get invalidated by this hash.
if (flag.PointsTo(&FLAG_profile_deserialization)) continue; if (flag.PointsTo(&v8_flags.profile_deserialization)) continue;
// Skip FLAG_random_seed to allow predictable code caching. // Skip v8_flags.random_seed to allow predictable code caching.
if (flag.PointsTo(&FLAG_random_seed)) continue; if (flag.PointsTo(&v8_flags.random_seed)) continue;
modified_args_as_string << flag; modified_args_as_string << flag;
} }
std::string args(modified_args_as_string.str()); std::string args(modified_args_as_string.str());
...@@ -688,7 +694,7 @@ int FlagList::SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags, ...@@ -688,7 +694,7 @@ int FlagList::SetFlagsFromCommandLine(int* argc, char** argv, bool remove_flags,
} }
} }
if (FLAG_help) { if (v8_flags.help) {
if (help_options.HasUsage()) { if (help_options.HasUsage()) {
PrintF(stdout, "%s", help_options.usage()); PrintF(stdout, "%s", help_options.usage());
} }
...@@ -911,9 +917,4 @@ void FlagList::ResetFlagHash() { ...@@ -911,9 +917,4 @@ void FlagList::ResetFlagHash() {
flag_hash = 0; flag_hash = 0;
} }
#undef FLAG_MODE_DEFINE } // namespace v8::internal
#undef FLAG_MODE_DEFINE_DEFAULTS
#undef FLAG_MODE_META
} // namespace internal
} // namespace v8
...@@ -8,16 +8,24 @@ ...@@ -8,16 +8,24 @@
#include "src/base/optional.h" #include "src/base/optional.h"
#include "src/common/globals.h" #include "src/common/globals.h"
#if V8_ENABLE_WEBASSEMBLY
// Include the wasm-limits.h header for some default values of Wasm flags.
// This can be reverted once we can use designated initializations (C++20) for
// {v8_flags} (defined in flags.cc) instead of specifying the default values in
// the header and using the default constructor.
#include "src/wasm/wasm-limits.h"
#endif
namespace v8::internal { namespace v8::internal {
// The value of a single flag (this is the type of all FLAG_* globals). // The value of a single flag (this is the type of all v8_flags.* fields).
template <typename T> template <typename T>
class FlagValue { class FlagValue {
public: public:
constexpr FlagValue(T value) : value_(value) {} explicit constexpr FlagValue(T value) : value_(value) {}
// Implicitly convert to a {T}. Not marked {constexpr} so we do not compiler // Implicitly convert to a {T}. Not marked {constexpr} so we do not get
// warnings about dead code (when checking readonly flags). // compiler warnings about dead code (when checking readonly flags).
operator T() const { return value_; } operator T() const { return value_; }
// Explicitly convert to a {T} via {value()}. This is {constexpr} so we can // Explicitly convert to a {T} via {value()}. This is {constexpr} so we can
...@@ -31,9 +39,24 @@ class FlagValue { ...@@ -31,9 +39,24 @@ class FlagValue {
T value_; T value_;
}; };
// Declare all of our flags. // Declare a struct to hold all of our flags.
struct alignas(kMinimumOSPageSize) FlagValues {
FlagValues() = default;
// No copying, moving, or assigning. This is a singleton struct.
FlagValues(const FlagValues&) = delete;
FlagValues(FlagValues&&) = delete;
FlagValues& operator=(const FlagValues&) = delete;
FlagValues& operator=(FlagValues&&) = delete;
#define FLAG_MODE_DECLARE #define FLAG_MODE_DECLARE
#include "src/flags/flag-definitions.h" #include "src/flags/flag-definitions.h" // NOLINT(build/include)
};
V8_EXPORT_PRIVATE extern FlagValues v8_flags;
// TODO(clemensb): Remove this after v10.7.
#define FLAG_MODE_DEFINE_GLOBAL_ALIASES
#include "src/flags/flag-definitions.h" // NOLINT(build/include)
// The global list of all flags. // The global list of all flags.
class V8_EXPORT_PRIVATE FlagList { class V8_EXPORT_PRIVATE FlagList {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment