Commit ee327c85 authored by fpizlo@apple.com's avatar fpizlo@apple.com

Fold constant typed arrays

https://bugs.webkit.org/show_bug.cgi?id=125205

Source/JavaScriptCore: 

Reviewed by Oliver Hunt and Mark Hahnenberg.
        
If by some other mechanism we have a typed array access on a compile-time constant
typed array pointer, then fold:
        
- Array bounds checks. Specifically, fold the load of length.
        
- Loading the vector.
        
This needs to install a watchpoint on the array itself because of the possibility of
neutering. Neutering is ridiculous. We do this without bloating the size of
ArrayBuffer or JSArrayBufferView in the common case (i.e. the case where you
allocated an array that didn't end up becoming a compile-time constant). To install
the watchpoint, we slowDownAndWasteMemory and then create an incoming reference to
the ArrayBuffer, where that incoming reference is from a watchpoint object. The
ArrayBuffer already knows about such incoming references and can fire the
watchpoints that way.
        
* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGDesiredWatchpoints.cpp:
(JSC::DFG::ArrayBufferViewWatchpointAdaptor::add):
(JSC::DFG::DesiredWatchpoints::addLazily):
* dfg/DFGDesiredWatchpoints.h:
(JSC::DFG::GenericSetAdaptor::add):
(JSC::DFG::GenericSetAdaptor::hasBeenInvalidated):
(JSC::DFG::ArrayBufferViewWatchpointAdaptor::hasBeenInvalidated):
(JSC::DFG::GenericDesiredWatchpoints::reallyAdd):
(JSC::DFG::GenericDesiredWatchpoints::areStillValid):
(JSC::DFG::GenericDesiredWatchpoints::isStillValid):
(JSC::DFG::GenericDesiredWatchpoints::shouldAssumeMixedState):
(JSC::DFG::DesiredWatchpoints::isStillValid):
(JSC::DFG::DesiredWatchpoints::shouldAssumeMixedState):
(JSC::DFG::DesiredWatchpoints::isValidOrMixed):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::tryGetFoldableView):
* dfg/DFGGraph.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::jumpForTypedArrayOutOfBounds):
(JSC::DFG::SpeculativeJIT::emitTypedArrayBoundsCheck):
(JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
(JSC::DFG::SpeculativeJIT::compilePutByValForIntTypedArray):
(JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
(JSC::DFG::SpeculativeJIT::compilePutByValForFloatTypedArray):
(JSC::DFG::SpeculativeJIT::compileConstantIndexedPropertyStorage):
(JSC::DFG::SpeculativeJIT::compileGetIndexedPropertyStorage):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGWatchpointCollectionPhase.cpp:
(JSC::DFG::WatchpointCollectionPhase::handle):
(JSC::DFG::WatchpointCollectionPhase::addLazily):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileGetIndexedPropertyStorage):
(JSC::FTL::LowerDFGToLLVM::compileGetByVal):
(JSC::FTL::LowerDFGToLLVM::compilePutByVal):
(JSC::FTL::LowerDFGToLLVM::typedArrayLength):
* runtime/ArrayBuffer.cpp:
(JSC::ArrayBuffer::transfer):
* runtime/ArrayBufferNeuteringWatchpoint.cpp: Added.
(JSC::ArrayBufferNeuteringWatchpoint::ArrayBufferNeuteringWatchpoint):
(JSC::ArrayBufferNeuteringWatchpoint::~ArrayBufferNeuteringWatchpoint):
(JSC::ArrayBufferNeuteringWatchpoint::finishCreation):
(JSC::ArrayBufferNeuteringWatchpoint::destroy):
(JSC::ArrayBufferNeuteringWatchpoint::create):
(JSC::ArrayBufferNeuteringWatchpoint::createStructure):
* runtime/ArrayBufferNeuteringWatchpoint.h: Added.
(JSC::ArrayBufferNeuteringWatchpoint::set):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:

LayoutTests: 

Reviewed by Oliver Hunt and Mark Hahnenberg.

* js/regress/fixed-typed-array-storage-expected.txt: Added.
* js/regress/fixed-typed-array-storage-var-index-expected.txt: Added.
* js/regress/fixed-typed-array-storage-var-index.html: Added.
* js/regress/fixed-typed-array-storage.html: Added.
* js/regress/script-tests/fixed-typed-array-storage-var-index.js: Added.
(foo):
* js/regress/script-tests/fixed-typed-array-storage.js: Added.
(foo):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@160150 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 16c7d7a2
2013-12-04 Filip Pizlo <fpizlo@apple.com>
Fold constant typed arrays
https://bugs.webkit.org/show_bug.cgi?id=125205
Reviewed by Oliver Hunt and Mark Hahnenberg.
* js/regress/fixed-typed-array-storage-expected.txt: Added.
* js/regress/fixed-typed-array-storage-var-index-expected.txt: Added.
* js/regress/fixed-typed-array-storage-var-index.html: Added.
* js/regress/fixed-typed-array-storage.html: Added.
* js/regress/script-tests/fixed-typed-array-storage-var-index.js: Added.
(foo):
* js/regress/script-tests/fixed-typed-array-storage.js: Added.
(foo):
2013-12-04 Zoltan Horvath <zoltan@webkit.org>
[CSS Shapes] Support inset for shape-outside
......
JSRegress/fixed-typed-array-storage
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
JSRegress/fixed-typed-array-storage-var-index
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/fixed-typed-array-storage-var-index.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/fixed-typed-array-storage.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
var array = new Int8Array(new ArrayBuffer(100));
function foo(i) {
return array[i];
}
for (var i = 0; i < 100000; ++i) {
if (foo(i % 100) != 0)
throw "Error";
}
var array = new Int8Array(new ArrayBuffer(100));
function foo() {
return array[0];
}
for (var i = 0; i < 100000; ++i) {
if (foo() != 0)
throw "Error";
}
......@@ -281,6 +281,7 @@ set(JavaScriptCore_SOURCES
runtime/ArgumentsIteratorConstructor.cpp
runtime/ArgumentsIteratorPrototype.cpp
runtime/ArrayBuffer.cpp
runtime/ArrayBufferNeuteringWatchpoint.cpp
runtime/ArrayBufferView.cpp
runtime/ArrayConstructor.cpp
runtime/ArrayIteratorConstructor.cpp
......
2013-12-04 Filip Pizlo <fpizlo@apple.com>
Fold constant typed arrays
https://bugs.webkit.org/show_bug.cgi?id=125205
Reviewed by Oliver Hunt and Mark Hahnenberg.
If by some other mechanism we have a typed array access on a compile-time constant
typed array pointer, then fold:
- Array bounds checks. Specifically, fold the load of length.
- Loading the vector.
This needs to install a watchpoint on the array itself because of the possibility of
neutering. Neutering is ridiculous. We do this without bloating the size of
ArrayBuffer or JSArrayBufferView in the common case (i.e. the case where you
allocated an array that didn't end up becoming a compile-time constant). To install
the watchpoint, we slowDownAndWasteMemory and then create an incoming reference to
the ArrayBuffer, where that incoming reference is from a watchpoint object. The
ArrayBuffer already knows about such incoming references and can fire the
watchpoints that way.
* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGDesiredWatchpoints.cpp:
(JSC::DFG::ArrayBufferViewWatchpointAdaptor::add):
(JSC::DFG::DesiredWatchpoints::addLazily):
* dfg/DFGDesiredWatchpoints.h:
(JSC::DFG::GenericSetAdaptor::add):
(JSC::DFG::GenericSetAdaptor::hasBeenInvalidated):
(JSC::DFG::ArrayBufferViewWatchpointAdaptor::hasBeenInvalidated):
(JSC::DFG::GenericDesiredWatchpoints::reallyAdd):
(JSC::DFG::GenericDesiredWatchpoints::areStillValid):
(JSC::DFG::GenericDesiredWatchpoints::isStillValid):
(JSC::DFG::GenericDesiredWatchpoints::shouldAssumeMixedState):
(JSC::DFG::DesiredWatchpoints::isStillValid):
(JSC::DFG::DesiredWatchpoints::shouldAssumeMixedState):
(JSC::DFG::DesiredWatchpoints::isValidOrMixed):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::tryGetFoldableView):
* dfg/DFGGraph.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::jumpForTypedArrayOutOfBounds):
(JSC::DFG::SpeculativeJIT::emitTypedArrayBoundsCheck):
(JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
(JSC::DFG::SpeculativeJIT::compilePutByValForIntTypedArray):
(JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
(JSC::DFG::SpeculativeJIT::compilePutByValForFloatTypedArray):
(JSC::DFG::SpeculativeJIT::compileConstantIndexedPropertyStorage):
(JSC::DFG::SpeculativeJIT::compileGetIndexedPropertyStorage):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGWatchpointCollectionPhase.cpp:
(JSC::DFG::WatchpointCollectionPhase::handle):
(JSC::DFG::WatchpointCollectionPhase::addLazily):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileGetIndexedPropertyStorage):
(JSC::FTL::LowerDFGToLLVM::compileGetByVal):
(JSC::FTL::LowerDFGToLLVM::compilePutByVal):
(JSC::FTL::LowerDFGToLLVM::typedArrayLength):
* runtime/ArrayBuffer.cpp:
(JSC::ArrayBuffer::transfer):
* runtime/ArrayBufferNeuteringWatchpoint.cpp: Added.
(JSC::ArrayBufferNeuteringWatchpoint::ArrayBufferNeuteringWatchpoint):
(JSC::ArrayBufferNeuteringWatchpoint::~ArrayBufferNeuteringWatchpoint):
(JSC::ArrayBufferNeuteringWatchpoint::finishCreation):
(JSC::ArrayBufferNeuteringWatchpoint::destroy):
(JSC::ArrayBufferNeuteringWatchpoint::create):
(JSC::ArrayBufferNeuteringWatchpoint::createStructure):
* runtime/ArrayBufferNeuteringWatchpoint.h: Added.
(JSC::ArrayBufferNeuteringWatchpoint::set):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
2013-12-04 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r160116.
......
......@@ -782,6 +782,8 @@ javascriptcore_sources += \
Source/JavaScriptCore/runtime/ArgumentsIteratorPrototype.h \
Source/JavaScriptCore/runtime/ArrayBuffer.cpp \
Source/JavaScriptCore/runtime/ArrayBuffer.h \
Source/JavaScriptCore/runtime/ArrayBufferNeuteringWatchpoint.cpp \
Source/JavaScriptCore/runtime/ArrayBufferNeuteringWatchpoint.h \
Source/JavaScriptCore/runtime/ArrayBufferView.cpp \
Source/JavaScriptCore/runtime/ArrayBufferView.h \
Source/JavaScriptCore/runtime/ArrayConstructor.cpp \
......
......@@ -524,6 +524,7 @@
<ClCompile Include="..\runtime\ArgumentsIteratorConstructor.cpp" />
<ClCompile Include="..\runtime\ArgumentsIteratorPrototype.cpp" />
<ClCompile Include="..\runtime\ArrayBuffer.cpp" />
<ClCompile Include="..\runtime\ArrayBufferNeuteringWatchpoint.cpp" />
<ClCompile Include="..\runtime\ArrayBufferView.cpp" />
<ClCompile Include="..\runtime\ArrayConstructor.cpp" />
<ClCompile Include="..\runtime\ArrayIteratorConstructor.cpp" />
......@@ -1090,6 +1091,7 @@
<ClInclude Include="..\runtime\ArgList.h" />
<ClInclude Include="..\runtime\Arguments.h" />
<ClInclude Include="..\runtime\ArrayBuffer.h" />
<ClInclude Include="..\runtime\ArrayBufferNeuteringWatchpoint.h" />
<ClInclude Include="..\runtime\ArrayBufferView.h" />
<ClInclude Include="..\runtime\ArrayConstructor.h" />
<ClInclude Include="..\runtime\ArrayConventions.h" />
......
......@@ -541,6 +541,8 @@
0FFB921E16D02F470055A5DB /* DFGVariadicFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F85A31E16AB76AE0077571E /* DFGVariadicFunction.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FFB921F16D033050055A5DB /* (null) in Headers */ = {isa = PBXBuildFile; settings = {ATTRIBUTES = (Private, ); }; };
0FFB922016D033B70055A5DB /* NodeConstructors.h in Headers */ = {isa = PBXBuildFile; fileRef = 930DAD030FB1EB1A0082D205 /* NodeConstructors.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FFC99D4184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFC99D2184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp */; };
0FFC99D5184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFC99D3184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FFC99D1184EC8AD009C10AB /* ConstantMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFC99D0184EC8AD009C10AB /* ConstantMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FFFC95714EF90A000C72532 /* DFGCFAPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFFC94B14EF909500C72532 /* DFGCFAPhase.cpp */; };
0FFFC95814EF90A200C72532 /* DFGCFAPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFFC94C14EF909500C72532 /* DFGCFAPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
......@@ -1846,6 +1848,8 @@
0FF729A1166AD347000F5BA3 /* ProfilerOriginStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProfilerOriginStack.cpp; path = profiler/ProfilerOriginStack.cpp; sourceTree = "<group>"; };
0FF729A2166AD347000F5BA3 /* ProfilerOriginStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProfilerOriginStack.h; path = profiler/ProfilerOriginStack.h; sourceTree = "<group>"; };
0FF922CF14F46B130041A24E /* JSCLLIntOffsetsExtractor */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = JSCLLIntOffsetsExtractor; sourceTree = BUILT_PRODUCTS_DIR; };
0FFC99D2184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBufferNeuteringWatchpoint.cpp; sourceTree = "<group>"; };
0FFC99D3184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayBufferNeuteringWatchpoint.h; sourceTree = "<group>"; };
0FFC99D0184EC8AD009C10AB /* ConstantMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConstantMode.h; sourceTree = "<group>"; };
0FFFC94B14EF909500C72532 /* DFGCFAPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGCFAPhase.cpp; path = dfg/DFGCFAPhase.cpp; sourceTree = "<group>"; };
0FFFC94C14EF909500C72532 /* DFGCFAPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCFAPhase.h; path = dfg/DFGCFAPhase.h; sourceTree = "<group>"; };
......@@ -3366,6 +3370,8 @@
A76140CA182982CB00750624 /* ArgumentsIteratorPrototype.h */,
A7A8AF2517ADB5F2005AB174 /* ArrayBuffer.cpp */,
A7A8AF2617ADB5F3005AB174 /* ArrayBuffer.h */,
0FFC99D2184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp */,
0FFC99D3184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h */,
A7A8AF2717ADB5F3005AB174 /* ArrayBufferView.cpp */,
A7A8AF2817ADB5F3005AB174 /* ArrayBufferView.h */,
BC7952060E15E8A800A898AB /* ArrayConstructor.cpp */,
......@@ -4478,6 +4484,7 @@
A7A8AF3817ADB5F3005AB174 /* Float32Array.h in Headers */,
A7A8AF3917ADB5F3005AB174 /* Float64Array.h in Headers */,
0F24E54317EA9F5900ABB217 /* FPRInfo.h in Headers */,
0FFC99D5184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h in Headers */,
0FDB2CC9173DA520007B3C1B /* FTLAbbreviatedTypes.h in Headers */,
0FEA0A08170513DB00BB722C /* FTLAbbreviations.h in Headers */,
0FEA0A1D1708B00700BB722C /* FTLAbstractHeap.h in Headers */,
......@@ -5641,6 +5648,7 @@
1428083A107EC0750013E7B2 /* JSStack.cpp in Sources */,
147F39D5107EC37600427A48 /* JSString.cpp in Sources */,
2600B5A6152BAAA70091EE5F /* JSStringJoiner.cpp in Sources */,
0FFC99D4184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp in Sources */,
1482B74E0A43032800517CFC /* JSStringRef.cpp in Sources */,
146AAB380B66A94400E55F16 /* JSStringRefCF.cpp in Sources */,
0F919D0C157EE09F004A4E7D /* JSSymbolTableObject.cpp in Sources */,
......
......@@ -28,8 +28,22 @@
#if ENABLE(DFG_JIT)
#include "ArrayBufferNeuteringWatchpoint.h"
#include "CodeBlock.h"
#include "Operations.h"
namespace JSC { namespace DFG {
void ArrayBufferViewWatchpointAdaptor::add(
CodeBlock* codeBlock, JSArrayBufferView* view, Watchpoint* watchpoint)
{
ArrayBufferNeuteringWatchpoint* neuteringWatchpoint =
ArrayBufferNeuteringWatchpoint::create(*codeBlock->vm());
neuteringWatchpoint->set()->add(watchpoint);
codeBlock->addConstant(neuteringWatchpoint);
codeBlock->vm()->heap.addReference(neuteringWatchpoint, view->buffer());
}
DesiredWatchpoints::DesiredWatchpoints() { }
DesiredWatchpoints::~DesiredWatchpoints() { }
......@@ -43,6 +57,11 @@ void DesiredWatchpoints::addLazily(InlineWatchpointSet& set)
m_inlineSets.addLazily(&set);
}
void DesiredWatchpoints::addLazily(JSArrayBufferView* view)
{
m_bufferViews.addLazily(view);
}
void DesiredWatchpoints::addLazily(CodeOrigin codeOrigin, ExitKind exitKind, WatchpointSet* set)
{
m_sets.addLazily(codeOrigin, exitKind, set);
......@@ -57,11 +76,14 @@ void DesiredWatchpoints::reallyAdd(CodeBlock* codeBlock, CommonData& commonData)
{
m_sets.reallyAdd(codeBlock, commonData);
m_inlineSets.reallyAdd(codeBlock, commonData);
m_bufferViews.reallyAdd(codeBlock, commonData);
}
bool DesiredWatchpoints::areStillValid() const
{
return m_sets.areStillValid() && m_inlineSets.areStillValid();
return m_sets.areStillValid()
&& m_inlineSets.areStillValid()
&& m_bufferViews.areStillValid();
}
} } // namespace JSC::DFG
......
......@@ -32,6 +32,7 @@
#include "CodeOrigin.h"
#include "DFGCommonData.h"
#include "JSArrayBufferView.h"
#include "Watchpoint.h"
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
......@@ -61,7 +62,26 @@ struct WatchpointForGenericWatchpointSet {
WatchpointSetType* m_set;
};
template<typename WatchpointSetType>
template<typename T>
struct GenericSetAdaptor {
static void add(CodeBlock*, T* set, Watchpoint* watchpoint)
{
return set->add(watchpoint);
}
static bool hasBeenInvalidated(T* set) { return set->hasBeenInvalidated(); }
};
struct ArrayBufferViewWatchpointAdaptor {
static void add(CodeBlock*, JSArrayBufferView*, Watchpoint*);
static bool hasBeenInvalidated(JSArrayBufferView* view)
{
bool result = !view->length();
WTF::loadLoadFence();
return result;
}
};
template<typename WatchpointSetType, typename Adaptor = GenericSetAdaptor<WatchpointSetType>>
class GenericDesiredWatchpoints {
WTF_MAKE_NONCOPYABLE(GenericDesiredWatchpoints);
#if !ASSERT_DISABLED
......@@ -92,7 +112,7 @@ public:
typename HashSet<WatchpointSetType*>::iterator end = m_sets.end();
for (; iter != end; ++iter) {
common.watchpoints.append(CodeBlockJettisoningWatchpoint(codeBlock));
(*iter)->add(&common.watchpoints.last());
Adaptor::add(codeBlock, *iter, &common.watchpoints.last());
}
for (unsigned i = m_profiledWatchpoints.size(); i--;) {
......@@ -100,7 +120,7 @@ public:
m_profiledWatchpoints[i];
common.profiledWatchpoints.append(
ProfiledCodeBlockJettisoningWatchpoint(watchpoint.m_codeOrigin, watchpoint.m_exitKind, codeBlock));
watchpoint.m_set->add(&common.profiledWatchpoints.last());
Adaptor::add(codeBlock, watchpoint.m_set, &common.profiledWatchpoints.last());
}
m_reallyAdded = true;
......@@ -111,12 +131,12 @@ public:
typename HashSet<WatchpointSetType*>::iterator iter = m_sets.begin();
typename HashSet<WatchpointSetType*>::iterator end = m_sets.end();
for (; iter != end; ++iter) {
if ((*iter)->hasBeenInvalidated())
if (Adaptor::hasBeenInvalidated(*iter))
return false;
}
for (unsigned i = m_profiledWatchpoints.size(); i--;) {
if (m_profiledWatchpoints[i].m_set->hasBeenInvalidated())
if (Adaptor::hasBeenInvalidated(m_profiledWatchpoints[i].m_set))
return false;
}
......@@ -126,7 +146,7 @@ public:
#if ASSERT_DISABLED
bool isStillValid(WatchpointSetType* set)
{
return set->isStillValid();
return !Adaptor::hasBeenInvalidated(set);
}
bool shouldAssumeMixedState(WatchpointSetType*)
......@@ -136,7 +156,7 @@ public:
#else
bool isStillValid(WatchpointSetType* set)
{
bool result = set->isStillValid();
bool result = !Adaptor::hasBeenInvalidated(set);
m_firstKnownState.add(set, result);
return result;
}
......@@ -147,7 +167,7 @@ public:
if (iter == m_firstKnownState.end())
return false;
return iter->value != set->isStillValid();
return iter->value != !Adaptor::hasBeenInvalidated(set);
}
#endif
......@@ -172,6 +192,7 @@ public:
void addLazily(WatchpointSet*);
void addLazily(InlineWatchpointSet&);
void addLazily(JSArrayBufferView*);
void addLazily(CodeOrigin, ExitKind, WatchpointSet*);
void addLazily(CodeOrigin, ExitKind, InlineWatchpointSet&);
......@@ -187,6 +208,10 @@ public:
{
return m_inlineSets.isStillValid(&set);
}
bool isStillValid(JSArrayBufferView* view)
{
return m_bufferViews.isStillValid(view);
}
bool shouldAssumeMixedState(WatchpointSet* set)
{
return m_sets.shouldAssumeMixedState(set);
......@@ -195,6 +220,10 @@ public:
{
return m_inlineSets.shouldAssumeMixedState(&set);
}
bool shouldAssumeMixedState(JSArrayBufferView* view)
{
return m_bufferViews.shouldAssumeMixedState(view);
}
bool isValidOrMixed(WatchpointSet* set)
{
return m_sets.isValidOrMixed(set);
......@@ -203,10 +232,15 @@ public:
{
return m_inlineSets.isValidOrMixed(&set);
}
bool isValidOrMixed(JSArrayBufferView* view)
{
return m_bufferViews.isValidOrMixed(view);
}
private:
GenericDesiredWatchpoints<WatchpointSet> m_sets;
GenericDesiredWatchpoints<InlineWatchpointSet> m_inlineSets;
GenericDesiredWatchpoints<JSArrayBufferView, ArrayBufferViewWatchpointAdaptor> m_bufferViews;
};
} } // namespace JSC::DFG
......
......@@ -740,6 +740,20 @@ WriteBarrierBase<Unknown>* Graph::tryGetRegisters(Node* node)
return activation->registers();
}
JSArrayBufferView* Graph::tryGetFoldableView(Node* node, ArrayMode arrayMode)
{
if (arrayMode.typedArrayType() == NotTypedArray)
return 0;
if (!node->hasConstant())
return 0;
JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(valueOfJSConstant(node));
if (!view)
return 0;
if (!watchpoints().isStillValid(view))
return 0;
return view;
}
} } // namespace JSC::DFG
#endif
......@@ -794,6 +794,8 @@ public:
JSActivation* tryGetActivation(Node*);
WriteBarrierBase<Unknown>* tryGetRegisters(Node*);
JSArrayBufferView* tryGetFoldableView(Node*, ArrayMode);
VM& m_vm;
Plan& m_plan;
CodeBlock* m_codeBlock;
......
......@@ -2325,6 +2325,31 @@ static void compileClampDoubleToByte(JITCompiler& jit, GPRReg result, FPRReg sou
}
JITCompiler::Jump SpeculativeJIT::jumpForTypedArrayOutOfBounds(Node* node, GPRReg baseGPR, GPRReg indexGPR)
{
if (node->op() == PutByValAlias)
return JITCompiler::Jump();
if (JSArrayBufferView* view = m_jit.graph().tryGetFoldableView(m_jit.graph().child(node, 0).node(), node->arrayMode())) {
uint32_t length = view->length();
Node* indexNode = m_jit.graph().child(node, 1).node();
if (m_jit.graph().isInt32Constant(indexNode) && static_cast<uint32_t>(m_jit.graph().valueOfInt32Constant(indexNode)) < length)
return JITCompiler::Jump();
return m_jit.branch32(
MacroAssembler::AboveOrEqual, indexGPR, MacroAssembler::Imm32(length));
}
return m_jit.branch32(
MacroAssembler::AboveOrEqual, indexGPR,
MacroAssembler::Address(baseGPR, JSArrayBufferView::offsetOfLength()));
}
void SpeculativeJIT::emitTypedArrayBoundsCheck(Node* node, GPRReg baseGPR, GPRReg indexGPR)
{
JITCompiler::Jump jump = jumpForTypedArrayOutOfBounds(node, baseGPR, indexGPR);
if (!jump.isSet())
return;
speculationCheck(OutOfBounds, JSValueRegs(), 0, jump);
}
void SpeculativeJIT::compileGetByValOnIntTypedArray(Node* node, TypedArrayType type)
{
ASSERT(isInt(type));
......@@ -2342,11 +2367,7 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(Node* node, TypedArrayType t
ASSERT(node->arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1())));
speculationCheck(
Uncountable, JSValueRegs(), 0,
m_jit.branch32(
MacroAssembler::AboveOrEqual, propertyReg,
MacroAssembler::Address(baseReg, JSArrayBufferView::offsetOfLength())));
emitTypedArrayBoundsCheck(node, baseReg, propertyReg);
switch (elementSize(type)) {
case 1:
if (isSigned(type))
......@@ -2509,9 +2530,7 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(GPRReg base, GPRReg propert
ASSERT_UNUSED(valueGPR, valueGPR != property);
ASSERT(valueGPR != base);
ASSERT(valueGPR != storageReg);
MacroAssembler::Jump outOfBounds;
if (node->op() == PutByVal)
outOfBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, property, MacroAssembler::Address(base, JSArrayBufferView::offsetOfLength()));
MacroAssembler::Jump outOfBounds = jumpForTypedArrayOutOfBounds(node, base, property);
switch (elementSize(type)) {
case 1:
......@@ -2526,7 +2545,7 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(GPRReg base, GPRReg propert
default:
CRASH();
}
if (node->op() == PutByVal)
if (outOfBounds.isSet())
outOfBounds.link(&m_jit);
noResult(node);
}
......@@ -2547,11 +2566,7 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType
FPRTemporary result(this);
FPRReg resultReg = result.fpr();
speculationCheck(
Uncountable, JSValueRegs(), 0,
m_jit.branch32(
MacroAssembler::AboveOrEqual, propertyReg,
MacroAssembler::Address(baseReg, JSArrayBufferView::offsetOfLength())));
emitTypedArrayBoundsCheck(node, baseReg, propertyReg);
switch (elementSize(type)) {
case 4:
m_jit.loadFloat(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), resultReg);
......@@ -2590,12 +2605,7 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(GPRReg base, GPRReg prope
ASSERT_UNUSED(baseUse, node->arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(baseUse)));
MacroAssembler::Jump outOfBounds;
if (node->op() == PutByVal) {
outOfBounds = m_jit.branch32(
MacroAssembler::AboveOrEqual, property,
MacroAssembler::Address(base, JSArrayBufferView::offsetOfLength()));
}
MacroAssembler::Jump outOfBounds = jumpForTypedArrayOutOfBounds(node, base, property);
switch (elementSize(type)) {
case 4: {
......@@ -2610,7 +2620,7 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(GPRReg base, GPRReg prope
default:
RELEASE_ASSERT_NOT_REACHED();
}
if (node->op() == PutByVal)
if (outOfBounds.isSet())
outOfBounds.link(&m_jit);
noResult(node);
}
......@@ -4035,8 +4045,27 @@ void SpeculativeJIT::compileStringZeroLength(Node* node)
#endif
}
bool SpeculativeJIT::compileConstantIndexedPropertyStorage(Node* node)
{
JSArrayBufferView* view = m_jit.graph().tryGetFoldableView(
node->child1().node(), node->arrayMode());
if (!view)
return false;
if (view->mode() == FastTypedArray)
return false;
GPRTemporary storage(this);
GPRReg storageGPR = storage.gpr();
m_jit.move(TrustedImmPtr(view->vector()), storageGPR);
storageResult(storageGPR, node);
return true;
}
void SpeculativeJIT::compileGetIndexedPropertyStorage(Node* node)
{
if (compileConstantIndexedPropertyStorage(node))
return;
SpeculateCellOperand base(this, node->child1());
GPRReg baseReg = base.gpr();
......
......@@ -2027,7 +2027,10 @@ public:
void compileArithIMul(Node*);
void compileArithDiv(Node*);
void compileArithMod(Node*);
bool compileConstantIndexedPropertyStorage(Node*);
void compileGetIndexedPropertyStorage(Node*);
JITCompiler::Jump jumpForTypedArrayOutOfBounds(Node*, GPRReg baseGPR, GPRReg indexGPR);
void emitTypedArrayBoundsCheck(Node*, GPRReg baseGPR, GPRReg indexGPR);
void compileGetTypedArrayByteOffset(Node*);
void compileGetByValOnIntTypedArray(Node*, TypedArrayType);
void compilePutByValForIntTypedArray(GPRReg base, GPRReg property, Node*, TypedArrayType);
......
......@@ -94,6 +94,19 @@ private:
if (m_node->arrayMode().type() == Array::String)
handleStringGetByVal();
if (JSArrayBufferView* view = m_graph.tryGetFoldableView(m_node->child1().node(), m_node->arrayMode()))
addLazily(view);
break;
case PutByVal:
if (JSArrayBufferView* view = m_graph.tryGetFoldableView(m_graph.varArgChild(m_node, 0).node(), m_node->arrayMode()))
addLazily(view);
break;
case GetArrayLength:
if (JSArrayBufferView* view = m_graph.tryGetFoldableView(m_node->child1().node(), m_node->arrayMode()))
addLazily(view);
break;
case StringCharAt:
......@@ -130,6 +143,18 @@ private:
addLazily(m_node->symbolTable()->m_functionEnteredOnce);
break;
case GetIndexedPropertyStorage:
if (JSArrayBufferView* view = m_graph.tryGetFoldableView(m_node->child1().node(), m_node->arrayMode())) {
// FIXME: It would be awesome to be able to fold the property storage for
// these GC-allocated typed arrays. For now it doesn't matter because the
// most common use-cases for constant typed arrays involve large arrays with
// aliased buffer views.
if (view->mode() == FastTypedArray)
break;
addLazily(view);
}
break;
default:
break;
}
......@@ -179,6 +204,10 @@ private:
{