2 * Copyright (C) 2011-2012, 2014-2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "HeapStatistics.h"
36 #include <wtf/DataLog.h>
37 #include <wtf/NumberOfCores.h>
38 #include <wtf/PageBlock.h>
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/StringExtras.h>
42 #if OS(DARWIN) && ENABLE(PARALLEL_GC)
43 #include <sys/sysctl.h>
47 #include "MacroAssemblerX86.h"
52 static bool parse(const char* string
, bool& value
)
54 if (!strcasecmp(string
, "true") || !strcasecmp(string
, "yes") || !strcmp(string
, "1")) {
58 if (!strcasecmp(string
, "false") || !strcasecmp(string
, "no") || !strcmp(string
, "0")) {
65 static bool parse(const char* string
, int32_t& value
)
67 return sscanf(string
, "%d", &value
) == 1;
70 static bool parse(const char* string
, unsigned& value
)
72 return sscanf(string
, "%u", &value
) == 1;
75 static bool parse(const char* string
, double& value
)
77 return sscanf(string
, "%lf", &value
) == 1;
80 static bool parse(const char* string
, OptionRange
& value
)
82 return value
.init(string
);
85 static bool parse(const char* string
, const char*& value
)
91 static bool parse(const char* string
, GCLogging::Level
& value
)
93 if (!strcasecmp(string
, "none") || !strcasecmp(string
, "no") || !strcasecmp(string
, "false") || !strcmp(string
, "0")) {
94 value
= GCLogging::None
;
98 if (!strcasecmp(string
, "basic") || !strcasecmp(string
, "yes") || !strcasecmp(string
, "true") || !strcmp(string
, "1")) {
99 value
= GCLogging::Basic
;
103 if (!strcasecmp(string
, "verbose") || !strcmp(string
, "2")) {
104 value
= GCLogging::Verbose
;
112 bool overrideOptionWithHeuristic(T
& variable
, const char* name
)
114 const char* stringValue
= getenv(name
);
118 if (parse(stringValue
, variable
))
121 fprintf(stderr
, "WARNING: failed to parse %s=%s\n", name
, stringValue
);
125 static unsigned computeNumberOfWorkerThreads(int maxNumberOfWorkerThreads
, int minimum
= 1)
127 int cpusToUse
= std::min(WTF::numberOfProcessorCores(), maxNumberOfWorkerThreads
);
129 // Be paranoid, it is the OS we're dealing with, after all.
130 ASSERT(cpusToUse
>= 1);
131 return std::max(cpusToUse
, minimum
);
134 static int32_t computePriorityDeltaOfWorkerThreads(int32_t twoCorePriorityDelta
, int32_t multiCorePriorityDelta
)
136 if (WTF::numberOfProcessorCores() <= 2)
137 return twoCorePriorityDelta
;
139 return multiCorePriorityDelta
;
142 static unsigned computeNumberOfGCMarkers(unsigned maxNumberOfGCMarkers
)
144 #if ENABLE(PARALLEL_GC)
145 return computeNumberOfWorkerThreads(maxNumberOfGCMarkers
);
147 UNUSED_PARAM(maxNumberOfGCMarkers
);
152 bool OptionRange::init(const char* rangeString
)
154 // rangeString should be in the form of [!]<low>[:<high>]
155 // where low and high are unsigned
159 if (m_state
> Uninitialized
)
167 m_rangeString
= rangeString
;
169 if (*rangeString
== '!') {
174 int scanResult
= sscanf(rangeString
, " %u:%u", &m_lowLimit
, &m_highLimit
);
176 if (!scanResult
|| scanResult
== EOF
) {
182 m_highLimit
= m_lowLimit
;
184 if (m_lowLimit
> m_highLimit
) {
189 m_state
= invert
? Inverted
: Normal
;
194 bool OptionRange::isInRange(unsigned count
)
196 if (m_state
< Normal
)
199 if ((m_lowLimit
<= count
) && (count
<= m_highLimit
))
200 return m_state
== Normal
? true : false;
202 return m_state
== Normal
? false : true;
205 void OptionRange::dump(PrintStream
& out
) const
207 out
.print(m_rangeString
);
210 Options::Entry
Options::s_options
[Options::numberOfOptions
];
211 Options::Entry
Options::s_defaultOptions
[Options::numberOfOptions
];
213 // Realize the names for each of the options:
214 const Options::EntryInfo
Options::s_optionsInfo
[Options::numberOfOptions
] = {
215 #define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \
216 { #name_, description_, Options::Type::type_##Type },
217 JSC_OPTIONS(FOR_EACH_OPTION
)
218 #undef FOR_EACH_OPTION
221 static void scaleJITPolicy()
223 auto& scaleFactor
= Options::jitPolicyScale();
224 if (scaleFactor
> 1.0)
226 else if (scaleFactor
< 0.0)
229 struct OptionToScale
{
230 Options::OptionID id
;
234 static const OptionToScale optionsToScale
[] = {
235 { Options::thresholdForJITAfterWarmUpID
, 0 },
236 { Options::thresholdForJITSoonID
, 0 },
237 { Options::thresholdForOptimizeAfterWarmUpID
, 1 },
238 { Options::thresholdForOptimizeAfterLongWarmUpID
, 1 },
239 { Options::thresholdForOptimizeSoonID
, 1 },
240 { Options::thresholdForFTLOptimizeSoonID
, 2 },
241 { Options::thresholdForFTLOptimizeAfterWarmUpID
, 2 }
244 const int numberOfOptionsToScale
= sizeof(optionsToScale
) / sizeof(OptionToScale
);
245 for (int i
= 0; i
< numberOfOptionsToScale
; i
++) {
246 Option
option(optionsToScale
[i
].id
);
247 ASSERT(option
.type() == Options::Type::int32Type
);
248 option
.int32Val() *= scaleFactor
;
249 option
.int32Val() = std::max(option
.int32Val(), optionsToScale
[i
].minVal
);
253 static void recomputeDependentOptions()
256 Options::useLLInt() = true;
257 Options::useJIT() = false;
258 Options::useDFGJIT() = false;
259 Options::useFTLJIT() = false;
261 #if !ENABLE(YARR_JIT)
262 Options::useRegExpJIT() = false;
264 #if !ENABLE(CONCURRENT_JIT)
265 Options::enableConcurrentJIT() = false;
268 Options::useDFGJIT() = false;
269 Options::useFTLJIT() = false;
272 Options::useFTLJIT() = false;
274 #if OS(WINDOWS) && CPU(X86)
275 // Disable JIT on Windows if SSE2 is not present
276 if (!MacroAssemblerX86::supportsFloatingPoint())
277 Options::useJIT() = false;
279 if (Options::showDisassembly()
280 || Options::showDFGDisassembly()
281 || Options::showFTLDisassembly()
282 || Options::dumpBytecodeAtDFGTime()
283 || Options::dumpGraphAtEachPhase()
284 || Options::verboseCompilation()
285 || Options::verboseFTLCompilation()
286 || Options::logCompilationChanges()
287 || Options::validateGraph()
288 || Options::validateGraphAtEachPhase()
289 || Options::verboseOSR()
290 || Options::verboseCompilationQueue()
291 || Options::reportCompileTimes()
292 || Options::reportFTLCompileTimes()
293 || Options::verboseCFA()
294 || Options::verboseFTLFailure())
295 Options::alwaysComputeHash() = true;
297 if (Option(Options::jitPolicyScaleID
).isOverridden())
300 if (Options::forceEagerCompilation()) {
301 Options::thresholdForJITAfterWarmUp() = 10;
302 Options::thresholdForJITSoon() = 10;
303 Options::thresholdForOptimizeAfterWarmUp() = 20;
304 Options::thresholdForOptimizeAfterLongWarmUp() = 20;
305 Options::thresholdForOptimizeSoon() = 20;
306 Options::thresholdForFTLOptimizeAfterWarmUp() = 20;
307 Options::thresholdForFTLOptimizeSoon() = 20;
308 Options::maximumEvalCacheableSourceLength() = 150000;
309 Options::enableConcurrentJIT() = false;
312 // Compute the maximum value of the reoptimization retry counter. This is simply
313 // the largest value at which we don't overflow the execute counter, when using it
314 // to left-shift the execution counter by this amount. Currently the value ends
315 // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles
316 // total on a 32-bit processor.
317 Options::reoptimizationRetryCounterMax() = 0;
318 while ((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << (Options::reoptimizationRetryCounterMax() + 1)) <= static_cast<int64_t>(std::numeric_limits
<int32_t>::max()))
319 Options::reoptimizationRetryCounterMax()++;
321 ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) > 0);
322 ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) <= static_cast<int64_t>(std::numeric_limits
<int32_t>::max()));
325 void Options::initialize()
327 static std::once_flag initializeOptionsOnceFlag
;
330 initializeOptionsOnceFlag
,
332 // Initialize each of the options with their default values:
333 #define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \
334 name_() = defaultValue_; \
335 name_##Default() = defaultValue_;
336 JSC_OPTIONS(FOR_EACH_OPTION
)
337 #undef FOR_EACH_OPTION
339 // It *probably* makes sense for other platforms to enable this.
340 #if PLATFORM(IOS) && CPU(ARM64)
341 enableLLVMFastISel() = true;
344 // Allow environment vars to override options if applicable.
345 // The evn var should be the name of the option prefixed with
347 #define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \
348 overrideOptionWithHeuristic(name_(), "JSC_" #name_);
349 JSC_OPTIONS(FOR_EACH_OPTION
)
350 #undef FOR_EACH_OPTION
353 ; // Deconfuse editors that do auto indentation
356 recomputeDependentOptions();
358 // Do range checks where needed and make corrections to the options:
359 ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
360 ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
361 ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);
363 if (Options::showOptions()) {
364 DumpLevel level
= static_cast<DumpLevel
>(Options::showOptions());
365 if (level
> DumpLevel::Verbose
)
366 level
= DumpLevel::Verbose
;
368 const char* title
= nullptr;
370 case DumpLevel::None
:
372 case DumpLevel::Overridden
:
373 title
= "Overridden JSC options:";
376 title
= "All JSC options:";
378 case DumpLevel::Verbose
:
379 title
= "All JSC options with descriptions:";
382 dumpAllOptions(level
, title
);
385 ensureOptionsAreCoherent();
389 // Parses a single command line option in the format "<optionName>=<value>"
390 // (no spaces allowed) and set the specified option if appropriate.
391 bool Options::setOption(const char* arg
)
393 // arg should look like this:
394 // <jscOptionName>=<appropriate value>
395 const char* equalStr
= strchr(arg
, '=');
399 const char* valueStr
= equalStr
+ 1;
401 // For each option, check if the specify arg is a match. If so, set the arg
402 // if the value makes sense. Otherwise, move on to checking the next option.
403 #define FOR_EACH_OPTION(type_, name_, defaultValue_, description_) \
404 if (!strncmp(arg, #name_, equalStr - arg)) { \
406 value = (defaultValue_); \
407 bool success = parse(valueStr, value); \
410 recomputeDependentOptions(); \
416 JSC_OPTIONS(FOR_EACH_OPTION
)
417 #undef FOR_EACH_OPTION
419 return false; // No option matched.
422 void Options::dumpAllOptions(DumpLevel level
, const char* title
, FILE* stream
)
425 fprintf(stream
, "%s\n", title
);
426 for (int id
= 0; id
< numberOfOptions
; id
++)
427 dumpOption(level
, static_cast<OptionID
>(id
), stream
, " ", "\n");
430 void Options::dumpOption(DumpLevel level
, OptionID id
, FILE* stream
, const char* header
, const char* footer
)
432 if (id
>= numberOfOptions
)
433 return; // Illegal option.
436 bool wasOverridden
= option
.isOverridden();
437 bool needsDescription
= (level
== DumpLevel::Verbose
&& option
.description());
439 if (level
== DumpLevel::Overridden
&& !wasOverridden
)
442 fprintf(stream
, "%s%s: ", header
, option
.name());
446 fprintf(stream
, " (default: ");
447 option
.defaultOption().dump(stream
);
448 fprintf(stream
, ")");
451 if (needsDescription
)
452 fprintf(stream
, " ... %s", option
.description());
454 fprintf(stream
, "%s", footer
);
457 void Options::ensureOptionsAreCoherent()
459 bool coherent
= true;
460 if (!(useLLInt() || useJIT())) {
462 dataLog("INCOHERENT OPTIONS: at least one of useLLInt or useJIT must be true\n");
468 void Option::dump(FILE* stream
) const
471 case Options::Type::boolType
:
472 fprintf(stream
, "%s", m_entry
.boolVal
? "true" : "false");
474 case Options::Type::unsignedType
:
475 fprintf(stream
, "%u", m_entry
.unsignedVal
);
477 case Options::Type::doubleType
:
478 fprintf(stream
, "%lf", m_entry
.doubleVal
);
480 case Options::Type::int32Type
:
481 fprintf(stream
, "%d", m_entry
.int32Val
);
483 case Options::Type::optionRangeType
:
484 fprintf(stream
, "%s", m_entry
.optionRangeVal
.rangeString());
486 case Options::Type::optionStringType
: {
487 const char* option
= m_entry
.optionStringVal
;
490 fprintf(stream
, "%s", option
);
493 case Options::Type::gcLogLevelType
: {
494 fprintf(stream
, "%s", GCLogging::levelAsString(m_entry
.gcLogLevelVal
));
500 bool Option::operator==(const Option
& other
) const
503 case Options::Type::boolType
:
504 return m_entry
.boolVal
== other
.m_entry
.boolVal
;
505 case Options::Type::unsignedType
:
506 return m_entry
.unsignedVal
== other
.m_entry
.unsignedVal
;
507 case Options::Type::doubleType
:
508 return (m_entry
.doubleVal
== other
.m_entry
.doubleVal
) || (isnan(m_entry
.doubleVal
) && isnan(other
.m_entry
.doubleVal
));
509 case Options::Type::int32Type
:
510 return m_entry
.int32Val
== other
.m_entry
.int32Val
;
511 case Options::Type::optionRangeType
:
512 return m_entry
.optionRangeVal
.rangeString() == other
.m_entry
.optionRangeVal
.rangeString();
513 case Options::Type::optionStringType
:
514 return (m_entry
.optionStringVal
== other
.m_entry
.optionStringVal
)
515 || (m_entry
.optionStringVal
&& other
.m_entry
.optionStringVal
&& !strcmp(m_entry
.optionStringVal
, other
.m_entry
.optionStringVal
));
516 case Options::Type::gcLogLevelType
:
517 return m_entry
.gcLogLevelVal
== other
.m_entry
.gcLogLevelVal
;