[DFG] Introduce IsCellWithType node and unify IsJSArray, IsRegExpObject and newly added IsProxyObject
https://bugs.webkit.org/show_bug.cgi?id=162000

Reviewed by Filip Pizlo.

JSTests:

* microbenchmarks/is-array-for-array.js: Added.
(isArray):
* microbenchmarks/is-array-for-mixed-case.js: Added.
(isArray):
* microbenchmarks/is-array-for-non-array-object.js: Added.
(isArray):
* microbenchmarks/is-array-for-proxy.js: Added.
(isArray):
(isArray.proxy.throw.new.Error.isArray):
(isArray.proxy.throw.new.Error):

Source/JavaScriptCore:

Sampling profiler tells that ES6SampleBench/Basic frequently calls Array.isArray(). This function is introduced in
ES5 and it is well-used to distinguish Array from the other objects. Moreover, this function is used in Array.prototype.xxx
methods as @isArray. So it's worth optimizing.

The difference between Array.isArray and @isJSArray is that Array.isArray need to consider about ProxyObject while
@isJSArray builtin intrinsic does not. So in this patch, we leverage the existing @isJSArray to implement Array.isArray.
Array.isArray is written in builtin JS code using @isJSArray and newly added @isProxyObject(). That allow us to inline
Array.isArray() code and the inlined code uses existing DFG nodes well.

Another problem is RuntimeArray and ArrayPrototype. They inherit JSArray and their JSType is ObjectType. But Array.isArray need
to return true for those types. While optimizing type checking in generic way by type display is nice, RuntimeArray and
ArrayPrototype are a bit tricky and it is super rare that these functions are passed to Array.isArray(). So instead of introducing
type display in this patch, we just introduce a new JSType, DerivedArrayType and use it in the above 2 use classes. Since
Array.isArray is specially handled in the spec (while we don't have any Date.isDate() like functions, only Array.isArray
is specified in the spec because we frequently want to distinguish Arrays from other Objects), optimizing Array.isArray specially
by introducing special DerivedArrayType is reasonable.

In LLInt level, we add a new opcode, op_is_proxy_object and op_is_derived_array. This works similar to op_is_jsarray.
And we also perform LLInt code cleanup by introducing a macro isCellWithType.

In baseline, we perform some clean up for op_is_proxy_object etc. Now duplicate code is reduced.

In DFG, we unify IsJSArray, IsRegExpObject, IsProxyObject, and IsDerivedArray into one IsCellWithType node. And we clean up
some AI code related to IsJSArray and IsRegExpObject since SpeculatedType now recognizes ProxyObject. IsJSArray and IsRegExpObject
does not do anything special for proxy objects.

The above change simplify things to create a new IsXXX DFG handling and paves the way for optimizing @isMap & @isSet in DFG.
Furthermore, introducing @isProxyObject() is nice for the first step to optimize ProxyObject handling.

Here is microbenchmark result. We can see stable performance improvement (Even if we use Proxies!).

                                            baseline                  patched

    is-array-for-array                   2.5156+-0.0288     ^      2.0668+-0.0285        ^ definitely 1.2171x faster
    is-array-for-mixed-case              4.7787+-0.0755     ^      4.4722+-0.0789        ^ definitely 1.0686x faster
    is-array-for-non-array-object        2.3596+-0.0368     ^      1.8178+-0.0262        ^ definitely 1.2980x faster
    is-array-for-proxy                   4.0469+-0.0437     ^      3.3845+-0.0404        ^ definitely 1.1957x faster

And ES6SampleBench/Basic reports 5.2% perf improvement. And now sampling result in ES6SampleBench/Basic does not pose Array.isArray.

    Benchmark             First Iteration        Worst 2%               Steady State
    baseline:Basic        28.59 ms +- 1.03 ms    15.08 ms +- 0.28 ms    1656.96 ms +- 18.02 ms
    patched:Basic         27.82 ms +- 0.44 ms    14.59 ms +- 0.16 ms    1574.65 ms +- 8.44 ms

* builtins/ArrayConstructor.js:
(isArray):
(from): Deleted.
* builtins/BuiltinNames.h:
* bytecode/BytecodeIntrinsicRegistry.h:
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/SpeculatedType.cpp:
(JSC::dumpSpeculation):
(JSC::speculationFromClassInfo):
(JSC::speculationFromStructure):
* bytecode/SpeculatedType.h:
(JSC::isProxyObjectSpeculation):
(JSC::isDerivedArraySpeculation):
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::emitIsProxyObject):
(JSC::BytecodeGenerator::emitIsDerivedArray):
(JSC::BytecodeGenerator::emitIsJSArray): Deleted.
* bytecompiler/NodesCodegen.cpp:
(JSC::BytecodeIntrinsicNode::emit_intrinsic_isProxyObject):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_isDerivedArray):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupIsCellWithType):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasQueriedType):
(JSC::DFG::Node::queriedType):
(JSC::DFG::Node::hasSpeculatedTypeForQuery):
(JSC::DFG::Node::speculatedTypeForQuery):
(JSC::DFG::Node::shouldSpeculateProxyObject):
(JSC::DFG::Node::shouldSpeculateDerivedArray):
(JSC::DFG::Node::loadVarargsData): Deleted.
(JSC::DFG::Node::shouldSpeculateArray): Deleted.
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::SafeToExecuteEdge::operator()):
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileIsCellWithType):
(JSC::DFG::SpeculativeJIT::speculateProxyObject):
(JSC::DFG::SpeculativeJIT::speculateDerivedArray):
(JSC::DFG::SpeculativeJIT::speculate):
(JSC::DFG::SpeculativeJIT::compileIsJSArray): Deleted.
(JSC::DFG::SpeculativeJIT::compileIsRegExpObject): Deleted.
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGUseKind.cpp:
(WTF::printInternal):
* dfg/DFGUseKind.h:
(JSC::DFG::typeFilterFor):
(JSC::DFG::isCell):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileIsCellWithType):
(JSC::FTL::DFG::LowerDFGToB3::speculate):
(JSC::FTL::DFG::LowerDFGToB3::isCellWithType):
(JSC::FTL::DFG::LowerDFGToB3::speculateProxyObject):
(JSC::FTL::DFG::LowerDFGToB3::speculateDerivedArray):
(JSC::FTL::DFG::LowerDFGToB3::compileIsJSArray): Deleted.
(JSC::FTL::DFG::LowerDFGToB3::compileIsRegExpObject): Deleted.
(JSC::FTL::DFG::LowerDFGToB3::isArray): Deleted.
(JSC::FTL::DFG::LowerDFGToB3::isRegExpObject): Deleted.
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emitIsCellWithType):
(JSC::JIT::emit_op_is_string):
(JSC::JIT::emit_op_is_jsarray):
(JSC::JIT::emit_op_is_proxy_object):
(JSC::JIT::emit_op_is_derived_array):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emitIsCellWithType):
(JSC::JIT::emit_op_is_string):
(JSC::JIT::emit_op_is_jsarray):
(JSC::JIT::emit_op_is_proxy_object):
(JSC::JIT::emit_op_is_derived_array):
* jsc.cpp:
(WTF::RuntimeArray::createStructure):
* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/ArrayConstructor.cpp:
(JSC::ArrayConstructor::finishCreation):
(JSC::isArraySlowInline):
(JSC::isArraySlow):
(JSC::arrayConstructorPrivateFuncIsArraySlow):
(JSC::arrayConstructorIsArray): Deleted.
* runtime/ArrayConstructor.h:
(JSC::isArray):
* runtime/ArrayPrototype.h:
(JSC::ArrayPrototype::createStructure):
* runtime/JSArray.h:
(JSC::JSArray::finishCreation):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSType.h:

Source/WebCore:

* bridge/runtime_array.h:
(JSC::RuntimeArray::createStructure):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@206065 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp
index e7aef53..b916276 100644
--- a/Source/JavaScriptCore/runtime/ArrayConstructor.cpp
+++ b/Source/JavaScriptCore/runtime/ArrayConstructor.cpp
@@ -35,12 +35,6 @@
 #include "ProxyObject.h"
 #include "JSCInlines.h"
 
-namespace JSC {
-
-static EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState*);
-
-}
-
 #include "ArrayConstructor.lut.h"
 
 namespace JSC {
@@ -67,7 +61,7 @@
     putDirectWithoutTransition(vm, vm.propertyNames->prototype, arrayPrototype, DontEnum | DontDelete | ReadOnly);
     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete);
     putDirectNonIndexAccessor(vm, vm.propertyNames->speciesSymbol, speciesSymbol, Accessor | ReadOnly | DontEnum);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->isArray, arrayConstructorIsArray, DontEnum, 1);
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->isArray, arrayConstructorIsArrayCodeGenerator, DontEnum);
 }
 
 // ------------------------------ Functions ---------------------------
@@ -122,11 +116,41 @@
     return CallType::Host;
 }
 
+static ALWAYS_INLINE bool isArraySlowInline(ExecState* exec, ProxyObject* proxy)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    while (true) {
+        if (proxy->isRevoked()) {
+            throwTypeError(exec, scope, ASCIILiteral("Array.isArray cannot be called on a Proxy that has been revoked"));
+            return false;
+        }
+        JSObject* argument = proxy->target();
+
+        if (argument->type() == ArrayType ||  argument->type() == DerivedArrayType)
+            return true;
+
+        if (argument->type() != ProxyObjectType)
+            return false;
+
+        proxy = jsCast<ProxyObject*>(argument);
+    }
+
+    ASSERT_NOT_REACHED();
+}
+
+bool isArraySlow(ExecState* exec, ProxyObject* argument)
+{
+    return isArraySlowInline(exec, argument);
+}
+
 // ES6 7.2.2
 // https://tc39.github.io/ecma262/#sec-isarray
-EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState* exec)
+EncodedJSValue JSC_HOST_CALL arrayConstructorPrivateFuncIsArraySlow(ExecState* exec)
 {
-    return JSValue::encode(jsBoolean(isArray(exec, exec->argument(0))));
+    ASSERT(jsDynamicCast<ProxyObject*>(exec->argument(0)));
+    return JSValue::encode(jsBoolean(isArraySlowInline(exec, jsCast<ProxyObject*>(exec->uncheckedArgument(0)))));
 }
 
 EncodedJSValue JSC_HOST_CALL arrayConstructorPrivateFuncIsArrayConstructor(ExecState* exec)
diff --git a/Source/JavaScriptCore/runtime/ArrayConstructor.h b/Source/JavaScriptCore/runtime/ArrayConstructor.h
index 82d236b..17d7608 100644
--- a/Source/JavaScriptCore/runtime/ArrayConstructor.h
+++ b/Source/JavaScriptCore/runtime/ArrayConstructor.h
@@ -64,34 +64,23 @@
 JSValue constructArrayWithSizeQuirk(ExecState*, ArrayAllocationProfile*, JSGlobalObject*, JSValue length, JSValue prototype = JSValue());
 
 EncodedJSValue JSC_HOST_CALL arrayConstructorPrivateFuncIsArrayConstructor(ExecState*);
+EncodedJSValue JSC_HOST_CALL arrayConstructorPrivateFuncIsArraySlow(ExecState*);
+bool isArraySlow(ExecState*, ProxyObject* argument);
 
 // ES6 7.2.2
 // https://tc39.github.io/ecma262/#sec-isarray
 inline bool isArray(ExecState* exec, JSValue argumentValue)
 {
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
     if (!argumentValue.isObject())
         return false;
 
     JSObject* argument = jsCast<JSObject*>(argumentValue);
-    while (true) {
-        if (argument->inherits(JSArray::info()))
-            return true;
+    if (argument->type() == ArrayType || argument->type() == DerivedArrayType)
+        return true;
 
-        if (argument->type() != ProxyObjectType)
-            return false;
-
-        ProxyObject* proxy = jsCast<ProxyObject*>(argument);
-        if (proxy->isRevoked()) {
-            throwTypeError(exec, scope, ASCIILiteral("Array.isArray cannot be called on a Proxy that has been revoked"));
-            return false;
-        }
-        argument = proxy->target();
-    }
-
-    ASSERT_NOT_REACHED();
+    if (argument->type() != ProxyObjectType)
+        return false;
+    return isArraySlow(exec, jsCast<ProxyObject*>(argument));
 }
 
 inline bool isArrayConstructor(JSValue argumentValue)
diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.h b/Source/JavaScriptCore/runtime/ArrayPrototype.h
index 38675cb..0237465 100644
--- a/Source/JavaScriptCore/runtime/ArrayPrototype.h
+++ b/Source/JavaScriptCore/runtime/ArrayPrototype.h
@@ -47,7 +47,7 @@
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
     {
-        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info(), ArrayClass);
+        return Structure::create(vm, globalObject, prototype, TypeInfo(DerivedArrayType, StructureFlags), info(), ArrayClass);
     }
 
     SpeciesWatchpointStatus speciesWatchpointStatus() const { return m_speciesWatchpointStatus; }
diff --git a/Source/JavaScriptCore/runtime/JSArray.h b/Source/JavaScriptCore/runtime/JSArray.h
index c3c5fb5..5619330 100644
--- a/Source/JavaScriptCore/runtime/JSArray.h
+++ b/Source/JavaScriptCore/runtime/JSArray.h
@@ -148,6 +148,12 @@
     }
         
 protected:
+    void finishCreation(VM& vm)
+    {
+        Base::finishCreation(vm);
+        ASSERT_WITH_MESSAGE(type() == ArrayType || type() == DerivedArrayType, "Instance inheriting JSArray should have either ArrayType or DerivedArrayType");
+    }
+
     static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
 
     static bool deleteProperty(JSCell*, ExecState*, PropertyName);
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
index 65b2c0a..4cf9df1 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
@@ -686,6 +686,7 @@
     JSFunction* privateFuncThisTimeValue = JSFunction::create(vm, this, 0, String(), dateProtoFuncGetTime);
     JSFunction* privateFuncThisNumberValue = JSFunction::create(vm, this, 0, String(), numberProtoFuncValueOf);
     JSFunction* privateFuncIsArrayConstructor = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArrayConstructor);
+    JSFunction* privateFuncIsArraySlow = JSFunction::create(vm, this, 0, String(), arrayConstructorPrivateFuncIsArraySlow);
     JSFunction* privateFuncConcatMemcpy = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncConcatMemcpy);
     JSFunction* privateFuncAppendMemcpy = JSFunction::create(vm, this, 0, String(), arrayProtoPrivateFuncAppendMemcpy);
     JSFunction* privateFuncConcatSlowPath = JSFunction::createBuiltinFunction(vm, arrayPrototypeConcatSlowPathCodeGenerator(vm), this);
@@ -750,6 +751,7 @@
         GlobalPropertyInfo(vm.propertyNames->builtinNames().setIteratorNextPrivateName(), JSFunction::create(vm, this, 0, String(), privateFuncSetIteratorNext), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().isMapPrivateName(), JSFunction::create(vm, this, 1, String(), privateFuncIsMap), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().isArrayPrivateName(), arrayConstructor->getDirect(vm, vm.propertyNames->isArray), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().isArraySlowPrivateName(), privateFuncIsArraySlow, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().isArrayConstructorPrivateName(), privateFuncIsArrayConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().concatMemcpyPrivateName(), privateFuncConcatMemcpy, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().appendMemcpyPrivateName(), privateFuncAppendMemcpy, DontEnum | DontDelete | ReadOnly),
diff --git a/Source/JavaScriptCore/runtime/JSType.h b/Source/JavaScriptCore/runtime/JSType.h
index 9e4fe90..0df74f2 100644
--- a/Source/JavaScriptCore/runtime/JSType.h
+++ b/Source/JavaScriptCore/runtime/JSType.h
@@ -60,10 +60,12 @@
     PureForwardingProxyType,
     ImpureProxyType,
     WithScopeType,
-    ArrayType,
     DirectArgumentsType,
     ScopedArgumentsType,
 
+    ArrayType,
+    DerivedArrayType,
+
     Int8ArrayType = 100,
     Int16ArrayType,
     Int32ArrayType,