Add Intrinsic Getters and use them to fix performance on the getters of TypedArray...
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Oct 2015 22:18:42 +0000 (22:18 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 16 Oct 2015 22:18:42 +0000 (22:18 +0000)
https://bugs.webkit.org/show_bug.cgi?id=149687

Patch by Keith Miller <keith@Keiths-MacBook-Pro-5.local> on 2015-10-16
Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Add the ability to create intrinsic getters in both the inline cache and the DFG/FTL. When the
getter fetched by a GetById has an intrinsic we know about we add a new intrinsic access case.
Once we get to the DFG, we observe that the access case was an intrinsic and add an appropriate
GetByIdVariant. We then parse the intrinsic into an appropriate DFG node.

The first intrinsics are the new TypedArray prototype getters length, byteLength, and byteOffset.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
(JSC::GetByIdStatus::computeFor):
* bytecode/GetByIdVariant.cpp:
(JSC::GetByIdVariant::GetByIdVariant):
(JSC::GetByIdVariant::operator=):
(JSC::GetByIdVariant::canMergeIntrinsicStructures):
(JSC::GetByIdVariant::attemptToMerge):
(JSC::GetByIdVariant::dumpInContext):
* bytecode/GetByIdVariant.h:
(JSC::GetByIdVariant::intrinsicFunction):
(JSC::GetByIdVariant::intrinsic):
(JSC::GetByIdVariant::callLinkStatus): Deleted.
* bytecode/PolymorphicAccess.cpp:
(JSC::AccessGenerationState::addWatchpoint):
(JSC::AccessGenerationState::restoreScratch):
(JSC::AccessGenerationState::succeed):
(JSC::AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling):
(JSC::AccessGenerationState::preserveLiveRegistersToStackForCall):
(JSC::AccessGenerationState::restoreLiveRegistersFromStackForCall):
(JSC::AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException):
(JSC::AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal):
(JSC::AccessGenerationState::originalExceptionHandler):
(JSC::AccessGenerationState::originalCallSiteIndex):
(JSC::AccessCase::getIntrinsic):
(JSC::AccessCase::clone):
(JSC::AccessCase::visitWeak):
(JSC::AccessCase::generate):
(WTF::printInternal):
(JSC::AccessCase::AccessCase): Deleted.
(JSC::AccessCase::get): Deleted.
(JSC::AccessCase::replace): Deleted.
(JSC::AccessCase::transition): Deleted.
* bytecode/PolymorphicAccess.h:
(JSC::AccessCase::isGet):
(JSC::AccessCase::isPut):
(JSC::AccessCase::isIn):
(JSC::AccessCase::intrinsicFunction):
(JSC::AccessCase::intrinsic):
(JSC::AccessGenerationState::AccessGenerationState):
(JSC::AccessGenerationState::liveRegistersForCall):
(JSC::AccessGenerationState::callSiteIndexForExceptionHandling):
(JSC::AccessGenerationState::numberOfStackBytesUsedForRegisterPreservation):
(JSC::AccessGenerationState::needsToRestoreRegistersIfException):
(JSC::AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite):
* bytecode/PutByIdVariant.h:
(JSC::PutByIdVariant::intrinsic):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::ArrayMode::alreadyChecked):
(JSC::DFG::arrayTypeToString):
(JSC::DFG::toTypedArrayType):
(JSC::DFG::refineTypedArrayType):
(JSC::DFG::permitsBoundsCheckLowering):
* dfg/DFGArrayMode.h:
(JSC::DFG::ArrayMode::supportsLength):
(JSC::DFG::ArrayMode::isSomeTypedArrayView):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::attemptToInlineCall):
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
(JSC::DFG::ByteCodeParser::handleIntrinsicGetter):
(JSC::DFG::ByteCodeParser::load):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::presenceLike): Deleted.
(JSC::DFG::ByteCodeParser::store): Deleted.
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::convertToGetArrayLength): Deleted.
(JSC::DFG::FixupPhase::prependGetArrayLength): Deleted.
(JSC::DFG::FixupPhase::fixupChecksInBlock): Deleted.
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::tryGetFoldableView):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::checkArray):
(JSC::DFG::SpeculativeJIT::compileGetArrayLength):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileGetArrayLength):
* jit/IntrinsicEmitter.cpp: Added.
(JSC::AccessCase::canEmitIntrinsicGetter):
(JSC::AccessCase::emitIntrinsicGetter):
* jit/Repatch.cpp:
(JSC::tryCacheGetByID):
* runtime/Intrinsic.h:
* runtime/JSArrayBufferView.cpp:
(JSC::JSArrayBufferView::put):
(JSC::JSArrayBufferView::defineOwnProperty):
(JSC::JSArrayBufferView::deleteProperty):
(JSC::JSArrayBufferView::getOwnNonIndexPropertyNames):
(JSC::JSArrayBufferView::getOwnPropertySlot): Deleted.
(JSC::JSArrayBufferView::finalize): Deleted.
* runtime/JSDataView.cpp:
(JSC::JSDataView::getOwnPropertySlot):
(JSC::JSDataView::put):
(JSC::JSDataView::defineOwnProperty):
(JSC::JSDataView::deleteProperty):
(JSC::JSDataView::getOwnNonIndexPropertyNames):
* runtime/JSDataView.h:
* runtime/JSFunction.h:
* runtime/JSFunctionInlines.h:
(JSC::JSFunction::intrinsic):
* runtime/JSGenericTypedArrayView.h:
* runtime/JSGenericTypedArrayViewInlines.h:
(JSC::JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot):
(JSC::JSGenericTypedArrayView<Adaptor>::defineOwnProperty):
(JSC::JSGenericTypedArrayView<Adaptor>::deleteProperty):
(JSC::JSGenericTypedArrayView<Adaptor>::getOwnPropertySlotByIndex): Deleted.
(JSC::JSGenericTypedArrayView<Adaptor>::visitChildren): Deleted.
* runtime/JSObject.cpp:
(JSC::JSObject::putDirectNativeIntrinsicGetter):
* runtime/JSObject.h:
* runtime/JSTypedArrayViewPrototype.cpp:
(JSC::JSTypedArrayViewPrototype::finishCreation):
* tests/stress/typedarray-add-property-to-base-object.js: Added.
(body.foo):
(body):
* tests/stress/typedarray-bad-getter.js: Added.
(body.foo):
(body.get Bar):
(body):
* tests/stress/typedarray-getter-on-self.js: Added.
(body.foo):
(body.bar):
(body.baz):
(body.get for):
(body):
* tests/stress/typedarray-intrinsic-getters-change-prototype.js: Added.
(body.foo):
(body.bar):
(body.baz):
(body):

LayoutTests:

Fix test since typedarrays no longer have length as a own property.

* js/dom/getOwnPropertyDescriptor-expected.txt:
* js/resources/getOwnPropertyDescriptor.js:

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

40 files changed:
LayoutTests/ChangeLog
LayoutTests/js/dom/getOwnPropertyDescriptor-expected.txt
LayoutTests/js/resources/getOwnPropertyDescriptor.js
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/GetByIdVariant.cpp
Source/JavaScriptCore/bytecode/GetByIdVariant.h
Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp
Source/JavaScriptCore/bytecode/PolymorphicAccess.h
Source/JavaScriptCore/bytecode/PutByIdVariant.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGArrayMode.cpp
Source/JavaScriptCore/dfg/DFGArrayMode.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/jit/IntrinsicEmitter.cpp [new file with mode: 0644]
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSArrayBufferView.cpp
Source/JavaScriptCore/runtime/JSDataView.cpp
Source/JavaScriptCore/runtime/JSDataView.h
Source/JavaScriptCore/runtime/JSFunction.h
Source/JavaScriptCore/runtime/JSFunctionInlines.h
Source/JavaScriptCore/runtime/JSGenericTypedArrayView.h
Source/JavaScriptCore/runtime/JSGenericTypedArrayViewInlines.h
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSTypedArrayViewPrototype.cpp
Source/JavaScriptCore/tests/stress/typedarray-add-property-to-base-object.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/typedarray-bad-getter.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/typedarray-getter-on-self.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/typedarray-intrinsic-getters-change-prototype.js [new file with mode: 0644]

index fc009a0..9170ec2 100644 (file)
@@ -1,3 +1,15 @@
+2015-10-16  Keith Miller  <keith@Keiths-MacBook-Pro-5.local>
+
+        Add Intrinsic Getters and use them to fix performance on the getters of TypedArray properties.
+        https://bugs.webkit.org/show_bug.cgi?id=149687
+
+        Reviewed by Geoffrey Garen.
+
+        Fix test since typedarrays no longer have length as a own property.
+
+        * js/dom/getOwnPropertyDescriptor-expected.txt:
+        * js/resources/getOwnPropertyDescriptor.js:
+
 2015-10-16  Brent Fulgham  <bfulgham@apple.com>
 
         Hide all plugin names except Flash, Java, and QuickTime
index ba1d78a..ef26314 100644 (file)
@@ -157,11 +157,6 @@ PASS Object.getOwnPropertyDescriptor(document.getElementsByClassName('pass'), 'l
 PASS Object.getOwnPropertyDescriptor(document.getElementsByClassName('pass'), 'length').hasOwnProperty('writable') is false
 PASS Object.getOwnPropertyDescriptor(document.getElementsByClassName('pass'), 'length').enumerable is true
 PASS Object.getOwnPropertyDescriptor(document.getElementsByClassName('pass'), 'length').configurable is false
-PASS Object.getOwnPropertyDescriptor(canvasPixelArray, 'length').value is canvasPixelArray.length
-PASS Object.getOwnPropertyDescriptor(canvasPixelArray, 'length').hasOwnProperty('get') is false
-PASS Object.getOwnPropertyDescriptor(canvasPixelArray, 'length').hasOwnProperty('set') is false
-PASS Object.getOwnPropertyDescriptor(canvasPixelArray, 'length').enumerable is true
-PASS Object.getOwnPropertyDescriptor(canvasPixelArray, 'length').configurable is false
 PASS Object.getOwnPropertyDescriptor(canvasPixelArray, 0).value is canvasPixelArray[0]
 PASS Object.getOwnPropertyDescriptor(canvasPixelArray, 0).hasOwnProperty('get') is false
 PASS Object.getOwnPropertyDescriptor(canvasPixelArray, 0).hasOwnProperty('set') is false
index 0a8e87e..2d9d586 100644 (file)
@@ -51,7 +51,6 @@ descriptorShouldBe("document.getElementsByClassName('pass')", "0", {writable: fa
 descriptorShouldBe("document.getElementsByClassName('pass')", "'length'", {get: undefined, set: undefined, enumerable: true, configurable: false});
 var canvas = document.createElement("canvas");
 var canvasPixelArray = canvas.getContext("2d").createImageData(10,10).data;
-descriptorShouldBe("canvasPixelArray", "'length'", {writable: false, enumerable: true, configurable: false, value:"canvasPixelArray.length"});
 descriptorShouldBe("canvasPixelArray", "0", {writable: true, enumerable: true, configurable: false, value:"canvasPixelArray[0]"});
 var select = document.createElement("select");
 select.innerHTML = "<option>foo</option>";
index 1aa0b00..5a84813 100644 (file)
@@ -373,6 +373,7 @@ set(JavaScriptCore_SOURCES
     jit/ExecutableAllocatorFixedVMPool.cpp
     jit/GCAwareJITStubRoutine.cpp
     jit/HostCallReturnValue.cpp
+    jit/IntrinsicEmitter.cpp
     jit/JIT.cpp
     jit/JITArithmetic.cpp
     jit/JITArithmetic32_64.cpp
index a4b9b62..4d2798e 100644 (file)
@@ -1,3 +1,157 @@
+2015-10-16  Keith Miller  <keith@Keiths-MacBook-Pro-5.local>
+
+        Add Intrinsic Getters and use them to fix performance on the getters of TypedArray properties.
+        https://bugs.webkit.org/show_bug.cgi?id=149687
+
+        Reviewed by Geoffrey Garen.
+
+        Add the ability to create intrinsic getters in both the inline cache and the DFG/FTL. When the
+        getter fetched by a GetById has an intrinsic we know about we add a new intrinsic access case.
+        Once we get to the DFG, we observe that the access case was an intrinsic and add an appropriate
+        GetByIdVariant. We then parse the intrinsic into an appropriate DFG node.
+
+        The first intrinsics are the new TypedArray prototype getters length, byteLength, and byteOffset.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
+        (JSC::GetByIdStatus::computeFor):
+        * bytecode/GetByIdVariant.cpp:
+        (JSC::GetByIdVariant::GetByIdVariant):
+        (JSC::GetByIdVariant::operator=):
+        (JSC::GetByIdVariant::canMergeIntrinsicStructures):
+        (JSC::GetByIdVariant::attemptToMerge):
+        (JSC::GetByIdVariant::dumpInContext):
+        * bytecode/GetByIdVariant.h:
+        (JSC::GetByIdVariant::intrinsicFunction):
+        (JSC::GetByIdVariant::intrinsic):
+        (JSC::GetByIdVariant::callLinkStatus): Deleted.
+        * bytecode/PolymorphicAccess.cpp:
+        (JSC::AccessGenerationState::addWatchpoint):
+        (JSC::AccessGenerationState::restoreScratch):
+        (JSC::AccessGenerationState::succeed):
+        (JSC::AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling):
+        (JSC::AccessGenerationState::preserveLiveRegistersToStackForCall):
+        (JSC::AccessGenerationState::restoreLiveRegistersFromStackForCall):
+        (JSC::AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException):
+        (JSC::AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal):
+        (JSC::AccessGenerationState::originalExceptionHandler):
+        (JSC::AccessGenerationState::originalCallSiteIndex):
+        (JSC::AccessCase::getIntrinsic):
+        (JSC::AccessCase::clone):
+        (JSC::AccessCase::visitWeak):
+        (JSC::AccessCase::generate):
+        (WTF::printInternal):
+        (JSC::AccessCase::AccessCase): Deleted.
+        (JSC::AccessCase::get): Deleted.
+        (JSC::AccessCase::replace): Deleted.
+        (JSC::AccessCase::transition): Deleted.
+        * bytecode/PolymorphicAccess.h:
+        (JSC::AccessCase::isGet):
+        (JSC::AccessCase::isPut):
+        (JSC::AccessCase::isIn):
+        (JSC::AccessCase::intrinsicFunction):
+        (JSC::AccessCase::intrinsic):
+        (JSC::AccessGenerationState::AccessGenerationState):
+        (JSC::AccessGenerationState::liveRegistersForCall):
+        (JSC::AccessGenerationState::callSiteIndexForExceptionHandling):
+        (JSC::AccessGenerationState::numberOfStackBytesUsedForRegisterPreservation):
+        (JSC::AccessGenerationState::needsToRestoreRegistersIfException):
+        (JSC::AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite):
+        * bytecode/PutByIdVariant.h:
+        (JSC::PutByIdVariant::intrinsic):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGArrayMode.cpp:
+        (JSC::DFG::ArrayMode::alreadyChecked):
+        (JSC::DFG::arrayTypeToString):
+        (JSC::DFG::toTypedArrayType):
+        (JSC::DFG::refineTypedArrayType):
+        (JSC::DFG::permitsBoundsCheckLowering):
+        * dfg/DFGArrayMode.h:
+        (JSC::DFG::ArrayMode::supportsLength):
+        (JSC::DFG::ArrayMode::isSomeTypedArrayView):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::attemptToInlineCall):
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        (JSC::DFG::ByteCodeParser::handleIntrinsicGetter):
+        (JSC::DFG::ByteCodeParser::load):
+        (JSC::DFG::ByteCodeParser::handleGetById):
+        (JSC::DFG::ByteCodeParser::presenceLike): Deleted.
+        (JSC::DFG::ByteCodeParser::store): Deleted.
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::convertToGetArrayLength): Deleted.
+        (JSC::DFG::FixupPhase::prependGetArrayLength): Deleted.
+        (JSC::DFG::FixupPhase::fixupChecksInBlock): Deleted.
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::tryGetFoldableView):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::checkArray):
+        (JSC::DFG::SpeculativeJIT::compileGetArrayLength):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileGetArrayLength):
+        * jit/IntrinsicEmitter.cpp: Added.
+        (JSC::AccessCase::canEmitIntrinsicGetter):
+        (JSC::AccessCase::emitIntrinsicGetter):
+        * jit/Repatch.cpp:
+        (JSC::tryCacheGetByID):
+        * runtime/Intrinsic.h:
+        * runtime/JSArrayBufferView.cpp:
+        (JSC::JSArrayBufferView::put):
+        (JSC::JSArrayBufferView::defineOwnProperty):
+        (JSC::JSArrayBufferView::deleteProperty):
+        (JSC::JSArrayBufferView::getOwnNonIndexPropertyNames):
+        (JSC::JSArrayBufferView::getOwnPropertySlot): Deleted.
+        (JSC::JSArrayBufferView::finalize): Deleted.
+        * runtime/JSDataView.cpp:
+        (JSC::JSDataView::getOwnPropertySlot):
+        (JSC::JSDataView::put):
+        (JSC::JSDataView::defineOwnProperty):
+        (JSC::JSDataView::deleteProperty):
+        (JSC::JSDataView::getOwnNonIndexPropertyNames):
+        * runtime/JSDataView.h:
+        * runtime/JSFunction.h:
+        * runtime/JSFunctionInlines.h:
+        (JSC::JSFunction::intrinsic):
+        * runtime/JSGenericTypedArrayView.h:
+        * runtime/JSGenericTypedArrayViewInlines.h:
+        (JSC::JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot):
+        (JSC::JSGenericTypedArrayView<Adaptor>::defineOwnProperty):
+        (JSC::JSGenericTypedArrayView<Adaptor>::deleteProperty):
+        (JSC::JSGenericTypedArrayView<Adaptor>::getOwnPropertySlotByIndex): Deleted.
+        (JSC::JSGenericTypedArrayView<Adaptor>::visitChildren): Deleted.
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::putDirectNativeIntrinsicGetter):
+        * runtime/JSObject.h:
+        * runtime/JSTypedArrayViewPrototype.cpp:
+        (JSC::JSTypedArrayViewPrototype::finishCreation):
+        * tests/stress/typedarray-add-property-to-base-object.js: Added.
+        (body.foo):
+        (body):
+        * tests/stress/typedarray-bad-getter.js: Added.
+        (body.foo):
+        (body.get Bar):
+        (body):
+        * tests/stress/typedarray-getter-on-self.js: Added.
+        (body.foo):
+        (body.bar):
+        (body.baz):
+        (body.get for):
+        (body):
+        * tests/stress/typedarray-intrinsic-getters-change-prototype.js: Added.
+        (body.foo):
+        (body.bar):
+        (body.baz):
+        (body):
+
 2015-10-16  Keith Miller  <keith_miller@apple.com>
 
         Fix some issues with TypedArrays
index c7be6ad..a542c90 100644 (file)
                C4F4B6F51A05C984005CAB76 /* generate_objc_protocol_types_implementation.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D71A05C76F005CAB76 /* generate_objc_protocol_types_implementation.py */; settings = {ATTRIBUTES = (Private, ); }; };
                C4F4B6F61A05C984005CAB76 /* objc_generator_templates.py in Headers */ = {isa = PBXBuildFile; fileRef = C4F4B6D81A05C76F005CAB76 /* objc_generator_templates.py */; settings = {ATTRIBUTES = (Private, ); }; };
                DC00039319D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h in Headers */ = {isa = PBXBuildFile; fileRef = DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */; };
+               DE5A0A001BA3AC3E003D4424 /* IntrinsicEmitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DE5A09FF1BA3AC3E003D4424 /* IntrinsicEmitter.cpp */; };
                DEA7E2441BBC677200D78440 /* JSTypedArrayViewPrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53F256E11B87E28000B4B768 /* JSTypedArrayViewPrototype.cpp */; };
                DEA7E2451BBC677F00D78440 /* JSTypedArrayViewPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = 53917E7C1B791106000EBD33 /* JSTypedArrayViewPrototype.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E124A8F70E555775003091F1 /* OpaqueJSString.h in Headers */ = {isa = PBXBuildFile; fileRef = E124A8F50E555775003091F1 /* OpaqueJSString.h */; settings = {ATTRIBUTES = (Private, ); }; };
                D21202280AD4310C00ED79B6 /* DateConversion.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DateConversion.cpp; sourceTree = "<group>"; };
                D21202290AD4310C00ED79B6 /* DateConversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DateConversion.h; sourceTree = "<group>"; };
                DC00039019D8BE6F00023EB0 /* DFGPreciseLocalClobberize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPreciseLocalClobberize.h; path = dfg/DFGPreciseLocalClobberize.h; sourceTree = "<group>"; };
+               DE5A09FF1BA3AC3E003D4424 /* IntrinsicEmitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntrinsicEmitter.cpp; sourceTree = "<group>"; };
                E124A8F50E555775003091F1 /* OpaqueJSString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpaqueJSString.h; sourceTree = "<group>"; };
                E124A8F60E555775003091F1 /* OpaqueJSString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpaqueJSString.cpp; sourceTree = "<group>"; };
                E178633F0D9BEC0000D74E75 /* InitializeThreading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InitializeThreading.h; sourceTree = "<group>"; };
                                0F24E53F17EA9F5900ABB217 /* GPRInfo.h */,
                                0F4680D014BBC5F800BFE272 /* HostCallReturnValue.cpp */,
                                0F4680D114BBC5F800BFE272 /* HostCallReturnValue.h */,
+                               DE5A09FF1BA3AC3E003D4424 /* IntrinsicEmitter.cpp */,
                                1429D92D0ED22D7000B89619 /* JIT.cpp */,
                                1429D92E0ED22D7000B89619 /* JIT.h */,
                                86A90ECF0EE7D51F00AB350D /* JITArithmetic.cpp */,
                                A77F1821164088B200640A47 /* CodeCache.cpp in Sources */,
                                0F8F9446166764F100D61971 /* CodeOrigin.cpp in Sources */,
                                86B5826714D2796C00A9C306 /* CodeProfile.cpp in Sources */,
+                               DE5A0A001BA3AC3E003D4424 /* IntrinsicEmitter.cpp in Sources */,
                                86B5826914D2797000A9C306 /* CodeProfiling.cpp in Sources */,
                                0F8F943C1667631300D61971 /* CodeSpecializationKind.cpp in Sources */,
                                0F8F94421667633500D61971 /* CodeType.cpp in Sources */,
index 9ef9b84..12c31ef 100644 (file)
@@ -207,10 +207,16 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
                  
             case ComplexGetStatus::Inlineable: {
                 std::unique_ptr<CallLinkStatus> callLinkStatus;
+                JSFunction* intrinsicFunction = nullptr;
+
                 switch (access.type()) {
                 case AccessCase::Load: {
                     break;
                 }
+                case AccessCase::IntrinsicGetter: {
+                    intrinsicFunction = access.intrinsicFunction();
+                    break;
+                }
                 case AccessCase::Getter: {
                     CallLinkInfo* callLinkInfo = access.callLinkInfo();
                     ASSERT(callLinkInfo);
@@ -227,8 +233,9 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
                  
                 GetByIdVariant variant(
                     StructureSet(structure), complexGetStatus.offset(),
-                    complexGetStatus.conditionSet(), WTF::move(callLinkStatus));
-                 
+                    complexGetStatus.conditionSet(), WTF::move(callLinkStatus),
+                    intrinsicFunction);
+
                 if (!result.appendVariant(variant))
                     return GetByIdStatus(slowPathState, true);
                 break;
@@ -314,7 +321,7 @@ GetByIdStatus GetByIdStatus::computeFor(const StructureSet& set, UniquedStringIm
         if (!isValidOffset(offset))
             return GetByIdStatus(TakesSlowPath); // It's probably a prototype lookup. Give up on life for now, even though we could totally be way smarter about it.
         if (attributes & Accessor)
-            return GetByIdStatus(MakesCalls); // We could be smarter here, like strenght-reducing this to a Call.
+            return GetByIdStatus(MakesCalls); // We could be smarter here, like strength-reducing this to a Call.
         
         if (!result.appendVariant(GetByIdVariant(structure, offset)))
             return GetByIdStatus(TakesSlowPath);
index ea6fa12..5f530dc 100644 (file)
@@ -35,16 +35,20 @@ namespace JSC {
 GetByIdVariant::GetByIdVariant(
     const StructureSet& structureSet, PropertyOffset offset,
     const ObjectPropertyConditionSet& conditionSet,
-    std::unique_ptr<CallLinkStatus> callLinkStatus)
+    std::unique_ptr<CallLinkStatus> callLinkStatus,
+    JSFunction* intrinsicFunction)
     : m_structureSet(structureSet)
     , m_conditionSet(conditionSet)
     , m_offset(offset)
     , m_callLinkStatus(WTF::move(callLinkStatus))
+    , m_intrinsicFunction(intrinsicFunction)
 {
     if (!structureSet.size()) {
         ASSERT(offset == invalidOffset);
         ASSERT(conditionSet.isEmpty());
     }
+    if (intrinsicFunction)
+        ASSERT(intrinsic() != NoIntrinsic);
 }
                      
 GetByIdVariant::~GetByIdVariant() { }
@@ -60,6 +64,7 @@ GetByIdVariant& GetByIdVariant::operator=(const GetByIdVariant& other)
     m_structureSet = other.m_structureSet;
     m_conditionSet = other.m_conditionSet;
     m_offset = other.m_offset;
+    m_intrinsicFunction = other.m_intrinsicFunction;
     if (other.m_callLinkStatus)
         m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus);
     else
@@ -67,6 +72,27 @@ GetByIdVariant& GetByIdVariant::operator=(const GetByIdVariant& other)
     return *this;
 }
 
+inline bool GetByIdVariant::canMergeIntrinsicStructures(const GetByIdVariant& other) const
+{
+    if (m_intrinsicFunction != other.m_intrinsicFunction)
+        return false;
+    switch (intrinsic()) {
+    case TypedArrayByteLengthIntrinsic: {
+        // We can merge these sets as long as the element size of the two sets is the same.
+        TypedArrayType thisType = (*m_structureSet.begin())->classInfo()->typedArrayStorageType;
+        TypedArrayType otherType = (*other.m_structureSet.begin())->classInfo()->typedArrayStorageType;
+
+        ASSERT(isTypedView(thisType) && isTypedView(otherType));
+
+        return logElementSize(thisType) == logElementSize(otherType);
+    }
+
+    default:
+        return true;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
 bool GetByIdVariant::attemptToMerge(const GetByIdVariant& other)
 {
     if (m_offset != other.m_offset)
@@ -74,6 +100,9 @@ bool GetByIdVariant::attemptToMerge(const GetByIdVariant& other)
     if (m_callLinkStatus || other.m_callLinkStatus)
         return false;
 
+    if (!canMergeIntrinsicStructures(other))
+        return false;
+
     if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty())
         return false;
     
@@ -107,6 +136,8 @@ void GetByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
     out.print(", offset = ", offset());
     if (m_callLinkStatus)
         out.print(", call = ", *m_callLinkStatus);
+    if (m_intrinsicFunction)
+        out.print(", intrinsic = ", *m_intrinsicFunction);
     out.print(">");
 }
 
index 714fb98..03a1e56 100644 (file)
@@ -43,8 +43,9 @@ public:
     GetByIdVariant(
         const StructureSet& structureSet = StructureSet(), PropertyOffset offset = invalidOffset,
         const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(),
-        std::unique_ptr<CallLinkStatus> callLinkStatus = nullptr);
-    
+        std::unique_ptr<CallLinkStatus> = nullptr,
+        JSFunction* = nullptr);
+
     ~GetByIdVariant();
     
     GetByIdVariant(const GetByIdVariant&);
@@ -60,7 +61,9 @@ public:
     
     PropertyOffset offset() const { return m_offset; }
     CallLinkStatus* callLinkStatus() const { return m_callLinkStatus.get(); }
-    
+    JSFunction* intrinsicFunction() const { return m_intrinsicFunction; }
+    Intrinsic intrinsic() const { return m_intrinsicFunction ? m_intrinsicFunction->intrinsic() : NoIntrinsic; }
+
     bool attemptToMerge(const GetByIdVariant& other);
     
     void dump(PrintStream&) const;
@@ -68,11 +71,14 @@ public:
     
 private:
     friend class GetByIdStatus;
+
+    bool canMergeIntrinsicStructures(const GetByIdVariant&) const;
     
     StructureSet m_structureSet;
     ObjectPropertyConditionSet m_conditionSet;
     PropertyOffset m_offset;
     std::unique_ptr<CallLinkStatus> m_callLinkStatus;
+    JSFunction* m_intrinsicFunction;
 };
 
 } // namespace JSC
index 04919bf..40e12ab 100644 (file)
@@ -47,167 +47,107 @@ namespace JSC {
 
 static const bool verbose = false;
 
-struct AccessGenerationState {
-    AccessGenerationState() 
-        : m_calculatedRegistersForCallAndExceptionHandling(false)
-        , m_needsToRestoreRegistersIfException(false)
-        , m_calculatedCallSiteIndex(false)
-    {
-    }
-    CCallHelpers* jit { nullptr };
-    ScratchRegisterAllocator* allocator;
-    unsigned numberOfBytesUsedToPreserveReusedRegisters { 0 };
-    PolymorphicAccess* access { nullptr };
-    StructureStubInfo* stubInfo { nullptr };
-    CCallHelpers::JumpList success;
-    CCallHelpers::JumpList failAndRepatch;
-    CCallHelpers::JumpList failAndIgnore;
-    GPRReg baseGPR { InvalidGPRReg };
-    JSValueRegs valueRegs;
-    GPRReg scratchGPR { InvalidGPRReg };
-    Vector<std::function<void(LinkBuffer&)>> callbacks;
-    const Identifier* ident;
-    std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
-    Vector<WriteBarrier<JSCell>> weakReferences;
-
-    Watchpoint* addWatchpoint(const ObjectPropertyCondition& condition = ObjectPropertyCondition())
-    {
-        return WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
-            watchpoints, jit->codeBlock(), stubInfo, condition);
-    }
-
-    void restoreScratch()
-    {
-        allocator->restoreReusedRegistersByPopping(*jit, numberOfBytesUsedToPreserveReusedRegisters);
-    }
-
-    void succeed()
-    {
-        restoreScratch();
-        success.append(jit->jump());
-    }
-
-    void calculateLiveRegistersForCallAndExceptionHandling()
-    {
-        if (!m_calculatedRegistersForCallAndExceptionHandling) {
-            m_calculatedRegistersForCallAndExceptionHandling = true;
+Watchpoint* AccessGenerationState::addWatchpoint(const ObjectPropertyCondition& condition)
+{
+    return WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
+        watchpoints, jit->codeBlock(), stubInfo, condition);
+}
 
-            m_liveRegistersToPreserveAtExceptionHandlingCallSite = jit->codeBlock()->jitCode()->liveRegistersToPreserveAtExceptionHandlingCallSite(jit->codeBlock(), stubInfo->callSiteIndex);
-            m_needsToRestoreRegistersIfException = m_liveRegistersToPreserveAtExceptionHandlingCallSite.numberOfSetRegisters() > 0;
-            if (m_needsToRestoreRegistersIfException)
-                RELEASE_ASSERT(JITCode::isOptimizingJIT(jit->codeBlock()->jitType()));
+void AccessGenerationState::restoreScratch()
+{
+    allocator->restoreReusedRegistersByPopping(*jit, numberOfBytesUsedToPreserveReusedRegisters);
+}
 
-            m_liveRegistersForCall = RegisterSet(m_liveRegistersToPreserveAtExceptionHandlingCallSite, allocator->usedRegisters());
-            m_liveRegistersForCall.exclude(RegisterSet::registersToNotSaveForCall());
-        }
-    }
+void AccessGenerationState::succeed()
+{
+    restoreScratch();
+    success.append(jit->jump());
+}
 
-    void preserveLiveRegistersToStackForCall()
-    {
-        unsigned extraStackPadding = 0;
-        unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(*jit, liveRegistersForCall(), extraStackPadding);
-        if (m_numberOfStackBytesUsedForRegisterPreservation != std::numeric_limits<unsigned>::max())
-            RELEASE_ASSERT(numberOfStackBytesUsedForRegisterPreservation == m_numberOfStackBytesUsedForRegisterPreservation);
-        m_numberOfStackBytesUsedForRegisterPreservation = numberOfStackBytesUsedForRegisterPreservation;
-    }
+void AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling()
+{
+    if (!m_calculatedRegistersForCallAndExceptionHandling) {
+        m_calculatedRegistersForCallAndExceptionHandling = true;
 
-    void restoreLiveRegistersFromStackForCall(bool isGetter)
-    {
-        RegisterSet dontRestore;
-        if (isGetter) {
-            // This is the result value. We don't want to overwrite the result with what we stored to the stack.
-            // We sometimes have to store it to the stack just in case we throw an exception and need the original value.
-            dontRestore.set(valueRegs); 
-        }
-        restoreLiveRegistersFromStackForCall(dontRestore);
-    }
+        m_liveRegistersToPreserveAtExceptionHandlingCallSite = jit->codeBlock()->jitCode()->liveRegistersToPreserveAtExceptionHandlingCallSite(jit->codeBlock(), stubInfo->callSiteIndex);
+        m_needsToRestoreRegistersIfException = m_liveRegistersToPreserveAtExceptionHandlingCallSite.numberOfSetRegisters() > 0;
+        if (m_needsToRestoreRegistersIfException)
+            RELEASE_ASSERT(JITCode::isOptimizingJIT(jit->codeBlock()->jitType()));
 
-    void restoreLiveRegistersFromStackForCallWithThrownException()
-    {
-        // Even if we're a getter, we don't want to ignore the result value like we normally do 
-        // because the getter threw, and therefore, didn't return a value that means anything. 
-        // Instead, we want to restore that register to what it was upon entering the getter 
-        // inline cache. The subtlety here is if the base and the result are the same register, 
-        // and the getter threw, we want OSR exit to see the original base value, not the result 
-        // of the getter call.
-        RegisterSet dontRestore = liveRegistersForCall();
-        // As an optimization here, we only need to restore what is live for exception handling.
-        // We can construct the dontRestore set to accomplish this goal by having it contain only
-        // what is live for call but not live for exception handling. By ignoring things that are 
-        // only live at the call but not the exception handler, we will only restore things live 
-        // at the exception handler.
-        dontRestore.exclude(liveRegistersToPreserveAtExceptionHandlingCallSite());
-        restoreLiveRegistersFromStackForCall(dontRestore);
+        m_liveRegistersForCall = RegisterSet(m_liveRegistersToPreserveAtExceptionHandlingCallSite, allocator->usedRegisters());
+        m_liveRegistersForCall.exclude(RegisterSet::registersToNotSaveForCall());
     }
+}
 
-    void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore)
-    {
-        unsigned extraStackPadding = 0;
-        ScratchRegisterAllocator::restoreRegistersFromStackForCall(*jit, liveRegistersForCall(), dontRestore, m_numberOfStackBytesUsedForRegisterPreservation, extraStackPadding);
-    }
+void AccessGenerationState::preserveLiveRegistersToStackForCall()
+{
+    unsigned extraStackPadding = 0;
+    unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(*jit, liveRegistersForCall(), extraStackPadding);
+    if (m_numberOfStackBytesUsedForRegisterPreservation != std::numeric_limits<unsigned>::max())
+        RELEASE_ASSERT(numberOfStackBytesUsedForRegisterPreservation == m_numberOfStackBytesUsedForRegisterPreservation);
+    m_numberOfStackBytesUsedForRegisterPreservation = numberOfStackBytesUsedForRegisterPreservation;
+}
 
-    const RegisterSet& liveRegistersForCall()
-    {
-        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
-        return m_liveRegistersForCall;
+void AccessGenerationState::restoreLiveRegistersFromStackForCall(bool isGetter)
+{
+    RegisterSet dontRestore;
+    if (isGetter) {
+        // This is the result value. We don't want to overwrite the result with what we stored to the stack.
+        // We sometimes have to store it to the stack just in case we throw an exception and need the original value.
+        dontRestore.set(valueRegs);
     }
+    restoreLiveRegistersFromStackForCall(dontRestore);
+}
 
-    CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal()
-    {
-        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
-
-        if (!m_calculatedCallSiteIndex) {
-            m_calculatedCallSiteIndex = true;
+void AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException()
+{
+    // Even if we're a getter, we don't want to ignore the result value like we normally do
+    // because the getter threw, and therefore, didn't return a value that means anything.
+    // Instead, we want to restore that register to what it was upon entering the getter
+    // inline cache. The subtlety here is if the base and the result are the same register,
+    // and the getter threw, we want OSR exit to see the original base value, not the result
+    // of the getter call.
+    RegisterSet dontRestore = liveRegistersForCall();
+    // As an optimization here, we only need to restore what is live for exception handling.
+    // We can construct the dontRestore set to accomplish this goal by having it contain only
+    // what is live for call but not live for exception handling. By ignoring things that are
+    // only live at the call but not the exception handler, we will only restore things live
+    // at the exception handler.
+    dontRestore.exclude(liveRegistersToPreserveAtExceptionHandlingCallSite());
+    restoreLiveRegistersFromStackForCall(dontRestore);
+}
 
-            if (m_needsToRestoreRegistersIfException)
-                m_callSiteIndex = jit->codeBlock()->newExceptionHandlingCallSiteIndex(stubInfo->callSiteIndex);
-            else
-                m_callSiteIndex = originalCallSiteIndex();
-        }
+void AccessGenerationState::restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore)
+{
+    unsigned extraStackPadding = 0;
+    ScratchRegisterAllocator::restoreRegistersFromStackForCall(*jit, liveRegistersForCall(), dontRestore, m_numberOfStackBytesUsedForRegisterPreservation, extraStackPadding);
+}
 
-        return m_callSiteIndex;
-    }
+CallSiteIndex AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal()
+{
+    RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
 
-    CallSiteIndex callSiteIndexForExceptionHandling()
-    {
-        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
-        RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
-        RELEASE_ASSERT(m_calculatedCallSiteIndex);
-        return m_callSiteIndex;
-    }
+    if (!m_calculatedCallSiteIndex) {
+        m_calculatedCallSiteIndex = true;
 
-    const HandlerInfo& originalExceptionHandler() const
-    { 
-        RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
-        HandlerInfo* exceptionHandler = jit->codeBlock()->handlerForIndex(stubInfo->callSiteIndex.bits());
-        RELEASE_ASSERT(exceptionHandler);
-        return *exceptionHandler;
+        if (m_needsToRestoreRegistersIfException)
+            m_callSiteIndex = jit->codeBlock()->newExceptionHandlingCallSiteIndex(stubInfo->callSiteIndex);
+        else
+            m_callSiteIndex = originalCallSiteIndex();
     }
 
-    unsigned numberOfStackBytesUsedForRegisterPreservation() const 
-    {
-        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
-        return m_numberOfStackBytesUsedForRegisterPreservation; 
-    }
+    return m_callSiteIndex;
+}
 
-    bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
-    CallSiteIndex originalCallSiteIndex() const { return stubInfo->callSiteIndex; }
+const HandlerInfo& AccessGenerationState::originalExceptionHandler() const
+{
+    RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
+    HandlerInfo* exceptionHandler = jit->codeBlock()->handlerForIndex(stubInfo->callSiteIndex.bits());
+    RELEASE_ASSERT(exceptionHandler);
+    return *exceptionHandler;
+}
 
-private:
-    const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite()
-    {
-        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
-        return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
-    }
-    
-    RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
-    RegisterSet m_liveRegistersForCall;
-    CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits<unsigned>::max()) };
-    unsigned m_numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
-    bool m_calculatedRegistersForCallAndExceptionHandling : 1;
-    bool m_needsToRestoreRegistersIfException : 1;
-    bool m_calculatedCallSiteIndex : 1;
-};
+CallSiteIndex AccessGenerationState::originalCallSiteIndex() const { return stubInfo->callSiteIndex; }
 
 AccessCase::AccessCase()
 {
@@ -319,6 +259,23 @@ std::unique_ptr<AccessCase> AccessCase::getLength(VM&, JSCell*, AccessType type)
     return result;
 }
 
+std::unique_ptr<AccessCase> AccessCase::getIntrinsic(
+    VM& vm, JSCell* owner, JSFunction* getter, PropertyOffset offset,
+    Structure* structure, const ObjectPropertyConditionSet& conditionSet)
+{
+    std::unique_ptr<AccessCase> result(new AccessCase());
+
+    result->m_type = IntrinsicGetter;
+    result->m_structure.set(vm, owner, structure);
+    result->m_conditionSet = conditionSet;
+    result->m_offset = offset;
+
+    result->m_rareData = std::make_unique<RareData>();
+    result->m_rareData->intrinsicFunction.set(vm, owner, getter);
+
+    return result;
+}
+
 AccessCase::~AccessCase()
 {
 }
@@ -355,6 +312,7 @@ std::unique_ptr<AccessCase> AccessCase::clone() const
         // NOTE: We don't copy the callLinkInfo, since that's created during code generation.
         result->m_rareData->customAccessor.opaque = rareData->customAccessor.opaque;
         result->m_rareData->customSlotBase = rareData->customSlotBase;
+        result->m_rareData->intrinsicFunction = rareData->intrinsicFunction;
     }
     return result;
 }
@@ -440,6 +398,8 @@ bool AccessCase::visitWeak(VM& vm) const
             m_rareData->callLinkInfo->visitWeak(vm);
         if (m_rareData->customSlotBase && !Heap::isMarked(m_rareData->customSlotBase.get()))
             return false;
+        if (m_rareData->intrinsicFunction && !Heap::isMarked(m_rareData->intrinsicFunction.get()))
+            return false;
     }
     return true;
 }
@@ -1106,6 +1066,22 @@ void AccessCase::generate(AccessGenerationState& state)
         jit.boxInt32(valueRegs.payloadGPR(), valueRegs);
         state.succeed();
         return;
+    }
+
+    case IntrinsicGetter: {
+        RELEASE_ASSERT(isValidOffset(offset()));
+
+        // We need to ensure the getter value does not move from under us. Note that GetterSetters
+        // are immutable so we just need to watch the property not any value inside it.
+        Structure* currStructure;
+        if (m_conditionSet.isEmpty())
+            currStructure = structure();
+        else
+            currStructure = m_conditionSet.slotBaseCondition().object()->structure();
+        currStructure->startWatchingPropertyForReplacements(vm, offset());
+
+        emitIntrinsicGetter(state);
+        return;
     } }
     
     RELEASE_ASSERT_NOT_REACHED();
@@ -1441,6 +1417,9 @@ void printInternal(PrintStream& out, AccessCase::AccessType type)
     case AccessCase::CustomSetter:
         out.print("CustomSetter");
         return;
+    case AccessCase::IntrinsicGetter:
+        out.print("IntrinsicGetter");
+        return;
     case AccessCase::InHit:
         out.print("InHit");
         return;
index ad09b55..3505281 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(JIT)
 
 #include "CodeOrigin.h"
+#include "JSFunctionInlines.h"
 #include "MacroAssembler.h"
 #include "ObjectPropertyConditionSet.h"
 #include "Opcode.h"
@@ -41,6 +42,7 @@ class CodeBlock;
 class PolymorphicAccess;
 class StructureStubInfo;
 class WatchpointsOnStructureStubInfo;
+class ScratchRegisterAllocator;
 
 struct AccessGenerationState;
 
@@ -57,6 +59,7 @@ public:
         Setter,
         CustomGetter,
         CustomSetter,
+        IntrinsicGetter,
         InHit,
         InMiss,
         ArrayLength,
@@ -77,6 +80,7 @@ public:
         case Miss:
         case Getter:
         case CustomGetter:
+        case IntrinsicGetter:
         case ArrayLength:
         case StringLength:
             return true;
@@ -90,6 +94,7 @@ public:
         case Miss:
         case Getter:
         case CustomGetter:
+        case IntrinsicGetter:
         case InHit:
         case InMiss:
         case ArrayLength:
@@ -110,6 +115,7 @@ public:
         case Miss:
         case Getter:
         case CustomGetter:
+        case IntrinsicGetter:
         case Transition:
         case Replace:
         case Setter:
@@ -147,6 +153,7 @@ public:
         const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
 
     static std::unique_ptr<AccessCase> getLength(VM&, JSCell* owner, AccessType);
+    static std::unique_ptr<AccessCase> getIntrinsic(VM&, JSCell* owner, JSFunction* intrinsic, PropertyOffset, Structure*, const ObjectPropertyConditionSet&);
     
     static std::unique_ptr<AccessCase> fromStructureStubInfo(VM&, JSCell* owner, StructureStubInfo&);
 
@@ -173,6 +180,15 @@ public:
     }
     
     ObjectPropertyConditionSet conditionSet() const { return m_conditionSet; }
+    JSFunction* intrinsicFunction() const
+    {
+        ASSERT(type() == IntrinsicGetter && m_rareData);
+        return m_rareData->intrinsicFunction.get();
+    }
+    Intrinsic intrinsic() const
+    {
+        return intrinsicFunction()->intrinsic();
+    }
 
     WatchpointSet* additionalSet() const
     {
@@ -220,6 +236,8 @@ public:
     // Is it still possible for this case to ever be taken?
     bool couldStillSucceed() const;
 
+    static bool canEmitIntrinsicGetter(JSFunction*, Structure*);
+
     // If this method returns true, then it's a good idea to remove 'other' from the access once 'this'
     // is added. This method assumes that in case of contradictions, 'this' represents a newer, and so
     // more useful, truth. This method can be conservative; it will return false when it doubt.
@@ -242,6 +260,7 @@ private:
 
     // Fall through on success, add a jump to the failure list on failure.
     void generate(AccessGenerationState&);
+    void emitIntrinsicGetter(AccessGenerationState&);
     
     AccessType m_type { Load };
     PropertyOffset m_offset { invalidOffset };
@@ -271,6 +290,7 @@ private:
             void* opaque;
         } customAccessor;
         WriteBarrier<JSObject> customSlotBase;
+        WriteBarrier<JSFunction> intrinsicFunction;
     };
 
     std::unique_ptr<RareData> m_rareData;
@@ -320,6 +340,83 @@ private:
     std::unique_ptr<Vector<WriteBarrier<JSCell>>> m_weakReferences;
 };
 
+struct AccessGenerationState {
+    AccessGenerationState()
+    : m_calculatedRegistersForCallAndExceptionHandling(false)
+    , m_needsToRestoreRegistersIfException(false)
+    , m_calculatedCallSiteIndex(false)
+    {
+    }
+    CCallHelpers* jit { nullptr };
+    ScratchRegisterAllocator* allocator;
+    unsigned numberOfBytesUsedToPreserveReusedRegisters { 0 };
+    PolymorphicAccess* access { nullptr };
+    StructureStubInfo* stubInfo { nullptr };
+    MacroAssembler::JumpList success;
+    MacroAssembler::JumpList failAndRepatch;
+    MacroAssembler::JumpList failAndIgnore;
+    GPRReg baseGPR { InvalidGPRReg };
+    JSValueRegs valueRegs;
+    GPRReg scratchGPR { InvalidGPRReg };
+    Vector<std::function<void(LinkBuffer&)>> callbacks;
+    const Identifier* ident;
+    std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
+    Vector<WriteBarrier<JSCell>> weakReferences;
+
+    Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition());
+
+    void restoreScratch();
+    void succeed();
+
+    void calculateLiveRegistersForCallAndExceptionHandling();
+
+    void preserveLiveRegistersToStackForCall();
+
+    void restoreLiveRegistersFromStackForCall(bool isGetter);
+    void restoreLiveRegistersFromStackForCallWithThrownException();
+    void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore);
+
+    const RegisterSet& liveRegistersForCall()
+    {
+        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
+        return m_liveRegistersForCall;
+    }
+
+    CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
+    CallSiteIndex callSiteIndexForExceptionHandling()
+    {
+        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
+        RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
+        RELEASE_ASSERT(m_calculatedCallSiteIndex);
+        return m_callSiteIndex;
+    }
+
+    const HandlerInfo& originalExceptionHandler() const;
+    unsigned numberOfStackBytesUsedForRegisterPreservation() const
+    {
+        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
+        return m_numberOfStackBytesUsedForRegisterPreservation;
+    }
+
+    bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
+    CallSiteIndex originalCallSiteIndex() const;
+    
+private:
+    const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite()
+    {
+        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
+        return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
+    }
+    
+    RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
+    RegisterSet m_liveRegistersForCall;
+    CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits<unsigned>::max()) };
+    unsigned m_numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
+    bool m_calculatedRegistersForCallAndExceptionHandling : 1;
+    bool m_needsToRestoreRegistersIfException : 1;
+    bool m_calculatedCallSiteIndex : 1;
+};
+
 } // namespace JSC
 
 namespace WTF {
index df9516d..29cd08d 100644 (file)
@@ -110,6 +110,9 @@ public:
     
     const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
     
+    // We don't support intrinsics for Setters (it would be sweet if we did) but we need this for templated helpers.
+    Intrinsic intrinsic() const { return NoIntrinsic; }
+    
     PropertyOffset offset() const
     {
         ASSERT(isSet());
index 7856753..d7539ee 100644 (file)
@@ -2022,6 +2022,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         case Array::Float64Array:
             filter(node->child1(), SpecFloat64Array);
             break;
+        case Array::AnyTypedArray:
+            filter(node->child1(), SpecTypedArrayView);
+            break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
             break;
index 75fd6d1..87ee3ad 100644 (file)
@@ -485,7 +485,10 @@ bool ArrayMode::alreadyChecked(Graph& graph, Node* node, const AbstractValue& va
 
     case Array::Float64Array:
         return speculationChecked(value.m_type, SpecFloat64Array);
-        
+
+    case Array::AnyTypedArray:
+        return speculationChecked(value.m_type, SpecTypedArrayView);
+
     case Array::SelectUsingPredictions:
     case Array::Unprofiled:
     case Array::SelectUsingArguments:
@@ -545,6 +548,8 @@ const char* arrayTypeToString(Array::Type type)
         return "Float32Array";
     case Array::Float64Array:
         return "Float64Array";
+    case Array::AnyTypedArray:
+        return "AnyTypedArray";
     default:
         // Better to return something then it is to crash. Remember, this method
         // is being called from our main diagnostic tool, the IR dumper. It's like
@@ -639,6 +644,9 @@ TypedArrayType toTypedArrayType(Array::Type type)
         return TypeFloat32;
     case Array::Float64Array:
         return TypeFloat64;
+    case Array::AnyTypedArray:
+        RELEASE_ASSERT_NOT_REACHED();
+        return NotTypedArray;
     default:
         return NotTypedArray;
     }
@@ -670,6 +678,19 @@ Array::Type toArrayType(TypedArrayType type)
     }
 }
 
+Array::Type refineTypedArrayType(Array::Type oldType, TypedArrayType newType)
+{
+    if (oldType == Array::Generic)
+        return oldType;
+    Array::Type newArrayType = toArrayType(newType);
+    if (newArrayType == Array::Generic)
+        return newArrayType;
+
+    if (oldType != newArrayType)
+        return Array::AnyTypedArray;
+    return oldType;
+}
+
 bool permitsBoundsCheckLowering(Array::Type type)
 {
     switch (type) {
@@ -685,6 +706,7 @@ bool permitsBoundsCheckLowering(Array::Type type)
     case Array::Uint32Array:
     case Array::Float32Array:
     case Array::Float64Array:
+    case Array::AnyTypedArray:
         return true;
     default:
         // These don't allow for bounds check lowering either because the bounds
index 9c988c0..a170fe6 100644 (file)
@@ -77,7 +77,8 @@ enum Type {
     Uint16Array,
     Uint32Array,
     Float32Array,
-    Float64Array
+    Float64Array,
+    AnyTypedArray
 };
 
 enum Class {
@@ -110,6 +111,7 @@ IndexingType toIndexingShape(Array::Type);
 
 TypedArrayType toTypedArrayType(Array::Type);
 Array::Type toArrayType(TypedArrayType);
+Array::Type refineTypedArrayType(Array::Type, TypedArrayType);
 
 bool permitsBoundsCheckLowering(Array::Type);
 
@@ -354,6 +356,16 @@ public:
         case Array::Unprofiled:
         case Array::ForceExit:
         case Array::Generic:
+        // TypedArrays do not have a self length property as of ES6.
+        case Array::Int8Array:
+        case Array::Int16Array:
+        case Array::Int32Array:
+        case Array::Uint8Array:
+        case Array::Uint8ClampedArray:
+        case Array::Uint16Array:
+        case Array::Uint32Array:
+        case Array::Float32Array:
+        case Array::Float64Array:
             return false;
         case Array::Int32:
         case Array::Double:
@@ -430,6 +442,11 @@ public:
     {
         return toTypedArrayType(type());
     }
+
+    bool isSomeTypedArrayView() const
+    {
+        return type() == Array::AnyTypedArray || isTypedView(typedArrayType());
+    }
     
     bool operator==(const ArrayMode& other) const
     {
index 0f941f6..69f54f1 100644 (file)
@@ -201,7 +201,9 @@ private:
     void cancelLinkingForBlock(InlineStackEntry*, BasicBlock*); // Only works when the given block is the last one to have been added for that inline stack entry.
     // Handle intrinsic functions. Return true if it succeeded, false if we need to plant a call.
     template<typename ChecksFunctor>
-    bool handleIntrinsic(int resultOperand, Intrinsic, int registerOffset, int argumentCountIncludingThis, SpeculatedType prediction, const ChecksFunctor& insertChecks);
+    bool handleIntrinsicCall(int resultOperand, Intrinsic, int registerOffset, int argumentCountIncludingThis, SpeculatedType prediction, const ChecksFunctor& insertChecks);
+    template<typename ChecksFunctor>
+    bool handleIntrinsicGetter(int resultOperand, const GetByIdVariant& intrinsicVariant, Node* thisNode, const ChecksFunctor& insertChecks);
     template<typename ChecksFunctor>
     bool handleTypedArrayConstructor(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, TypedArrayType, const ChecksFunctor& insertChecks);
     template<typename ChecksFunctor>
@@ -1590,7 +1592,7 @@ bool ByteCodeParser::attemptToInlineCall(Node* callTargetNode, int resultOperand
     
         Intrinsic intrinsic = callee.intrinsicFor(specializationKind);
         if (intrinsic != NoIntrinsic) {
-            if (handleIntrinsic(resultOperand, intrinsic, registerOffset, argumentCountIncludingThis, prediction, insertChecksWithAccounting)) {
+            if (handleIntrinsicCall(resultOperand, intrinsic, registerOffset, argumentCountIncludingThis, prediction, insertChecksWithAccounting)) {
                 RELEASE_ASSERT(didInsertChecks);
                 addToGraph(Phantom, callTargetNode);
                 emitArgumentPhantoms(registerOffset, argumentCountIncludingThis);
@@ -1980,9 +1982,12 @@ bool ByteCodeParser::handleMinMax(int resultOperand, NodeType op, int registerOf
 }
 
 template<typename ChecksFunctor>
-bool ByteCodeParser::handleIntrinsic(int resultOperand, Intrinsic intrinsic, int registerOffset, int argumentCountIncludingThis, SpeculatedType prediction, const ChecksFunctor& insertChecks)
+bool ByteCodeParser::handleIntrinsicCall(int resultOperand, Intrinsic intrinsic, int registerOffset, int argumentCountIncludingThis, SpeculatedType prediction, const ChecksFunctor& insertChecks)
 {
     switch (intrinsic) {
+
+    // Intrinsic Functions:
+
     case AbsIntrinsic: {
         if (argumentCountIncludingThis == 1) { // Math.abs()
             insertChecks();
@@ -2269,6 +2274,80 @@ bool ByteCodeParser::handleIntrinsic(int resultOperand, Intrinsic intrinsic, int
 }
 
 template<typename ChecksFunctor>
+bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, const GetByIdVariant& variant, Node* thisNode, const ChecksFunctor& insertChecks)
+{
+    switch (variant.intrinsic()) {
+    case TypedArrayByteLengthIntrinsic: {
+        insertChecks();
+
+        TypedArrayType type = (*variant.structureSet().begin())->classInfo()->typedArrayStorageType;
+        Array::Type arrayType = toArrayType(type);
+        size_t logSize = logElementSize(type);
+
+        variant.structureSet().forEach([&] (Structure* structure) {
+            TypedArrayType curType = structure->classInfo()->typedArrayStorageType;
+            ASSERT(logSize == logElementSize(curType));
+            arrayType = refineTypedArrayType(arrayType, curType);
+            ASSERT(arrayType != Array::Generic);
+        });
+
+        Node* lengthNode = addToGraph(GetArrayLength, OpInfo(ArrayMode(arrayType).asWord()), thisNode);
+
+        if (!logSize) {
+            set(VirtualRegister(resultOperand), lengthNode);
+            return true;
+        }
+
+        // We can use a BitLShift here because typed arrays will never have a byteLength
+        // that overflows int32.
+        Node* shiftNode = jsConstant(jsNumber(logSize));
+        set(VirtualRegister(resultOperand), addToGraph(BitLShift, lengthNode, shiftNode));
+
+        return true;
+    }
+
+    case TypedArrayLengthIntrinsic: {
+        insertChecks();
+
+        TypedArrayType type = (*variant.structureSet().begin())->classInfo()->typedArrayStorageType;
+        Array::Type arrayType = toArrayType(type);
+
+        variant.structureSet().forEach([&] (Structure* structure) {
+            TypedArrayType curType = structure->classInfo()->typedArrayStorageType;
+            arrayType = refineTypedArrayType(arrayType, curType);
+            ASSERT(arrayType != Array::Generic);
+        });
+
+        set(VirtualRegister(resultOperand), addToGraph(GetArrayLength, OpInfo(ArrayMode(arrayType).asWord()), thisNode));
+
+        return true;
+
+    }
+
+    case TypedArrayByteOffsetIntrinsic: {
+        insertChecks();
+
+        TypedArrayType type = (*variant.structureSet().begin())->classInfo()->typedArrayStorageType;
+        Array::Type arrayType = toArrayType(type);
+
+        variant.structureSet().forEach([&] (Structure* structure) {
+            TypedArrayType curType = structure->classInfo()->typedArrayStorageType;
+            arrayType = refineTypedArrayType(arrayType, curType);
+            ASSERT(arrayType != Array::Generic);
+        });
+
+        set(VirtualRegister(resultOperand), addToGraph(GetTypedArrayByteOffset, OpInfo(ArrayMode(arrayType).asWord()), thisNode));
+
+        return true;
+    }
+
+    default:
+        return false;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+template<typename ChecksFunctor>
 bool ByteCodeParser::handleTypedArrayConstructor(
     int resultOperand, InternalFunction* function, int registerOffset,
     int argumentCountIncludingThis, TypedArrayType type, const ChecksFunctor& insertChecks)
@@ -2692,7 +2771,7 @@ Node* ByteCodeParser::load(
     
     SpeculatedType loadPrediction;
     NodeType loadOp;
-    if (variant.callLinkStatus()) {
+    if (variant.callLinkStatus() || variant.intrinsic() != NoIntrinsic) {
         loadPrediction = SpecCellOther;
         loadOp = GetGetterSetterByOffset;
     } else {
@@ -2763,6 +2842,12 @@ void ByteCodeParser::handleGetById(
         //    optimal, if there is some rarely executed case in the chain that requires a lot
         //    of checks and those checks are not watchpointable.
         for (const GetByIdVariant& variant : getByIdStatus.variants()) {
+            if (variant.intrinsic() != NoIntrinsic) {
+                set(VirtualRegister(destinationOperand),
+                    addToGraph(getById, OpInfo(identifierNumber), OpInfo(prediction), base));
+                return;
+            }
+
             if (variant.conditionSet().isEmpty()) {
                 cases.append(
                     MultiGetByOffsetCase(
@@ -2805,14 +2890,25 @@ void ByteCodeParser::handleGetById(
 
     if (m_graph.compilation())
         m_graph.compilation()->noticeInlinedGetById();
-    
-    if (!variant.callLinkStatus()) {
+
+    if (!variant.callLinkStatus() && variant.intrinsic() == NoIntrinsic) {
         set(VirtualRegister(destinationOperand), loadedValue);
         return;
     }
     
     Node* getter = addToGraph(GetGetter, loadedValue);
-    
+
+    if (handleIntrinsicGetter(destinationOperand, variant, base,
+            [&] () {
+                addToGraph(CheckCell, OpInfo(m_graph.freeze(variant.intrinsicFunction())), getter, base);
+            })) {
+        addToGraph(Phantom, getter);
+        return;
+    }
+
+    if (variant.intrinsic() != NoIntrinsic)
+        ASSERT(variant.intrinsic() == NoIntrinsic);
+
     // Make a call. We don't try to get fancy with using the smallest operand number because
     // the stack layout phase should compress the stack anyway.
     
index 16006e3..6c85d59 100644 (file)
@@ -575,6 +575,11 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
             read(MiscFields);
             def(HeapLocation(IndexedPropertyLoc, TypedArrayProperties, node->child1(), node->child2()), LazyNode(node));
             return;
+        // We should not get an AnyTypedArray in a GetByVal as AnyTypedArray is only created from intrinsics, which
+        // are only added from Inline Caching a GetById.
+        case Array::AnyTypedArray:
+            DFG_CRASH(graph, node, "impossible array mode for get");
+            return;
         }
         RELEASE_ASSERT_NOT_REACHED();
         return;
@@ -679,6 +684,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
             // FIXME: We can't def() anything here because these operations truncate their inputs.
             // https://bugs.webkit.org/show_bug.cgi?id=134737
             return;
+        case Array::AnyTypedArray:
         case Array::String:
         case Array::DirectArguments:
         case Array::ScopedArguments:
@@ -818,15 +824,15 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         case Array::String:
             def(PureValue(node, mode.asWord()));
             return;
-            
+
         case Array::DirectArguments:
         case Array::ScopedArguments:
             read(MiscFields);
             def(HeapLocation(ArrayLengthLoc, MiscFields, node->child1()), LazyNode(node));
             return;
-            
+
         default:
-            ASSERT(mode.typedArrayType() != NotTypedArray);
+            ASSERT(mode.isSomeTypedArrayView());
             read(MiscFields);
             def(HeapLocation(ArrayLengthLoc, MiscFields, node->child1()), LazyNode(node));
             return;
index 0fe818c..72cb7f9 100644 (file)
@@ -1036,14 +1036,6 @@ private:
                     attemptToMakeGetArrayLength(node);
                     break;
                 }
-                if (uid == vm().propertyNames->byteLength.impl()) {
-                    attemptToMakeGetTypedArrayByteLength(node);
-                    break;
-                }
-                if (uid == vm().propertyNames->byteOffset.impl()) {
-                    attemptToMakeGetTypedArrayByteOffset(node);
-                    break;
-                }
             }
             fixEdge<CellUse>(node->child1());
             break;
@@ -1163,11 +1155,19 @@ private:
             break;
         }
 
-        case GetArrayLength:
+        case GetArrayLength: {
+            fixEdge<KnownCellUse>(node->child1());
+            break;
+        }
+
+        case GetTypedArrayByteOffset: {
+            fixEdge<KnownCellUse>(node->child1());
+            break;
+        }
+
         case Phi:
         case Upsilon:
         case GetIndexedPropertyStorage:
-        case GetTypedArrayByteOffset:
         case LastNodeType:
         case CheckTierUpInLoop:
         case CheckTierUpAtReturn:
@@ -2064,39 +2064,7 @@ private:
         convertToGetArrayLength(node, arrayMode);
         return true;
     }
-    
-    bool attemptToMakeGetTypedArrayByteLength(Node* node)
-    {
-        if (!isInt32Speculation(node->prediction()))
-            return false;
-        
-        TypedArrayType type = typedArrayTypeFromSpeculation(node->child1()->prediction());
-        if (!isTypedView(type))
-            return false;
-        
-        if (elementSize(type) == 1) {
-            convertToGetArrayLength(node, ArrayMode(toArrayType(type)));
-            return true;
-        }
-        
-        Node* length = prependGetArrayLength(
-            node->origin, node->child1().node(), ArrayMode(toArrayType(type)));
-        
-        Node* shiftAmount = m_insertionSet.insertNode(
-            m_indexInBlock, SpecInt32, JSConstant, node->origin,
-            OpInfo(m_graph.freeze(jsNumber(logElementSize(type)))));
-        
-        // We can use a BitLShift here because typed arrays will never have a byteLength
-        // that overflows int32.
-        node->setOp(BitLShift);
-        node->clearFlags(NodeMustGenerate);
-        observeUseKindOnNode(length, Int32Use);
-        observeUseKindOnNode(shiftAmount, Int32Use);
-        node->child1() = Edge(length, Int32Use);
-        node->child2() = Edge(shiftAmount, Int32Use);
-        return true;
-    }
-    
+
     void convertToGetArrayLength(Node* node, ArrayMode arrayMode)
     {
         node->setOp(GetArrayLength);
@@ -2119,25 +2087,6 @@ private:
             OpInfo(arrayMode.asWord()), Edge(child, KnownCellUse), Edge(storage));
     }
     
-    bool attemptToMakeGetTypedArrayByteOffset(Node* node)
-    {
-        if (!isInt32Speculation(node->prediction()))
-            return false;
-        
-        TypedArrayType type = typedArrayTypeFromSpeculation(node->child1()->prediction());
-        if (!isTypedView(type))
-            return false;
-        
-        checkArray(
-            ArrayMode(toArrayType(type)), node->origin, node->child1().node(),
-            0, neverNeedsStorage);
-        
-        node->setOp(GetTypedArrayByteOffset);
-        node->clearFlags(NodeMustGenerate);
-        fixEdge<KnownCellUse>(node->child1());
-        return true;
-    }
-    
     void fixupChecksInBlock(BasicBlock* block)
     {
         if (!block)
index f2807e4..899a368 100644 (file)
@@ -1215,7 +1215,7 @@ JSArrayBufferView* Graph::tryGetFoldableView(JSValue value)
 
 JSArrayBufferView* Graph::tryGetFoldableView(JSValue value, ArrayMode arrayMode)
 {
-    if (arrayMode.typedArrayType() == NotTypedArray)
+    if (arrayMode.type() != Array::AnyTypedArray && arrayMode.typedArrayType() == NotTypedArray)
         return nullptr;
     return tryGetFoldableView(value);
 }
index 98961a2..a415075 100644 (file)
@@ -230,6 +230,12 @@ private:
             break;
         }
 
+        case GetTypedArrayByteOffset:
+        case GetArrayLength: {
+            changed |= setPrediction(SpecInt32);
+            break;
+        }
+
         case StringCharCodeAt: {
             changed |= setPrediction(SpecInt32);
             break;
@@ -540,8 +546,6 @@ private:
         }
 
         case PutByValAlias:
-        case GetArrayLength:
-        case GetTypedArrayByteOffset:
         case DoubleAsInt32:
         case GetLocalUnlinked:
         case CheckArray:
index 8f53211..f1c4c08 100755 (executable)
@@ -767,6 +767,7 @@ void SpeculativeJIT::checkArray(Node* node)
     const ClassInfo* expectedClassInfo = 0;
     
     switch (node->arrayMode().type()) {
+    case Array::AnyTypedArray:
     case Array::String:
         RELEASE_ASSERT_NOT_REACHED(); // Should have been a Phantom(String:)
         break;
@@ -4665,7 +4666,7 @@ void SpeculativeJIT::compileGetArrayLength(Node* node)
         break;
     }
     default: {
-        ASSERT(isTypedView(node->arrayMode().typedArrayType()));
+        ASSERT(node->arrayMode().isSomeTypedArrayView());
         SpeculateCellOperand base(this, node->child1());
         GPRTemporary result(this, Reuse, base);
         GPRReg baseGPR = base.gpr();
index e2f8ad5..5d88bb1 100644 (file)
@@ -251,7 +251,7 @@ inline CapabilityLevel canCompile(Node* node)
         case Array::ScopedArguments:
             break;
         default:
-            if (isTypedView(node->arrayMode().typedArrayType()))
+            if (node->arrayMode().isSomeTypedArrayView())
                 break;
             return CannotCompile;
         }
index a4afb38..1be8bce 100644 (file)
@@ -2410,7 +2410,7 @@ private:
         }
             
         default:
-            if (isTypedView(m_node->arrayMode().typedArrayType())) {
+            if (m_node->arrayMode().isSomeTypedArrayView()) {
                 setInt32(
                     m_out.load32NonNegative(lowCell(m_node->child1()), m_heaps.JSArrayBufferView_length));
                 return;
diff --git a/Source/JavaScriptCore/jit/IntrinsicEmitter.cpp b/Source/JavaScriptCore/jit/IntrinsicEmitter.cpp
new file mode 100644 (file)
index 0000000..1713895
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "CCallHelpers.h"
+#include "CallFrame.h"
+#include "CodeBlock.h"
+#include "JSArrayBufferView.h"
+#include "JSCJSValueInlines.h"
+#include "JSCellInlines.h"
+#include "PolymorphicAccess.h"
+
+namespace JSC {
+
+typedef CCallHelpers::TrustedImm32 TrustedImm32;
+typedef CCallHelpers::Imm32 Imm32;
+typedef CCallHelpers::TrustedImmPtr TrustedImmPtr;
+typedef CCallHelpers::ImmPtr ImmPtr;
+typedef CCallHelpers::TrustedImm64 TrustedImm64;
+typedef CCallHelpers::Imm64 Imm64;
+
+bool AccessCase::canEmitIntrinsicGetter(JSFunction* getter, Structure* structure)
+{
+
+    switch (getter->intrinsic()) {
+    case TypedArrayByteOffsetIntrinsic:
+    case TypedArrayByteLengthIntrinsic:
+    case TypedArrayLengthIntrinsic: {
+        TypedArrayType type = structure->classInfo()->typedArrayStorageType;
+
+        if (!isTypedView(type))
+            return false;
+        
+        return true;
+    }
+    default:
+        return false;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+void AccessCase::emitIntrinsicGetter(AccessGenerationState& state)
+{
+    CCallHelpers& jit = *state.jit;
+    JSValueRegs valueRegs = state.valueRegs;
+    GPRReg baseGPR = state.baseGPR;
+    GPRReg valueGPR = valueRegs.payloadGPR();
+
+    switch (intrinsic()) {
+    case TypedArrayLengthIntrinsic: {
+        jit.load32(MacroAssembler::Address(state.baseGPR, JSArrayBufferView::offsetOfLength()), valueGPR);
+        jit.boxInt32(valueGPR, valueRegs);
+        state.succeed();
+        return;
+    }
+
+    case TypedArrayByteLengthIntrinsic: {
+        TypedArrayType type = structure()->classInfo()->typedArrayStorageType;
+
+        jit.load32(MacroAssembler::Address(state.baseGPR, JSArrayBufferView::offsetOfLength()), valueGPR);
+
+        if (elementSize(type) > 1) {
+            // We can use a bitshift here since we TypedArrays cannot have byteLength that overflows an int32.
+            jit.lshift32(valueGPR, Imm32(logElementSize(type)), valueGPR);
+        }
+
+        jit.boxInt32(valueGPR, valueRegs);
+        state.succeed();
+        return;
+    }
+
+    case TypedArrayByteOffsetIntrinsic: {
+        GPRReg scratchGPR = state.scratchGPR;
+
+        CCallHelpers::Jump emptyByteOffset = jit.branch32(
+            MacroAssembler::NotEqual,
+            MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfMode()),
+            TrustedImm32(WastefulTypedArray));
+
+        jit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
+        jit.loadPtr(MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfVector()), valueGPR);
+        jit.loadPtr(MacroAssembler::Address(scratchGPR, Butterfly::offsetOfArrayBuffer()), scratchGPR);
+        jit.loadPtr(MacroAssembler::Address(scratchGPR, ArrayBuffer::offsetOfData()), scratchGPR);
+        jit.subPtr(scratchGPR, valueGPR);
+
+        CCallHelpers::Jump done = jit.jump();
+        
+        emptyByteOffset.link(&jit);
+        jit.move(TrustedImmPtr(0), valueGPR);
+        
+        done.link(&jit);
+        
+        jit.boxInt32(valueGPR, valueRegs);
+        state.succeed();
+        return;
+    }
+
+    default:
+        break;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace JSC
index 6099dc4..e0ac36e 100644 (file)
@@ -224,18 +224,31 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
     VM& vm = exec->vm();
 
     std::unique_ptr<AccessCase> newCase;
+    JSFunction* getter = nullptr;
+    ObjectPropertyConditionSet conditionSet;
+    JSCell* baseCell = baseValue.asCell();
+    Structure* structure = baseCell->structure(vm);
+
+    if (slot.isCacheableGetter())
+        getter = jsDynamicCast<JSFunction*>(slot.getterSetter()->getter());
 
     if (isJSArray(baseValue) && propertyName == exec->propertyNames().length)
         newCase = AccessCase::getLength(vm, codeBlock, AccessCase::ArrayLength);
     else if (isJSString(baseValue) && propertyName == exec->propertyNames().length)
         newCase = AccessCase::getLength(vm, codeBlock, AccessCase::StringLength);
-    else {
+    else if (getter && AccessCase::canEmitIntrinsicGetter(getter, structure)) {
+        if (slot.slotBase() != baseValue) {
+            conditionSet = generateConditionsForPrototypePropertyHit(vm, codeBlock->ownerExecutable(), exec, structure, slot.slotBase(), propertyName.impl());
+            if (!conditionSet.isValid())
+                return GiveUpOnCache;
+        }
+
+        newCase = AccessCase::getIntrinsic(vm, codeBlock, getter, slot.cachedOffset(), structure, conditionSet);
+
+    } else {
         if (!slot.isCacheable() && !slot.isUnset())
             return GiveUpOnCache;
 
-        JSCell* baseCell = baseValue.asCell();
-        Structure* structure = baseCell->structure(vm);
-
         bool loadTargetFromProxy = false;
         if (baseCell->type() == PureForwardingProxyType) {
             baseValue = jsCast<JSProxy*>(baseCell)->target();
@@ -265,8 +278,7 @@ static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, con
         }
 
         PropertyOffset offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
-        
-        ObjectPropertyConditionSet conditionSet;
+
         if (slot.isUnset() || slot.slotBase() != baseValue) {
             if (structure->typeInfo().prohibitsPropertyCaching() || structure->isDictionary())
                 return GiveUpOnCache;
index fe552a7..d4befb6 100644 (file)
@@ -29,6 +29,7 @@
 namespace JSC {
 
 enum Intrinsic {
+    // Call intrinsics.
     NoIntrinsic,
     AbsIntrinsic,
     MinIntrinsic,
@@ -54,6 +55,11 @@ enum Intrinsic {
     IMulIntrinsic,
     FRoundIntrinsic,
 
+    // Getter intrinsics.
+    TypedArrayLengthIntrinsic,
+    TypedArrayByteLengthIntrinsic,
+    TypedArrayByteOffsetIntrinsic,
+
     // Debugging intrinsics. These are meant to be used as testing hacks within
     // jsc.cpp and should never be exposed to users.
     DFGTrueIntrinsic,
index a7281aa..73f306f 100644 (file)
@@ -140,10 +140,6 @@ bool JSArrayBufferView::getOwnPropertySlot(
     JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(object);
-    if (propertyName == exec->propertyNames().byteOffset) {
-        slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->byteOffset()));
-        return true;
-    }
     
     if (propertyName == exec->propertyNames().buffer) {
         slot.setValue(
@@ -160,9 +156,7 @@ void JSArrayBufferView::put(
     PutPropertySlot& slot)
 {
     JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
-    if (propertyName == exec->propertyNames().byteLength
-        || propertyName == exec->propertyNames().byteOffset
-        || propertyName == exec->propertyNames().buffer) {
+    if (propertyName == exec->propertyNames().buffer) {
         reject(exec, slot.isStrictMode(), "Attempting to write to read-only typed array property.");
         return;
     }
@@ -175,9 +169,7 @@ bool JSArrayBufferView::defineOwnProperty(
     const PropertyDescriptor& descriptor, bool shouldThrow)
 {
     JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(object);
-    if (propertyName == exec->propertyNames().byteLength
-        || propertyName == exec->propertyNames().byteOffset
-        || propertyName == exec->propertyNames().buffer)
+    if (propertyName == exec->propertyNames().buffer)
         return reject(exec, shouldThrow, "Attempting to define read-only typed array property.");
     
     return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
@@ -187,9 +179,7 @@ bool JSArrayBufferView::deleteProperty(
     JSCell* cell, ExecState* exec, PropertyName propertyName)
 {
     JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(cell);
-    if (propertyName == exec->propertyNames().byteLength
-        || propertyName == exec->propertyNames().byteOffset
-        || propertyName == exec->propertyNames().buffer)
+    if (propertyName == exec->propertyNames().buffer)
         return false;
     
     return Base::deleteProperty(thisObject, exec, propertyName);
@@ -200,12 +190,9 @@ void JSArrayBufferView::getOwnNonIndexPropertyNames(
 {
     JSArrayBufferView* thisObject = jsCast<JSArrayBufferView*>(object);
     
-    // length/byteOffset/byteLength are DontEnum, at least in Firefox.
-    if (mode.includeDontEnumProperties()) {
-        array.add(exec->propertyNames().byteOffset);
-        array.add(exec->propertyNames().byteLength);
+    if (mode.includeDontEnumProperties())
         array.add(exec->propertyNames().buffer);
-    }
+
     
     Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode);
 }
index 8d4cc25..75c26db 100644 (file)
@@ -30,6 +30,7 @@
 #include "DataView.h"
 #include "Error.h"
 #include "JSCInlines.h"
+#include "Reject.h"
 
 namespace JSC {
 
@@ -102,10 +103,64 @@ bool JSDataView::getOwnPropertySlot(
         slot.setValue(thisObject, DontEnum | ReadOnly, jsNumber(thisObject->m_length));
         return true;
     }
-    
+    if (propertyName == exec->propertyNames().byteOffset) {
+        slot.setValue(thisObject, DontEnum | ReadOnly, jsNumber(thisObject->byteOffset()));
+        return true;
+    }
+
     return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
 }
 
+void JSDataView::put(
+    JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value,
+    PutPropertySlot& slot)
+{
+    JSDataView* thisObject = jsCast<JSDataView*>(cell);
+    if (propertyName == exec->propertyNames().byteLength
+        || propertyName == exec->propertyNames().byteOffset) {
+        reject(exec, slot.isStrictMode(), "Attempting to write to read-only typed array property.");
+        return;
+    }
+
+    Base::put(thisObject, exec, propertyName, value, slot);
+}
+
+bool JSDataView::defineOwnProperty(
+    JSObject* object, ExecState* exec, PropertyName propertyName,
+    const PropertyDescriptor& descriptor, bool shouldThrow)
+{
+    JSDataView* thisObject = jsCast<JSDataView*>(object);
+    if (propertyName == exec->propertyNames().byteLength
+        || propertyName == exec->propertyNames().byteOffset)
+        return reject(exec, shouldThrow, "Attempting to define read-only typed array property.");
+
+    return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
+}
+
+bool JSDataView::deleteProperty(
+    JSCell* cell, ExecState* exec, PropertyName propertyName)
+{
+    JSDataView* thisObject = jsCast<JSDataView*>(cell);
+    if (propertyName == exec->propertyNames().byteLength
+        || propertyName == exec->propertyNames().byteOffset)
+        return false;
+
+    return Base::deleteProperty(thisObject, exec, propertyName);
+}
+
+void JSDataView::getOwnNonIndexPropertyNames(
+    JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
+{
+    JSDataView* thisObject = jsCast<JSDataView*>(object);
+    
+    if (mode.includeDontEnumProperties()) {
+        array.add(exec->propertyNames().byteOffset);
+        array.add(exec->propertyNames().byteLength);
+    }
+    
+    Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode);
+}
+
 ArrayBuffer* JSDataView::slowDownAndWasteMemory(JSArrayBufferView*)
 {
     UNREACHABLE_FOR_PLATFORM();
index 74477b0..ec2e858 100644 (file)
@@ -59,6 +59,11 @@ public:
 
 protected:
     static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
+    static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
+    static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
+    static bool deleteProperty(JSCell*, ExecState*, PropertyName);
+
+    static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
 
     static ArrayBuffer* slowDownAndWasteMemory(JSArrayBufferView*);
     static PassRefPtr<ArrayBufferView> getTypedArrayImpl(JSArrayBufferView*);
index 2744693..4d3f131 100644 (file)
@@ -84,9 +84,10 @@ public:
 
     ExecutableBase* executable() const { return m_executable.get(); }
 
-    // To call either of these methods include Executable.h
+    // To call any of these methods include JSFunctionInlines.h
     bool isHostFunction() const;
     FunctionExecutable* jsExecutable() const;
+    Intrinsic intrinsic() const;
 
     JS_EXPORT_PRIVATE const SourceCode* sourceCode() const;
 
index 4f75ab9..a1f7c67 100644 (file)
@@ -73,6 +73,11 @@ inline bool JSFunction::isHostFunction() const
     return m_executable->isHostFunction();
 }
 
+inline Intrinsic JSFunction::intrinsic() const
+{
+    return executable()->intrinsic();
+}
+
 inline bool JSFunction::isBuiltinFunction() const
 {
 #if ENABLE(WEBASSEMBLY)
index a66cd13..15dfe2c 100644 (file)
@@ -277,7 +277,6 @@ protected:
     static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
     static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
     
-    static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
     
     static void visitChildren(JSCell*, SlotVisitor&);
index eb2c712..5b0b594 100644 (file)
@@ -293,16 +293,7 @@ bool JSGenericTypedArrayView<Adaptor>::getOwnPropertySlot(
     JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
-    if (propertyName == exec->propertyNames().length) {
-        slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->length()));
-        return true;
-    }
-    
-    if (propertyName == exec->propertyNames().byteLength) {
-        slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->byteLength()));
-        return true;
-    }
-    
+
     Optional<uint32_t> index = parseIndex(propertyName);
     if (index && thisObject->canGetIndexQuickly(index.value())) {
         slot.setValue(thisObject, DontDelete | ReadOnly, thisObject->getIndexQuickly(index.value()));
@@ -319,12 +310,6 @@ void JSGenericTypedArrayView<Adaptor>::put(
 {
     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
     
-    if (propertyName == exec->propertyNames().length) {
-        // Firefox appears to simply ignore attempts to store to the length property.
-        // Even in strict mode. I will do the same.
-        return;
-    }
-    
     if (Optional<uint32_t> index = parseIndex(propertyName)) {
         putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode());
         return;
@@ -343,7 +328,7 @@ bool JSGenericTypedArrayView<Adaptor>::defineOwnProperty(
     // This is matching Firefox behavior. In particular, it rejects all attempts to
     // defineOwnProperty for indexed properties on typed arrays, even if they're out
     // of bounds.
-    if (propertyName == exec->propertyNames().length || parseIndex(propertyName))
+    if (parseIndex(propertyName))
         return reject(exec, shouldThrow, "Attempting to write to a read-only typed array property.");
     
     return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
@@ -355,7 +340,7 @@ bool JSGenericTypedArrayView<Adaptor>::deleteProperty(
 {
     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
     
-    if (propertyName == exec->propertyNames().length || parseIndex(propertyName))
+    if (parseIndex(propertyName))
         return false;
     
     return Base::deleteProperty(thisObject, exec, propertyName);
@@ -408,18 +393,6 @@ bool JSGenericTypedArrayView<Adaptor>::deletePropertyByIndex(
 }
 
 template<typename Adaptor>
-void JSGenericTypedArrayView<Adaptor>::getOwnNonIndexPropertyNames(
-    JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
-{
-    JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(object);
-    
-    if (mode.includeDontEnumProperties())
-        array.add(exec->propertyNames().length);
-    
-    Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode);
-}
-
-template<typename Adaptor>
 void JSGenericTypedArrayView<Adaptor>::getOwnPropertyNames(
     JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
 {
index f8ff316..0b0fa2d 100644 (file)
@@ -2287,6 +2287,14 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV
     }
 }
 
+void JSObject::putDirectNativeIntrinsicGetter(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
+{
+    GetterSetter* accessor = GetterSetter::create(vm, globalObject);
+    JSFunction* function = JSFunction::create(vm, globalObject, 0, name.string(), nativeFunction, intrinsic);
+    accessor->setGetter(vm, globalObject, function);
+    putDirectNonIndexAccessor(vm, name, accessor, attributes);
+}
+
 void JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
 {
     StringImpl* name = propertyName.publicName();
index c406ba1..abf5a5b 100644 (file)
@@ -595,6 +595,7 @@ public:
     void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); }
     void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); }
 
+    JS_EXPORT_PRIVATE void putDirectNativeIntrinsicGetter(VM&, JSGlobalObject*, Identifier, NativeFunction, Intrinsic, unsigned attributes);
     JS_EXPORT_PRIVATE void putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
     JS_EXPORT_PRIVATE JSFunction* putDirectBuiltinFunction(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes);
     JSFunction* putDirectBuiltinFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes);
@@ -1471,13 +1472,13 @@ ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name)
         vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes))
 
 // Helper for defining native getters on properties.
-#define JSC_NATIVE_GETTER(jsName, cppName, attributes, length) do { \
-        Identifier ident = makeIdentifier(vm, (jsName)); \
-        GetterSetter* accessor = GetterSetter::create(vm, globalObject); \
-        JSFunction* function = JSFunction::create(vm, globalObject, (length), ident.string(), (cppName)); \
-        accessor->setGetter(vm, globalObject, function); \
-        putDirectNonIndexAccessor(vm, ident, accessor, (attributes) | Accessor); \
-    } while (false)
+#define JSC_NATIVE_INTRINSIC_GETTER(jsName, cppName, attributes, intrinsic)  \
+    putDirectNativeIntrinsicGetter(\
+        vm, globalObject, makeIdentifier(vm, (jsName)), (cppName), \
+        (intrinsic), ((attributes) | Accessor))
+
+#define JSC_NATIVE_GETTER(jsName, cppName, attributes) \
+    JSC_NATIVE_INTRINSIC_GETTER((jsName), (cppName), (attributes), NoIntrinsic)
 
 
 } // namespace JSC
index a3484ca..53dd03b 100644 (file)
@@ -213,8 +213,8 @@ void JSTypedArrayViewPrototype::finishCreation(VM& vm, JSGlobalObject* globalObj
 
     ASSERT(inherits(info()));
 
-    JSC_NATIVE_GETTER(vm.propertyNames->byteLength, typedArrayViewProtoGetterFuncByteLength, DontEnum | ReadOnly | DontDelete, 0);
-    JSC_NATIVE_GETTER(vm.propertyNames->byteOffset, typedArrayViewProtoGetterFuncByteOffset, DontEnum | ReadOnly | DontDelete, 0);
+    JSC_NATIVE_INTRINSIC_GETTER(vm.propertyNames->byteLength, typedArrayViewProtoGetterFuncByteLength, DontEnum | ReadOnly | DontDelete, TypedArrayByteLengthIntrinsic);
+    JSC_NATIVE_INTRINSIC_GETTER(vm.propertyNames->byteOffset, typedArrayViewProtoGetterFuncByteOffset, DontEnum | ReadOnly | DontDelete, TypedArrayByteOffsetIntrinsic);
     JSC_NATIVE_FUNCTION("copyWithin", typedArrayViewProtoFuncCopyWithin, DontEnum, 2);
     JSC_BUILTIN_FUNCTION("every", typedArrayPrototypeEveryCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION("filter", typedArrayPrototypeFilterCodeGenerator, DontEnum);
@@ -228,7 +228,7 @@ void JSTypedArrayViewPrototype::finishCreation(VM& vm, JSGlobalObject* globalObj
     JSC_NATIVE_FUNCTION(vm.propertyNames->join, typedArrayViewProtoFuncJoin, DontEnum, 1);
     JSC_NATIVE_FUNCTION(vm.propertyNames->keys, typedArrayViewProtoFuncKeys, DontEnum, 0);
     JSC_NATIVE_FUNCTION("lastIndexOf", typedArrayViewProtoFuncLastIndexOf, DontEnum, 1);
-    JSC_NATIVE_GETTER(vm.propertyNames->length, typedArrayViewProtoGetterFuncLength, DontEnum | ReadOnly | DontDelete, 0);
+    JSC_NATIVE_INTRINSIC_GETTER(vm.propertyNames->length, typedArrayViewProtoGetterFuncLength, DontEnum | ReadOnly | DontDelete, TypedArrayLengthIntrinsic);
     JSC_BUILTIN_FUNCTION("map", typedArrayPrototypeMapCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION("reduce", typedArrayPrototypeReduceCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION("reduceRight", typedArrayPrototypeReduceRightCodeGenerator, DontEnum);
diff --git a/Source/JavaScriptCore/tests/stress/typedarray-add-property-to-base-object.js b/Source/JavaScriptCore/tests/stress/typedarray-add-property-to-base-object.js
new file mode 100644 (file)
index 0000000..f8e2760
--- /dev/null
@@ -0,0 +1,21 @@
+// This tests that we don't fast path intrinsics when they should not be fast pathed. Currently,
+// that means that we don't inline length and byteLength when they are "connected" to a TypedArray.
+
+(function body() {
+    function foo(a) {
+        return a.length + a.byteLength + a.byteOffset;
+    }
+
+    let array = new Int32Array(10);
+
+    for (let i = 0; i < 100000; i++)
+        foo(array);
+
+
+    Object.defineProperty(array, "length", { value: 0 });
+    Object.defineProperty(array, "byteLength", { value: 0 });
+    Object.defineProperty(array, "byteOffset", { value: 0 });
+
+    if (foo(array) !== 0)
+        throw "wrong number!";
+})();
diff --git a/Source/JavaScriptCore/tests/stress/typedarray-bad-getter.js b/Source/JavaScriptCore/tests/stress/typedarray-bad-getter.js
new file mode 100644 (file)
index 0000000..c75f4a8
--- /dev/null
@@ -0,0 +1,36 @@
+// This tests that we don't fast path intrinsics when they should not be fast pathed. Currently,
+// that means that we don't inline length, byteLength, and byteOffset when they are called
+// from a non-TypedArray.
+
+(function body() {
+    function foo(a) {
+        return a.length + a.byteLength + a.byteOffset;
+    }
+    noInline(foo);
+
+    let proto = { }
+
+    let properties = ["length", "byteLength", "byteOffset"];
+    properties.map(function(name) {
+        let getter = Int32Array.prototype.__lookupGetter__(name);
+        Object.defineProperty(proto, name, { get : getter });
+    });
+
+    function Bar() {
+        return this;
+    }
+
+    Bar.prototype = proto;
+    let bar = new Bar();
+
+    let noThrow = false;
+    for (let i = 0; i < 100000; i++) {
+        try {
+            foo(bar);
+            noThrow = true
+        } catch (e) {
+        }
+        if (noThrow)
+            throw "broken";
+    }
+})();
diff --git a/Source/JavaScriptCore/tests/stress/typedarray-getter-on-self.js b/Source/JavaScriptCore/tests/stress/typedarray-getter-on-self.js
new file mode 100644 (file)
index 0000000..293a0d3
--- /dev/null
@@ -0,0 +1,43 @@
+// This tests that intrinsics that are attached to self of an object correctly abort
+// when the self value is changed.
+
+(function body() {
+    function foo(a) {
+        return a.length;
+    }
+    noInline(foo);
+
+    function bar(a) {
+        return a.byteLength;
+    }
+    noInline(bar);
+
+    function baz(a) {
+        return a.byteOffset;
+    }
+    noInline(baz);
+
+    let array = new Int32Array(10);
+
+    let properties = ["length", "byteLength", "byteOffset"];
+    properties.map(function(name) {
+        let getter = Int32Array.prototype.__lookupGetter__(name);
+        Object.defineProperty(array, name, { get: getter, configurable: true });
+    });
+
+    for (let i = 0; i < 100000; i++)
+        foo(array);
+
+    properties.map(function(name) {
+        Object.defineProperty(array, name, { value: null });
+    });
+
+    if (foo(array) !== null)
+        throw "length should have been null";
+
+    if (bar(array) !== null)
+        throw "byteLength should have been null";
+
+    if (baz(array) !== null)
+        throw "byteOffset should have been null";
+})();
diff --git a/Source/JavaScriptCore/tests/stress/typedarray-intrinsic-getters-change-prototype.js b/Source/JavaScriptCore/tests/stress/typedarray-intrinsic-getters-change-prototype.js
new file mode 100644 (file)
index 0000000..2e27b6f
--- /dev/null
@@ -0,0 +1,39 @@
+// This tests that TypedArray length and byteLength correctly dump code when the prototypes move.
+
+(function body() {
+    function foo(a) {
+        return a.length;
+    }
+    noInline(foo);
+
+    function bar(a) {
+        return a.byteLength;
+    }
+    noInline(bar);
+
+    function baz(a) {
+        return a.byteOffset;
+    }
+    noInline(baz);
+
+    let array = new Int32Array(15);
+
+    while(numberOfDFGCompiles(foo) < 1) {
+        foo(array);
+        bar(array);
+        baz(array);
+    }
+
+    Object.setPrototypeOf(array, null);
+
+    let passed = false;
+
+    if (foo(array) !== undefined)
+        throw "length should have become undefined when the prototype changed";
+    if (bar(array) !== undefined)
+        throw "byteLength should have become undefined when the prototype changed";
+    if (baz(array) !== undefined)
+        throw "byteOffset should have become undefined when the prototype changed";
+
+
+})();