Commit 8624c4b8 authored by fpizlo@apple.com's avatar fpizlo@apple.com

Reveal array bounds checks in DFG IR

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

Reviewed by Oliver Hunt and Mark Hahnenberg.
        
In SSA mode, this reveals array bounds checks and the load of array length in DFG IR,
making this a candidate for LICM.

This also fixes a long-standing performance bug where the JSObject slow paths would
always create contiguous storage, rather than type-specialized storage, when doing a
"storage creating" storage, like:
        
    var o = {};
    o[0] = 42;

* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/ExitKind.cpp:
(JSC::exitKindToString):
(JSC::exitKindIsCountable):
* bytecode/ExitKind.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::::executeEffects):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::permitsBoundsCheckLowering):
(JSC::DFG::ArrayMode::permitsBoundsCheckLowering):
* dfg/DFGArrayMode.h:
(JSC::DFG::ArrayMode::lengthNeedsStorage):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::compileInThreadImpl):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSSALoweringPhase.cpp: Added.
(JSC::DFG::SSALoweringPhase::SSALoweringPhase):
(JSC::DFG::SSALoweringPhase::run):
(JSC::DFG::SSALoweringPhase::handleNode):
(JSC::DFG::SSALoweringPhase::lowerBoundsCheck):
(JSC::DFG::performSSALowering):
* dfg/DFGSSALoweringPhase.h: Added.
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileDoublePutByVal):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compileContiguousPutByVal):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileCheckInBounds):
(JSC::FTL::LowerDFGToLLVM::compileGetByVal):
(JSC::FTL::LowerDFGToLLVM::compilePutByVal):
(JSC::FTL::LowerDFGToLLVM::contiguousPutByValOutOfBounds):
* runtime/JSObject.cpp:
(JSC::JSObject::convertUndecidedForValue):
(JSC::JSObject::createInitialForValueAndSet):
(JSC::JSObject::putByIndexBeyondVectorLength):
(JSC::JSObject::putDirectIndexBeyondVectorLength):
* runtime/JSObject.h:
* tests/stress/float32array-out-of-bounds.js: Added.
(make):
(foo):
(test):
* tests/stress/int32-object-out-of-bounds.js: Added.
(make):
(foo):
(test):
* tests/stress/int32-out-of-bounds.js: Added.
(foo):
(test):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@160347 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 7ca0a156
......@@ -162,6 +162,7 @@ set(JavaScriptCore_SOURCES
dfg/DFGPredictionPropagationPhase.cpp
dfg/DFGResurrectionForValidationPhase.cpp
dfg/DFGSSAConversionPhase.cpp
dfg/DFGSSALoweringPhase.cpp
dfg/DFGSpeculativeJIT.cpp
dfg/DFGSpeculativeJIT32_64.cpp
dfg/DFGSpeculativeJIT64.cpp
......
2013-12-08 Filip Pizlo <fpizlo@apple.com>
Reveal array bounds checks in DFG IR
https://bugs.webkit.org/show_bug.cgi?id=125253
Reviewed by Oliver Hunt and Mark Hahnenberg.
In SSA mode, this reveals array bounds checks and the load of array length in DFG IR,
making this a candidate for LICM.
This also fixes a long-standing performance bug where the JSObject slow paths would
always create contiguous storage, rather than type-specialized storage, when doing a
"storage creating" storage, like:
var o = {};
o[0] = 42;
* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/ExitKind.cpp:
(JSC::exitKindToString):
(JSC::exitKindIsCountable):
* bytecode/ExitKind.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::::executeEffects):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::permitsBoundsCheckLowering):
(JSC::DFG::ArrayMode::permitsBoundsCheckLowering):
* dfg/DFGArrayMode.h:
(JSC::DFG::ArrayMode::lengthNeedsStorage):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::compileInThreadImpl):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSSALoweringPhase.cpp: Added.
(JSC::DFG::SSALoweringPhase::SSALoweringPhase):
(JSC::DFG::SSALoweringPhase::run):
(JSC::DFG::SSALoweringPhase::handleNode):
(JSC::DFG::SSALoweringPhase::lowerBoundsCheck):
(JSC::DFG::performSSALowering):
* dfg/DFGSSALoweringPhase.h: Added.
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileDoublePutByVal):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compileContiguousPutByVal):
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileCheckInBounds):
(JSC::FTL::LowerDFGToLLVM::compileGetByVal):
(JSC::FTL::LowerDFGToLLVM::compilePutByVal):
(JSC::FTL::LowerDFGToLLVM::contiguousPutByValOutOfBounds):
* runtime/JSObject.cpp:
(JSC::JSObject::convertUndecidedForValue):
(JSC::JSObject::createInitialForValueAndSet):
(JSC::JSObject::putByIndexBeyondVectorLength):
(JSC::JSObject::putDirectIndexBeyondVectorLength):
* runtime/JSObject.h:
* tests/stress/float32array-out-of-bounds.js: Added.
(make):
(foo):
(test):
* tests/stress/int32-object-out-of-bounds.js: Added.
(make):
(foo):
(test):
* tests/stress/int32-out-of-bounds.js: Added.
(foo):
(test):
2013-12-09 Sam Weinig <sam@webkit.org>
Replace use of WTF::FixedArray with std::array
......
......@@ -377,6 +377,8 @@ javascriptcore_sources += \
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h \
Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp \
Source/JavaScriptCore/dfg/DFGSSAConversionPhase.h \
Source/JavaScriptCore/dfg/DFGSSALoweringPhase.cpp \
Source/JavaScriptCore/dfg/DFGSSALoweringPhase.h \
Source/JavaScriptCore/dfg/DFGStackLayoutPhase.cpp \
Source/JavaScriptCore/dfg/DFGStackLayoutPhase.h \
Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp \
......
......@@ -411,6 +411,7 @@
<ClCompile Include="..\dfg\DFGSpeculativeJIT32_64.cpp" />
<ClCompile Include="..\dfg\DFGSpeculativeJIT64.cpp" />
<ClCompile Include="..\dfg\DFGSSAConversionPhase.cpp" />
<ClCompile Include="..\dfg\DFGSSALoweringPhase.cpp" />
<ClCompile Include="..\dfg\DFGStackLayoutPhase.cpp" />
<ClCompile Include="..\dfg\DFGStrengthReductionPhase.cpp" />
<ClCompile Include="..\dfg\DFGThunks.cpp" />
......@@ -918,6 +919,7 @@
<ClInclude Include="..\dfg\DFGSlowPathGenerator.h" />
<ClInclude Include="..\dfg\DFGSpeculativeJIT.h" />
<ClInclude Include="..\dfg\DFGSSAConversionPhase.h" />
<ClInclude Include="..\dfg\DFGSSALoweringPhase.h" />
<ClInclude Include="..\dfg\DFGStackLayoutPhase.h" />
<ClInclude Include="..\dfg\DFGStrengthReductionPhase.h" />
<ClInclude Include="..\dfg\DFGStructureAbstractValue.h" />
......
......@@ -381,6 +381,8 @@
0FC097A2146B28CC00CF2442 /* DFGThunks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC097A0146B28C700CF2442 /* DFGThunks.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FC20CB51852E2C600C9E954 /* DFGStrengthReductionPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC20CB31852E2C600C9E954 /* DFGStrengthReductionPhase.cpp */; };
0FC20CB61852E2C600C9E954 /* DFGStrengthReductionPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC20CB41852E2C600C9E954 /* DFGStrengthReductionPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FC20CB918556A3500C9E954 /* DFGSSALoweringPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC20CB718556A3500C9E954 /* DFGSSALoweringPhase.cpp */; };
0FC20CBA18556A3500C9E954 /* DFGSSALoweringPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC20CB818556A3500C9E954 /* DFGSSALoweringPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FC314121814559100033232 /* RegisterSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC314101814559100033232 /* RegisterSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FC314131814559100033232 /* TempRegisterSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC314111814559100033232 /* TempRegisterSet.cpp */; };
0FC3141518146D7000033232 /* RegisterSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC3141418146D7000033232 /* RegisterSet.cpp */; };
......@@ -1705,6 +1707,8 @@
0FC097A0146B28C700CF2442 /* DFGThunks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGThunks.h; path = dfg/DFGThunks.h; sourceTree = "<group>"; };
0FC20CB31852E2C600C9E954 /* DFGStrengthReductionPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGStrengthReductionPhase.cpp; path = dfg/DFGStrengthReductionPhase.cpp; sourceTree = "<group>"; };
0FC20CB41852E2C600C9E954 /* DFGStrengthReductionPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGStrengthReductionPhase.h; path = dfg/DFGStrengthReductionPhase.h; sourceTree = "<group>"; };
0FC20CB718556A3500C9E954 /* DFGSSALoweringPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGSSALoweringPhase.cpp; path = dfg/DFGSSALoweringPhase.cpp; sourceTree = "<group>"; };
0FC20CB818556A3500C9E954 /* DFGSSALoweringPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGSSALoweringPhase.h; path = dfg/DFGSSALoweringPhase.h; sourceTree = "<group>"; };
0FC314101814559100033232 /* RegisterSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterSet.h; sourceTree = "<group>"; };
0FC314111814559100033232 /* TempRegisterSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TempRegisterSet.cpp; sourceTree = "<group>"; };
0FC3141418146D7000033232 /* RegisterSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterSet.cpp; sourceTree = "<group>"; };
......@@ -3964,6 +3968,8 @@
86880F4C14353B2100B08D42 /* DFGSpeculativeJIT64.cpp */,
A7D89CF017A0B8CC00773AD8 /* DFGSSAConversionPhase.cpp */,
A7D89CF117A0B8CC00773AD8 /* DFGSSAConversionPhase.h */,
0FC20CB718556A3500C9E954 /* DFGSSALoweringPhase.cpp */,
0FC20CB818556A3500C9E954 /* DFGSSALoweringPhase.h */,
0F9FB4F217FCB91700CB67F8 /* DFGStackLayoutPhase.cpp */,
0F9FB4F317FCB91700CB67F8 /* DFGStackLayoutPhase.h */,
0FC20CB31852E2C600C9E954 /* DFGStrengthReductionPhase.cpp */,
......@@ -4387,6 +4393,7 @@
A73E1331179624CD00E4DEA8 /* DFGDesiredStructureChains.h in Headers */,
C2C0F7CE17BBFC5B00464FE4 /* DFGDesiredTransitions.h in Headers */,
0FE8534C1723CDA500B618F5 /* DFGDesiredWatchpoints.h in Headers */,
0FC20CBA18556A3500C9E954 /* DFGSSALoweringPhase.h in Headers */,
C2981FD917BAEE4B00A3BC98 /* DFGDesiredWeakReferences.h in Headers */,
C2981FDD17BAFF4400A3BC98 /* DFGDesiredWriteBarriers.h in Headers */,
0FF427651591A1CE004CB9FF /* DFGDisassembler.h in Headers */,
......@@ -5722,6 +5729,7 @@
969A079A0ED1D3AE00F1F681 /* Opcode.cpp in Sources */,
14280850107EC0D70013E7B2 /* Operations.cpp in Sources */,
0FE228EE1436AB2C00196C48 /* Options.cpp in Sources */,
0FC20CB918556A3500C9E954 /* DFGSSALoweringPhase.cpp in Sources */,
148F21BC107EC54D0042EC2C /* Parser.cpp in Sources */,
93052C340FB792190048FDC3 /* ParserArena.cpp in Sources */,
0F9FC8C314E1B5FE00D52AE0 /* PolymorphicPutByIdList.cpp in Sources */,
......
......@@ -64,8 +64,6 @@ const char* exitKindToString(ExitKind kind)
return "LoadFromHole";
case OutOfBounds:
return "OutOfBounds";
case StoreToHoleOrOutOfBounds:
return "StoreToHoleOrOutOfBounds";
case InadequateCoverage:
return "InadequateCoverage";
case ArgumentsEscaped:
......@@ -96,7 +94,6 @@ bool exitKindIsCountable(ExitKind kind)
case LoadFromHole: // Already counted directly by the baseline JIT.
case StoreToHole: // Already counted directly by the baseline JIT.
case OutOfBounds: // Already counted directly by the baseline JIT.
case StoreToHoleOrOutOfBounds: // Already counted directly by the baseline JIT.
return false;
default:
return true;
......
......@@ -44,7 +44,6 @@ enum ExitKind {
StoreToHole, // We had a store to a hole.
LoadFromHole, // We had a load from a hole.
OutOfBounds, // We had an out-of-bounds access to an array.
StoreToHoleOrOutOfBounds, // We're simultaneously speculating that we're in bounds and not accessing a hole, and one of those things didn't pan out.
InadequateCoverage, // We exited because we ended up in code that didn't have profiling coverage.
ArgumentsEscaped, // We exited because arguments escaped but we didn't expect them to.
NotStringObject, // We exited because we shouldn't have attempted to optimize string object access.
......
......@@ -1473,6 +1473,19 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
case CheckInBounds: {
JSValue left = forNode(node->child1()).value();
JSValue right = forNode(node->child2()).value();
if (left && right && left.isInt32() && right.isInt32()
&& static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) {
m_state.setFoundConstants(true);
break;
}
node->setCanExit(true);
break;
}
case PutById:
case PutByIdDirect:
node->setCanExit(true);
......
......@@ -548,6 +548,36 @@ Array::Type toArrayType(TypedArrayType type)
}
}
bool permitsBoundsCheckLowering(Array::Type type)
{
switch (type) {
case Array::Int32:
case Array::Double:
case Array::Contiguous:
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 true;
default:
// These don't allow for bounds check lowering either because the bounds
// check involves something other than GetArrayLength (like ArrayStorage),
// or because the bounds check isn't a speculation (like String, sort of),
// or because the type implies an impure access.
return false;
}
}
bool ArrayMode::permitsBoundsCheckLowering() const
{
return DFG::permitsBoundsCheckLowering(type()) && isInBounds();
}
void ArrayMode::dump(PrintStream& out) const
{
out.print(type(), arrayClass(), speculation(), conversion());
......
......@@ -110,6 +110,8 @@ IndexingType toIndexingShape(Array::Type);
TypedArrayType toTypedArrayType(Array::Type);
Array::Type toArrayType(TypedArrayType);
bool permitsBoundsCheckLowering(Array::Type);
class ArrayMode {
public:
ArrayMode()
......@@ -292,7 +294,17 @@ public:
bool lengthNeedsStorage() const
{
return isJSArray();
switch (type()) {
case Array::Undecided:
case Array::Int32:
case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
return true;
default:
return false;
}
}
ArrayMode modeForPut() const
......@@ -342,6 +354,8 @@ public:
}
}
bool permitsBoundsCheckLowering() const;
bool benefitsFromOriginalArray() const
{
switch (type()) {
......
......@@ -116,6 +116,7 @@ void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write
case ExtractOSREntryLocal:
case Int52ToDouble:
case Int52ToValue:
case CheckInBounds:
case ConstantStoragePointer:
return;
......
......@@ -142,6 +142,19 @@ private:
break;
}
case CheckInBounds: {
JSValue left = m_state.forNode(node->child1()).value();
JSValue right = m_state.forNode(node->child2()).value();
if (left && right && left.isInt32() && right.isInt32()
&& static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) {
node->convertToPhantom();
eliminated = true;
break;
}
break;
}
case GetById:
case GetByIdFlush: {
CodeOrigin codeOrigin = node->codeOrigin;
......
......@@ -877,6 +877,7 @@ private:
case Int52ToValue:
case InvalidationPoint:
case CheckArray:
case CheckInBounds:
case ConstantStoragePointer:
// These are just nodes that we don't currently expect to see during fixup.
// If we ever wanted to insert them prior to fixup, then we just have to create
......
......@@ -192,6 +192,7 @@ namespace JSC { namespace DFG {
macro(FunctionReentryWatchpoint, NodeMustGenerate) \
macro(CheckFunction, NodeMustGenerate) \
macro(AllocationProfileWatchpoint, NodeMustGenerate) \
macro(CheckInBounds, NodeMustGenerate) \
\
/* Optimizations for array mutation. */\
macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
......
......@@ -52,6 +52,7 @@
#include "DFGPredictionPropagationPhase.h"
#include "DFGResurrectionForValidationPhase.h"
#include "DFGSSAConversionPhase.h"
#include "DFGSSALoweringPhase.h"
#include "DFGStackLayoutPhase.h"
#include "DFGStrengthReductionPhase.h"
#include "DFGTierUpCheckInjectionPhase.h"
......@@ -266,6 +267,7 @@ Plan::CompilationPath Plan::compileInThreadImpl(LongLivedState& longLivedState)
performLoopPreHeaderCreation(dfg);
performCPSRethreading(dfg);
performSSAConversion(dfg);
performSSALowering(dfg);
performLivenessAnalysis(dfg);
performCFA(dfg);
performLICM(dfg);
......
......@@ -510,7 +510,8 @@ private:
case CheckTierUpAndOSREnter:
case InvalidationPoint:
case Int52ToValue:
case Int52ToDouble: {
case Int52ToDouble:
case CheckInBounds: {
// This node should never be visible at this stage of compilation. It is
// inserted by fixup(), which follows this phase.
RELEASE_ASSERT_NOT_REACHED();
......
/*
* Copyright (C) 2013 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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 "DFGSSALoweringPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGBasicBlockInlines.h"
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "Operations.h"
namespace JSC { namespace DFG {
class SSALoweringPhase : public Phase {
static const bool verbose = false;
public:
SSALoweringPhase(Graph& graph)
: Phase(graph, "SSA lowering")
, m_insertionSet(graph)
{
}
bool run()
{
RELEASE_ASSERT(m_graph.m_form == SSA);
for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
m_block = m_graph.block(blockIndex);
if (!m_block)
continue;
for (m_nodeIndex = 0; m_nodeIndex < m_block->size(); ++m_nodeIndex) {
m_node = m_block->at(m_nodeIndex);
handleNode();
}
m_insertionSet.execute(m_block);
}
return true;
}
private:
void handleNode()
{
switch (m_node->op()) {
case GetByVal:
lowerBoundsCheck(m_node->child1(), m_node->child2(), m_node->child3());
break;
case PutByVal:
case PutByValDirect:
lowerBoundsCheck(
m_graph.varArgChild(m_node, 0),
m_graph.varArgChild(m_node, 1),
m_graph.varArgChild(m_node, 3));
break;
default:
break;
}
}
void lowerBoundsCheck(Edge base, Edge index, Edge storage)
{
if (!m_node->arrayMode().permitsBoundsCheckLowering())
return;
if (!m_node->arrayMode().lengthNeedsStorage())
storage = Edge();
Node* length = m_insertionSet.insertNode(
m_nodeIndex, SpecInt32, GetArrayLength, m_node->codeOrigin,
OpInfo(m_node->arrayMode().asWord()), base, storage);
m_insertionSet.insertNode(
m_nodeIndex, SpecInt32, CheckInBounds, m_node->codeOrigin,
index, Edge(length, KnownInt32Use));
}
InsertionSet m_insertionSet;
BasicBlock* m_block;
unsigned m_nodeIndex;
Node* m_node;
};
bool performSSALowering(Graph& graph)
{
SamplingRegion samplingRegion("DFG SSA Lowering Phase");
return runPhase<SSALoweringPhase>(graph);
}
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)
/*
* Copyright (C) 2013 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
* 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.
*/
#ifndef DFGSSALoweringPhase_h
#define DFGSSALoweringPhase_h
#if ENABLE(DFG_JIT)
namespace JSC { namespace DFG {
class Graph;
// Performs DFG->DFG lowerings that are only appropriate for SSA form and the FTL
// backend. This is intended to be run after SSAConversionPhase.
bool performSSALowering(Graph&);
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)
#endif // DFGSSALoweringPhase_h
......@@ -245,6 +245,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
case NotifyWrite:
case FunctionReentryWatchpoint:
case TypedArrayWatchpoint:
case CheckInBounds:
case ConstantStoragePointer:
return true;
......
......@@ -1740,7 +1740,7 @@ void SpeculativeJIT::compileDoublePutByVal(Node* node, SpeculateCellOperand& bas
if (arrayMode.isInBounds()) {
speculationCheck(
StoreToHoleOrOutOfBounds, JSValueRegs(), 0,
OutOfBounds, JSValueRegs(), 0,
m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
} else {
MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
......
......@@ -1767,7 +1767,7 @@ void SpeculativeJIT::compileContiguousPutByVal(Node* node, BaseOperandType& base
if (arrayMode.isInBounds()) {
speculationCheck(
StoreToHoleOrOutOfBounds, JSValueRegs(), 0,
OutOfBounds, JSValueRegs(), 0,
m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
} else {
MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
......@@ -4767,6 +4767,7 @@ void SpeculativeJIT::compile(Node* node)
case CheckTierUpAndOSREnter:
case Int52ToDouble:
case Int52ToValue:
case CheckInBounds:
RELEASE_ASSERT_NOT_REACHED();
break;
}
......
......@@ -2957,7 +2957,7 @@ void SpeculativeJIT::compile(Node* node)
if (arrayMode.isInBounds()) {
speculationCheck(
StoreToHoleOrOutOfBounds, JSValueRegs(), 0,
OutOfBounds, JSValueRegs(), 0,
m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
} else {
MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
......@@ -5065,6 +5065,7 @@ void SpeculativeJIT::compile(Node* node)
case Upsilon:
case GetArgument:
case ExtractOSREntryLocal:
case CheckInBounds:
RELEASE_ASSERT_NOT_REACHED();
break;
}
......
......@@ -109,6 +109,8 @@ inline CapabilityLevel canCompile(Node* node)
case ValueToInt32:
case Branch:
case LogicalNot:
case CheckInBounds:
case ConstantStoragePointer:
// These are OK.
break;
case GetById:
......
......@@ -355,6 +355,9 @@ private:
case GetArrayLength:
compileGetArrayLength();
break;
case CheckInBounds:
compileCheckInBounds();
break;
case GetByVal:
compileGetByVal();
break;
......@@ -1475,6 +1478,13 @@ private:
}
}
void compileCheckInBounds()
{
speculate(
OutOfBounds, noValue(), 0,
m_out.aboveOrEqual(lowInt32(m_node->child1()), lowInt32(m_node->child2())));
}
void compileGetByVal()
{
switch (m_node->arrayMode().type()) {
......@@ -1487,11 +1497,6 @@ private:
m_heaps.indexedInt32Properties : m_heaps.indexedContiguousProperties;
if (m_node->arrayMode().isInBounds()) {
speculate(
OutOfBounds, noValue(), 0,
m_out.aboveOrEqual(
index, m_out.load32(storage, m_heaps.Butterfly_publicLength)));
LValue result = m_out.load64(baseIndex(heap, storage, index, m_node->child2()));
speculate(LoadFromHole, noValue(), 0, m_out.isZero64(result));
setJSValue(result);
......@@ -1532,11 +1537,6 @@ private:
IndexedAbstractHeap& heap = m_heaps.indexedDoubleProperties;
if (m_node->arrayMode().isInBounds()) {
speculate(
OutOfBounds, noValue(), 0,
m_out.aboveOrEqual(
index, m_out.load32(storage, m_heaps.Butterfly_publicLength)));
LValue result = m_out.loadDouble(
baseIndex(heap, storage, index, m_node->child2()));
......@@ -1600,11 +1600,6 @@ private:
TypedArrayType type = m_node->arrayMode().typedArrayType();
if (isTypedView(type)) {
speculate(
OutOfBounds, noValue(), 0,
m_out.aboveOrEqual(
index, typedArrayLength(m_node->child1(), m_node->arrayMode())));
TypedPointer pointer = TypedPointer(
m_heaps.typedArrayProperties,
m_out.add(
......@@ -1792,14 +1787,6 @@ private:
TypedArrayType type = m_node->arrayMode().typedArrayType();
if (isTypedView(type)) {
if (m_node->op() != PutByValAlias) {
speculate(
OutOfBounds, noValue(), 0,
m_out.aboveOrEqual(
index,
typedArrayLength(child1, m_node->arrayMode(), base)));
}
TypedPointer pointer = TypedPointer(
m_heaps.typedArrayProperties,
m_out.add(
......@@ -3085,15 +3072,12 @@ private:
template<typename FunctionType>
void contiguousPutByValOutOfBounds(
FunctionType slowPathFunction,
LValue base, LValue storage, LValue index, LValue value,
FunctionType slowPathFunction, LValue base, LValue storage, LValue index, LValue value,
LBasicBlock continuation)
{
LValue isNotInBounds = m_out.aboveOrEqual(
index, m_out.load32(storage, m_heaps.Butterfly_publicLength));
if (m_node->arrayMode().isInBounds())
speculate(StoreToHoleOrOutOfBounds, noValue(), 0, isNotInBounds);
else {
if (!m_node->arrayMode().isInBounds()) {
LBasicBlock notInBoundsCase =
FTL_NEW_BLOCK(m_out, ("PutByVal not in bounds"));
LBasicBlock performStore =
......
......@@ -906,7 +906,7 @@ void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
return;
}