]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/Options.cpp
c9c698f7368cfd67ff8d447ee7c03bc92baae9da
[apple/javascriptcore.git] / runtime / Options.cpp
1 /*
2 * Copyright (C) 2011-2012, 2014-2015 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
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.
24 */
25
26 #include "config.h"
27 #include "Options.h"
28
29 #include "HeapStatistics.h"
30 #include <algorithm>
31 #include <limits>
32 #include <math.h>
33 #include <mutex>
34 #include <stdlib.h>
35 #include <string.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>
41
42 #if OS(DARWIN) && ENABLE(PARALLEL_GC)
43 #include <sys/sysctl.h>
44 #endif
45
46 #if OS(WINDOWS)
47 #include "MacroAssemblerX86.h"
48 #endif
49
50 namespace JSC {
51
52 static bool parse(const char* string, bool& value)
53 {
54 if (!strcasecmp(string, "true") || !strcasecmp(string, "yes") || !strcmp(string, "1")) {
55 value = true;
56 return true;
57 }
58 if (!strcasecmp(string, "false") || !strcasecmp(string, "no") || !strcmp(string, "0")) {
59 value = false;
60 return true;
61 }
62 return false;
63 }
64
65 static bool parse(const char* string, int32_t& value)
66 {
67 return sscanf(string, "%d", &value) == 1;
68 }
69
70 static bool parse(const char* string, unsigned& value)
71 {
72 return sscanf(string, "%u", &value) == 1;
73 }
74
75 static bool parse(const char* string, double& value)
76 {
77 return sscanf(string, "%lf", &value) == 1;
78 }
79
80 static bool parse(const char* string, OptionRange& value)
81 {
82 return value.init(string);
83 }
84
85 static bool parse(const char* string, const char*& value)
86 {
87 value = string;
88 return true;
89 }
90
91 static bool parse(const char* string, GCLogging::Level& value)
92 {
93 if (!strcasecmp(string, "none") || !strcasecmp(string, "no") || !strcasecmp(string, "false") || !strcmp(string, "0")) {
94 value = GCLogging::None;
95 return true;
96 }
97
98 if (!strcasecmp(string, "basic") || !strcasecmp(string, "yes") || !strcasecmp(string, "true") || !strcmp(string, "1")) {
99 value = GCLogging::Basic;
100 return true;
101 }
102
103 if (!strcasecmp(string, "verbose") || !strcmp(string, "2")) {
104 value = GCLogging::Verbose;
105 return true;
106 }
107
108 return false;
109 }
110
111 template<typename T>
112 bool overrideOptionWithHeuristic(T& variable, const char* name)
113 {
114 const char* stringValue = getenv(name);
115 if (!stringValue)
116 return false;
117
118 if (parse(stringValue, variable))
119 return true;
120
121 fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
122 return false;
123 }
124
125 static unsigned computeNumberOfWorkerThreads(int maxNumberOfWorkerThreads, int minimum = 1)
126 {
127 int cpusToUse = std::min(WTF::numberOfProcessorCores(), maxNumberOfWorkerThreads);
128
129 // Be paranoid, it is the OS we're dealing with, after all.
130 ASSERT(cpusToUse >= 1);
131 return std::max(cpusToUse, minimum);
132 }
133
134 static int32_t computePriorityDeltaOfWorkerThreads(int32_t twoCorePriorityDelta, int32_t multiCorePriorityDelta)
135 {
136 if (WTF::numberOfProcessorCores() <= 2)
137 return twoCorePriorityDelta;
138
139 return multiCorePriorityDelta;
140 }
141
142 static unsigned computeNumberOfGCMarkers(unsigned maxNumberOfGCMarkers)
143 {
144 #if ENABLE(PARALLEL_GC)
145 return computeNumberOfWorkerThreads(maxNumberOfGCMarkers);
146 #else
147 UNUSED_PARAM(maxNumberOfGCMarkers);
148 return 1;
149 #endif
150 }
151
152 bool OptionRange::init(const char* rangeString)
153 {
154 // rangeString should be in the form of [!]<low>[:<high>]
155 // where low and high are unsigned
156
157 bool invert = false;
158
159 if (m_state > Uninitialized)
160 return true;
161
162 if (!rangeString) {
163 m_state = InitError;
164 return false;
165 }
166
167 m_rangeString = rangeString;
168
169 if (*rangeString == '!') {
170 invert = true;
171 rangeString++;
172 }
173
174 int scanResult = sscanf(rangeString, " %u:%u", &m_lowLimit, &m_highLimit);
175
176 if (!scanResult || scanResult == EOF) {
177 m_state = InitError;
178 return false;
179 }
180
181 if (scanResult == 1)
182 m_highLimit = m_lowLimit;
183
184 if (m_lowLimit > m_highLimit) {
185 m_state = InitError;
186 return false;
187 }
188
189 m_state = invert ? Inverted : Normal;
190
191 return true;
192 }
193
194 bool OptionRange::isInRange(unsigned count)
195 {
196 if (m_state < Normal)
197 return true;
198
199 if ((m_lowLimit <= count) && (count <= m_highLimit))
200 return m_state == Normal ? true : false;
201
202 return m_state == Normal ? false : true;
203 }
204
205 void OptionRange::dump(PrintStream& out) const
206 {
207 out.print(m_rangeString);
208 }
209
210 Options::Entry Options::s_options[Options::numberOfOptions];
211 Options::Entry Options::s_defaultOptions[Options::numberOfOptions];
212
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
219 };
220
221 static void scaleJITPolicy()
222 {
223 auto& scaleFactor = Options::jitPolicyScale();
224 if (scaleFactor > 1.0)
225 scaleFactor = 1.0;
226 else if (scaleFactor < 0.0)
227 scaleFactor = 0.0;
228
229 struct OptionToScale {
230 Options::OptionID id;
231 int32_t minVal;
232 };
233
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 }
242 };
243
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);
250 }
251 }
252
253 static void recomputeDependentOptions()
254 {
255 #if !ENABLE(JIT)
256 Options::useLLInt() = true;
257 Options::useJIT() = false;
258 Options::useDFGJIT() = false;
259 Options::useFTLJIT() = false;
260 #endif
261 #if !ENABLE(YARR_JIT)
262 Options::useRegExpJIT() = false;
263 #endif
264 #if !ENABLE(CONCURRENT_JIT)
265 Options::enableConcurrentJIT() = false;
266 #endif
267 #if !ENABLE(DFG_JIT)
268 Options::useDFGJIT() = false;
269 Options::useFTLJIT() = false;
270 #endif
271 #if !ENABLE(FTL_JIT)
272 Options::useFTLJIT() = false;
273 #endif
274 #if OS(WINDOWS) && CPU(X86)
275 // Disable JIT on Windows if SSE2 is not present
276 if (!MacroAssemblerX86::supportsFloatingPoint())
277 Options::useJIT() = false;
278 #endif
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;
296
297 if (Option(Options::jitPolicyScaleID).isOverridden())
298 scaleJITPolicy();
299
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;
310 }
311
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()++;
320
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()));
323 }
324
325 void Options::initialize()
326 {
327 static std::once_flag initializeOptionsOnceFlag;
328
329 std::call_once(
330 initializeOptionsOnceFlag,
331 [] {
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
338
339 // It *probably* makes sense for other platforms to enable this.
340 #if PLATFORM(IOS) && CPU(ARM64)
341 enableLLVMFastISel() = true;
342 #endif
343
344 // Allow environment vars to override options if applicable.
345 // The evn var should be the name of the option prefixed with
346 // "JSC_".
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
351
352 #if 0
353 ; // Deconfuse editors that do auto indentation
354 #endif
355
356 recomputeDependentOptions();
357
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);
362
363 if (Options::showOptions()) {
364 DumpLevel level = static_cast<DumpLevel>(Options::showOptions());
365 if (level > DumpLevel::Verbose)
366 level = DumpLevel::Verbose;
367
368 const char* title = nullptr;
369 switch (level) {
370 case DumpLevel::None:
371 break;
372 case DumpLevel::Overridden:
373 title = "Overridden JSC options:";
374 break;
375 case DumpLevel::All:
376 title = "All JSC options:";
377 break;
378 case DumpLevel::Verbose:
379 title = "All JSC options with descriptions:";
380 break;
381 }
382 dumpAllOptions(level, title);
383 }
384
385 ensureOptionsAreCoherent();
386 });
387 }
388
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)
392 {
393 // arg should look like this:
394 // <jscOptionName>=<appropriate value>
395 const char* equalStr = strchr(arg, '=');
396 if (!equalStr)
397 return false;
398
399 const char* valueStr = equalStr + 1;
400
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)) { \
405 type_ value; \
406 value = (defaultValue_); \
407 bool success = parse(valueStr, value); \
408 if (success) { \
409 name_() = value; \
410 recomputeDependentOptions(); \
411 return true; \
412 } \
413 return false; \
414 }
415
416 JSC_OPTIONS(FOR_EACH_OPTION)
417 #undef FOR_EACH_OPTION
418
419 return false; // No option matched.
420 }
421
422 void Options::dumpAllOptions(DumpLevel level, const char* title, FILE* stream)
423 {
424 if (title)
425 fprintf(stream, "%s\n", title);
426 for (int id = 0; id < numberOfOptions; id++)
427 dumpOption(level, static_cast<OptionID>(id), stream, " ", "\n");
428 }
429
430 void Options::dumpOption(DumpLevel level, OptionID id, FILE* stream, const char* header, const char* footer)
431 {
432 if (id >= numberOfOptions)
433 return; // Illegal option.
434
435 Option option(id);
436 bool wasOverridden = option.isOverridden();
437 bool needsDescription = (level == DumpLevel::Verbose && option.description());
438
439 if (level == DumpLevel::Overridden && !wasOverridden)
440 return;
441
442 fprintf(stream, "%s%s: ", header, option.name());
443 option.dump(stream);
444
445 if (wasOverridden) {
446 fprintf(stream, " (default: ");
447 option.defaultOption().dump(stream);
448 fprintf(stream, ")");
449 }
450
451 if (needsDescription)
452 fprintf(stream, " ... %s", option.description());
453
454 fprintf(stream, "%s", footer);
455 }
456
457 void Options::ensureOptionsAreCoherent()
458 {
459 bool coherent = true;
460 if (!(useLLInt() || useJIT())) {
461 coherent = false;
462 dataLog("INCOHERENT OPTIONS: at least one of useLLInt or useJIT must be true\n");
463 }
464 if (!coherent)
465 CRASH();
466 }
467
468 void Option::dump(FILE* stream) const
469 {
470 switch (type()) {
471 case Options::Type::boolType:
472 fprintf(stream, "%s", m_entry.boolVal ? "true" : "false");
473 break;
474 case Options::Type::unsignedType:
475 fprintf(stream, "%u", m_entry.unsignedVal);
476 break;
477 case Options::Type::doubleType:
478 fprintf(stream, "%lf", m_entry.doubleVal);
479 break;
480 case Options::Type::int32Type:
481 fprintf(stream, "%d", m_entry.int32Val);
482 break;
483 case Options::Type::optionRangeType:
484 fprintf(stream, "%s", m_entry.optionRangeVal.rangeString());
485 break;
486 case Options::Type::optionStringType: {
487 const char* option = m_entry.optionStringVal;
488 if (!option)
489 option = "";
490 fprintf(stream, "%s", option);
491 break;
492 }
493 case Options::Type::gcLogLevelType: {
494 fprintf(stream, "%s", GCLogging::levelAsString(m_entry.gcLogLevelVal));
495 break;
496 }
497 }
498 }
499
500 bool Option::operator==(const Option& other) const
501 {
502 switch (type()) {
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;
518 }
519 return false;
520 }
521
522 } // namespace JSC
523