Reduce the amount of memory needed to store Options.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Nov 2019 20:06:14 +0000 (20:06 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Nov 2019 20:06:14 +0000 (20:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=202105
<rdar://problem/55641041>

Reviewed by Robin Morisset.

Options used to be stored as an array of unions of all option types, where the
size of the array is around 349 elements.  We also needed a second array for the
default option value.  We now store each option (and its default value) as a type
specific field in an OptionsStorage struct.  This reduces the size of storage for
Options from 16752 to 2192 bytes.  This, in turn, allows JSC::Config to fit in a
single page.

The reason we previously stored options in an array of unions is to allow us to
randomly access each option by its id.  We now store the offset of the option
field in the Options::s_constMetaData array (previously called s_optionsInfo).
With this offset, we can compute the address of the option value field in
the OptionsStorage struct in the JSC::Config.

In this patch, we also:

1. Renamed Options::s_optionsInfo to Options::s_constMetaData.

2. Refactor the Option class into the OptionReader::Option class.

   Previously, the Option class provided another way to access option values,
   specifically when we need to access them by id.  It also provided a means to
   change that option value.  However, it practice, we can do without this, and
   remove a lot of the code.  This class now exists as OptionReader::Option which
   only provides a means to read information about options, but not change them
   The only client for this class is the dumpOption() function.

3. Removed the OptionEntry class.  It previously served 2 purpose:
   a. Define the option types.  The types are now defined in OptionsStorage.
   b. Define a union abstract type large enough to store any option value.
      This is now expressed as a union member in OptionReader::Option.

   OptionEntry.h also defines the OptionRange class.  OptionRange is now moved
   to OptionsList.h.

4. Changed the implementation of the option value parse functions to return
   Optional values instead of taking a reference to the value.  This makes the
   code cleaner and easier to read on the client side.

5. Fixed scaleJITPolicy() to not rely on the Option class (see 2 above), and use
   the canonical way to read and write option values instead i.e. via the
   Options::<optionName>() accessors.

6. Fixed recomputeDependentOptions() to rely on the Option class (see 2 above).
   We can compare the option against its default value using the canonical
   accessors.

7. Fixed Options::initialize() to only compute the default option value once.
   The "default" value may actually be a function call.  Though current uses does
   not have side effects if called more than once, it is best to not assume this.

8. Moved the definition of  MAXIMUM_NUMBER_OF_FTL_COMPILER_THREADS and
   enableWebAssemblyStreamingApi  from Options.h to OptionsList.h because they are
   used there first.

9. Reduced the size of the JSC::Config to 1 PageSize.

* API/glib/JSCOptions.cpp:
(jscOptionsSetValue):
(jscOptionsGetValue):
(jsc_options_foreach):
* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/JSCConfig.h:
* runtime/OptionEntry.h: Removed.
* runtime/Options.cpp:
(JSC::parse):
(JSC::scaleJITPolicy):
(JSC::recomputeDependentOptions):
(JSC::Options::addressOfOption):
(JSC::Options::addressOfOptionDefault):
(JSC::Options::initialize):
(JSC::Options::setOptionWithoutAlias):
(JSC::invertBoolOptionValue):
(JSC::Options::setAliasedOption):
(JSC::Options::dumpAllOptions):
(JSC::OptionReader::Option::operator!= const):
(JSC::OptionReader::Option::name const):
(JSC::OptionReader::Option::description const):
(JSC::OptionReader::Option::type const):
(JSC::OptionReader::Option::availability const):
(JSC::OptionReader::Option::isOverridden const):
(JSC::OptionReader::Option::Option):
(JSC::Options::dumpOption):
(JSC::OptionReader::optionFor):
(JSC::OptionReader::defaultFor):
(JSC::OptionReader::Option::initValue):
(JSC::OptionReader::Option::dump const):
(JSC::OptionReader::Option::operator== const):
(JSC::Option::dump const): Deleted.
(JSC::Option::operator== const): Deleted.
* runtime/Options.h:
(JSC::Option::Option): Deleted.
(JSC::Option::operator!= const): Deleted.
(JSC::Option::id const): Deleted.
(JSC::Option::name const): Deleted.
(JSC::Option::description const): Deleted.
(JSC::Option::type const): Deleted.
(JSC::Option::availability const): Deleted.
(JSC::Option::isOverridden const): Deleted.
(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):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@252557 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/runtime/JSCConfig.h
Source/JavaScriptCore/runtime/OptionEntry.h
Source/JavaScriptCore/runtime/Options.cpp
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/runtime/OptionsList.h

index 655f5f0..e730b85 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;                                  \
+        OptionsStorage::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_();               \
+        OptionsStorage::type_ valueToGet = Options::name_();               \
         valueToGValue(valueToGet, value);                               \
         return TRUE;                                                    \
     }
@@ -617,7 +617,7 @@ 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 { };                            \
+        OptionsStorage::type_ defaultValue { };                            \
         auto optionType = jscOptionsType(defaultValue);                 \
         if (function (#name_, optionType, description_, userData))      \
             return;                                                     \
index b6f0aed..0f8c419 100644 (file)
@@ -943,7 +943,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 91e251f..eee7f69 100644 (file)
@@ -1,3 +1,122 @@
+2019-11-18  Mark Lam  <mark.lam@apple.com>
+
+        Reduce the amount of memory needed to store Options.
+        https://bugs.webkit.org/show_bug.cgi?id=202105
+        <rdar://problem/55641041>
+
+        Reviewed by Robin Morisset.
+
+        Options used to be stored as an array of unions of all option types, where the
+        size of the array is around 349 elements.  We also needed a second array for the
+        default option value.  We now store each option (and its default value) as a type
+        specific field in an OptionsStorage struct.  This reduces the size of storage for
+        Options from 16752 to 2192 bytes.  This, in turn, allows JSC::Config to fit in a
+        single page.
+        The reason we previously stored options in an array of unions is to allow us to
+        randomly access each option by its id.  We now store the offset of the option
+        field in the Options::s_constMetaData array (previously called s_optionsInfo).
+        With this offset, we can compute the address of the option value field in
+        the OptionsStorage struct in the JSC::Config.
+        In this patch, we also:
+
+        1. Renamed Options::s_optionsInfo to Options::s_constMetaData.
+
+        2. Refactor the Option class into the OptionReader::Option class.
+
+           Previously, the Option class provided another way to access option values,
+           specifically when we need to access them by id.  It also provided a means to
+           change that option value.  However, it practice, we can do without this, and
+           remove a lot of the code.  This class now exists as OptionReader::Option which
+           only provides a means to read information about options, but not change them
+           The only client for this class is the dumpOption() function.
+
+        3. Removed the OptionEntry class.  It previously served 2 purpose:
+           a. Define the option types.  The types are now defined in OptionsStorage.
+           b. Define a union abstract type large enough to store any option value.
+              This is now expressed as a union member in OptionReader::Option.
+           OptionEntry.h also defines the OptionRange class.  OptionRange is now moved
+           to OptionsList.h.
+        4. Changed the implementation of the option value parse functions to return
+           Optional values instead of taking a reference to the value.  This makes the
+           code cleaner and easier to read on the client side.
+
+        5. Fixed scaleJITPolicy() to not rely on the Option class (see 2 above), and use
+           the canonical way to read and write option values instead i.e. via the
+           Options::<optionName>() accessors.
+
+        6. Fixed recomputeDependentOptions() to rely on the Option class (see 2 above).
+           We can compare the option against its default value using the canonical
+           accessors.
+
+        7. Fixed Options::initialize() to only compute the default option value once.
+           The "default" value may actually be a function call.  Though current uses does
+           not have side effects if called more than once, it is best to not assume this.
+
+        8. Moved the definition of  MAXIMUM_NUMBER_OF_FTL_COMPILER_THREADS and
+           enableWebAssemblyStreamingApi  from Options.h to OptionsList.h because they are
+           used there first.
+
+        9. Reduced the size of the JSC::Config to 1 PageSize.
+        * API/glib/JSCOptions.cpp:
+        (jscOptionsSetValue):
+        (jscOptionsGetValue):
+        (jsc_options_foreach):
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * runtime/JSCConfig.h:
+        * runtime/OptionEntry.h: Removed.
+        * runtime/Options.cpp:
+        (JSC::parse):
+        (JSC::scaleJITPolicy):
+        (JSC::recomputeDependentOptions):
+        (JSC::Options::addressOfOption):
+        (JSC::Options::addressOfOptionDefault):
+        (JSC::Options::initialize):
+        (JSC::Options::setOptionWithoutAlias):
+        (JSC::invertBoolOptionValue):
+        (JSC::Options::setAliasedOption):
+        (JSC::Options::dumpAllOptions):
+        (JSC::OptionReader::Option::operator!= const):
+        (JSC::OptionReader::Option::name const):
+        (JSC::OptionReader::Option::description const):
+        (JSC::OptionReader::Option::type const):
+        (JSC::OptionReader::Option::availability const):
+        (JSC::OptionReader::Option::isOverridden const):
+        (JSC::OptionReader::Option::Option):
+        (JSC::Options::dumpOption):
+        (JSC::OptionReader::optionFor):
+        (JSC::OptionReader::defaultFor):
+        (JSC::OptionReader::Option::initValue):
+        (JSC::OptionReader::Option::dump const):
+        (JSC::OptionReader::Option::operator== const):
+        (JSC::Option::dump const): Deleted.
+        (JSC::Option::operator== const): Deleted.
+        * runtime/Options.h:
+        (JSC::Option::Option): Deleted.
+        (JSC::Option::operator!= const): Deleted.
+        (JSC::Option::id const): Deleted.
+        (JSC::Option::name const): Deleted.
+        (JSC::Option::description const): Deleted.
+        (JSC::Option::type const): Deleted.
+        (JSC::Option::availability const): Deleted.
+        (JSC::Option::isOverridden const): Deleted.
+        (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):
+
 2019-11-18  Chris Polcyn  <cpolcyn@apple.com>
 
         Publish JavaScriptCore as Clang module
index 363bef4..0038eb7 100644 (file)
                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 */,
index 056dcaf..2f48f75 100644 (file)
@@ -25,7 +25,6 @@
 
 #pragma once
 
-#include "OptionEntry.h"
 #include "OptionsList.h"
 #include <wtf/StdLibExtras.h>
 
@@ -40,7 +39,7 @@ constexpr size_t PageSize = 16 * KB;
 constexpr size_t PageSize = 4 * KB;
 #endif
 
-constexpr size_t ConfigSizeToProtect = 32 * KB;
+constexpr size_t ConfigSizeToProtect = PageSize;
 
 #if ENABLE(SEPARATED_WX_HEAP)
 using JITWriteSeparateHeapsFunction = void (*)(off_t, const void*, size_t);
@@ -82,8 +81,7 @@ struct Config {
             bool useFastPermisionsJITCopy;
 #endif
 
-            OptionEntry options[NumberOfOptions];
-            OptionEntry defaultOptions[NumberOfOptions];
+            OptionsStorage options;
         };
         char ensureSize[ConfigSizeToProtect];
     };
index 3bd6ccc..e69de29 100644 (file)
@@ -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 3b7b6db..6569aff 100644 (file)
@@ -41,6 +41,7 @@
 #include <wtf/Compiler.h>
 #include <wtf/DataLog.h>
 #include <wtf/NumberOfCores.h>
+#include <wtf/Optional.h>
 #include <wtf/PointerPreparations.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/text/StringBuilder.h>
 
 namespace JSC {
 
-static bool parse(const char* string, bool& value)
-{
-    if (equalLettersIgnoringASCIICase(string, "true") || equalLettersIgnoringASCIICase(string, "yes") || !strcmp(string, "1")) {
-        value = true;
-        return true;
-    }
-    if (equalLettersIgnoringASCIICase(string, "false") || equalLettersIgnoringASCIICase(string, "no") || !strcmp(string, "0")) {
-        value = false;
-        return true;
-    }
-    return false;
-}
+template<typename T>
+Optional<T> parse(const char* string);
 
-static bool parse(const char* string, int32_t& value)
+template<>
+Optional<OptionsStorage::Bool> parse(const char* string)
 {
-    return sscanf(string, "%d", &value) == 1;
+    if (equalLettersIgnoringASCIICase(string, "true") || equalLettersIgnoringASCIICase(string, "yes") || !strcmp(string, "1"))
+        return true;
+    if (equalLettersIgnoringASCIICase(string, "false") || equalLettersIgnoringASCIICase(string, "no") || !strcmp(string, "0"))
+        return false;
+    return WTF::nullopt;
 }
 
-static bool parse(const char* string, unsigned& value)
+template<>
+Optional<OptionsStorage::Int32> parse(const char* string)
 {
-    return sscanf(string, "%u", &value) == 1;
+    int32_t value;
+    if (sscanf(string, "%d", &value) == 1)
+        return value;
+    return WTF::nullopt;
 }
 
-static bool UNUSED_FUNCTION parse(const char* string, unsigned long& value)
+template<>
+Optional<OptionsStorage::Unsigned> parse(const char* string)
 {
-    return sscanf(string, "%lu", &value);
+    unsigned value;
+    if (sscanf(string, "%u", &value) == 1)
+        return value;
+    return WTF::nullopt;
 }
 
-static bool UNUSED_FUNCTION parse(const char* string, unsigned long long& value)
+#if CPU(ADDRESS64)
+template<>
+Optional<OptionsStorage::Size> parse(const char* string)
 {
-    return sscanf(string, "%llu", &value);
+    size_t value;
+    if (sscanf(string, "%zu", &value) == 1)
+        return value;
+    return WTF::nullopt;
 }
+#endif // CPU(ADDRESS64)
 
-static bool parse(const char* string, double& value)
+template<>
+Optional<OptionsStorage::Double> parse(const char* string)
 {
-    return sscanf(string, "%lf", &value) == 1;
+    double value;
+    if (sscanf(string, "%lf", &value) == 1)
+        return value;
+    return WTF::nullopt;
 }
 
-static bool parse(const char* string, OptionRange& value)
+template<>
+Optional<OptionsStorage::OptionRange> parse(const char* string)
 {
-    return value.init(string);
+    OptionRange range;
+    if (range.init(string))
+        return range;
+    return WTF::nullopt;
 }
 
-static bool parse(const char* string, const char*& value)
+template<>
+Optional<OptionsStorage::OptionString> parse(const char* string)
 {
-    if (!strlen(string)) {
-        value = nullptr;
-        return true;
-    }
+    const char* value = nullptr;
+    if (!strlen(string))
+        return value;
 
     // FIXME <https://webkit.org/b/169057>: This could leak if this option is set more than once.
     // Given that Options are typically used for testing, this isn't considered to be a problem.
     value = WTF::fastStrDup(string);
-    return true;
+    return value;
 }
 
-static bool parse(const char* string, GCLogging::Level& value)
+template<>
+Optional<OptionsStorage::GCLogLevel> parse(const char* string)
 {
-    if (equalLettersIgnoringASCIICase(string, "none") || equalLettersIgnoringASCIICase(string, "no") || equalLettersIgnoringASCIICase(string, "false") || !strcmp(string, "0")) {
-        value = GCLogging::None;
-        return true;
-    }
+    if (equalLettersIgnoringASCIICase(string, "none") || equalLettersIgnoringASCIICase(string, "no") || equalLettersIgnoringASCIICase(string, "false") || !strcmp(string, "0"))
+        return GCLogging::None;
 
-    if (equalLettersIgnoringASCIICase(string, "basic") || equalLettersIgnoringASCIICase(string, "yes") || equalLettersIgnoringASCIICase(string, "true") || !strcmp(string, "1")) {
-        value = GCLogging::Basic;
-        return true;
-    }
+    if (equalLettersIgnoringASCIICase(string, "basic") || equalLettersIgnoringASCIICase(string, "yes") || equalLettersIgnoringASCIICase(string, "true") || !strcmp(string, "1"))
+        return GCLogging::Basic;
 
-    if (equalLettersIgnoringASCIICase(string, "verbose") || !strcmp(string, "2")) {
-        value = GCLogging::Verbose;
-        return true;
-    }
+    if (equalLettersIgnoringASCIICase(string, "verbose") || !strcmp(string, "2"))
+        return GCLogging::Verbose;
 
-    return false;
+    return WTF::nullopt;
 }
 
 bool Options::isAvailable(Options::ID id, Options::Availability availability)
@@ -168,8 +181,13 @@ bool overrideOptionWithHeuristic(T& variable, Options::ID id, const char* name,
     if (!stringValue)
         return false;
     
-    if (available && parse(stringValue, variable))
-        return true;
+    if (available) {
+        Optional<T> value = parse<T>(stringValue);
+        if (value) {
+            variable = value.value();
+            return true;
+        }
+    }
     
     fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
     return false;
@@ -283,9 +301,9 @@ void OptionRange::dump(PrintStream& out) const
 }
 
 // Realize the names for each of the options:
-const Options::EntryInfo Options::s_optionsInfo[Options::numberOfOptions] = {
+const Options::ConstMetaData Options::s_constMetaData[NumberOfOptions] = {
 #define FILL_OPTION_INFO(type_, name_, defaultValue_, availability_, description_) \
-    { #name_, description_, Options::Type::type_, Availability::availability_ },
+    { #name_, description_, Options::Type::type_, Availability::availability_, offsetof(OptionsStorage, name_), offsetof(OptionsStorage, name_##Default) },
     FOR_EACH_JSC_OPTION(FILL_OPTION_INFO)
 #undef FILL_OPTION_INFO
 };
@@ -298,28 +316,18 @@ static void scaleJITPolicy()
     else if (scaleFactor < 0.0)
         scaleFactor = 0.0;
 
-    struct OptionToScale {
-        Options::ID id;
-        int32_t minVal;
+    auto scaleOption = [&] (int32_t& optionValue, int32_t minValue) {
+        optionValue *= scaleFactor;
+        optionValue = std::max(optionValue, minValue);
     };
 
-    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 }
-    };
-
-    const 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);
-    }
+    scaleOption(Options::thresholdForJITAfterWarmUp(), 0);
+    scaleOption(Options::thresholdForJITSoon(), 0);
+    scaleOption(Options::thresholdForOptimizeAfterWarmUp(), 1);
+    scaleOption(Options::thresholdForOptimizeAfterLongWarmUp(), 1);
+    scaleOption(Options::thresholdForOptimizeSoon(), 1);
+    scaleOption(Options::thresholdForFTLOptimizeSoon(), 2);
+    scaleOption(Options::thresholdForFTLOptimizeAfterWarmUp(), 2);
 }
 
 static void overrideDefaults()
@@ -446,7 +454,7 @@ static void recomputeDependentOptions()
     if (!Options::useConcurrentGC())
         Options::collectContinuously() = false;
 
-    if (Option(Options::jitPolicyScaleID).isOverridden())
+    if (Options::jitPolicyScale() != Options::jitPolicyScaleDefault())
         scaleJITPolicy();
     
     if (Options::forceEagerCompilation()) {
@@ -516,6 +524,18 @@ static void recomputeDependentOptions()
         Options::randomIntegrityAuditRate() = 1.0;
 }
 
+inline void* Options::addressOfOption(Options::ID id)
+{
+    auto offset = Options::s_constMetaData[id].offsetOfOption;
+    return reinterpret_cast<uint8_t*>(&g_jscConfig.options) + offset;
+}
+
+inline void* Options::addressOfOptionDefault(Options::ID id)
+{
+    auto offset = Options::s_constMetaData[id].offsetOfOptionDefault;
+    return reinterpret_cast<uint8_t*>(&g_jscConfig.options) + offset;
+}
+
 void Options::initialize()
 {
     static std::once_flag initializeOptionsOnceFlag;
@@ -523,13 +543,23 @@ void Options::initialize()
     std::call_once(
         initializeOptionsOnceFlag,
         [] {
+            // Sanity check that options address computation is working.
+            RELEASE_ASSERT(Options::addressOfOption(useKernTCSMID) ==  &Options::useKernTCSM());
+            RELEASE_ASSERT(Options::addressOfOptionDefault(useKernTCSMID) ==  &Options::useKernTCSMDefault());
+            RELEASE_ASSERT(Options::addressOfOption(gcMaxHeapSizeID) ==  &Options::gcMaxHeapSize());
+            RELEASE_ASSERT(Options::addressOfOptionDefault(gcMaxHeapSizeID) ==  &Options::gcMaxHeapSizeDefault());
+            RELEASE_ASSERT(Options::addressOfOption(forceOSRExitToLLIntID) ==  &Options::forceOSRExitToLLInt());
+            RELEASE_ASSERT(Options::addressOfOptionDefault(forceOSRExitToLLIntID) ==  &Options::forceOSRExitToLLIntDefault());
+
 #ifndef NDEBUG
             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
 
@@ -732,7 +762,7 @@ 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)      \
@@ -740,11 +770,10 @@ bool Options::setOptionWithoutAlias(const char* arg)
         if (Availability::availability_ != Availability::Normal     \
             && !isAvailable(name_##ID, Availability::availability_)) \
             return false;                                          \
-        OptionEntry::type_ value;                                  \
-        value = (defaultValue_);                                   \
-        bool success = parse(valueStr, value);                     \
-        if (success) {                                             \
-            name_() = value;                                       \
+        Optional<OptionsStorage::type_> value;                     \
+        value = parse<OptionsStorage::type_>(valueStr);            \
+        if (value) {                                               \
+            name_() = value.value();                               \
             correctOptions();                                      \
             recomputeDependentOptions();                           \
             return true;                                           \
@@ -758,13 +787,12 @@ bool Options::setOptionWithoutAlias(const char* arg)
     return false; // No option matched.
 }
 
-static bool invertBoolOptionValue(const char* valueStr, const char*& invertedValueStr)
+static const char* invertBoolOptionValue(const char* valueStr)
 {
-    bool boolValue;
-    if (!parse(valueStr, boolValue))
-        return false;
-    invertedValueStr = boolValue ? "false" : "true";
-    return true;
+    Optional<OptionsStorage::Bool> value = parse<OptionsStorage::Bool>(valueStr);
+    if (!value)
+        return nullptr;
+    return value.value() ? "false" : "true";
 }
 
 
@@ -788,8 +816,8 @@ bool Options::setAliasedOption(const char* arg)
             unaliasedOption = unaliasedOption + equalStr;               \
         else {                                                          \
             ASSERT(equivalence == InvertedOption);                      \
-            const char* invertedValueStr = nullptr;                     \
-            if (!invertBoolOptionValue(equalStr + 1, invertedValueStr)) \
+            auto* invertedValueStr = invertBoolOptionValue(equalStr + 1); \
+            if (!invertedValueStr)                                      \
                 return false;                                           \
             unaliasedOption = unaliasedOption + "=" + invertedValueStr; \
         }                                                               \
@@ -821,7 +849,7 @@ 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);
@@ -840,13 +868,54 @@ void Options::dumpAllOptions(FILE* stream, DumpLevel level, const char* title)
     fprintf(stream, "%s", builder.toString().utf8().data());
 }
 
+struct OptionReader {
+    class Option {
+    public:
+        void dump(StringBuilder&) const;
+
+        bool operator==(const Option& other) const;
+        bool operator!=(const Option& other) const { return !(*this == other); }
+
+        const char* name() const { return Options::s_constMetaData[m_id].name; }
+        const char* description() const { return Options::s_constMetaData[m_id].description; }
+        Options::Type type() const { return Options::s_constMetaData[m_id].type; }
+        Options::Availability availability() const { return Options::s_constMetaData[m_id].availability; }
+        bool isOverridden() const { return *this != OptionReader::defaultFor(m_id); }
+
+    private:
+        Option(Options::ID id, void* addressOfValue)
+            : m_id(id)
+        {
+            initValue(addressOfValue);
+        }
+
+        void initValue(void* addressOfValue);
+
+        Options::ID m_id;
+        union {
+            bool m_bool;
+            unsigned m_unsigned;
+            double m_double;
+            int32_t m_int32;
+            size_t m_size;
+            OptionRange m_optionRange;
+            const char* m_optionString;
+            GCLogging::Level m_gcLogLevel;
+        };
+
+        friend struct OptionReader;
+    };
+
+    static const Option optionFor(Options::ID);
+    static const Option defaultFor(Options::ID);
+};
+
 void Options::dumpOption(StringBuilder& builder, DumpLevel level, Options::ID id,
     const char* header, const char* footer, DumpDefaultsOption dumpDefaultsOption)
 {
-    if (id >= numberOfOptions)
-        return; // Illegal option.
+    RELEASE_ASSERT(static_cast<size_t>(id) < NumberOfOptions);
 
-    Option option(id);
+    auto option = OptionReader::optionFor(id);
     Availability availability = option.availability();
     if (availability != Availability::Normal && !isAvailable(id, availability))
         return;
@@ -863,8 +932,9 @@ void Options::dumpOption(StringBuilder& builder, DumpLevel level, Options::ID id
     option.dump(builder);
 
     if (wasOverridden && (dumpDefaultsOption == DumpDefaults)) {
+        auto defaultOption = OptionReader::defaultFor(id);
         builder.appendLiteral(" (default: ");
-        option.defaultOption().dump(builder);
+        defaultOption.dump(builder);
         builder.appendLiteral(")");
     }
 
@@ -885,29 +955,70 @@ void Options::ensureOptionsAreCoherent()
         CRASH();
 }
 
-void Option::dump(StringBuilder& builder) const
+const OptionReader::Option OptionReader::optionFor(Options::ID id)
+{
+    return Option(id, Options::addressOfOption(id));
+}
+
+const OptionReader::Option OptionReader::defaultFor(Options::ID id)
+{
+    return Option(id, Options::addressOfOptionDefault(id));
+}
+
+void OptionReader::Option::initValue(void* addressOfValue)
+{
+    Options::Type type = Options::s_constMetaData[m_id].type;
+    switch (type) {
+    case Options::Type::Bool:
+        memcpy(&m_bool, addressOfValue, sizeof(OptionsStorage::Bool));
+        break;
+    case Options::Type::Unsigned:
+        memcpy(&m_unsigned, addressOfValue, sizeof(OptionsStorage::Unsigned));
+        break;
+    case Options::Type::Double:
+        memcpy(&m_double, addressOfValue, sizeof(OptionsStorage::Double));
+        break;
+    case Options::Type::Int32:
+        memcpy(&m_int32, addressOfValue, sizeof(OptionsStorage::Int32));
+        break;
+    case Options::Type::Size:
+        memcpy(&m_size, addressOfValue, sizeof(OptionsStorage::Size));
+        break;
+    case Options::Type::OptionRange:
+        memcpy(&m_optionRange, addressOfValue, sizeof(OptionsStorage::OptionRange));
+        break;
+    case Options::Type::OptionString:
+        memcpy(&m_optionString, addressOfValue, sizeof(OptionsStorage::OptionString));
+        break;
+    case Options::Type::GCLogLevel:
+        memcpy(&m_gcLogLevel, addressOfValue, sizeof(OptionsStorage::GCLogLevel));
+        break;
+    }
+}
+
+void OptionReader::Option::dump(StringBuilder& builder) const
 {
     switch (type()) {
     case Options::Type::Bool:
-        builder.append(m_entry.valBool ? "true" : "false");
+        builder.append(m_bool ? "true" : "false");
         break;
     case Options::Type::Unsigned:
-        builder.appendNumber(m_entry.valUnsigned);
+        builder.appendNumber(m_unsigned);
         break;
     case Options::Type::Size:
-        builder.appendNumber(m_entry.valSize);
+        builder.appendNumber(m_size);
         break;
     case Options::Type::Double:
-        builder.appendFixedPrecisionNumber(m_entry.valDouble);
+        builder.appendFixedPrecisionNumber(m_double);
         break;
     case Options::Type::Int32:
-        builder.appendNumber(m_entry.valInt32);
+        builder.appendNumber(m_int32);
         break;
     case Options::Type::OptionRange:
-        builder.append(m_entry.valOptionRange.rangeString());
+        builder.append(m_optionRange.rangeString());
         break;
     case Options::Type::OptionString: {
-        const char* option = m_entry.valOptionString;
+        const char* option = m_optionString;
         if (!option)
             option = "";
         builder.append('"');
@@ -916,32 +1027,33 @@ void Option::dump(StringBuilder& builder) const
         break;
     }
     case Options::Type::GCLogLevel: {
-        builder.append(GCLogging::levelAsString(m_entry.valGCLogLevel));
+        builder.append(GCLogging::levelAsString(m_gcLogLevel));
         break;
     }
     }
 }
 
-bool Option::operator==(const Option& other) const
+bool OptionReader::Option::operator==(const Option& other) const
 {
+    ASSERT(type() == other.type());
     switch (type()) {
     case Options::Type::Bool:
-        return m_entry.valBool == other.m_entry.valBool;
+        return m_bool == other.m_bool;
     case Options::Type::Unsigned:
-        return m_entry.valUnsigned == other.m_entry.valUnsigned;
+        return m_unsigned == other.m_unsigned;
     case Options::Type::Size:
-        return m_entry.valSize == other.m_entry.valSize;
+        return m_size == other.m_size;
     case Options::Type::Double:
-        return (m_entry.valDouble == other.m_entry.valDouble) || (std::isnan(m_entry.valDouble) && std::isnan(other.m_entry.valDouble));
+        return (m_double == other.m_double) || (std::isnan(m_double) && std::isnan(other.m_double));
     case Options::Type::Int32:
-        return m_entry.valInt32 == other.m_entry.valInt32;
+        return m_int32 == other.m_int32;
     case Options::Type::OptionRange:
-        return m_entry.valOptionRange.rangeString() == other.m_entry.valOptionRange.rangeString();
+        return m_optionRange.rangeString() == other.m_optionRange.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));
+        return (m_optionString == other.m_optionString)
+            || (m_optionString && other.m_optionString && !strcmp(m_optionString, other.m_optionString));
     case Options::Type::GCLogLevel:
-        return m_entry.valGCLogLevel == other.m_entry.valGCLogLevel;
+        return m_gcLogLevel == other.m_gcLogLevel;
     }
     return false;
 }
index 58c9b3e..93bd8ca 100644 (file)
@@ -39,28 +39,16 @@ using WTF::StringBuilder;
 
 namespace JSC {
 
-#if PLATFORM(IOS_FAMILY)
-#define MAXIMUM_NUMBER_OF_FTL_COMPILER_THREADS 2
-#else
-#define MAXIMUM_NUMBER_OF_FTL_COMPILER_THREADS 8
-#endif
-
-#if ENABLE(WEBASSEMBLY_STREAMING_API)
-constexpr bool enableWebAssemblyStreamingApi = true;
-#else
-constexpr bool enableWebAssemblyStreamingApi = false;
-#endif
-
 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
@@ -69,13 +57,12 @@ public:
 #define DECLARE_OPTION_ID(type_, name_, defaultValue_, availability_, description_) \
     name_##ID,
 
-    enum ID {
+    enum ID : uint16_t {
         FOR_EACH_JSC_OPTION(DECLARE_OPTION_ID)
-        numberOfOptions
     };
 #undef DECLARE_OPTION_ID
 
-    enum class Type {
+    enum class Type : uint8_t {
         Bool,
         Unsigned,
         Double,
@@ -102,8 +89,8 @@ 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_; }
+    ALWAYS_INLINE static OptionsStorage::type_& name_() { return g_jscConfig.options.name_; }  \
+    ALWAYS_INLINE static OptionsStorage::type_& name_##Default() { return g_jscConfig.options.name_##Default; }
 
     FOR_EACH_JSC_OPTION(DECLARE_OPTION_ACCESSORS)
 #undef DECLARE_OPTION_ACCESSORS
@@ -111,13 +98,13 @@ public:
     static bool isAvailable(ID, Availability);
 
 private:
-
-    // For storing constant meta data about each option:
-    struct EntryInfo {
+    struct ConstMetaData {
         const char* name;
         const char* description;
         Type type;
         Availability availability;
+        uint16_t offsetOfOption;
+        uint16_t offsetOfOptionDefault;
     };
 
     Options();
@@ -136,115 +123,12 @@ private:
     static bool setAliasedOption(const char* arg);
     static bool overrideAliasedOptionWithHeuristic(const char* name);
 
-    static const EntryInfo s_optionsInfo[numberOfOptions];
+    inline static void* addressOfOption(Options::ID);
+    inline static void* addressOfOptionDefault(Options::ID);
 
-    friend class Option;
-};
+    static const ConstMetaData s_constMetaData[NumberOfOptions];
 
-class Option {
-public:
-    Option(Options::ID id)
-        : m_id(id)
-        , m_entry(g_jscConfig.options[m_id])
-    {
-    }
-    
-    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;
-    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;
+    friend struct OptionReader;
 };
 
-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;
-}
-
-inline bool Option::isOverridden() const
-{
-    return *this != defaultOption();
-}
-
-inline const Option Option::defaultOption() const
-{
-    return Option(m_id, g_jscConfig.defaultOptions[m_id]);
-}
-
-inline bool& Option::boolVal()
-{
-    return m_entry.valBool;
-}
-
-inline unsigned& Option::unsignedVal()
-{
-    return m_entry.valUnsigned;
-}
-
-inline double& Option::doubleVal()
-{
-    return m_entry.valDouble;
-}
-
-inline int32_t& Option::int32Val()
-{
-    return m_entry.valInt32;
-}
-
-inline OptionRange Option::optionRangeVal()
-{
-    return m_entry.valOptionRange;
-}
-
-inline const char* Option::optionStringVal()
-{
-    return m_entry.valOptionString;
-}
-
-inline GCLogging::Level& Option::gcLogLevelVal()
-{
-    return m_entry.valGCLogLevel;
-}
-
 } // namespace JSC
index cf145f1..eeb95bd 100644 (file)
 
 #pragma once
 
+#include "GCLogging.h"
+
+using WTF::PrintStream;
+
 namespace JSC {
 
+#if PLATFORM(IOS_FAMILY)
+#define MAXIMUM_NUMBER_OF_FTL_COMPILER_THREADS 2
+#else
+#define MAXIMUM_NUMBER_OF_FTL_COMPILER_THREADS 8
+#endif
+
+#if ENABLE(WEBASSEMBLY_STREAMING_API)
+constexpr bool enableWebAssemblyStreamingApi = true;
+#else
+constexpr bool enableWebAssemblyStreamingApi = false;
+#endif
+
 // 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 fields in OptionsStorage embedded in JSC::Config.
 //
-//     Options::initialize() will initialize the array 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
-// set like so:
+//     Options::initialize() will initialize the option 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 set like
+// so:
 //
 //     bool jitIsOn = Options::useJIT();  // Get the option value.
 //     Options::useJIT() = false;         // Sets the option value.
@@ -54,6 +70,10 @@ 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 instantiated.
+// On instantiation of the first VM instance, the Options will be write protected
+// and cannot be modified thereafter.
 
 #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.") \
@@ -527,5 +547,54 @@ constexpr size_t countNumberOfJSCOptions()
 
 constexpr size_t NumberOfOptions = countNumberOfJSCOptions();
 
-} // 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;
+};
+
+struct OptionsStorage {
+    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;
+
+#define DECLARE_OPTION(type_, name_, defaultValue_, availability_, description_) \
+    type_ name_; \
+    type_ name_##Default;
+FOR_EACH_JSC_OPTION(DECLARE_OPTION)
+#undef DECLARE_OPTION
+};
+
+// Options::Metadata's offsetOfOption and offsetOfOptionDefault relies on this.
+static_assert(sizeof(OptionsStorage) <= 16 * KB);
+
+} // namespace JSC