/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "config.h"
#include "Options.h"
+#include "HeapStatistics.h"
+#include <algorithm>
#include <limits>
+#include <stdlib.h>
+#include <string.h>
+#include <wtf/DataLog.h>
#include <wtf/NumberOfCores.h>
#include <wtf/PageBlock.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/StringExtras.h>
#if OS(DARWIN) && ENABLE(PARALLEL_GC)
#include <sys/sysctl.h>
#endif
-// Set to 1 to control the heuristics using environment variables.
-#define ENABLE_RUN_TIME_HEURISTICS 0
-
-#if ENABLE(RUN_TIME_HEURISTICS)
-#include <stdio.h>
-#include <stdlib.h>
-#include <wtf/StdLibExtras.h>
-#endif
-
-namespace JSC { namespace Options {
-
-bool useJIT;
-
-unsigned maximumOptimizationCandidateInstructionCount;
-
-unsigned maximumFunctionForCallInlineCandidateInstructionCount;
-unsigned maximumFunctionForConstructInlineCandidateInstructionCount;
-
-unsigned maximumInliningDepth;
-
-int32_t thresholdForJITAfterWarmUp;
-int32_t thresholdForJITSoon;
-
-int32_t thresholdForOptimizeAfterWarmUp;
-int32_t thresholdForOptimizeAfterLongWarmUp;
-int32_t thresholdForOptimizeSoon;
-
-int32_t executionCounterIncrementForLoop;
-int32_t executionCounterIncrementForReturn;
+namespace JSC {
-unsigned desiredSpeculativeSuccessFailRatio;
-
-double likelyToTakeSlowCaseThreshold;
-double couldTakeSlowCaseThreshold;
-unsigned likelyToTakeSlowCaseMinimumCount;
-unsigned couldTakeSlowCaseMinimumCount;
-
-double osrExitProminenceForFrequentExitSite;
-
-unsigned largeFailCountThresholdBase;
-unsigned largeFailCountThresholdBaseForLoop;
-unsigned forcedOSRExitCountForReoptimization;
-
-unsigned reoptimizationRetryCounterMax;
-unsigned reoptimizationRetryCounterStep;
-
-unsigned minimumOptimizationDelay;
-unsigned maximumOptimizationDelay;
-double desiredProfileLivenessRate;
-double desiredProfileFullnessRate;
-
-double doubleVoteRatioForDoubleFormat;
-
-unsigned minimumNumberOfScansBetweenRebalance;
-unsigned gcMarkStackSegmentSize;
-unsigned minimumNumberOfCellsToKeep;
-unsigned maximumNumberOfSharedSegments;
-unsigned sharedStackWakeupThreshold;
-unsigned numberOfGCMarkers;
-unsigned opaqueRootMergeThreshold;
-
-#if ENABLE(RUN_TIME_HEURISTICS)
static bool parse(const char* string, bool& value)
{
if (!strcasecmp(string, "true") || !strcasecmp(string, "yes") || !strcmp(string, "1")) {
return sscanf(string, "%lf", &value) == 1;
}
-template<typename T, typename U>
-void setHeuristic(T& variable, const char* name, U value)
+static bool parse(const char* string, OptionRange& value)
{
- const char* stringValue = getenv(name);
- if (!stringValue) {
- variable = safeCast<T>(value);
- return;
+ return value.init(string);
+}
+
+static bool parse(const char* string, const char*& value)
+{
+ value = string;
+ return true;
+}
+
+static bool parse(const char* string, GCLogging::Level& value)
+{
+ if (!strcasecmp(string, "none") || !strcasecmp(string, "no") || !strcasecmp(string, "false") || !strcmp(string, "0")) {
+ value = GCLogging::None;
+ return true;
+ }
+
+ if (!strcasecmp(string, "basic") || !strcasecmp(string, "yes") || !strcasecmp(string, "true") || !strcmp(string, "1")) {
+ value = GCLogging::Basic;
+ return true;
+ }
+
+ if (!strcasecmp(string, "verbose") || !strcmp(string, "2")) {
+ value = GCLogging::Verbose;
+ return true;
}
+
+ return false;
+}
+
+template<typename T>
+bool overrideOptionWithHeuristic(T& variable, const char* name)
+{
+#if !OS(WINCE)
+ const char* stringValue = getenv(name);
+ if (!stringValue)
+ return false;
if (parse(stringValue, variable))
- return;
+ return true;
fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
- variable = safeCast<T>(value);
+#endif
+ return false;
+}
+
+static unsigned computeNumberOfWorkerThreads(int maxNumberOfWorkerThreads, int minimum = 1)
+{
+ int cpusToUse = std::min(WTF::numberOfProcessorCores(), maxNumberOfWorkerThreads);
+
+ // Be paranoid, it is the OS we're dealing with, after all.
+ ASSERT(cpusToUse >= 1);
+ return std::max(cpusToUse, minimum);
}
-#define SET(variable, value) setHeuristic(variable, "JSC_" #variable, value)
+static int32_t computePriorityDeltaOfWorkerThreads(int32_t twoCorePriorityDelta, int32_t multiCorePriorityDelta)
+{
+ if (WTF::numberOfProcessorCores() <= 2)
+ return twoCorePriorityDelta;
+
+ return multiCorePriorityDelta;
+}
+
+static unsigned computeNumberOfGCMarkers(unsigned maxNumberOfGCMarkers)
+{
+#if ENABLE(PARALLEL_GC)
+ return computeNumberOfWorkerThreads(maxNumberOfGCMarkers);
#else
-#define SET(variable, value) variable = value
+ UNUSED_PARAM(maxNumberOfGCMarkers);
+ return 1;
#endif
+}
-void initializeOptions()
+bool OptionRange::init(const char* rangeString)
{
- SET(useJIT, true);
-
- SET(maximumOptimizationCandidateInstructionCount, 10000);
-
- SET(maximumFunctionForCallInlineCandidateInstructionCount, 180);
- SET(maximumFunctionForConstructInlineCandidateInstructionCount, 100);
-
- SET(maximumInliningDepth, 5);
+ // rangeString should be in the form of [!]<low>[:<high>]
+ // where low and high are unsigned
- SET(thresholdForJITAfterWarmUp, 100);
- SET(thresholdForJITSoon, 100);
+ bool invert = false;
- SET(thresholdForOptimizeAfterWarmUp, 1000);
- SET(thresholdForOptimizeAfterLongWarmUp, 5000);
- SET(thresholdForOptimizeSoon, 1000);
+ if (m_state > Uninitialized)
+ return true;
- SET(executionCounterIncrementForLoop, 1);
- SET(executionCounterIncrementForReturn, 15);
+ if (!rangeString) {
+ m_state = InitError;
+ return false;
+ }
- SET(desiredSpeculativeSuccessFailRatio, 6);
-
- SET(likelyToTakeSlowCaseThreshold, 0.15);
- SET(couldTakeSlowCaseThreshold, 0.05); // Shouldn't be zero because some ops will spuriously take slow case, for example for linking or caching.
- SET(likelyToTakeSlowCaseMinimumCount, 100);
- SET(couldTakeSlowCaseMinimumCount, 10);
-
- SET(osrExitProminenceForFrequentExitSite, 0.3);
+ m_rangeString = rangeString;
- SET(largeFailCountThresholdBase, 20);
- SET(largeFailCountThresholdBaseForLoop, 1);
- SET(forcedOSRExitCountForReoptimization, 250);
+ if (*rangeString == '!') {
+ invert = true;
+ rangeString++;
+ }
- SET(reoptimizationRetryCounterStep, 1);
+ int scanResult = sscanf(rangeString, " %u:%u", &m_lowLimit, &m_highLimit);
- SET(minimumOptimizationDelay, 1);
- SET(maximumOptimizationDelay, 5);
- SET(desiredProfileLivenessRate, 0.75);
- SET(desiredProfileFullnessRate, 0.35);
-
- SET(doubleVoteRatioForDoubleFormat, 2);
-
- SET(minimumNumberOfScansBetweenRebalance, 10000);
- SET(gcMarkStackSegmentSize, pageSize());
- SET(minimumNumberOfCellsToKeep, 10);
- SET(maximumNumberOfSharedSegments, 3);
- SET(sharedStackWakeupThreshold, 1);
- SET(opaqueRootMergeThreshold, 1000);
-
- int cpusToUse = 1;
-#if ENABLE(PARALLEL_GC)
- cpusToUse = WTF::numberOfProcessorCores();
+ if (!scanResult || scanResult == EOF) {
+ m_state = InitError;
+ return false;
+ }
+
+ if (scanResult == 1)
+ m_highLimit = m_lowLimit;
+
+ if (m_lowLimit > m_highLimit) {
+ m_state = InitError;
+ return false;
+ }
+
+ m_state = invert ? Inverted : Normal;
+
+ return true;
+}
+
+bool OptionRange::isInRange(unsigned count)
+{
+ if (m_state < Normal)
+ return true;
+
+ if ((m_lowLimit <= count) && (count <= m_highLimit))
+ return m_state == Normal ? true : false;
+
+ return m_state == Normal ? false : true;
+}
+
+Options::Entry Options::s_options[Options::numberOfOptions];
+
+// Realize the names for each of the options:
+const Options::EntryInfo Options::s_optionsInfo[Options::numberOfOptions] = {
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ { #name_, Options::type_##Type },
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+};
+
+static void recomputeDependentOptions()
+{
+#if !ENABLE(JIT)
+ Options::useLLInt() = true;
+ Options::useJIT() = false;
+ Options::useDFGJIT() = false;
+ Options::useFTLJIT() = false;
+#endif
+#if !ENABLE(YARR_JIT)
+ Options::useRegExpJIT() = false;
+#endif
+#if !ENABLE(CONCURRENT_JIT)
+ Options::enableConcurrentJIT() = false;
+#endif
+#if !ENABLE(DFG_JIT)
+ Options::useDFGJIT() = false;
+ Options::useFTLJIT() = false;
+#endif
+#if !ENABLE(FTL_JIT)
+ Options::useFTLJIT() = false;
#endif
- // We don't scale so well beyond 4.
- if (cpusToUse > 4)
- cpusToUse = 4;
- // Be paranoid, it is the OS we're dealing with, after all.
- if (cpusToUse < 1)
- cpusToUse = 1;
-
- SET(numberOfGCMarkers, cpusToUse);
- ASSERT(thresholdForOptimizeAfterLongWarmUp >= thresholdForOptimizeAfterWarmUp);
- ASSERT(thresholdForOptimizeAfterWarmUp >= thresholdForOptimizeSoon);
- ASSERT(thresholdForOptimizeAfterWarmUp >= 0);
+ if (Options::showDisassembly()
+ || Options::showDFGDisassembly()
+ || Options::showFTLDisassembly()
+ || Options::dumpBytecodeAtDFGTime()
+ || Options::dumpGraphAtEachPhase()
+ || Options::verboseCompilation()
+ || Options::verboseFTLCompilation()
+ || Options::logCompilationChanges()
+ || Options::validateGraph()
+ || Options::validateGraphAtEachPhase()
+ || Options::verboseOSR()
+ || Options::verboseCompilationQueue()
+ || Options::reportCompileTimes()
+ || Options::reportFTLCompileTimes()
+ || Options::verboseCFA()
+ || Options::verboseFTLFailure())
+ Options::alwaysComputeHash() = true;
// Compute the maximum value of the reoptimization retry counter. This is simply
// the largest value at which we don't overflow the execute counter, when using it
// to left-shift the execution counter by this amount. Currently the value ends
// up being 18, so this loop is not so terrible; it probably takes up ~100 cycles
// total on a 32-bit processor.
- reoptimizationRetryCounterMax = 0;
- while ((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << (reoptimizationRetryCounterMax + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()))
- reoptimizationRetryCounterMax++;
+ Options::reoptimizationRetryCounterMax() = 0;
+ while ((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << (Options::reoptimizationRetryCounterMax() + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()))
+ Options::reoptimizationRetryCounterMax()++;
+
+ ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) > 0);
+ ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
+}
+
+void Options::initialize()
+{
+ // Initialize each of the options with their default values:
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ name_() = defaultValue_;
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+
+ // Allow environment vars to override options if applicable.
+ // The evn var should be the name of the option prefixed with
+ // "JSC_".
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ if (overrideOptionWithHeuristic(name_(), "JSC_" #name_)) \
+ s_options[OPT_##name_].didOverride = true;
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+
+#if 0
+ ; // Deconfuse editors that do auto indentation
+#endif
- ASSERT((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) > 0);
- ASSERT((static_cast<int64_t>(thresholdForOptimizeAfterLongWarmUp) << reoptimizationRetryCounterMax) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
+ recomputeDependentOptions();
+
+ // Do range checks where needed and make corrections to the options:
+ ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
+ ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
+ ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);
}
-} } // namespace JSC::Options
+// Parses a single command line option in the format "<optionName>=<value>"
+// (no spaces allowed) and set the specified option if appropriate.
+bool Options::setOption(const char* arg)
+{
+ // arg should look like this:
+ // <jscOptionName>=<appropriate value>
+ const char* equalStr = strchr(arg, '=');
+ if (!equalStr)
+ return false;
+
+ const char* valueStr = equalStr + 1;
+
+ // For each option, check if the specify arg is a match. If so, set the arg
+ // if the value makes sense. Otherwise, move on to checking the next option.
+#define FOR_EACH_OPTION(type_, name_, defaultValue_) \
+ if (!strncmp(arg, #name_, equalStr - arg)) { \
+ type_ value; \
+ value = (defaultValue_); \
+ bool success = parse(valueStr, value); \
+ if (success) { \
+ name_() = value; \
+ s_options[OPT_##name_].didOverride = true; \
+ recomputeDependentOptions(); \
+ return true; \
+ } \
+ return false; \
+ }
+
+ JSC_OPTIONS(FOR_EACH_OPTION)
+#undef FOR_EACH_OPTION
+
+ return false; // No option matched.
+}
+
+void Options::dumpAllOptions(FILE* stream)
+{
+ fprintf(stream, "JSC runtime options:\n");
+ for (int id = 0; id < numberOfOptions; id++)
+ dumpOption(static_cast<OptionID>(id), stream, " ", "\n");
+}
+
+void Options::dumpOption(OptionID id, FILE* stream, const char* header, const char* footer)
+{
+ if (id >= numberOfOptions)
+ return; // Illegal option.
+
+ fprintf(stream, "%s%s: ", header, s_optionsInfo[id].name);
+ switch (s_optionsInfo[id].type) {
+ case boolType:
+ fprintf(stream, "%s", s_options[id].u.boolVal?"true":"false");
+ break;
+ case unsignedType:
+ fprintf(stream, "%u", s_options[id].u.unsignedVal);
+ break;
+ case doubleType:
+ fprintf(stream, "%lf", s_options[id].u.doubleVal);
+ break;
+ case int32Type:
+ fprintf(stream, "%d", s_options[id].u.int32Val);
+ break;
+ case optionRangeType:
+ fprintf(stream, "%s", s_options[id].u.optionRangeVal.rangeString());
+ break;
+ case optionStringType: {
+ const char* option = s_options[id].u.optionStringVal;
+ if (!option)
+ option = "";
+ fprintf(stream, "%s", option);
+ break;
+ }
+ case gcLogLevelType: {
+ fprintf(stream, "%s", GCLogging::levelAsString(s_options[id].u.gcLogLevelVal));
+ break;
+ }
+ }
+ fprintf(stream, "%s", footer);
+}
+} // namespace JSC