Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (231467 => 231468)
--- trunk/Source/_javascript_Core/ChangeLog 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/ChangeLog 2018-05-08 00:07:20 UTC (rev 231468)
@@ -1,3 +1,46 @@
+2018-05-06 Filip Pizlo <fpi...@apple.com>
+
+ InPlaceAbstractState::beginBasicBlock shouldn't have to clear any abstract values
+ https://bugs.webkit.org/show_bug.cgi?id=185365
+
+ Reviewed by Saam Barati.
+
+ This patch does three things to improve compile times:
+
+ - Fixes some inlining goofs.
+
+ - Adds the ability to measure compile times with run-jsc-benchmarks.
+
+ - Dramatically improves the performance of InPlaceAbstractState::beginBasicBlock by removing the
+ code that clears abstract values. It turns out that on constant folding "needed" this, in the
+ sense that this was the only thing protecting it from loading the abstract value of a no-result
+ node and then concluding that because it had a non-empty m_value, it could be constant-folded.
+ Any node that produces a result will explicitly set its abstract value, so this problem can
+ also be guarded by just having constant folding check if the node it wants to fold returns any
+ result.
+
+ Solid 0.96% compile time speed-up across SunSpider-CompileTime and V8Spider-CompileTime.
+
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGAbstractValue.cpp:
+ (JSC::DFG::AbstractValue::set):
+ * dfg/DFGAbstractValue.h:
+ (JSC::DFG::AbstractValue::merge):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::doToChildrenWithNode):
+ (JSC::DFG::Graph::doToChildren):
+ * dfg/DFGInPlaceAbstractState.cpp:
+ (JSC::DFG::InPlaceAbstractState::beginBasicBlock):
+ * jit/JIT.cpp:
+ (JSC::JIT::totalCompileTime):
+ * jit/JIT.h:
+ * jsc.cpp:
+ (GlobalObject::finishCreation):
+ (functionTotalCompileTime):
+
2018-05-05 Filip Pizlo <fpi...@apple.com>
DFG AI doesn't need to merge valuesAtTail - it can just assign them
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (231467 => 231468)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2018-05-08 00:07:20 UTC (rev 231468)
@@ -3410,8 +3410,6 @@
case CheckTraps:
case LogShadowChickenPrologue:
case LogShadowChickenTail:
- break;
-
case ProfileType:
case ProfileControlFlow:
case Phantom:
@@ -3421,6 +3419,10 @@
case CheckTypeInfoFlags:
case SuperSamplerBegin:
case SuperSamplerEnd:
+ case CheckTierUpAndOSREnter:
+ case LoopHint:
+ case ZombieHint:
+ case ExitOK:
break;
case ParseInt: {
@@ -3483,10 +3485,6 @@
break;
}
- case CheckTierUpAndOSREnter:
- case LoopHint:
- case ZombieHint:
- case ExitOK:
break;
case Unreachable:
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractValue.cpp (231467 => 231468)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractValue.cpp 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractValue.cpp 2018-05-08 00:07:20 UTC (rev 231468)
@@ -53,9 +53,9 @@
if (!!value && value.value().isCell()) {
Structure* structure = value.structure();
StructureRegistrationResult result;
- RegisteredStructure RegisteredStructure = graph.registerStructure(structure, result);
+ RegisteredStructure registeredStructure = graph.registerStructure(structure, result);
if (result == StructureRegisteredAndWatched) {
- m_structure = RegisteredStructure;
+ m_structure = registeredStructure;
if (clobberState == StructuresAreClobbered) {
m_arrayModes = ALL_ARRAY_MODES;
m_structure.clobber();
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h (231467 => 231468)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractValue.h 2018-05-08 00:07:20 UTC (rev 231468)
@@ -243,7 +243,7 @@
return !(*this == other);
}
- bool merge(const AbstractValue& other)
+ ALWAYS_INLINE bool merge(const AbstractValue& other)
{
if (other.isClear())
return false;
Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (231467 => 231468)
--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2018-05-08 00:07:20 UTC (rev 231468)
@@ -852,7 +852,7 @@
// about such things.
break;
}
- if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
+ if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant() || !node->result())
continue;
// Interesting fact: this freezing that we do right here may turn an fragile value into
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (231467 => 231468)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.h 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h 2018-05-08 00:07:20 UTC (rev 231468)
@@ -84,23 +84,12 @@
thingToDo(_node, (graph).m_varArgChildren[_childIdx]); \
} \
} else { \
- if (!_node->child1()) { \
- ASSERT( \
- !_node->child2() \
- && !_node->child3()); \
- break; \
+ for (unsigned _edgeIndex = 0; _edgeIndex < AdjacencyList::Size; _edgeIndex++) { \
+ Edge& _edge = _node->children.child(_edgeIndex); \
+ if (!_edge) \
+ break; \
+ thingToDo(_node, _edge); \
} \
- thingToDo(_node, _node->child1()); \
- \
- if (!_node->child2()) { \
- ASSERT(!_node->child3()); \
- break; \
- } \
- thingToDo(_node, _node->child2()); \
- \
- if (!_node->child3()) \
- break; \
- thingToDo(_node, _node->child3()); \
} \
} while (false)
@@ -719,13 +708,13 @@
}
template<typename ChildFunctor>
- void doToChildrenWithNode(Node* node, const ChildFunctor& functor)
+ ALWAYS_INLINE void doToChildrenWithNode(Node* node, const ChildFunctor& functor)
{
DFG_NODE_DO_TO_CHILDREN(*this, node, functor);
}
template<typename ChildFunctor>
- void doToChildren(Node* node, const ChildFunctor& functor)
+ ALWAYS_INLINE void doToChildren(Node* node, const ChildFunctor& functor)
{
doToChildrenWithNode(
node,
Modified: trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp (231467 => 231468)
--- trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/dfg/DFGInPlaceAbstractState.cpp 2018-05-08 00:07:20 UTC (rev 231468)
@@ -60,13 +60,6 @@
ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->variablesAtTail.numberOfLocals());
m_abstractValues.resize();
-
- for (size_t i = 0; i < basicBlock->size(); i++) {
- NodeFlowProjection::forEach(
- basicBlock->at(i), [&] (NodeFlowProjection nodeProjection) {
- forNode(nodeProjection).clear();
- });
- }
m_variables = basicBlock->valuesAtHead;
Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (231467 => 231468)
--- trunk/Source/_javascript_Core/jit/JIT.cpp 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp 2018-05-08 00:07:20 UTC (rev 231468)
@@ -1011,6 +1011,11 @@
return result;
}
+Seconds JIT::totalCompileTime()
+{
+ return totalBaselineCompileTime + totalDFGCompileTime + totalFTLCompileTime;
+}
+
} // namespace JSC
#endif // ENABLE(JIT)
Modified: trunk/Source/_javascript_Core/jit/JIT.h (231467 => 231468)
--- trunk/Source/_javascript_Core/jit/JIT.h 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/jit/JIT.h 2018-05-08 00:07:20 UTC (rev 231468)
@@ -250,6 +250,7 @@
static int stackPointerOffsetFor(CodeBlock*);
JS_EXPORT_PRIVATE static HashMap<CString, Seconds> compileTimeStats();
+ JS_EXPORT_PRIVATE static Seconds totalCompileTime();
private:
void privateCompileMainPass();
Modified: trunk/Source/_javascript_Core/jsc.cpp (231467 => 231468)
--- trunk/Source/_javascript_Core/jsc.cpp 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/_javascript_Core/jsc.cpp 2018-05-08 00:07:20 UTC (rev 231468)
@@ -347,6 +347,7 @@
static EncodedJSValue JSC_HOST_CALL functionFlashHeapAccess(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDisableRichSourceInfo(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionMallocInALoop(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionTotalCompileTime(ExecState*);
struct Script {
enum class StrictMode {
@@ -606,6 +607,7 @@
addFunction(vm, "disableRichSourceInfo", functionDisableRichSourceInfo, 0);
addFunction(vm, "mallocInALoop", functionMallocInALoop, 0);
+ addFunction(vm, "totalCompileTime", functionTotalCompileTime, 0);
}
void addFunction(VM& vm, JSObject* object, const char* name, NativeFunction function, unsigned arguments)
@@ -1809,6 +1811,11 @@
return JSValue::encode(jsUndefined());
}
+EncodedJSValue JSC_HOST_CALL functionTotalCompileTime(ExecState*)
+{
+ return JSValue::encode(jsNumber(JIT::totalCompileTime().milliseconds()));
+}
+
template<typename ValueType>
typename std::enable_if<!std::is_fundamental<ValueType>::value>::type addOption(VM&, JSObject*, Identifier, ValueType) { }
Modified: trunk/Source/WTF/ChangeLog (231467 => 231468)
--- trunk/Source/WTF/ChangeLog 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/WTF/ChangeLog 2018-05-08 00:07:20 UTC (rev 231468)
@@ -1,3 +1,18 @@
+2018-05-06 Filip Pizlo <fpi...@apple.com>
+
+ InPlaceAbstractState::beginBasicBlock shouldn't have to clear any abstract values
+ https://bugs.webkit.org/show_bug.cgi?id=185365
+
+ Reviewed by Saam Barati.
+
+ Fix some inlining goof-ups.
+
+ * wtf/TinyPtrSet.h:
+ (WTF::TinyPtrSet::add):
+ (WTF::TinyPtrSet::merge):
+ (WTF::TinyPtrSet::addOutOfLine):
+ (WTF::TinyPtrSet::mergeOtherOutOfLine):
+
2018-05-06 Yusuke Suzuki <utatane....@gmail.com>
[WTF] Embed default atomic string table in Thread
Modified: trunk/Source/WTF/wtf/TinyPtrSet.h (231467 => 231468)
--- trunk/Source/WTF/wtf/TinyPtrSet.h 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Source/WTF/wtf/TinyPtrSet.h 2018-05-08 00:07:20 UTC (rev 231468)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -103,7 +103,7 @@
}
// Returns true if the value was added, or false if the value was already there.
- bool add(T value)
+ ALWAYS_INLINE bool add(T value)
{
ASSERT(value);
if (isThin()) {
@@ -156,7 +156,7 @@
return containsOutOfLine(value);
}
- bool merge(const TinyPtrSet& other)
+ ALWAYS_INLINE bool merge(const TinyPtrSet& other)
{
if (other.isThin()) {
if (other.singleEntry())
@@ -164,25 +164,7 @@
return false;
}
- OutOfLineList* list = other.list();
- if (list->m_length >= 2) {
- if (isThin()) {
- OutOfLineList* myNewList = OutOfLineList::create(
- list->m_length + !!singleEntry());
- if (singleEntry()) {
- myNewList->m_length = 1;
- myNewList->list()[0] = singleEntry();
- }
- set(myNewList);
- }
- bool changed = false;
- for (unsigned i = 0; i < list->m_length; ++i)
- changed |= addOutOfLine(list->list()[i]);
- return changed;
- }
-
- ASSERT(list->m_length);
- return add(list->list()[0]);
+ return mergeOtherOutOfLine(other);
}
template<typename Functor>
@@ -379,7 +361,7 @@
static const unsigned defaultStartingSize = 4;
- bool addOutOfLine(T value)
+ NEVER_INLINE bool addOutOfLine(T value)
{
OutOfLineList* list = this->list();
for (unsigned i = 0; i < list->m_length; ++i) {
@@ -402,6 +384,29 @@
return true;
}
+ NEVER_INLINE bool mergeOtherOutOfLine(const TinyPtrSet& other)
+ {
+ OutOfLineList* list = other.list();
+ if (list->m_length >= 2) {
+ if (isThin()) {
+ OutOfLineList* myNewList = OutOfLineList::create(
+ list->m_length + !!singleEntry());
+ if (singleEntry()) {
+ myNewList->m_length = 1;
+ myNewList->list()[0] = singleEntry();
+ }
+ set(myNewList);
+ }
+ bool changed = false;
+ for (unsigned i = 0; i < list->m_length; ++i)
+ changed |= addOutOfLine(list->list()[i]);
+ return changed;
+ }
+
+ ASSERT(list->m_length);
+ return add(list->list()[0]);
+ }
+
bool containsOutOfLine(T value) const
{
OutOfLineList* list = this->list();
Modified: trunk/Tools/ChangeLog (231467 => 231468)
--- trunk/Tools/ChangeLog 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Tools/ChangeLog 2018-05-08 00:07:20 UTC (rev 231468)
@@ -1,3 +1,14 @@
+2018-05-06 Filip Pizlo <fpi...@apple.com>
+
+ InPlaceAbstractState::beginBasicBlock shouldn't have to clear any abstract values
+ https://bugs.webkit.org/show_bug.cgi?id=185365
+
+ Reviewed by Saam Barati.
+
+ Make it possible to measure compile times.
+
+ * Scripts/run-jsc-benchmarks:
+
2018-05-07 Leo Balter <leonardo.bal...@gmail.com>
Test262-Runner: Add base module to the local deps - unicore/Exact.pl
Modified: trunk/Tools/Scripts/run-jsc-benchmarks (231467 => 231468)
--- trunk/Tools/Scripts/run-jsc-benchmarks 2018-05-08 00:05:08 UTC (rev 231467)
+++ trunk/Tools/Scripts/run-jsc-benchmarks 2018-05-08 00:07:20 UTC (rev 231468)
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
-# Copyright (C) 2011-2015 Apple Inc. All rights reserved.
+# Copyright (C) 2011-2018 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -216,8 +216,10 @@
$outer=4
$quantum=1000
$includeSunSpider=true
+$includeSunSpiderCompileTime=true
$includeLongSpider=false
$includeV8=true
+$includeV8CompileTime=true
$includeKraken=true
$includeJSBench=true
$includeMicrobenchmarks=true
@@ -311,7 +313,11 @@
puts "--dont-copy-vms Don't copy VMs even when doing a remote benchmarking run;"
puts " instead assume that they are already there."
puts "--sunspider Only run SunSpider."
- puts "--v8-spider Only run V8."
+ puts "--sunspider-compile-time"
+ puts " Only run the SunSpider compile time benchmark."
+ puts "--v8-spider Only run SunSpider-style V8."
+ puts "--v8-spider-compile-time"
+ puts " Only run the SunSpider-style V8 compile time benchmark."
puts "--kraken Only run Kraken."
puts "--js-bench Only run JSBench."
puts "--microbenchmarks Only run microbenchmarks."
@@ -856,6 +862,20 @@
end
end
+# Benchmark that consists of a single file and must be loaded in its own global object each
+# time (i.e. run()), and returns the time spent compiling
+class SingleFileCompileTimeBenchmarkParameters
+ attr_reader :benchPath
+
+ def initialize(benchPath)
+ @benchPath = benchPath
+ end
+
+ def kind
+ :singleFileCompileTimeBenchmark
+ end
+end
+
# Benchmark that consists of one or more data files that should be loaded globally, followed
# by a command to run the benchmark.
class MultiFileTimedBenchmarkParameters
@@ -999,6 +1019,23 @@
doublePuts($stderr,file,"print(\"#{name}: #{plan.vm}: #{plan.iteration}: #{innerIndex}: Time: \"+__bencher_run(#{benchParams.benchPath.inspect}));")
doublePuts($stderr,file,"gc();") unless plan.vm.shouldMeasureGC
}
+ elsif benchParams.kind == :singleFileCompileTimeBenchmark
+ doublePuts($stderr,file,"function __bencher_run(__bencher_what) {")
+ doublePuts($stderr,file," var __compileTimeBefore = totalCompileTime();")
+ $rerun.times {
+ doublePuts($stderr,file," run(__bencher_what);")
+ }
+ doublePuts($stderr,file," return totalCompileTime() - __compileTimeBefore;")
+ doublePuts($stderr,file,"}")
+ $warmup.times {
+ doublePuts($stderr,file,"__bencher_run(#{benchParams.benchPath.inspect})")
+ doublePuts($stderr,file,"gc();") unless plan.vm.shouldMeasureGC
+ }
+ $inner.times {
+ | innerIndex |
+ doublePuts($stderr,file,"print(\"#{name}: #{plan.vm}: #{plan.iteration}: #{innerIndex}: Time: \"+__bencher_run(#{benchParams.benchPath.inspect}));")
+ doublePuts($stderr,file,"gc();") unless plan.vm.shouldMeasureGC
+ }
else
raise unless benchParams.kind == :singleFileTimedBenchmark
doublePuts($stderr,file,"function __bencher_run(__bencher_what) {")
@@ -1539,6 +1576,10 @@
"x#{weight} "
end
end
+
+ def environment
+ {}
+ end
end
class SunSpiderBenchmark
@@ -1641,6 +1682,23 @@
end
end
+class CompileTimeBenchmark
+ include Benchmark
+
+ def initialize(name, fullPath)
+ @name = name
+ @fullPath = fullPath
+ end
+
+ def emitRunCode(plan)
+ emitBenchRunCode(fullname, plan, SingleFileCompileTimeBenchmarkParameters.new(ensureFile("CompileTime-#{@name}", @fullPath)))
+ end
+
+ def environment
+ {"JSC_useConcurrentJIT" => "false", "JSC_reportTotalCompileTimes" => "true"}
+ end
+end
+
class KrakenBenchmark
include Benchmark
@@ -2038,7 +2096,12 @@
end
def environment
- @environment
+ result = @environment.clone
+ benchmark.environment.each_pair {
+ | key, value |
+ result[key] = value
+ }
+ result
end
def prefix
@@ -2722,8 +2785,10 @@
def resetBenchOptionsIfNecessary
unless $sawBenchOptions
$includeSunSpider = false
+ $includeSunSpiderCompileTime = false
$includeLongSpider = false
$includeV8 = false
+ $includeV8CompileTime = false
$includeKraken = false
$includeJSBench = false
$includeMicrobenchmarks = false
@@ -2748,8 +2813,10 @@
['--minimum', GetoptLong::REQUIRED_ARGUMENT],
['--timing-mode', GetoptLong::REQUIRED_ARGUMENT],
['--sunspider', GetoptLong::NO_ARGUMENT],
+ ['--sunspider-compile-time', GetoptLong::NO_ARGUMENT],
['--longspider', GetoptLong::NO_ARGUMENT],
['--v8-spider', GetoptLong::NO_ARGUMENT],
+ ['--v8-spider-compile-time', GetoptLong::NO_ARGUMENT],
['--kraken', GetoptLong::NO_ARGUMENT],
['--js-bench', GetoptLong::NO_ARGUMENT],
['--microbenchmarks', GetoptLong::NO_ARGUMENT],
@@ -2828,6 +2895,9 @@
when '--sunspider'
resetBenchOptionsIfNecessary
$includeSunSpider = true
+ when '--sunspider-compile-time'
+ resetBenchOptionsIfNecessary
+ $includeSunSpiderCompileTime = true
when '--longspider'
resetBenchOptionsIfNecessary
$includeLongSpider = true
@@ -2834,6 +2904,9 @@
when '--v8-spider'
resetBenchOptionsIfNecessary
$includeV8 = true
+ when '--v8-spider-compile-time'
+ resetBenchOptionsIfNecessary
+ $includeV8CompileTime = true
when '--kraken'
resetBenchOptionsIfNecessary
$includeKraken = true
@@ -3002,6 +3075,7 @@
}
SUNSPIDER = BenchmarkSuite.new("SunSpider", :arithmeticMean, 0)
+ SUNSPIDER_COMPILE_TIME = BenchmarkSuite.new("SunSpider-CompileTime", :arithmeticMean, 0)
WARMUP = BenchmarkSuite.new("WARMUP", :arithmeticMean, 0)
["3d-cube", "3d-morph", "3d-raytrace", "access-binary-trees",
"access-fannkuch", "access-nbody", "access-nsieve",
@@ -3013,6 +3087,7 @@
"string-unpack-code", "string-validate-input"].each {
| name |
SUNSPIDER.add SunSpiderBenchmark.new(name)
+ SUNSPIDER_COMPILE_TIME.add CompileTimeBenchmark.new(name, "#{SUNSPIDER_PATH}/#{name}.js")
WARMUP.addIgnoringPattern SunSpiderBenchmark.new(name)
}
@@ -3029,10 +3104,12 @@
}
V8 = BenchmarkSuite.new("V8Spider", :geometricMean, 0)
+ V8_COMPILE_TIME = BenchmarkSuite.new("V8Spider-CompileTime", :geometricMean, 0)
["crypto", "deltablue", "earley-boyer", "raytrace",
"regexp", "richards", "splay"].each {
| name |
V8.add V8Benchmark.new(name)
+ V8_COMPILE_TIME.add CompileTimeBenchmark.new(name, "#{V8_PATH}/v8-#{name}.js")
}
OCTANE = BenchmarkSuite.new("Octane", :geometricMean, 1)
@@ -3193,6 +3270,10 @@
$suites << SUNSPIDER
end
+ if $includeSunSpiderCompileTime and not SUNSPIDER_COMPILE_TIME.empty?
+ $suites << SUNSPIDER_COMPILE_TIME
+ end
+
if $includeLongSpider and not LONGSPIDER.empty?
$suites << LONGSPIDER
end
@@ -3201,6 +3282,10 @@
$suites << V8
end
+ if $includeV8CompileTime and not V8_COMPILE_TIME.empty?
+ $suites << V8_COMPILE_TIME
+ end
+
if $includeOctane and not OCTANE.empty?
if OCTANE_PATH
$suites << OCTANE