Disable JIT on IA-32 without SSE2
[WebKit-https.git] / Source / JavaScriptCore / runtime / Options.cpp
1 /*
2  * Copyright (C) 2011-2018 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 "AssemblerCommon.h"
30 #include "LLIntCommon.h"
31 #include "MinimumReservedZoneSize.h"
32 #include "SigillCrashAnalyzer.h"
33 #include <algorithm>
34 #include <limits>
35 #include <math.h>
36 #include <mutex>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <wtf/ASCIICType.h>
40 #include <wtf/Compiler.h>
41 #include <wtf/DataLog.h>
42 #include <wtf/NumberOfCores.h>
43 #include <wtf/PointerPreparations.h>
44 #include <wtf/StdLibExtras.h>
45 #include <wtf/text/StringBuilder.h>
46 #include <wtf/threads/Signals.h>
47
48 #if PLATFORM(COCOA)
49 #include <crt_externs.h>
50 #endif
51
52 #if OS(WINDOWS) && ENABLE(JIT)
53 #include "MacroAssembler.h"
54 #endif
55
56 namespace JSC {
57
58 namespace {
59 #ifdef NDEBUG
60 bool restrictedOptionsEnabled = false;
61 #else
62 bool restrictedOptionsEnabled = true;
63 #endif
64 }
65
66 void Options::enableRestrictedOptions(bool enableOrNot)
67 {
68     restrictedOptionsEnabled = enableOrNot;
69 }
70     
71 static bool parse(const char* string, bool& value)
72 {
73     if (equalLettersIgnoringASCIICase(string, "true") || equalLettersIgnoringASCIICase(string, "yes") || !strcmp(string, "1")) {
74         value = true;
75         return true;
76     }
77     if (equalLettersIgnoringASCIICase(string, "false") || equalLettersIgnoringASCIICase(string, "no") || !strcmp(string, "0")) {
78         value = false;
79         return true;
80     }
81     return false;
82 }
83
84 static bool parse(const char* string, int32_t& value)
85 {
86     return sscanf(string, "%d", &value) == 1;
87 }
88
89 static bool parse(const char* string, unsigned& value)
90 {
91     return sscanf(string, "%u", &value) == 1;
92 }
93
94 static bool UNUSED_FUNCTION parse(const char* string, unsigned long& value)
95 {
96     return sscanf(string, "%lu", &value);
97 }
98
99 static bool UNUSED_FUNCTION parse(const char* string, unsigned long long& value)
100 {
101     return sscanf(string, "%llu", &value);
102 }
103
104 static bool parse(const char* string, double& value)
105 {
106     return sscanf(string, "%lf", &value) == 1;
107 }
108
109 static bool parse(const char* string, OptionRange& value)
110 {
111     return value.init(string);
112 }
113
114 static bool parse(const char* string, const char*& value)
115 {
116     if (!strlen(string)) {
117         value = nullptr;
118         return true;
119     }
120
121     // FIXME <https://webkit.org/b/169057>: This could leak if this option is set more than once.
122     // Given that Options are typically used for testing, this isn't considered to be a problem.
123     value = WTF::fastStrDup(string);
124     return true;
125 }
126
127 static bool parse(const char* string, GCLogging::Level& value)
128 {
129     if (equalLettersIgnoringASCIICase(string, "none") || equalLettersIgnoringASCIICase(string, "no") || equalLettersIgnoringASCIICase(string, "false") || !strcmp(string, "0")) {
130         value = GCLogging::None;
131         return true;
132     }
133
134     if (equalLettersIgnoringASCIICase(string, "basic") || equalLettersIgnoringASCIICase(string, "yes") || equalLettersIgnoringASCIICase(string, "true") || !strcmp(string, "1")) {
135         value = GCLogging::Basic;
136         return true;
137     }
138
139     if (equalLettersIgnoringASCIICase(string, "verbose") || !strcmp(string, "2")) {
140         value = GCLogging::Verbose;
141         return true;
142     }
143
144     return false;
145 }
146
147 bool Options::isAvailable(Options::ID id, Options::Availability availability)
148 {
149     if (availability == Availability::Restricted)
150         return restrictedOptionsEnabled;
151     ASSERT(availability == Availability::Configurable);
152     
153     UNUSED_PARAM(id);
154 #if !defined(NDEBUG)
155     if (id == maxSingleAllocationSizeID)
156         return true;
157 #endif
158 #if OS(DARWIN)
159     if (id == useSigillCrashAnalyzerID)
160         return true;
161 #endif
162     if (id == traceLLIntExecutionID)
163         return !!LLINT_TRACING;
164     if (id == traceLLIntSlowPathID)
165         return !!LLINT_TRACING;
166     return false;
167 }
168
169 template<typename T>
170 bool overrideOptionWithHeuristic(T& variable, Options::ID id, const char* name, Options::Availability availability)
171 {
172     bool available = (availability == Options::Availability::Normal)
173         || Options::isAvailable(id, availability);
174
175     const char* stringValue = getenv(name);
176     if (!stringValue)
177         return false;
178     
179     if (available && parse(stringValue, variable))
180         return true;
181     
182     fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
183     return false;
184 }
185
186 bool Options::overrideAliasedOptionWithHeuristic(const char* name)
187 {
188     const char* stringValue = getenv(name);
189     if (!stringValue)
190         return false;
191
192     String aliasedOption;
193     aliasedOption = String(&name[4]) + "=" + stringValue;
194     if (Options::setOption(aliasedOption.utf8().data()))
195         return true;
196
197     fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
198     return false;
199 }
200
201 static unsigned computeNumberOfWorkerThreads(int maxNumberOfWorkerThreads, int minimum = 1)
202 {
203     int cpusToUse = std::min(WTF::numberOfProcessorCores(), maxNumberOfWorkerThreads);
204
205     // Be paranoid, it is the OS we're dealing with, after all.
206     ASSERT(cpusToUse >= 1);
207     return std::max(cpusToUse, minimum);
208 }
209
210 static int32_t computePriorityDeltaOfWorkerThreads(int32_t twoCorePriorityDelta, int32_t multiCorePriorityDelta)
211 {
212     if (WTF::numberOfProcessorCores() <= 2)
213         return twoCorePriorityDelta;
214
215     return multiCorePriorityDelta;
216 }
217
218 static unsigned computeNumberOfGCMarkers(unsigned maxNumberOfGCMarkers)
219 {
220     return computeNumberOfWorkerThreads(maxNumberOfGCMarkers);
221 }
222
223 const char* const OptionRange::s_nullRangeStr = "<null>";
224
225 bool OptionRange::init(const char* rangeString)
226 {
227     // rangeString should be in the form of [!]<low>[:<high>]
228     // where low and high are unsigned
229
230     bool invert = false;
231
232     if (!rangeString) {
233         m_state = InitError;
234         return false;
235     }
236
237     if (!strcmp(rangeString, s_nullRangeStr)) {
238         m_state = Uninitialized;
239         return true;
240     }
241     
242     const char* p = rangeString;
243
244     if (*p == '!') {
245         invert = true;
246         p++;
247     }
248
249     int scanResult = sscanf(p, " %u:%u", &m_lowLimit, &m_highLimit);
250
251     if (!scanResult || scanResult == EOF) {
252         m_state = InitError;
253         return false;
254     }
255
256     if (scanResult == 1)
257         m_highLimit = m_lowLimit;
258
259     if (m_lowLimit > m_highLimit) {
260         m_state = InitError;
261         return false;
262     }
263
264     // FIXME <https://webkit.org/b/169057>: This could leak if this particular option is set more than once.
265     // Given that these options are used for testing, this isn't considered to be problem.
266     m_rangeString = WTF::fastStrDup(rangeString);
267     m_state = invert ? Inverted : Normal;
268
269     return true;
270 }
271
272 bool OptionRange::isInRange(unsigned count)
273 {
274     if (m_state < Normal)
275         return true;
276
277     if ((m_lowLimit <= count) && (count <= m_highLimit))
278         return m_state == Normal ? true : false;
279
280     return m_state == Normal ? false : true;
281 }
282
283 void OptionRange::dump(PrintStream& out) const
284 {
285     out.print(m_rangeString);
286 }
287
288 Options::Entry Options::s_options[Options::numberOfOptions];
289 Options::Entry Options::s_defaultOptions[Options::numberOfOptions];
290
291 // Realize the names for each of the options:
292 const Options::EntryInfo Options::s_optionsInfo[Options::numberOfOptions] = {
293 #define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
294     { #name_, description_, Options::Type::type_##Type, Availability::availability_ },
295     JSC_OPTIONS(FOR_EACH_OPTION)
296 #undef FOR_EACH_OPTION
297 };
298
299 static void scaleJITPolicy()
300 {
301     auto& scaleFactor = Options::jitPolicyScale();
302     if (scaleFactor > 1.0)
303         scaleFactor = 1.0;
304     else if (scaleFactor < 0.0)
305         scaleFactor = 0.0;
306
307     struct OptionToScale {
308         Options::ID id;
309         int32_t minVal;
310     };
311
312     static const OptionToScale optionsToScale[] = {
313         { Options::thresholdForJITAfterWarmUpID, 0 },
314         { Options::thresholdForJITSoonID, 0 },
315         { Options::thresholdForOptimizeAfterWarmUpID, 1 },
316         { Options::thresholdForOptimizeAfterLongWarmUpID, 1 },
317         { Options::thresholdForOptimizeSoonID, 1 },
318         { Options::thresholdForFTLOptimizeSoonID, 2 },
319         { Options::thresholdForFTLOptimizeAfterWarmUpID, 2 }
320     };
321
322     const int numberOfOptionsToScale = sizeof(optionsToScale) / sizeof(OptionToScale);
323     for (int i = 0; i < numberOfOptionsToScale; i++) {
324         Option option(optionsToScale[i].id);
325         ASSERT(option.type() == Options::Type::int32Type);
326         option.int32Val() *= scaleFactor;
327         option.int32Val() = std::max(option.int32Val(), optionsToScale[i].minVal);
328     }
329 }
330
331 static void overrideDefaults()
332 {
333 #if !PLATFORM(IOS)
334     if (WTF::numberOfProcessorCores() < 4)
335 #endif
336     {
337         Options::maximumMutatorUtilization() = 0.6;
338         Options::concurrentGCMaxHeadroom() = 1.4;
339         Options::minimumGCPauseMS() = 1;
340         Options::useStochasticMutatorScheduler() = false;
341         if (WTF::numberOfProcessorCores() <= 1)
342             Options::gcIncrementScale() = 1;
343         else
344             Options::gcIncrementScale() = 0;
345     }
346
347 #if PLATFORM(IOS)
348     // On iOS, we control heap growth using process memory footprint. Therefore these values can be agressive.
349     Options::smallHeapRAMFraction() = 0.8;
350     Options::mediumHeapRAMFraction() = 0.9;
351
352 #if !PLATFORM(WATCHOS) && defined(__LP64__)
353     Options::useSigillCrashAnalyzer() = true;
354 #endif
355 #endif
356
357 #if !ENABLE(SIGNAL_BASED_VM_TRAPS)
358     Options::usePollingTraps() = true;
359 #endif
360
361 #if !ENABLE(WEBASSEMBLY_FAST_MEMORY)
362     Options::useWebAssemblyFastMemory() = false;
363 #endif
364
365 #if !HAVE(MACH_EXCEPTIONS)
366     Options::useMachForExceptions() = false;
367 #endif
368 }
369
370 static void recomputeDependentOptions()
371 {
372 #if !defined(NDEBUG)
373     Options::validateDFGExceptionHandling() = true;
374 #endif
375 #if !ENABLE(JIT)
376     Options::useLLInt() = true;
377     Options::useJIT() = false;
378     Options::useDFGJIT() = false;
379     Options::useFTLJIT() = false;
380     Options::useDOMJIT() = false;
381 #endif
382 #if !ENABLE(YARR_JIT)
383     Options::useRegExpJIT() = false;
384 #endif
385 #if !ENABLE(CONCURRENT_JS)
386     Options::useConcurrentJIT() = false;
387 #endif
388 #if !ENABLE(DFG_JIT)
389     Options::useDFGJIT() = false;
390     Options::useFTLJIT() = false;
391 #endif
392 #if !ENABLE(FTL_JIT)
393     Options::useFTLJIT() = false;
394 #endif
395     
396 #if !CPU(X86_64) && !CPU(ARM64)
397     Options::useConcurrentGC() = false;
398 #endif
399     
400 #if ENABLE(JIT) && CPU(X86)
401     // Disable JIT on IA-32 if SSE2 is not present
402     if (!MacroAssemblerX86::supportsFloatingPoint())
403         Options::useJIT() = false;
404 #endif
405
406     WTF_SET_POINTER_PREPARATION_OPTIONS();
407
408     if (!Options::useJIT())
409         Options::useWebAssembly() = false;
410
411     if (!Options::useWebAssembly())
412         Options::useFastTLSForWasmContext() = false;
413     
414     if (Options::dumpDisassembly()
415         || Options::dumpDFGDisassembly()
416         || Options::dumpFTLDisassembly()
417         || Options::dumpBytecodeAtDFGTime()
418         || Options::dumpGraphAtEachPhase()
419         || Options::dumpDFGGraphAtEachPhase()
420         || Options::dumpDFGFTLGraphAtEachPhase()
421         || Options::dumpB3GraphAtEachPhase()
422         || Options::dumpAirGraphAtEachPhase()
423         || Options::verboseCompilation()
424         || Options::verboseFTLCompilation()
425         || Options::logCompilationChanges()
426         || Options::validateGraph()
427         || Options::validateGraphAtEachPhase()
428         || Options::verboseOSR()
429         || Options::verboseCompilationQueue()
430         || Options::reportCompileTimes()
431         || Options::reportBaselineCompileTimes()
432         || Options::reportDFGCompileTimes()
433         || Options::reportFTLCompileTimes()
434         || Options::logPhaseTimes()
435         || Options::verboseCFA()
436         || Options::verboseDFGFailure()
437         || Options::verboseFTLFailure())
438         Options::alwaysComputeHash() = true;
439     
440     if (!Options::useConcurrentGC())
441         Options::collectContinuously() = false;
442
443     if (Option(Options::jitPolicyScaleID).isOverridden())
444         scaleJITPolicy();
445     
446     if (Options::forceEagerCompilation()) {
447         Options::thresholdForJITAfterWarmUp() = 10;
448         Options::thresholdForJITSoon() = 10;
449         Options::thresholdForOptimizeAfterWarmUp() = 20;
450         Options::thresholdForOptimizeAfterLongWarmUp() = 20;
451         Options::thresholdForOptimizeSoon() = 20;
452         Options::thresholdForFTLOptimizeAfterWarmUp() = 20;
453         Options::thresholdForFTLOptimizeSoon() = 20;
454         Options::maximumEvalCacheableSourceLength() = 150000;
455         Options::useConcurrentJIT() = false;
456     }
457     if (Options::useMaximalFlushInsertionPhase()) {
458         Options::useOSREntryToDFG() = false;
459         Options::useOSREntryToFTL() = false;
460     }
461     
462 #if PLATFORM(IOS) && CPU(ARM64)
463     // Override globally for now. Longer term we'll just make the default
464     // be to have this option enabled, and have platforms that don't support
465     // it just silently use a single mapping.
466     Options::useSeparatedWXHeap() = true;
467 #else
468     Options::useSeparatedWXHeap() = false;
469 #endif
470
471     if (Options::alwaysUseShadowChicken())
472         Options::maximumInliningDepth() = 1;
473
474     // Compute the maximum value of the reoptimization retry counter. This is simply
475     // the largest value at which we don't overflow the execute counter, when using it
476     // to left-shift the execution counter by this amount. Currently the value ends
477     // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles
478     // total on a 32-bit processor.
479     Options::reoptimizationRetryCounterMax() = 0;
480     while ((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << (Options::reoptimizationRetryCounterMax() + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()))
481         Options::reoptimizationRetryCounterMax()++;
482
483     ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) > 0);
484     ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
485
486 #if !defined(NDEBUG)
487     if (Options::maxSingleAllocationSize())
488         fastSetMaxSingleAllocationSize(Options::maxSingleAllocationSize());
489     else
490         fastSetMaxSingleAllocationSize(std::numeric_limits<size_t>::max());
491 #endif
492
493     if (Options::useZombieMode()) {
494         Options::sweepSynchronously() = true;
495         Options::scribbleFreeCells() = true;
496     }
497
498     if (Options::useSigillCrashAnalyzer())
499         enableSigillCrashAnalyzer();
500
501     if (Options::reservedZoneSize() < minimumReservedZoneSize)
502         Options::reservedZoneSize() = minimumReservedZoneSize;
503     if (Options::softReservedZoneSize() < Options::reservedZoneSize() + minimumReservedZoneSize)
504         Options::softReservedZoneSize() = Options::reservedZoneSize() + minimumReservedZoneSize;
505
506 #if USE(JSVALUE32_64)
507     // FIXME: Make probe OSR exit work on 32-bit:
508     // https://bugs.webkit.org/show_bug.cgi?id=177956
509     Options::useProbeOSRExit() = false;
510 #endif
511 }
512
513 void Options::initialize()
514 {
515     static std::once_flag initializeOptionsOnceFlag;
516     
517     std::call_once(
518         initializeOptionsOnceFlag,
519         [] {
520             // Initialize each of the options with their default values:
521 #define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
522             name_() = defaultValue_;                                    \
523             name_##Default() = defaultValue_;
524             JSC_OPTIONS(FOR_EACH_OPTION)
525 #undef FOR_EACH_OPTION
526
527             overrideDefaults();
528                 
529             // Allow environment vars to override options if applicable.
530             // The evn var should be the name of the option prefixed with
531             // "JSC_".
532 #if PLATFORM(COCOA)
533             bool hasBadOptions = false;
534             for (char** envp = *_NSGetEnviron(); *envp; envp++) {
535                 const char* env = *envp;
536                 if (!strncmp("JSC_", env, 4)) {
537                     if (!Options::setOption(&env[4])) {
538                         dataLog("ERROR: invalid option: ", *envp, "\n");
539                         hasBadOptions = true;
540                     }
541                 }
542             }
543             if (hasBadOptions && Options::validateOptions())
544                 CRASH();
545 #else // PLATFORM(COCOA)
546 #define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
547             overrideOptionWithHeuristic(name_(), name_##ID, "JSC_" #name_, Availability::availability_);
548             JSC_OPTIONS(FOR_EACH_OPTION)
549 #undef FOR_EACH_OPTION
550 #endif // PLATFORM(COCOA)
551
552 #define FOR_EACH_OPTION(aliasedName_, unaliasedName_, equivalence_) \
553             overrideAliasedOptionWithHeuristic("JSC_" #aliasedName_);
554             JSC_ALIASED_OPTIONS(FOR_EACH_OPTION)
555 #undef FOR_EACH_OPTION
556
557 #if 0
558                 ; // Deconfuse editors that do auto indentation
559 #endif
560     
561             recomputeDependentOptions();
562
563             // Do range checks where needed and make corrections to the options:
564             ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
565             ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
566             ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);
567             ASSERT(Options::criticalGCMemoryThreshold() > 0.0 && Options::criticalGCMemoryThreshold() < 1.0);
568
569             dumpOptionsIfNeeded();
570             ensureOptionsAreCoherent();
571
572 #if HAVE(MACH_EXCEPTIONS)
573             if (Options::useMachForExceptions())
574                 handleSignalsWithMach();
575 #endif
576         });
577 }
578
579 void Options::dumpOptionsIfNeeded()
580 {
581     if (Options::dumpOptions()) {
582         DumpLevel level = static_cast<DumpLevel>(Options::dumpOptions());
583         if (level > DumpLevel::Verbose)
584             level = DumpLevel::Verbose;
585             
586         const char* title = nullptr;
587         switch (level) {
588         case DumpLevel::None:
589             break;
590         case DumpLevel::Overridden:
591             title = "Overridden JSC options:";
592             break;
593         case DumpLevel::All:
594             title = "All JSC options:";
595             break;
596         case DumpLevel::Verbose:
597             title = "All JSC options with descriptions:";
598             break;
599         }
600
601         StringBuilder builder;
602         dumpAllOptions(builder, level, title, nullptr, "   ", "\n", DumpDefaults);
603         dataLog(builder.toString());
604     }
605 }
606
607 static bool isSeparator(char c)
608 {
609     return isASCIISpace(c) || (c == ',');
610 }
611
612 bool Options::setOptions(const char* optionsStr)
613 {
614     Vector<char*> options;
615
616     size_t length = strlen(optionsStr);
617     char* optionsStrCopy = WTF::fastStrDup(optionsStr);
618     char* end = optionsStrCopy + length;
619     char* p = optionsStrCopy;
620
621     while (p < end) {
622         // Skip separators (white space or commas).
623         while (p < end && isSeparator(*p))
624             p++;
625         if (p == end)
626             break;
627
628         char* optionStart = p;
629         p = strstr(p, "=");
630         if (!p) {
631             dataLogF("'=' not found in option string: %p\n", optionStart);
632             WTF::fastFree(optionsStrCopy);
633             return false;
634         }
635         p++;
636
637         char* valueBegin = p;
638         bool hasStringValue = false;
639         const int minStringLength = 2; // The min is an empty string i.e. 2 double quotes.
640         if ((p + minStringLength < end) && (*p == '"')) {
641             p = strstr(p + 1, "\"");
642             if (!p) {
643                 dataLogF("Missing trailing '\"' in option string: %p\n", optionStart);
644                 WTF::fastFree(optionsStrCopy);
645                 return false; // End of string not found.
646             }
647             hasStringValue = true;
648         }
649
650         // Find next separator (white space or commas).
651         while (p < end && !isSeparator(*p))
652             p++;
653         if (!p)
654             p = end; // No more " " separator. Hence, this is the last arg.
655
656         // If we have a well-formed string value, strip the quotes.
657         if (hasStringValue) {
658             char* valueEnd = p;
659             ASSERT((*valueBegin == '"') && ((valueEnd - valueBegin) >= minStringLength) && (valueEnd[-1] == '"'));
660             memmove(valueBegin, valueBegin + 1, valueEnd - valueBegin - minStringLength);
661             valueEnd[-minStringLength] = '\0';
662         }
663
664         // Strip leading -- if present.
665         if ((p -  optionStart > 2) && optionStart[0] == '-' && optionStart[1] == '-')
666             optionStart += 2;
667
668         *p++ = '\0';
669         options.append(optionStart);
670     }
671
672     bool success = true;
673     for (auto& option : options) {
674         bool optionSuccess = setOption(option);
675         if (!optionSuccess) {
676             dataLogF("Failed to set option : %s\n", option);
677             success = false;
678         }
679     }
680
681     recomputeDependentOptions();
682
683     dumpOptionsIfNeeded();
684
685     ensureOptionsAreCoherent();
686
687     WTF::fastFree(optionsStrCopy);
688
689     return success;
690 }
691
692 // Parses a single command line option in the format "<optionName>=<value>"
693 // (no spaces allowed) and set the specified option if appropriate.
694 bool Options::setOptionWithoutAlias(const char* arg)
695 {
696     // arg should look like this:
697     //   <jscOptionName>=<appropriate value>
698     const char* equalStr = strchr(arg, '=');
699     if (!equalStr)
700         return false;
701
702     const char* valueStr = equalStr + 1;
703
704     // For each option, check if the specify arg is a match. If so, set the arg
705     // if the value makes sense. Otherwise, move on to checking the next option.
706 #define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
707     if (strlen(#name_) == static_cast<size_t>(equalStr - arg)      \
708         && !strncmp(arg, #name_, equalStr - arg)) {                \
709         if (Availability::availability_ != Availability::Normal     \
710             && !isAvailable(name_##ID, Availability::availability_)) \
711             return false;                                          \
712         type_ value;                                               \
713         value = (defaultValue_);                                   \
714         bool success = parse(valueStr, value);                     \
715         if (success) {                                             \
716             name_() = value;                                       \
717             recomputeDependentOptions();                           \
718             return true;                                           \
719         }                                                          \
720         return false;                                              \
721     }
722
723     JSC_OPTIONS(FOR_EACH_OPTION)
724 #undef FOR_EACH_OPTION
725
726     return false; // No option matched.
727 }
728
729 static bool invertBoolOptionValue(const char* valueStr, const char*& invertedValueStr)
730 {
731     bool boolValue;
732     if (!parse(valueStr, boolValue))
733         return false;
734     invertedValueStr = boolValue ? "false" : "true";
735     return true;
736 }
737
738
739 bool Options::setAliasedOption(const char* arg)
740 {
741     // arg should look like this:
742     //   <jscOptionName>=<appropriate value>
743     const char* equalStr = strchr(arg, '=');
744     if (!equalStr)
745         return false;
746
747 #if COMPILER(CLANG)
748 #if __has_warning("-Wtautological-compare")
749 #pragma clang diagnostic push
750 #pragma clang diagnostic ignored "-Wtautological-compare"
751 #endif
752 #endif
753
754     // For each option, check if the specify arg is a match. If so, set the arg
755     // if the value makes sense. Otherwise, move on to checking the next option.
756 #define FOR_EACH_OPTION(aliasedName_, unaliasedName_, equivalence) \
757     if (strlen(#aliasedName_) == static_cast<size_t>(equalStr - arg)    \
758         && !strncmp(arg, #aliasedName_, equalStr - arg)) {              \
759         String unaliasedOption(#unaliasedName_);                        \
760         if (equivalence == SameOption)                                  \
761             unaliasedOption = unaliasedOption + equalStr;               \
762         else {                                                          \
763             ASSERT(equivalence == InvertedOption);                      \
764             const char* invertedValueStr = nullptr;                     \
765             if (!invertBoolOptionValue(equalStr + 1, invertedValueStr)) \
766                 return false;                                           \
767             unaliasedOption = unaliasedOption + "=" + invertedValueStr; \
768         }                                                               \
769         return setOptionWithoutAlias(unaliasedOption.utf8().data());   \
770     }
771
772     JSC_ALIASED_OPTIONS(FOR_EACH_OPTION)
773 #undef FOR_EACH_OPTION
774
775 #if COMPILER(CLANG)
776 #if __has_warning("-Wtautological-compare")
777 #pragma clang diagnostic pop
778 #endif
779 #endif
780
781     return false; // No option matched.
782 }
783
784 bool Options::setOption(const char* arg)
785 {
786     bool success = setOptionWithoutAlias(arg);
787     if (success)
788         return true;
789     return setAliasedOption(arg);
790 }
791
792
793 void Options::dumpAllOptions(StringBuilder& builder, DumpLevel level, const char* title,
794     const char* separator, const char* optionHeader, const char* optionFooter, DumpDefaultsOption dumpDefaultsOption)
795 {
796     if (title) {
797         builder.append(title);
798         builder.append('\n');
799     }
800
801     for (int id = 0; id < numberOfOptions; id++) {
802         if (separator && id)
803             builder.append(separator);
804         dumpOption(builder, level, static_cast<ID>(id), optionHeader, optionFooter, dumpDefaultsOption);
805     }
806 }
807
808 void Options::dumpAllOptionsInALine(StringBuilder& builder)
809 {
810     dumpAllOptions(builder, DumpLevel::All, nullptr, " ", nullptr, nullptr, DontDumpDefaults);
811 }
812
813 void Options::dumpAllOptions(FILE* stream, DumpLevel level, const char* title)
814 {
815     StringBuilder builder;
816     dumpAllOptions(builder, level, title, nullptr, "   ", "\n", DumpDefaults);
817     fprintf(stream, "%s", builder.toString().utf8().data());
818 }
819
820 void Options::dumpOption(StringBuilder& builder, DumpLevel level, Options::ID id,
821     const char* header, const char* footer, DumpDefaultsOption dumpDefaultsOption)
822 {
823     if (id >= numberOfOptions)
824         return; // Illegal option.
825
826     Option option(id);
827     Availability availability = option.availability();
828     if (availability != Availability::Normal && !isAvailable(id, availability))
829         return;
830
831     bool wasOverridden = option.isOverridden();
832     bool needsDescription = (level == DumpLevel::Verbose && option.description());
833
834     if (level == DumpLevel::Overridden && !wasOverridden)
835         return;
836
837     if (header)
838         builder.append(header);
839     builder.append(option.name());
840     builder.append('=');
841     option.dump(builder);
842
843     if (wasOverridden && (dumpDefaultsOption == DumpDefaults)) {
844         builder.appendLiteral(" (default: ");
845         option.defaultOption().dump(builder);
846         builder.appendLiteral(")");
847     }
848
849     if (needsDescription) {
850         builder.appendLiteral("   ... ");
851         builder.append(option.description());
852     }
853
854     builder.append(footer);
855 }
856
857 void Options::ensureOptionsAreCoherent()
858 {
859     bool coherent = true;
860     if (!(useLLInt() || useJIT())) {
861         coherent = false;
862         dataLog("INCOHERENT OPTIONS: at least one of useLLInt or useJIT must be true\n");
863     }
864     if (!coherent)
865         CRASH();
866 }
867
868 void Option::dump(StringBuilder& builder) const
869 {
870     switch (type()) {
871     case Options::Type::boolType:
872         builder.append(m_entry.boolVal ? "true" : "false");
873         break;
874     case Options::Type::unsignedType:
875         builder.appendNumber(m_entry.unsignedVal);
876         break;
877     case Options::Type::sizeType:
878         builder.appendNumber(m_entry.sizeVal);
879         break;
880     case Options::Type::doubleType:
881         builder.appendNumber(m_entry.doubleVal);
882         break;
883     case Options::Type::int32Type:
884         builder.appendNumber(m_entry.int32Val);
885         break;
886     case Options::Type::optionRangeType:
887         builder.append(m_entry.optionRangeVal.rangeString());
888         break;
889     case Options::Type::optionStringType: {
890         const char* option = m_entry.optionStringVal;
891         if (!option)
892             option = "";
893         builder.append('"');
894         builder.append(option);
895         builder.append('"');
896         break;
897     }
898     case Options::Type::gcLogLevelType: {
899         builder.append(GCLogging::levelAsString(m_entry.gcLogLevelVal));
900         break;
901     }
902     }
903 }
904
905 bool Option::operator==(const Option& other) const
906 {
907     switch (type()) {
908     case Options::Type::boolType:
909         return m_entry.boolVal == other.m_entry.boolVal;
910     case Options::Type::unsignedType:
911         return m_entry.unsignedVal == other.m_entry.unsignedVal;
912     case Options::Type::sizeType:
913         return m_entry.sizeVal == other.m_entry.sizeVal;
914     case Options::Type::doubleType:
915         return (m_entry.doubleVal == other.m_entry.doubleVal) || (std::isnan(m_entry.doubleVal) && std::isnan(other.m_entry.doubleVal));
916     case Options::Type::int32Type:
917         return m_entry.int32Val == other.m_entry.int32Val;
918     case Options::Type::optionRangeType:
919         return m_entry.optionRangeVal.rangeString() == other.m_entry.optionRangeVal.rangeString();
920     case Options::Type::optionStringType:
921         return (m_entry.optionStringVal == other.m_entry.optionStringVal)
922             || (m_entry.optionStringVal && other.m_entry.optionStringVal && !strcmp(m_entry.optionStringVal, other.m_entry.optionStringVal));
923     case Options::Type::gcLogLevelType:
924         return m_entry.gcLogLevelVal == other.m_entry.gcLogLevelVal;
925     }
926     return false;
927 }
928
929 } // namespace JSC
930