We should have a DFG intrinsic that checks if a value is a TypedArrayView
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 Jun 2016 01:39:01 +0000 (01:39 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 Jun 2016 01:39:01 +0000 (01:39 +0000)
https://bugs.webkit.org/show_bug.cgi?id=159048

Reviewed by Saam Barati.

This patch adds a new DFG Intrinsic that checks if a value is a TypedArrayView.
The intrinsic, IsTypedArrayView, works in the same way that the other Is<insert-type>
DFG nodes work. Additionally, a new builtin function isTypedArrayView has been added.
These changes are needed to fix regressions in %TypedArray%.prototype.subarray.

* builtins/BuiltinNames.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileIsTypedArrayView):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileIsTypedArrayView):
(JSC::FTL::DFG::LowerDFGToB3::isTypedArrayView):
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSTypedArrayViewPrototype.cpp:
(JSC::typedArrayViewPrivateFuncIsTypedArrayView):
* runtime/JSTypedArrayViewPrototype.h:
* tests/stress/istypedarrayview-intrinsic.js: Added.
(makeFn):
(typedArrays.forEach):
(let.test):
(test):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@202363 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/BuiltinNames.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSTypedArrayViewPrototype.cpp
Source/JavaScriptCore/runtime/JSTypedArrayViewPrototype.h
Source/JavaScriptCore/tests/stress/istypedarrayview-intrinsic.js [new file with mode: 0644]

index 0a3738d..72ddffa 100644 (file)
@@ -1,3 +1,55 @@
+2016-06-22  Keith Miller  <keith_miller@apple.com>
+
+        We should have a DFG intrinsic that checks if a value is a TypedArrayView
+        https://bugs.webkit.org/show_bug.cgi?id=159048
+
+        Reviewed by Saam Barati.
+
+        This patch adds a new DFG Intrinsic that checks if a value is a TypedArrayView.
+        The intrinsic, IsTypedArrayView, works in the same way that the other Is<insert-type>
+        DFG nodes work. Additionally, a new builtin function isTypedArrayView has been added.
+        These changes are needed to fix regressions in %TypedArray%.prototype.subarray.
+
+        * builtins/BuiltinNames.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileIsTypedArrayView):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileIsTypedArrayView):
+        (JSC::FTL::DFG::LowerDFGToB3::isTypedArrayView):
+        * runtime/Intrinsic.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSTypedArrayViewPrototype.cpp:
+        (JSC::typedArrayViewPrivateFuncIsTypedArrayView):
+        * runtime/JSTypedArrayViewPrototype.h:
+        * tests/stress/istypedarrayview-intrinsic.js: Added.
+        (makeFn):
+        (typedArrays.forEach):
+        (let.test):
+        (test):
+
 2016-06-21  Anders Carlsson  <andersca@apple.com>
 
         Fix build.
index 0bba106..f085008 100644 (file)
@@ -121,6 +121,7 @@ namespace JSC {
     macro(thisNumberValue) \
     macro(newTargetLocal) \
     macro(derivedConstructor) \
+    macro(isTypedArrayView) \
     macro(isBoundFunction) \
     macro(hasInstanceBoundFunction) \
     macro(instanceOf) \
index 7dccac3..ad827d5 100644 (file)
@@ -1035,7 +1035,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case IsObject:
     case IsObjectOrNull:
     case IsFunction:
-    case IsRegExpObject: {
+    case IsRegExpObject:
+    case IsTypedArrayView: {
         AbstractValue child = forNode(node->child1());
         if (child.value()) {
             bool constantWasSet = true;
@@ -1097,6 +1098,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             case IsEmpty:
                 setConstant(node, jsBoolean(child.value().isEmpty()));
                 break;
+            case IsTypedArrayView:
+                setConstant(node, jsBoolean(child.value().isObject() && isTypedView(child.value().getObject()->classInfo()->typedArrayStorageType)));
+                break;
             default:
                 constantWasSet = false;
                 break;
@@ -1268,6 +1272,19 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
             break;
 
+        case IsTypedArrayView:
+            if (!(child.m_type & ~SpecTypedArrayView)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+            if (!(child.m_type & SpecTypedArrayView)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            break;
+
         default:
             break;
         }
index d0767e2..f697a6e 100644 (file)
@@ -2314,6 +2314,14 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case IsTypedArrayViewIntrinsic: {
+        ASSERT(argumentCountIncludingThis == 2);
+
+        insertChecks();
+        set(VirtualRegister(resultOperand), addToGraph(IsTypedArrayView, OpInfo(prediction), get(virtualRegisterForArgument(1, registerOffset))));
+        return true;
+    }
+
     case StringPrototypeReplaceIntrinsic: {
         if (argumentCountIncludingThis != 3)
             return false;
index 9a48d58..97a404b 100644 (file)
@@ -172,6 +172,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case IsString:
     case IsObject:
     case IsRegExpObject:
+    case IsTypedArrayView:
     case LogicalNot:
     case CheckInBounds:
     case DoubleRep:
index ca92b58..8d8f9e2 100644 (file)
@@ -165,6 +165,7 @@ bool doesGC(Graph& graph, Node* node)
     case IsObjectOrNull:
     case IsFunction:
     case IsRegExpObject:
+    case IsTypedArrayView:
     case TypeOf:
     case LogicalNot:
     case ToPrimitive:
index a25d820..5cf0087 100644 (file)
@@ -1547,6 +1547,7 @@ private:
         case DeleteById:
         case DeleteByVal:
         case IsJSArray:
+        case IsTypedArrayView:
         case IsEmpty:
         case IsUndefined:
         case IsBoolean:
index de34a48..542b47f 100644 (file)
@@ -317,6 +317,7 @@ namespace JSC { namespace DFG {
     macro(IsObjectOrNull, NodeResultBoolean) \
     macro(IsFunction, NodeResultBoolean) \
     macro(IsRegExpObject, NodeResultBoolean) \
+    macro(IsTypedArrayView, NodeResultBoolean) \
     macro(TypeOf, NodeResultJS) \
     macro(LogicalNot, NodeResultBoolean) \
     macro(ToPrimitive, NodeResultJS | NodeMustGenerate) \
index 615474b..792aa85 100644 (file)
@@ -792,7 +792,8 @@ private:
         case IsObject:
         case IsObjectOrNull:
         case IsFunction:
-        case IsRegExpObject: {
+        case IsRegExpObject:
+        case IsTypedArrayView: {
             setPrediction(SpecBoolean);
             break;
         }
index 60484b0..f8e6613 100644 (file)
@@ -266,6 +266,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case IsObjectOrNull:
     case IsFunction:
     case IsRegExpObject:
+    case IsTypedArrayView:
     case TypeOf:
     case LogicalNot:
     case CallObjectConstructor:
index b914d39..f0f5b3a 100644 (file)
@@ -3455,6 +3455,36 @@ void SpeculativeJIT::compileIsRegExpObject(Node* node)
     blessedBooleanResult(resultGPR, node);
 }
 
+void SpeculativeJIT::compileIsTypedArrayView(Node* node)
+{
+    JSValueOperand value(this, node->child1());
+#if USE(JSVALUE64)
+    GPRTemporary result(this, Reuse, value);
+#else
+    GPRTemporary result(this, Reuse, value, PayloadWord);
+#endif
+
+    JSValueRegs valueRegs = value.jsValueRegs();
+    GPRReg resultGPR = result.gpr();
+
+    JITCompiler::Jump isNotCell = m_jit.branchIfNotCell(valueRegs);
+
+    m_jit.load8(JITCompiler::Address(valueRegs.payloadGPR(), JSCell::typeInfoTypeOffset()), resultGPR);
+    m_jit.sub32(TrustedImm32(Int8ArrayType), resultGPR);
+    m_jit.compare32(JITCompiler::BelowOrEqual,
+        resultGPR,
+        TrustedImm32(Float64ArrayType - Int8ArrayType),
+        resultGPR);
+    blessBoolean(resultGPR);
+    JITCompiler::Jump done = m_jit.jump();
+
+    isNotCell.link(&m_jit);
+    moveFalseTo(resultGPR);
+
+    done.link(&m_jit);
+    blessedBooleanResult(resultGPR, node);
+}
+
 void SpeculativeJIT::compileCallObjectConstructor(Node* node)
 {
     RELEASE_ASSERT(node->child1().useKind() == UntypedUse);
index 11416c4..debd258 100644 (file)
@@ -754,6 +754,7 @@ public:
 
     void compileIsJSArray(Node*);
     void compileIsRegExpObject(Node*);
+    void compileIsTypedArrayView(Node*);
 
     void emitCall(Node*);
     
index a03589a..5b182b4 100644 (file)
@@ -4733,6 +4733,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case IsTypedArrayView: {
+        compileIsTypedArrayView(node);
+        break;
+    }
+
     case TypeOf: {
         compileTypeOf(node);
         break;
index c8b4e94..b63ba1a 100644 (file)
@@ -4658,6 +4658,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case IsTypedArrayView: {
+        compileIsTypedArrayView(node);
+        break;
+    }
+
     case TypeOf: {
         compileTypeOf(node);
         break;
index d462afb..93e1c26 100644 (file)
@@ -187,6 +187,7 @@ inline CapabilityLevel canCompile(Node* node)
     case IsObjectOrNull:
     case IsFunction:
     case IsRegExpObject:
+    case IsTypedArrayView:
     case CheckTypeInfoFlags:
     case OverridesHasInstance:
     case InstanceOf:
index 15bfe91..a65da79 100644 (file)
@@ -900,6 +900,9 @@ private:
         case IsRegExpObject:
             compileIsRegExpObject();
             break;
+        case IsTypedArrayView:
+            compileIsTypedArrayView();
+            break;
         case TypeOf:
             compileTypeOf();
             break;
@@ -6107,6 +6110,24 @@ private:
         setBoolean(m_out.phi(m_out.boolean, notCellResult, cellResult));
     }
 
+    void compileIsTypedArrayView()
+    {
+        LValue value = lowJSValue(m_node->child1());
+
+        LBasicBlock isCellCase = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        ValueFromBlock notCellResult = m_out.anchor(m_out.booleanFalse);
+        m_out.branch(isCell(value, provenType(m_node->child1())), unsure(isCellCase), unsure(continuation));
+
+        LBasicBlock lastNext = m_out.appendTo(isCellCase, continuation);
+        ValueFromBlock cellResult = m_out.anchor(isTypedArrayView(value, provenType(m_node->child1())));
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setBoolean(m_out.phi(m_out.boolean, notCellResult, cellResult));
+    }
+
     void compileTypeOf()
     {
         Edge child = m_node->child1();
@@ -10076,6 +10097,18 @@ private:
             m_out.load8ZeroExt32(cell, m_heaps.JSCell_typeInfoType),
             m_out.constInt32(ArrayType));
     }
+
+    LValue isTypedArrayView(LValue cell, SpeculatedType type = SpecFullTop)
+    {
+        if (LValue proven = isProvenValue(type & SpecCell, SpecTypedArrayView))
+            return proven;
+        LValue jsType = m_out.sub(
+            m_out.load8ZeroExt32(cell, m_heaps.JSCell_typeInfoType),
+            m_out.constInt32(Int8ArrayType));
+        return m_out.belowOrEqual(
+            jsType,
+            m_out.constInt32(Float64ArrayType - Int8ArrayType));
+    }
     
     LValue isObject(LValue cell, SpeculatedType type = SpecFullTop)
     {
index dfd8301..4a07939 100644 (file)
@@ -60,6 +60,7 @@ enum JS_EXPORT_PRIVATE Intrinsic {
     FRoundIntrinsic,
     TruncIntrinsic,
     IsRegExpObjectIntrinsic,
+    IsTypedArrayViewIntrinsic,
     BoundThisNoArgsFunctionCallIntrinsic,
 
     // Getter intrinsics.
index ab019ca..7c93274 100644 (file)
@@ -655,6 +655,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     JSFunction* privateFuncTypedArrayLength = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncLength);
     JSFunction* privateFuncTypedArrayGetOriginalConstructor = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncGetOriginalConstructor);
     JSFunction* privateFuncTypedArraySort = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncSort);
+    JSFunction* privateFuncIsTypedArrayView = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncIsTypedArrayView, IsTypedArrayViewIntrinsic);
     JSFunction* privateFuncIsBoundFunction = JSFunction::create(vm, this, 0, String(), isBoundFunction);
     JSFunction* privateFuncHasInstanceBoundFunction = JSFunction::create(vm, this, 0, String(), hasInstanceBoundFunction);
     JSFunction* privateFuncInstanceOf = JSFunction::create(vm, this, 0, String(), objectPrivateFuncInstanceOf);
@@ -703,6 +704,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->builtinNames().typedArrayLengthPrivateName(), privateFuncTypedArrayLength, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().typedArrayGetOriginalConstructorPrivateName(), privateFuncTypedArrayGetOriginalConstructor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().typedArraySortPrivateName(), privateFuncTypedArraySort, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().isTypedArrayViewPrivateName(), privateFuncIsTypedArrayView, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().isBoundFunctionPrivateName(), privateFuncIsBoundFunction, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().hasInstanceBoundFunctionPrivateName(), privateFuncHasInstanceBoundFunction, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().instanceOfPrivateName(), privateFuncInstanceOf, DontEnum | DontDelete | ReadOnly),
index a562567..4b50e18 100644 (file)
@@ -64,6 +64,12 @@ namespace JSC {
     RELEASE_ASSERT_NOT_REACHED();                                                               \
 } while (false)
 
+EncodedJSValue JSC_HOST_CALL typedArrayViewPrivateFuncIsTypedArrayView(ExecState* exec)
+{
+    JSValue value = exec->uncheckedArgument(0);
+    return JSValue::encode(jsBoolean(value.isCell() && isTypedView(value.asCell()->classInfo()->typedArrayStorageType)));
+}
+
 EncodedJSValue JSC_HOST_CALL typedArrayViewPrivateFuncLength(ExecState* exec)
 {
     JSArrayBufferView* thisObject = jsDynamicCast<JSArrayBufferView*>(exec->argument(0));
index 6798323..df05ed9 100644 (file)
@@ -46,6 +46,7 @@ public:
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
 };
 
+EncodedJSValue JSC_HOST_CALL typedArrayViewPrivateFuncIsTypedArrayView(ExecState*);
 EncodedJSValue JSC_HOST_CALL typedArrayViewPrivateFuncSort(ExecState*);
 EncodedJSValue JSC_HOST_CALL typedArrayViewPrivateFuncLength(ExecState*);
 EncodedJSValue JSC_HOST_CALL typedArrayViewPrivateFuncGetOriginalConstructor(ExecState*);
diff --git a/Source/JavaScriptCore/tests/stress/istypedarrayview-intrinsic.js b/Source/JavaScriptCore/tests/stress/istypedarrayview-intrinsic.js
new file mode 100644 (file)
index 0000000..833d22c
--- /dev/null
@@ -0,0 +1,75 @@
+let typedArrays = [Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array];
+
+function makeFn(dontInline) {
+    let foo = createBuiltin(`(function (a) { "use strict"; return @isTypedArrayView(a); })`);
+    if (dontInline)
+        noInline(foo)
+    return foo;
+}
+
+typedArrays.forEach(function() {
+    let test = Function(
+        `
+        let foo = makeFn();
+        let bar = makeFn(true);
+        let view = new Int8Array(10);
+
+        for (i = 0; i < 100000; i++) {
+            if (!foo(view))
+                throw new Error(i);
+            if (!bar(view))
+                throw new Error(i);
+        }
+        `
+    );
+    test();
+});
+
+typedArrays.forEach(constructor1 => {
+    typedArrays.forEach(constructor2 => {
+        let test = Function(
+            `
+            let foo = makeFn();
+            let bar = makeFn(true);
+            let view1 = new ${constructor1.name}(10);
+            let view2 = new ${constructor2.name}(10);
+
+            for (i = 0; i < 100000; i++) {
+                let view = i % 2 === 0 ? view1 : view2;
+                if (!foo(view))
+                    throw new Error(i);
+                if (!bar(view))
+                    throw new Error(i);
+            }
+            `
+        );
+        test();
+    });
+});
+
+let test = function() {
+    let foo = makeFn();
+    let bar = makeFn(true);
+    for (i = 0; i < 100000; i++) {
+        if (foo(true))
+            throw new Error(i);
+        if (bar(true))
+            throw new Error(i);
+    }
+}
+test();
+
+test = function() {
+    let bar = makeFn(true);
+    let view = new Int8Array(10);
+    let obj = new DataView(new ArrayBuffer(10));
+    for (i = 0; i < 100000; i++) {
+        if (i % 2 === 0) {
+            if (!foo(view))
+                throw new Error(i);
+        } else {
+            if (foo(obj))
+                throw new Error(i);
+        }
+    }
+}