Reduce the amount of memory needed to store Options.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 17 Oct 2019 05:45:58 +0000 (05:45 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 17 Oct 2019 05:45:58 +0000 (05:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=202105

Reviewed by Yusuke Suzuki.

The size of the JSC::Config needed to store the Options is now reduced to 4K
instead of 16K, enabled by constexpr template magic.

1. Instead of all options in a large array of OptionEntry (which is a union of
   all the option types), we now have separate arrays for each of the types of
   options.  For example,

        Removed g_jscConfig.options[].
        Added g_jscConfig.typeBoolOptions[].
        Added g_jscConfig.typeInt32Options[].
        Added g_jscConfig.typeDoubleOptions[].
        ...

   We used to find the storage for the option using g_jscConfig.options[Options::ID].
   We now find the storage for each type of option using
   g_jscConfig.options[optionTypeSpecificIndex<OptionTypeID, OptionID>()].  For
   example, Options::useJIT() used to be implemented as:

       inline bool& Options::useJIT()
       {
            return g_jscConfig.options[Options::useJITID];
       }

   ... which is now replaced with:

       inline bool& Options::useJIT()
       {
            return g_jscConfig.typeBoolOptions[optionTypeSpecificIndex<OptionTypeID::Bool, OptionID::useJIT>()];
       }

   MSVC has a bug where it cannot handle very large source files: see
   https://developercommunity.visualstudio.com/content/problem/653301/fatal-error-c1002-compiler-is-out-of-heap-space-in.html.
   This bug prevents this patch from building on MSVC.  To workaround this bug,
   we don't inline the Options accessors when COMPILER(MSVC).  This prevents MSVC
   from having to parse the large body of template code code to used to implement
   the inline accessors in every file that #include "Options.h".

   Also add the @no-unify attribute to Options.cpp (where we put the accessors)
   so that it doesn't trigger the MSVC bug also.

2. Introduce the optionTypeSpecificIndex() constexpr template function for
   computing the index of each option in their respective type specific options
   array.

3. Introduce OptionTypes, OptionTypeID, and OptionID.

   The OptionTypes namespace replaces OptionEntry as the container of option types.
   The OptionID enum class replaces Options::ID.
   The OptionTypeID enum class is new and is used together with OptionID in
       constexpr templates to compute the typeSpecificIndex of options.

4. Removed the OptionEntry struct and OptionEntry.h.  After (1), this struct is
   only used in the Option class.  We just moved the union of option types (that
   OptionEntry embeds) into the Option class.

   Moved class OptionRange into OptionsList.h.

5. Removed the large OptionEntry arrays from JSC::Config.
   Added type specific options arrays.
   Also ordered these arrays to maximize compactness and minimize internal fragmentation.

6. Changed scaleJITPolicy() to go directly to g_jscConfig.typeInt32Options[]
   instead of going through the Option wrapper object.  This allows us to simplify
   things and make the Option class a read only interface of options.

7. Changed Options::initialize() to only compute the option default value once.
   The default value specified in the OptionsList may not always be a constant.
   Sometimes, it is a function call.

8. The Option class now only gives read only access to the options.

   The Option class' role is to provide an interface for reading an option at any
   given OptionID without first knowing about the type of the specific option.
   It is useful for iterating options, and is currently only used by
   Options::dumpOption().

   Technically, we could merge all the Option class code into its single client.
   We opted not to do this because the amount of code is non-trivial, and the
   Option class does a good job of encapsulating this functionality.

* API/glib/JSCOptions.cpp:
(jscOptionsSetValue):
(jscOptionsGetValue):
(jsc_options_foreach):
(jsc_options_get_option_group):
* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* runtime/JSCConfig.h:
* runtime/OptionEntry.h: Removed.
* runtime/Options.cpp:
(JSC::Options::isAvailable):
(JSC::overrideOptionWithHeuristic):
(JSC::optionTypeSpecificIndex):
(JSC::scaleJITPolicy):
(JSC::recomputeDependentOptions):
(JSC::Options::initialize):
(JSC::Options::setOptionWithoutAlias):
(JSC::Options::dumpAllOptions):
(JSC::Options::dumpOption):
(JSC::Option::Option):
(JSC::Option::defaultOption const):
(JSC::Option::dump const):
(JSC::Option::operator== const):
* runtime/Options.h:
(JSC::Option::id const):
(JSC::Option::name const):
(JSC::Option::description const):
(JSC::Option::type const):
(JSC::Option::availability const):
(JSC::Option::isOverridden const):
(JSC::Option::Option):
(JSC::Option::idIndex const):
(JSC::optionTypeSpecificIndex):
(JSC::Option::defaultOption const): Deleted.
(JSC::Option::boolVal): Deleted.
(JSC::Option::unsignedVal): Deleted.
(JSC::Option::doubleVal): Deleted.
(JSC::Option::int32Val): Deleted.
(JSC::Option::optionRangeVal): Deleted.
(JSC::Option::optionStringVal): Deleted.
(JSC::Option::gcLogLevelVal): Deleted.
* runtime/OptionsList.h:
(JSC::OptionRange::operator= ):
(JSC::OptionRange::rangeString const):
(JSC::countNumberOfJSCOptionsOfType):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@251226 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/API/glib/JSCOptions.cpp
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/runtime/JSCConfig.h
Source/JavaScriptCore/runtime/OptionEntry.h [deleted file]
Source/JavaScriptCore/runtime/Options.cpp
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/runtime/OptionsList.h

index 655f5f0..99f82ac 100644 (file)
@@ -168,7 +168,7 @@ static gboolean jscOptionsSetValue(const char* option, const GValue* value)
 {
 #define SET_OPTION_VALUE(type_, name_, defaultValue_, availability_, description_) \
     if (!g_strcmp0(#name_, option)) {                                   \
-        OptionEntry::type_ valueToSet;                                  \
+        OptionTypes::type_ valueToSet;                                  \
         if (!valueFromGValue(value, valueToSet))                        \
             return FALSE;                                               \
         Options::name_() = valueToSet;                                  \
@@ -186,7 +186,7 @@ static gboolean jscOptionsGetValue(const char* option, GValue* value)
 {
 #define GET_OPTION_VALUE(type_, name_, defaultValue_, availability_, description_) \
     if (!g_strcmp0(#name_, option)) {                                   \
-        OptionEntry::type_ valueToGet = Options::name_();               \
+        OptionTypes::type_ valueToGet = Options::name_();               \
         valueToGValue(valueToGet, value);                               \
         return TRUE;                                                    \
     }
@@ -616,8 +616,8 @@ void jsc_options_foreach(JSCOptionsFunc function, gpointer userData)
 
 #define VISIT_OPTION(type_, name_, defaultValue_, availability_, description_) \
     if (Options::Availability::availability_ == Options::Availability::Normal \
-        || Options::isAvailable(Options::name_##ID, Options::Availability::availability_)) { \
-        OptionEntry::type_ defaultValue { };                            \
+        || Options::isAvailable(OptionID::name_, Options::Availability::availability_)) { \
+        OptionTypes::type_ defaultValue { };                            \
         auto optionType = jscOptionsType(defaultValue);                 \
         if (function (#name_, optionType, description_, userData))      \
             return;                                                     \
@@ -666,7 +666,7 @@ GOptionGroup* jsc_options_get_option_group(void)
     GArray* entries = g_array_new(TRUE, TRUE, sizeof(GOptionEntry));
 #define REGISTER_OPTION(type_, name_, defaultValue_, availability_, description_) \
     if (Options::Availability::availability_ == Options::Availability::Normal \
-        || Options::isAvailable(Options::name_##ID, Options::Availability::availability_)) { \
+        || Options::isAvailable(OptionID::name_, Options::Availability::availability_)) { \
         GUniquePtr<char> name(g_strdup_printf("jsc-%s", #name_));       \
         entries = g_array_set_size(entries, entries->len + 1); \
         GOptionEntry* entry = &g_array_index(entries, GOptionEntry, entries->len - 1); \
index 17722a1..78b6add 100644 (file)
@@ -920,7 +920,6 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     runtime/ObjectInitializationScope.h
     runtime/ObjectPrototype.h
     runtime/Operations.h
-    runtime/OptionEntry.h
     runtime/Options.h
     runtime/OptionsList.h
     runtime/ParseInt.h
index 0820223..5cdb66c 100644 (file)
@@ -1,3 +1,137 @@
+2019-10-16  Mark Lam  <mark.lam@apple.com>
+
+        Reduce the amount of memory needed to store Options.
+        https://bugs.webkit.org/show_bug.cgi?id=202105
+
+        Reviewed by Yusuke Suzuki.
+
+        The size of the JSC::Config needed to store the Options is now reduced to 4K
+        instead of 16K, enabled by constexpr template magic.
+
+        1. Instead of all options in a large array of OptionEntry (which is a union of
+           all the option types), we now have separate arrays for each of the types of
+           options.  For example,
+
+                Removed g_jscConfig.options[].
+                Added g_jscConfig.typeBoolOptions[].
+                Added g_jscConfig.typeInt32Options[].
+                Added g_jscConfig.typeDoubleOptions[].
+                ...
+
+           We used to find the storage for the option using g_jscConfig.options[Options::ID].
+           We now find the storage for each type of option using
+           g_jscConfig.options[optionTypeSpecificIndex<OptionTypeID, OptionID>()].  For
+           example, Options::useJIT() used to be implemented as:
+
+               inline bool& Options::useJIT()
+               {
+                    return g_jscConfig.options[Options::useJITID];
+               }
+
+           ... which is now replaced with:
+
+               inline bool& Options::useJIT()
+               {
+                    return g_jscConfig.typeBoolOptions[optionTypeSpecificIndex<OptionTypeID::Bool, OptionID::useJIT>()];
+               }
+
+           MSVC has a bug where it cannot handle very large source files: see
+           https://developercommunity.visualstudio.com/content/problem/653301/fatal-error-c1002-compiler-is-out-of-heap-space-in.html.
+           This bug prevents this patch from building on MSVC.  To workaround this bug,
+           we don't inline the Options accessors when COMPILER(MSVC).  This prevents MSVC
+           from having to parse the large body of template code code to used to implement
+           the inline accessors in every file that #include "Options.h".
+
+           Also add the @no-unify attribute to Options.cpp (where we put the accessors)
+           so that it doesn't trigger the MSVC bug also.
+
+        2. Introduce the optionTypeSpecificIndex() constexpr template function for
+           computing the index of each option in their respective type specific options
+           array.
+
+        3. Introduce OptionTypes, OptionTypeID, and OptionID.
+
+           The OptionTypes namespace replaces OptionEntry as the container of option types.
+           The OptionID enum class replaces Options::ID.
+           The OptionTypeID enum class is new and is used together with OptionID in
+               constexpr templates to compute the typeSpecificIndex of options.
+
+        4. Removed the OptionEntry struct and OptionEntry.h.  After (1), this struct is
+           only used in the Option class.  We just moved the union of option types (that
+           OptionEntry embeds) into the Option class.
+
+           Moved class OptionRange into OptionsList.h.
+
+        5. Removed the large OptionEntry arrays from JSC::Config.
+           Added type specific options arrays.
+           Also ordered these arrays to maximize compactness and minimize internal fragmentation.
+
+        6. Changed scaleJITPolicy() to go directly to g_jscConfig.typeInt32Options[]
+           instead of going through the Option wrapper object.  This allows us to simplify
+           things and make the Option class a read only interface of options.
+
+        7. Changed Options::initialize() to only compute the option default value once.
+           The default value specified in the OptionsList may not always be a constant.
+           Sometimes, it is a function call.
+
+        8. The Option class now only gives read only access to the options.
+
+           The Option class' role is to provide an interface for reading an option at any
+           given OptionID without first knowing about the type of the specific option.
+           It is useful for iterating options, and is currently only used by
+           Options::dumpOption().
+
+           Technically, we could merge all the Option class code into its single client.
+           We opted not to do this because the amount of code is non-trivial, and the
+           Option class does a good job of encapsulating this functionality.
+
+        * API/glib/JSCOptions.cpp:
+        (jscOptionsSetValue):
+        (jscOptionsGetValue):
+        (jsc_options_foreach):
+        (jsc_options_get_option_group):
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * runtime/JSCConfig.h:
+        * runtime/OptionEntry.h: Removed.
+        * runtime/Options.cpp:
+        (JSC::Options::isAvailable):
+        (JSC::overrideOptionWithHeuristic):
+        (JSC::optionTypeSpecificIndex):
+        (JSC::scaleJITPolicy):
+        (JSC::recomputeDependentOptions):
+        (JSC::Options::initialize):
+        (JSC::Options::setOptionWithoutAlias):
+        (JSC::Options::dumpAllOptions):
+        (JSC::Options::dumpOption):
+        (JSC::Option::Option):
+        (JSC::Option::defaultOption const):
+        (JSC::Option::dump const):
+        (JSC::Option::operator== const):
+        * runtime/Options.h:
+        (JSC::Option::id const):
+        (JSC::Option::name const):
+        (JSC::Option::description const):
+        (JSC::Option::type const):
+        (JSC::Option::availability const):
+        (JSC::Option::isOverridden const):
+        (JSC::Option::Option):
+        (JSC::Option::idIndex const):
+        (JSC::optionTypeSpecificIndex):
+        (JSC::Option::defaultOption const): Deleted.
+        (JSC::Option::boolVal): Deleted.
+        (JSC::Option::unsignedVal): Deleted.
+        (JSC::Option::doubleVal): Deleted.
+        (JSC::Option::int32Val): Deleted.
+        (JSC::Option::optionRangeVal): Deleted.
+        (JSC::Option::optionStringVal): Deleted.
+        (JSC::Option::gcLogLevelVal): Deleted.
+        * runtime/OptionsList.h:
+        (JSC::OptionRange::operator= ):
+        (JSC::OptionRange::rangeString const):
+        (JSC::countNumberOfJSCOptionsOfType):
+
 2019-10-16  Keith Miller  <keith_miller@apple.com>
 
         Move assert in Wasm::Plan::fail.
index 8c99e17..7fe7c7b 100644 (file)
                FE1BD0211E72027900134BC9 /* CellProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1BD0201E72027000134BC9 /* CellProfile.h */; };
                FE1BD0251E72053800134BC9 /* HeapVerifier.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1BD0231E72052F00134BC9 /* HeapVerifier.h */; };
                FE1C0FFD1B193E9800B53FCA /* Exception.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1C0FFC1B193E9800B53FCA /* Exception.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               FE1D6D4523580E1F007A5C26 /* Options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE228EA1436AB2300196C48 /* Options.cpp */; };
                FE1E2C3F2240DD5800F6B729 /* MacroAssemblerARM64E.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1E2C3E2240D30B00F6B729 /* MacroAssemblerARM64E.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE1E2C402240DD6200F6B729 /* ARM64EAssembler.h in Headers */ = {isa = PBXBuildFile; fileRef = FE1E2C3D2240D2F600F6B729 /* ARM64EAssembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE20CE9E15F04A9500DF3430 /* LLIntCLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = FE20CE9C15F04A9500DF3430 /* LLIntCLoop.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE3022D71E42857300BAC493 /* VMInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = FE3022D51E42856700BAC493 /* VMInspector.h */; };
                FE318FE01CAC982F00DFCC54 /* ECMAScriptSpecInternalFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = FE318FDE1CAC8C5300DFCC54 /* ECMAScriptSpecInternalFunctions.h */; };
                FE3422121D6B81C30032BE88 /* ThrowScope.h in Headers */ = {isa = PBXBuildFile; fileRef = FE3422111D6B818C0032BE88 /* ThrowScope.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               FE3842322324D51B009DD445 /* OptionEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = FE3842302324D51A009DD445 /* OptionEntry.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE3842332324D51B009DD445 /* OptionsList.h in Headers */ = {isa = PBXBuildFile; fileRef = FE3842312324D51B009DD445 /* OptionsList.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE384EE61ADDB7AD0055DE2C /* JSDollarVM.h in Headers */ = {isa = PBXBuildFile; fileRef = FE384EE21ADDB7AD0055DE2C /* JSDollarVM.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FE3A06A61C10B72D00390FDD /* JITBitOrGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = FE3A06A41C10B70800390FDD /* JITBitOrGenerator.h */; };
                FE35C2FA21B1E6C7000F4CA8 /* Options.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = Options.rb; sourceTree = "<group>"; };
                FE35C2FB21B1E6C7000F4CA8 /* OpcodeGroup.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = OpcodeGroup.rb; sourceTree = "<group>"; };
                FE35C2FC21B1E6C7000F4CA8 /* Metadata.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; path = Metadata.rb; sourceTree = "<group>"; };
-               FE3842302324D51A009DD445 /* OptionEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OptionEntry.h; sourceTree = "<group>"; };
                FE3842312324D51B009DD445 /* OptionsList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OptionsList.h; sourceTree = "<group>"; };
                FE384EE11ADDB7AD0055DE2C /* JSDollarVM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDollarVM.cpp; sourceTree = "<group>"; };
                FE384EE21ADDB7AD0055DE2C /* JSDollarVM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDollarVM.h; sourceTree = "<group>"; };
                                E39EEAF22281244C008474F4 /* ObjectToStringAdaptiveStructureWatchpoint.h */,
                                F692A8770255597D01FF60F7 /* Operations.cpp */,
                                F692A8780255597D01FF60F7 /* Operations.h */,
-                               FE3842302324D51A009DD445 /* OptionEntry.h */,
                                0FE228EA1436AB2300196C48 /* Options.cpp */,
                                0FE228EB1436AB2300196C48 /* Options.h */,
                                FE3842312324D51B009DD445 /* OptionsList.h */,
                                0F2BDC2C151FDE9100CD8910 /* Operands.h in Headers */,
                                A70447EA17A0BD4600F5898E /* OperandsInlines.h in Headers */,
                                BC18C4480E16F5CD00B34460 /* Operations.h in Headers */,
-                               FE3842322324D51B009DD445 /* OptionEntry.h in Headers */,
                                0FE228ED1436AB2700196C48 /* Options.h in Headers */,
                                FE3842332324D51B009DD445 /* OptionsList.h in Headers */,
                                E356987222841187008CDCCB /* PackedCellPtr.h in Headers */,
                                536B314A1F71C5990037FC33 /* UnifiedSource87.cpp in Sources */,
                                536B315C1F71C5990037FC33 /* UnifiedSource88.cpp in Sources */,
                                536B31621F71C5990037FC33 /* UnifiedSource89.cpp in Sources */,
+                               FE1D6D4523580E1F007A5C26 /* Options.cpp in Sources */,
                                536B31421F71C5990037FC33 /* UnifiedSource90.cpp in Sources */,
                                536B31491F71C5990037FC33 /* UnifiedSource91.cpp in Sources */,
                                536B31521F71C5990037FC33 /* UnifiedSource92.cpp in Sources */,
index 7ced68b..68c31f3 100644 (file)
@@ -904,7 +904,7 @@ runtime/ObjectInitializationScope.cpp
 runtime/ObjectPrototype.cpp
 runtime/ObjectToStringAdaptiveStructureWatchpoint.cpp
 runtime/Operations.cpp
-runtime/Options.cpp
+runtime/Options.cpp @no-unify
 runtime/ProgramExecutable.cpp
 runtime/PromiseDeferredTimer.cpp
 runtime/PropertyDescriptor.cpp
index 056dcaf..430eff2 100644 (file)
@@ -25,7 +25,6 @@
 
 #pragma once
 
-#include "OptionEntry.h"
 #include "OptionsList.h"
 #include <wtf/StdLibExtras.h>
 
@@ -36,12 +35,12 @@ class FixedVMPoolExecutableAllocator;
 
 #if CPU(ARM64) || PLATFORM(WATCHOS)
 constexpr size_t PageSize = 16 * KB;
+constexpr size_t ConfigSizeToProtect = 1 * PageSize;
 #else
 constexpr size_t PageSize = 4 * KB;
+constexpr size_t ConfigSizeToProtect = 1 * PageSize;
 #endif
 
-constexpr size_t ConfigSizeToProtect = 32 * KB;
-
 #if ENABLE(SEPARATED_WX_HEAP)
 using JITWriteSeparateHeapsFunction = void (*)(off_t, const void*, size_t);
 #endif
@@ -82,8 +81,29 @@ struct Config {
             bool useFastPermisionsJITCopy;
 #endif
 
-            OptionEntry options[NumberOfOptions];
-            OptionEntry defaultOptions[NumberOfOptions];
+            bool typeBoolOptions[NumberOfBoolOptions];
+            bool typeBoolDefaultOptions[NumberOfBoolOptions];
+
+            GCLogging::Level typeGCLogLevelOptions[NumberOfGCLogLevelOptions];
+            GCLogging::Level typeGCLogLevelDefaultOptions[NumberOfGCLogLevelOptions];
+
+            int32_t typeInt32Options[NumberOfInt32Options];
+            int32_t typeInt32DefaultOptions[NumberOfInt32Options];
+
+            unsigned typeUnsignedOptions[NumberOfUnsignedOptions];
+            unsigned typeUnsignedDefaultOptions[NumberOfUnsignedOptions];
+
+            double typeDoubleOptions[NumberOfDoubleOptions];
+            double typeDoubleDefaultOptions[NumberOfDoubleOptions];
+
+            size_t typeSizeOptions[NumberOfSizeOptions];
+            size_t typeSizeDefaultOptions[NumberOfSizeOptions];
+
+            const char* typeOptionStringOptions[NumberOfOptionStringOptions];
+            const char* typeOptionStringDefaultOptions[NumberOfOptionStringOptions];
+
+            OptionRange typeOptionRangeOptions[NumberOfOptionRangeOptions];
+            OptionRange typeOptionRangeDefaultOptions[NumberOfOptionRangeOptions];
         };
         char ensureSize[ConfigSizeToProtect];
     };
diff --git a/Source/JavaScriptCore/runtime/OptionEntry.h b/Source/JavaScriptCore/runtime/OptionEntry.h
deleted file mode 100644 (file)
index 3bd6ccc..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2019 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#pragma once
-
-#include "GCLogging.h"
-#include <wtf/PrintStream.h>
-
-namespace JSC {
-
-class OptionRange {
-private:
-    enum RangeState { Uninitialized, InitError, Normal, Inverted };
-public:
-    OptionRange& operator= (const int& rhs)
-    { // Only needed for initialization
-        if (!rhs) {
-            m_state = Uninitialized;
-            m_rangeString = 0;
-            m_lowLimit = 0;
-            m_highLimit = 0;
-        }
-        return *this;
-    }
-
-    bool init(const char*);
-    bool isInRange(unsigned);
-    const char* rangeString() const { return (m_state > InitError) ? m_rangeString : s_nullRangeStr; }
-    
-    void dump(PrintStream& out) const;
-
-private:
-    static const char* const s_nullRangeStr;
-
-    RangeState m_state;
-    const char* m_rangeString;
-    unsigned m_lowLimit;
-    unsigned m_highLimit;
-};
-
-// For storing for an option value:
-union OptionEntry {
-    using Bool = bool;
-    using Unsigned = unsigned;
-    using Double = double;
-    using Int32 = int32_t;
-    using Size = size_t;
-    using OptionRange = JSC::OptionRange;
-    using OptionString = const char*;
-    using GCLogLevel = GCLogging::Level;
-
-    bool valBool;
-    unsigned valUnsigned;
-    double valDouble;
-    int32_t valInt32;
-    size_t valSize;
-    OptionRange valOptionRange;
-    const char* valOptionString;
-    GCLogging::Level valGCLogLevel;
-};
-
-} // namespace JSC
index 5dafe81..850ee71 100644 (file)
@@ -132,7 +132,7 @@ static bool parse(const char* string, GCLogging::Level& value)
     return false;
 }
 
-bool Options::isAvailable(Options::ID id, Options::Availability availability)
+bool Options::isAvailable(OptionID id, Options::Availability availability)
 {
     if (availability == Availability::Restricted)
         return g_jscConfig.restrictedOptionsEnabled;
@@ -140,26 +140,26 @@ bool Options::isAvailable(Options::ID id, Options::Availability availability)
     
     UNUSED_PARAM(id);
 #if !defined(NDEBUG)
-    if (id == maxSingleAllocationSizeID)
+    if (id == OptionID::maxSingleAllocationSize)
         return true;
 #endif
 #if OS(DARWIN)
-    if (id == useSigillCrashAnalyzerID)
+    if (id == OptionID::useSigillCrashAnalyzer)
         return true;
 #endif
 #if ENABLE(ASSEMBLER) && OS(LINUX)
-    if (id == logJITCodeForPerfID)
+    if (id == OptionID::logJITCodeForPerf)
         return true;
 #endif
-    if (id == traceLLIntExecutionID)
+    if (id == OptionID::traceLLIntExecution)
         return !!LLINT_TRACING;
-    if (id == traceLLIntSlowPathID)
+    if (id == OptionID::traceLLIntSlowPath)
         return !!LLINT_TRACING;
     return false;
 }
 
 template<typename T>
-bool overrideOptionWithHeuristic(T& variable, Options::ID id, const char* name, Options::Availability availability)
+bool overrideOptionWithHeuristic(T& variable, OptionID id, const char* name, Options::Availability availability)
 {
     bool available = (availability == Options::Availability::Normal)
         || Options::isAvailable(id, availability);
@@ -282,10 +282,45 @@ void OptionRange::dump(PrintStream& out) const
     out.print(m_rangeString);
 }
 
+// FIXME: This is a workaround for MSVC's inability to handle large sources.
+// Once the MSVC bug has been fixed, we can remove this snippet of code and
+// make its counterpart in Options.h unconditional.
+// See https://developercommunity.visualstudio.com/content/problem/653301/fatal-error-c1002-compiler-is-out-of-heap-space-in.html
+#if COMPILER(MSVC)
+
+template<OptionTypeID type, OptionID id>
+constexpr size_t optionTypeSpecificIndex()
+{
+    size_t index = 0;
+#define COUNT_INDEX_AND_FIND_MATCH(type_, name_, defaultValue_, availability_, description_) \
+    if (id == OptionID::name_) \
+        return index; \
+    if (type == OptionTypeID::type_) \
+        index++;
+
+    FOR_EACH_JSC_OPTION(COUNT_INDEX_AND_FIND_MATCH);
+#undef COUNT_INDEX_AND_FIND_MATCH
+    return InvalidOptionIndex;
+}
+
+#define DEFINE_OPTION_ACCESSORS(type_, name_, defaultValue_, availability_, description_) \
+    JS_EXPORT_PRIVATE OptionTypes::type_& Options::name_() \
+    { \
+        return g_jscConfig.type##type_##Options[optionTypeSpecificIndex<OptionTypeID::type_, OptionID::name_>()]; \
+    }  \
+    JS_EXPORT_PRIVATE OptionTypes::type_& Options::name_##Default() \
+    { \
+        return g_jscConfig.type##type_##DefaultOptions[optionTypeSpecificIndex<OptionTypeID::type_, OptionID::name_>()]; \
+    }
+    FOR_EACH_JSC_OPTION(DEFINE_OPTION_ACCESSORS)
+#undef DEFINE_OPTION_ACCESSORS
+
+#endif // COMPILER(MSVC)
+
 // Realize the names for each of the options:
-const Options::EntryInfo Options::s_optionsInfo[Options::numberOfOptions] = {
+const Options::EntryInfo Options::s_optionsInfo[NumberOfOptions] = {
 #define FILL_OPTION_INFO(type_, name_, defaultValue_, availability_, description_) \
-    { #name_, description_, Options::Type::type_, Availability::availability_ },
+    { #name_, description_, OptionTypeID::type_, Availability::availability_, optionTypeSpecificIndex<OptionTypeID::type_, OptionID::name_>() },
     FOR_EACH_JSC_OPTION(FILL_OPTION_INFO)
 #undef FILL_OPTION_INFO
 };
@@ -299,26 +334,25 @@ static void scaleJITPolicy()
         scaleFactor = 0.0;
 
     struct OptionToScale {
-        Options::ID id;
+        size_t index;
         int32_t minVal;
     };
 
     static const OptionToScale optionsToScale[] = {
-        { Options::thresholdForJITAfterWarmUpID, 0 },
-        { Options::thresholdForJITSoonID, 0 },
-        { Options::thresholdForOptimizeAfterWarmUpID, 1 },
-        { Options::thresholdForOptimizeAfterLongWarmUpID, 1 },
-        { Options::thresholdForOptimizeSoonID, 1 },
-        { Options::thresholdForFTLOptimizeSoonID, 2 },
-        { Options::thresholdForFTLOptimizeAfterWarmUpID, 2 }
+        { optionTypeSpecificIndex<OptionTypeID::Int32, OptionID::thresholdForJITAfterWarmUp>(), 0 },
+        { optionTypeSpecificIndex<OptionTypeID::Int32, OptionID::thresholdForJITSoon>(), 0 },
+        { optionTypeSpecificIndex<OptionTypeID::Int32, OptionID::thresholdForOptimizeAfterWarmUp>(), 1 },
+        { optionTypeSpecificIndex<OptionTypeID::Int32, OptionID::thresholdForOptimizeAfterLongWarmUp>(), 1 },
+        { optionTypeSpecificIndex<OptionTypeID::Int32, OptionID::thresholdForOptimizeSoon>(), 1 },
+        { optionTypeSpecificIndex<OptionTypeID::Int32, OptionID::thresholdForFTLOptimizeSoon>(), 2 },
+        { optionTypeSpecificIndex<OptionTypeID::Int32, OptionID::thresholdForFTLOptimizeAfterWarmUp>(), 2 },
     };
 
-    const int numberOfOptionsToScale = sizeof(optionsToScale) / sizeof(OptionToScale);
+    constexpr int numberOfOptionsToScale = sizeof(optionsToScale) / sizeof(OptionToScale);
     for (int i = 0; i < numberOfOptionsToScale; i++) {
-        Option option(optionsToScale[i].id);
-        ASSERT(option.type() == Options::Type::Int32);
-        option.int32Val() *= scaleFactor;
-        option.int32Val() = std::max(option.int32Val(), optionsToScale[i].minVal);
+        int32_t& optionValue = g_jscConfig.typeInt32Options[optionsToScale[i].index];
+        optionValue *= scaleFactor;
+        optionValue = std::max(optionValue, optionsToScale[i].minVal);
     }
 }
 
@@ -441,7 +475,7 @@ static void recomputeDependentOptions()
     if (!Options::useConcurrentGC())
         Options::collectContinuously() = false;
 
-    if (Option(Options::jitPolicyScaleID).isOverridden())
+    if (Option(OptionID::jitPolicyScale).isOverridden())
         scaleJITPolicy();
     
     if (Options::forceEagerCompilation()) {
@@ -522,9 +556,11 @@ void Options::initialize()
             Config::enableRestrictedOptions();
 #endif
             // Initialize each of the options with their default values:
-#define INIT_OPTION(type_, name_, defaultValue_, availability_, description_) \
-            name_() = defaultValue_; \
-            name_##Default() = defaultValue_;
+#define INIT_OPTION(type_, name_, defaultValue_, availability_, description_) { \
+                auto value = defaultValue_; \
+                name_() = value; \
+                name_##Default() = value; \
+            }
             FOR_EACH_JSC_OPTION(INIT_OPTION)
 #undef INIT_OPTION
 
@@ -548,7 +584,7 @@ void Options::initialize()
                 CRASH();
 #else // PLATFORM(COCOA)
 #define OVERRIDE_OPTION_WITH_HEURISTICS(type_, name_, defaultValue_, availability_, description_) \
-            overrideOptionWithHeuristic(name_(), name_##ID, "JSC_" #name_, Availability::availability_);
+            overrideOptionWithHeuristic(name_(), OptionID::name_, "JSC_" #name_, Availability::availability_);
             FOR_EACH_JSC_OPTION(OVERRIDE_OPTION_WITH_HEURISTICS)
 #undef OVERRIDE_OPTION_WITH_HEURISTICS
 #endif // PLATFORM(COCOA)
@@ -727,16 +763,15 @@ bool Options::setOptionWithoutAlias(const char* arg)
 
     const char* valueStr = equalStr + 1;
 
-    // For each option, check if the specify arg is a match. If so, set the arg
+    // For each option, check if the specified arg is a match. If so, set the arg
     // if the value makes sense. Otherwise, move on to checking the next option.
 #define SET_OPTION_IF_MATCH(type_, name_, defaultValue_, availability_, description_) \
     if (strlen(#name_) == static_cast<size_t>(equalStr - arg)      \
         && !strncmp(arg, #name_, equalStr - arg)) {                \
-        if (Availability::availability_ != Availability::Normal     \
-            && !isAvailable(name_##ID, Availability::availability_)) \
+        if (Availability::availability_ != Availability::Normal    \
+            && !isAvailable(OptionID::name_, Availability::availability_)) \
             return false;                                          \
-        OptionEntry::type_ value;                                  \
-        value = (defaultValue_);                                   \
+        OptionTypes::type_ value;                                  \
         bool success = parse(valueStr, value);                     \
         if (success) {                                             \
             name_() = value;                                       \
@@ -816,10 +851,10 @@ void Options::dumpAllOptions(StringBuilder& builder, DumpLevel level, const char
         builder.append('\n');
     }
 
-    for (int id = 0; id < numberOfOptions; id++) {
+    for (size_t id = 0; id < NumberOfOptions; id++) {
         if (separator && id)
             builder.append(separator);
-        dumpOption(builder, level, static_cast<ID>(id), optionHeader, optionFooter, dumpDefaultsOption);
+        dumpOption(builder, level, static_cast<OptionID>(id), optionHeader, optionFooter, dumpDefaultsOption);
     }
 }
 
@@ -835,10 +870,10 @@ void Options::dumpAllOptions(FILE* stream, DumpLevel level, const char* title)
     fprintf(stream, "%s", builder.toString().utf8().data());
 }
 
-void Options::dumpOption(StringBuilder& builder, DumpLevel level, Options::ID id,
+void Options::dumpOption(StringBuilder& builder, DumpLevel level, OptionID id,
     const char* header, const char* footer, DumpDefaultsOption dumpDefaultsOption)
 {
-    if (id >= numberOfOptions)
+    if (static_cast<size_t>(id) >= NumberOfOptions)
         return; // Illegal option.
 
     Option option(id);
@@ -880,29 +915,71 @@ void Options::ensureOptionsAreCoherent()
         CRASH();
 }
 
+Option::Option(OptionID id)
+    : m_id(id)
+{
+    unsigned index = static_cast<unsigned>(m_id);
+    unsigned typeSpecificIndex = Options::s_optionsInfo[index].typeSpecificIndex;
+    OptionTypeID type = Options::s_optionsInfo[index].type;
+
+    switch (type) {
+
+#define HANDLE_CASE(OptionType_, type_) \
+    case OptionTypeID::OptionType_: \
+        ASSERT(typeSpecificIndex < NumberOf##OptionType_##Options); \
+        m_val##OptionType_ = g_jscConfig.type##OptionType_##Options[typeSpecificIndex]; \
+        break;
+
+    FOR_EACH_JSC_OPTION_TYPE(HANDLE_CASE)
+#undef HANDLE_CASE
+    }
+}
+
+const Option Option::defaultOption() const
+{
+    Option result;
+    unsigned index = static_cast<unsigned>(m_id);
+    unsigned typeSpecificIndex = Options::s_optionsInfo[index].typeSpecificIndex;
+    OptionTypeID type = Options::s_optionsInfo[index].type;
+
+    result.m_id = m_id;
+    switch (type) {
+
+#define HANDLE_CASE(OptionType_, type_) \
+    case OptionTypeID::OptionType_: \
+        ASSERT(typeSpecificIndex < NumberOf##OptionType_##Options); \
+        result.m_val##OptionType_ = g_jscConfig.type##OptionType_##DefaultOptions[typeSpecificIndex]; \
+        break;
+
+    FOR_EACH_JSC_OPTION_TYPE(HANDLE_CASE)
+#undef HANDLE_CASE
+    }
+    return result;
+}
+
 void Option::dump(StringBuilder& builder) const
 {
     switch (type()) {
-    case Options::Type::Bool:
-        builder.append(m_entry.valBool ? "true" : "false");
+    case OptionTypeID::Bool:
+        builder.append(m_valBool ? "true" : "false");
         break;
-    case Options::Type::Unsigned:
-        builder.appendNumber(m_entry.valUnsigned);
+    case OptionTypeID::Unsigned:
+        builder.appendNumber(m_valUnsigned);
         break;
-    case Options::Type::Size:
-        builder.appendNumber(m_entry.valSize);
+    case OptionTypeID::Size:
+        builder.appendNumber(m_valSize);
         break;
-    case Options::Type::Double:
-        builder.appendFixedPrecisionNumber(m_entry.valDouble);
+    case OptionTypeID::Double:
+        builder.appendFixedPrecisionNumber(m_valDouble);
         break;
-    case Options::Type::Int32:
-        builder.appendNumber(m_entry.valInt32);
+    case OptionTypeID::Int32:
+        builder.appendNumber(m_valInt32);
         break;
-    case Options::Type::OptionRange:
-        builder.append(m_entry.valOptionRange.rangeString());
+    case OptionTypeID::OptionRange:
+        builder.append(m_valOptionRange.rangeString());
         break;
-    case Options::Type::OptionString: {
-        const char* option = m_entry.valOptionString;
+    case OptionTypeID::OptionString: {
+        const char* option = m_valOptionString;
         if (!option)
             option = "";
         builder.append('"');
@@ -910,8 +987,8 @@ void Option::dump(StringBuilder& builder) const
         builder.append('"');
         break;
     }
-    case Options::Type::GCLogLevel: {
-        builder.append(GCLogging::levelAsString(m_entry.valGCLogLevel));
+    case OptionTypeID::GCLogLevel: {
+        builder.append(GCLogging::levelAsString(m_valGCLogLevel));
         break;
     }
     }
@@ -920,23 +997,23 @@ void Option::dump(StringBuilder& builder) const
 bool Option::operator==(const Option& other) const
 {
     switch (type()) {
-    case Options::Type::Bool:
-        return m_entry.valBool == other.m_entry.valBool;
-    case Options::Type::Unsigned:
-        return m_entry.valUnsigned == other.m_entry.valUnsigned;
-    case Options::Type::Size:
-        return m_entry.valSize == other.m_entry.valSize;
-    case Options::Type::Double:
-        return (m_entry.valDouble == other.m_entry.valDouble) || (std::isnan(m_entry.valDouble) && std::isnan(other.m_entry.valDouble));
-    case Options::Type::Int32:
-        return m_entry.valInt32 == other.m_entry.valInt32;
-    case Options::Type::OptionRange:
-        return m_entry.valOptionRange.rangeString() == other.m_entry.valOptionRange.rangeString();
-    case Options::Type::OptionString:
-        return (m_entry.valOptionString == other.m_entry.valOptionString)
-            || (m_entry.valOptionString && other.m_entry.valOptionString && !strcmp(m_entry.valOptionString, other.m_entry.valOptionString));
-    case Options::Type::GCLogLevel:
-        return m_entry.valGCLogLevel == other.m_entry.valGCLogLevel;
+    case OptionTypeID::Bool:
+        return m_valBool == other.m_valBool;
+    case OptionTypeID::Unsigned:
+        return m_valUnsigned == other.m_valUnsigned;
+    case OptionTypeID::Size:
+        return m_valSize == other.m_valSize;
+    case OptionTypeID::Double:
+        return (m_valDouble == other.m_valDouble) || (std::isnan(m_valDouble) && std::isnan(other.m_valDouble));
+    case OptionTypeID::Int32:
+        return m_valInt32 == other.m_valInt32;
+    case OptionTypeID::OptionRange:
+        return m_valOptionRange.rangeString() == other.m_valOptionRange.rangeString();
+    case OptionTypeID::OptionString:
+        return (m_valOptionString == other.m_valOptionString)
+            || (m_valOptionString && other.m_valOptionString && !strcmp(m_valOptionString, other.m_valOptionString));
+    case OptionTypeID::GCLogLevel:
+        return m_valGCLogLevel == other.m_valGCLogLevel;
     }
     return false;
 }
index 58c9b3e..13a74cc 100644 (file)
@@ -29,7 +29,6 @@
 #include "JSExportMacros.h"
 #include <stdint.h>
 #include <stdio.h>
-#include <wtf/PrintStream.h>
 #include <wtf/StdLibExtras.h>
 
 namespace WTF {
@@ -53,39 +52,19 @@ constexpr bool enableWebAssemblyStreamingApi = false;
 
 class Options {
 public:
-    enum class DumpLevel {
+    enum class DumpLevel : uint8_t {
         None = 0,
         Overridden,
         All,
         Verbose
     };
 
-    enum class Availability {
+    enum class Availability : uint8_t {
         Normal = 0,
         Restricted,
         Configurable
     };
 
-#define DECLARE_OPTION_ID(type_, name_, defaultValue_, availability_, description_) \
-    name_##ID,
-
-    enum ID {
-        FOR_EACH_JSC_OPTION(DECLARE_OPTION_ID)
-        numberOfOptions
-    };
-#undef DECLARE_OPTION_ID
-
-    enum class Type {
-        Bool,
-        Unsigned,
-        Double,
-        Int32,
-        Size,
-        OptionRange,
-        OptionString,
-        GCLogLevel,
-    };
-
     JS_EXPORT_PRIVATE static void initialize();
 
     // Parses a string of options where each option is of the format "--<optionName>=<value>"
@@ -101,14 +80,21 @@ public:
 
     JS_EXPORT_PRIVATE static void ensureOptionsAreCoherent();
 
-#define DECLARE_OPTION_ACCESSORS(type_, name_, defaultValue_, availability_, description_) \
-    ALWAYS_INLINE static OptionEntry::type_& name_() { return g_jscConfig.options[name_##ID].val##type_; }  \
-    ALWAYS_INLINE static OptionEntry::type_& name_##Default() { return g_jscConfig.defaultOptions[name_##ID].val##type_; }
+// FIXME: This is a workaround for MSVC's inability to handle large sources.
+// See https://developercommunity.visualstudio.com/content/problem/653301/fatal-error-c1002-compiler-is-out-of-heap-space-in.html
+#if COMPILER(MSVC)
+#define OPTION_ACCESSOR_LINKAGE JS_EXPORT_PRIVATE
+#else
+#define OPTION_ACCESSOR_LINKAGE ALWAYS_INLINE
+#endif
 
+#define DECLARE_OPTION_ACCESSORS(type_, name_, defaultValue_, availability_, description_) \
+    OPTION_ACCESSOR_LINKAGE static OptionTypes::type_& name_(); \
+    OPTION_ACCESSOR_LINKAGE static OptionTypes::type_& name_##Default();
     FOR_EACH_JSC_OPTION(DECLARE_OPTION_ACCESSORS)
 #undef DECLARE_OPTION_ACCESSORS
 
-    static bool isAvailable(ID, Availability);
+    static bool isAvailable(OptionID, Availability);
 
 private:
 
@@ -116,8 +102,9 @@ private:
     struct EntryInfo {
         const char* name;
         const char* description;
-        Type type;
+        OptionTypeID type;
         Availability availability;
+        unsigned typeSpecificIndex;
     };
 
     Options();
@@ -129,122 +116,89 @@ private:
     static void dumpOptionsIfNeeded();
     static void dumpAllOptions(StringBuilder&, DumpLevel, const char* title,
         const char* separator, const char* optionHeader, const char* optionFooter, DumpDefaultsOption);
-    static void dumpOption(StringBuilder&, DumpLevel, ID,
+    static void dumpOption(StringBuilder&, DumpLevel, OptionID,
         const char* optionHeader, const char* optionFooter, DumpDefaultsOption);
 
     static bool setOptionWithoutAlias(const char* arg);
     static bool setAliasedOption(const char* arg);
     static bool overrideAliasedOptionWithHeuristic(const char* name);
 
-    static const EntryInfo s_optionsInfo[numberOfOptions];
+    static const EntryInfo s_optionsInfo[NumberOfOptions];
 
     friend class Option;
 };
 
 class Option {
 public:
-    Option(Options::ID id)
-        : m_id(id)
-        , m_entry(g_jscConfig.options[m_id])
-    {
-    }
-    
+    Option(OptionID);
+
     void dump(StringBuilder&) const;
 
     bool operator==(const Option& other) const;
     bool operator!=(const Option& other) const { return !(*this == other); }
     
-    Options::ID id() const { return m_id; }
-    const char* name() const;
-    const char* description() const;
-    Options::Type type() const;
-    Options::Availability availability() const;
-    bool isOverridden() const;
+    OptionID id() const { return m_id; }
+    const char* name() const { return Options::s_optionsInfo[idIndex()].name; }
+    const char* description() const { return Options::s_optionsInfo[idIndex()].description; }
+    OptionTypeID type() const { return Options::s_optionsInfo[idIndex()].type; }
+    Options::Availability availability() const { return Options::s_optionsInfo[idIndex()].availability; }
+    bool isOverridden() const { return *this != defaultOption(); }
     const Option defaultOption() const;
-    
-    bool& boolVal();
-    unsigned& unsignedVal();
-    double& doubleVal();
-    int32_t& int32Val();
-    OptionRange optionRangeVal();
-    const char* optionStringVal();
-    GCLogging::Level& gcLogLevelVal();
-    
-private:
-    // Only used for constructing default Options.
-    Option(Options::ID id, OptionEntry& entry)
-        : m_id(id)
-        , m_entry(entry)
-    {
-    }
-    
-    Options::ID m_id;
-    OptionEntry& m_entry;
-};
-
-inline const char* Option::name() const
-{
-    return Options::s_optionsInfo[m_id].name;
-}
-
-inline const char* Option::description() const
-{
-    return Options::s_optionsInfo[m_id].description;
-}
-
-inline Options::Type Option::type() const
-{
-    return Options::s_optionsInfo[m_id].type;
-}
 
-inline Options::Availability Option::availability() const
-{
-    return Options::s_optionsInfo[m_id].availability;
-}
+#define DECLARE_ACCESSOR(OptionType_, type_) \
+    type_ val##OptionType_() const { return m_val##OptionType_; }
+    FOR_EACH_JSC_OPTION_TYPE(DECLARE_ACCESSOR)
+#undef DECLARE_ACCESSOR
 
-inline bool Option::isOverridden() const
-{
-    return *this != defaultOption();
-}
+private:
+    Option() { }
 
-inline const Option Option::defaultOption() const
-{
-    return Option(m_id, g_jscConfig.defaultOptions[m_id]);
-}
+    size_t idIndex() const { return static_cast<size_t>(m_id); }
 
-inline bool& Option::boolVal()
-{
-    return m_entry.valBool;
-}
+    OptionID m_id;
+    union {
 
-inline unsigned& Option::unsignedVal()
-{
-    return m_entry.valUnsigned;
-}
+#define DECLARE_MEMBER(OptionType_, type_) \
+        type_ m_val##OptionType_;
+    FOR_EACH_JSC_OPTION_TYPE(DECLARE_MEMBER)
+#undef DECLARE_MEMBER
 
-inline double& Option::doubleVal()
-{
-    return m_entry.valDouble;
-}
+    };
+};
 
-inline int32_t& Option::int32Val()
-{
-    return m_entry.valInt32;
-}
+// FIXME: This is a workaround for MSVC's inability to handle large sources.
+// Once the MSVC bug has been fixed, we can make the following unconditional and
+// remove its counterpart MSVC version in Options.cpp.
+// See https://developercommunity.visualstudio.com/content/problem/653301/fatal-error-c1002-compiler-is-out-of-heap-space-in.html
+#if !COMPILER(MSVC)
 
-inline OptionRange Option::optionRangeVal()
+template<OptionTypeID type, OptionID id>
+constexpr size_t optionTypeSpecificIndex()
 {
-    return m_entry.valOptionRange;
+    size_t index = 0;
+#define COUNT_INDEX_AND_FIND_MATCH(type_, name_, defaultValue_, availability_, description_) \
+    if (id == OptionID::name_) \
+        return index; \
+    if (type == OptionTypeID::type_) \
+        index++;
+
+    FOR_EACH_JSC_OPTION(COUNT_INDEX_AND_FIND_MATCH);
+#undef COUNT_INDEX_AND_FIND_MATCH
+    return InvalidOptionIndex;
 }
 
-inline const char* Option::optionStringVal()
-{
-    return m_entry.valOptionString;
-}
+#define DEFINE_OPTION_ACCESSORS(type_, name_, defaultValue_, availability_, description_) \
+    ALWAYS_INLINE OptionTypes::type_& Options::name_() \
+    { \
+        return g_jscConfig.type##type_##Options[optionTypeSpecificIndex<OptionTypeID::type_, OptionID::name_>()]; \
+    }  \
+    ALWAYS_INLINE OptionTypes::type_& Options::name_##Default() \
+    { \
+        return g_jscConfig.type##type_##DefaultOptions[optionTypeSpecificIndex<OptionTypeID::type_, OptionID::name_>()]; \
+    }
+    FOR_EACH_JSC_OPTION(DEFINE_OPTION_ACCESSORS)
+#undef DEFINE_OPTION_ACCESSORS
 
-inline GCLogging::Level& Option::gcLogLevelVal()
-{
-    return m_entry.valGCLogLevel;
-}
+#endif // !COMPILER(MSVC)
 
 } // namespace JSC
index 461f038..92cb5c5 100644 (file)
 
 #pragma once
 
+#include "GCLogging.h"
+#include <wtf/PrintStream.h>
+
 namespace JSC {
 
 // How do JSC VM options work?
 // ===========================
 // The FOR_EACH_JSC_OPTION() macro below defines a list of all JSC options in use,
 // along with their types and default values. The options values are actually
-// realized as an array of OptionEntry elements in JSC::Config.
+// realized as arrays of each of the OptionTypes in JSC::Config.
 //
-//     Options::initialize() will initialize the array of options values with
+//     Options::initialize() will initialize the arrays of options values with
 // the defaults specified in FOR_EACH_JSC_OPTION() below. After that, the values can
 // be programmatically read and written to using an accessor method with the
 // same name as the option. For example, the option "useJIT" can be read and
@@ -54,6 +57,68 @@ namespace JSC {
 // checks are done after the option values are set. If you alter the option
 // values after the sanity checks (for your own testing), then you're liable to
 // ensure that the new values set are sane and reasonable for your own run.
+//
+// Any modifications to options must be done before the first VM is instantiate.
+// On instantiation of the first VM instance, the Options will be write protected
+// and cannot be modified thereafter.
+
+class OptionRange {
+private:
+    enum RangeState { Uninitialized, InitError, Normal, Inverted };
+public:
+    OptionRange& operator= (const int& rhs)
+    { // Only needed for initialization
+        if (!rhs) {
+            m_state = Uninitialized;
+            m_rangeString = 0;
+            m_lowLimit = 0;
+            m_highLimit = 0;
+        }
+        return *this;
+    }
+
+    bool init(const char*);
+    bool isInRange(unsigned);
+    const char* rangeString() const { return (m_state > InitError) ? m_rangeString : s_nullRangeStr; }
+    
+    void dump(PrintStream& out) const;
+
+private:
+    static const char* const s_nullRangeStr;
+
+    RangeState m_state;
+    const char* m_rangeString;
+    unsigned m_lowLimit;
+    unsigned m_highLimit;
+};
+
+#define FOR_EACH_JSC_OPTION_TYPE(v) \
+    v(Bool, bool) \
+    v(Unsigned, unsigned) \
+    v(Double, double) \
+    v(Int32, int32_t) \
+    v(Size, size_t) \
+    v(OptionRange, OptionRange) \
+    v(OptionString, const char*) \
+    v(GCLogLevel, GCLogging::Level) \
+
+namespace OptionTypes {
+
+#define DECLARE_TYPES(OptionType_, type_) \
+    using OptionType_ = type_;
+    FOR_EACH_JSC_OPTION_TYPE(DECLARE_TYPES)
+#undef DECLARE_TYPES
+
+} // namespace OptionTypes
+
+enum class OptionTypeID : uint8_t {
+
+#define DECLARE_TYPEID(OptionType_, type_) \
+    OptionType_,
+    FOR_EACH_JSC_OPTION_TYPE(DECLARE_TYPEID)
+#undef DECLARE_TYPEID
+
+};
 
 #define FOR_EACH_JSC_OPTION(v)                                          \
     v(Bool, useKernTCSM, true, Normal, "Note: this needs to go before other options since they depend on this value.") \
@@ -510,6 +575,14 @@ enum OptionEquivalence {
     v(maximumFTLCandidateInstructionCount, maximumFTLCandidateBytecodeCost, SameOption) \
     v(maximumInliningCallerSize, maximumInliningCallerBytecodeCost, SameOption) \
 
+#define DECLARE_OPTION_ID(type_, name_, defaultValue_, availability_, description_) \
+    name_,
+enum class OptionID : uint16_t {
+    FOR_EACH_JSC_OPTION(DECLARE_OPTION_ID)
+};
+#undef DECLARE_OPTION_ID
+
+static constexpr size_t InvalidOptionIndex = std::numeric_limits<size_t>::max();
 
 constexpr size_t countNumberOfJSCOptions()
 {
@@ -522,5 +595,24 @@ constexpr size_t countNumberOfJSCOptions()
 
 constexpr size_t NumberOfOptions = countNumberOfJSCOptions();
 
-} // namespace JSC
+constexpr size_t countNumberOfJSCOptionsOfType(OptionTypeID type)
+{
+    size_t count = 0;
+#define COUNT_OPTION(type_, name_, defaultValue_, availability_, description_) \
+    if (type == OptionTypeID::type_) \
+        count++;
+    FOR_EACH_JSC_OPTION(COUNT_OPTION);
+#undef COUNT_OPTION
+    return count;
+}
+
+constexpr size_t NumberOfBoolOptions = countNumberOfJSCOptionsOfType(OptionTypeID::Bool);
+constexpr size_t NumberOfUnsignedOptions = countNumberOfJSCOptionsOfType(OptionTypeID::Unsigned);
+constexpr size_t NumberOfDoubleOptions = countNumberOfJSCOptionsOfType(OptionTypeID::Double);
+constexpr size_t NumberOfInt32Options = countNumberOfJSCOptionsOfType(OptionTypeID::Int32);
+constexpr size_t NumberOfSizeOptions = countNumberOfJSCOptionsOfType(OptionTypeID::Size);
+constexpr size_t NumberOfOptionRangeOptions = countNumberOfJSCOptionsOfType(OptionTypeID::OptionRange);
+constexpr size_t NumberOfOptionStringOptions = countNumberOfJSCOptionsOfType(OptionTypeID::OptionString);
+constexpr size_t NumberOfGCLogLevelOptions = countNumberOfJSCOptionsOfType(OptionTypeID::GCLogLevel);
 
+} // namespace JSC