FTL should do polymorphic PutById inlining
https://bugs.webkit.org/show_bug.cgi?id=129210
Source/JavaScriptCore:
Reviewed by Mark Hahnenberg and Oliver Hunt.
This makes PutByIdStatus inform us about polymorphic cases by returning an array of
PutByIdVariants. The DFG now has a node called MultiPutByOffset that indicates a
selection of multiple inlined PutByIdVariants.
MultiPutByOffset is almost identical to MultiGetByOffset, which we added in
http://trac.webkit.org/changeset/164207.
This also does some FTL refactoring to make MultiPutByOffset share code with some nodes
that generate similar code.
1% speed-up on V8v7 due to splay improving by 6.8%. Splay does the thing where it
sometimes swaps field insertion order, creating fake polymorphism.
* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeFromLLInt):
(JSC::PutByIdStatus::computeFor):
(JSC::PutByIdStatus::computeForStubInfo):
(JSC::PutByIdStatus::dump):
* bytecode/PutByIdStatus.h:
(JSC::PutByIdStatus::PutByIdStatus):
(JSC::PutByIdStatus::isSimple):
(JSC::PutByIdStatus::numVariants):
(JSC::PutByIdStatus::variants):
(JSC::PutByIdStatus::at):
(JSC::PutByIdStatus::operator[]):
* bytecode/PutByIdVariant.cpp: Added.
(JSC::PutByIdVariant::dump):
(JSC::PutByIdVariant::dumpInContext):
* bytecode/PutByIdVariant.h: Added.
(JSC::PutByIdVariant::PutByIdVariant):
(JSC::PutByIdVariant::replace):
(JSC::PutByIdVariant::transition):
(JSC::PutByIdVariant::kind):
(JSC::PutByIdVariant::isSet):
(JSC::PutByIdVariant::operator!):
(JSC::PutByIdVariant::structure):
(JSC::PutByIdVariant::oldStructure):
(JSC::PutByIdVariant::newStructure):
(JSC::PutByIdVariant::structureChain):
(JSC::PutByIdVariant::offset):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::emitPrototypeChecks):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::emitPutById):
(JSC::DFG::ByteCodeParser::handlePutById):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::checkStructureElimination):
(JSC::DFG::CSEPhase::structureTransitionWatchpointElimination):
(JSC::DFG::CSEPhase::putStructureStoreElimination):
(JSC::DFG::CSEPhase::getByOffsetLoadElimination):
(JSC::DFG::CSEPhase::putByOffsetStoreElimination):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGGraph.h:
* dfg/DFGNode.cpp:
(JSC::DFG::MultiPutByOffsetData::writesStructures):
(JSC::DFG::MultiPutByOffsetData::reallocatesStorage):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToPutByOffset):
(JSC::DFG::Node::hasMultiPutByOffsetData):
(JSC::DFG::Node::multiPutByOffsetData):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compilePutStructure):
(JSC::FTL::LowerDFGToLLVM::compileAllocatePropertyStorage):
(JSC::FTL::LowerDFGToLLVM::compileReallocatePropertyStorage):
(JSC::FTL::LowerDFGToLLVM::compileGetByOffset):
(JSC::FTL::LowerDFGToLLVM::compileMultiGetByOffset):
(JSC::FTL::LowerDFGToLLVM::compilePutByOffset):
(JSC::FTL::LowerDFGToLLVM::compileMultiPutByOffset):
(JSC::FTL::LowerDFGToLLVM::loadProperty):
(JSC::FTL::LowerDFGToLLVM::storeProperty):
(JSC::FTL::LowerDFGToLLVM::addressOfProperty):
(JSC::FTL::LowerDFGToLLVM::storageForTransition):
(JSC::FTL::LowerDFGToLLVM::allocatePropertyStorage):
(JSC::FTL::LowerDFGToLLVM::reallocatePropertyStorage):
(JSC::FTL::LowerDFGToLLVM::emitStoreBarrier):
* tests/stress/fold-multi-put-by-offset-to-put-by-offset.js: Added.
* tests/stress/multi-put-by-offset-reallocation-butterfly-cse.js: Added.
* tests/stress/multi-put-by-offset-reallocation-cases.js: Added.
LayoutTests:
Reviewed by Mark Hahnenberg and Oliver Hunt.
Add a microbenchmark for polymorphic PutById.
* js/regress/polymorphic-put-by-id-expected.txt: Added.
* js/regress/polymorphic-put-by-id.html: Added.
* js/regress/script-tests/polymorphic-put-by-id.js: Added.
(foo):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@164620 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 06dabdd..dea2576 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,17 @@
+2014-02-24 Filip Pizlo <fpizlo@apple.com>
+
+ FTL should do polymorphic PutById inlining
+ https://bugs.webkit.org/show_bug.cgi?id=129210
+
+ Reviewed by Mark Hahnenberg and Oliver Hunt.
+
+ Add a microbenchmark for polymorphic PutById.
+
+ * js/regress/polymorphic-put-by-id-expected.txt: Added.
+ * js/regress/polymorphic-put-by-id.html: Added.
+ * js/regress/script-tests/polymorphic-put-by-id.js: Added.
+ (foo):
+
2014-02-24 Samuel White <samuel_white@apple.com>
AX: AccessibilityObject::findMatchingObjects should never include 'this' in results.
diff --git a/LayoutTests/js/regress/polymorphic-put-by-id-expected.txt b/LayoutTests/js/regress/polymorphic-put-by-id-expected.txt
new file mode 100644
index 0000000..182d079
--- /dev/null
+++ b/LayoutTests/js/regress/polymorphic-put-by-id-expected.txt
@@ -0,0 +1,10 @@
+JSRegress/polymorphic-put-by-id
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/polymorphic-put-by-id.html b/LayoutTests/js/regress/polymorphic-put-by-id.html
new file mode 100644
index 0000000..e2df520
--- /dev/null
+++ b/LayoutTests/js/regress/polymorphic-put-by-id.html
@@ -0,0 +1,12 @@
+<!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/polymorphic-put-by-id.js"></script>
+<script src="resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/polymorphic-put-by-id.js b/LayoutTests/js/regress/script-tests/polymorphic-put-by-id.js
new file mode 100644
index 0000000..89dd793
--- /dev/null
+++ b/LayoutTests/js/regress/script-tests/polymorphic-put-by-id.js
@@ -0,0 +1,24 @@
+function foo(o) {
+ for (var i = 0; i < 100; ++i)
+ o.f = (o.f | 0) + 42;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100000; ++i) {
+ var object;
+ if ((i % 3) == 0)
+ object = {g:3};
+ else if ((i % 3) == 1)
+ object = {f:1, g:2};
+ else if ((i % 3) == 2)
+ object = {g:1, f:2};
+ foo(object);
+ if (object.f != 42 * 100 + (i % 3))
+ throw "Error: bad result for i = " + i + ": " + object.f;
+}
+
+var r = {g:3, h:4, f:5};
+foo(r);
+if (r.f != 5 + 42 * 100)
+ throw "Error: bad result at end: " + r.f;
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 715e5d2..0db0cdc 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -82,6 +82,7 @@
bytecode/PreciseJumpTargets.cpp
bytecode/ProfiledCodeBlockJettisoningWatchpoint.cpp
bytecode/PutByIdStatus.cpp
+ bytecode/PutByIdVariant.cpp
bytecode/ReduceWhitespace.cpp
bytecode/SamplingTool.cpp
bytecode/SpecialPointer.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 2190df2..55d74ee 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,119 @@
+2014-02-24 Filip Pizlo <fpizlo@apple.com>
+
+ FTL should do polymorphic PutById inlining
+ https://bugs.webkit.org/show_bug.cgi?id=129210
+
+ Reviewed by Mark Hahnenberg and Oliver Hunt.
+
+ This makes PutByIdStatus inform us about polymorphic cases by returning an array of
+ PutByIdVariants. The DFG now has a node called MultiPutByOffset that indicates a
+ selection of multiple inlined PutByIdVariants.
+
+ MultiPutByOffset is almost identical to MultiGetByOffset, which we added in
+ http://trac.webkit.org/changeset/164207.
+
+ This also does some FTL refactoring to make MultiPutByOffset share code with some nodes
+ that generate similar code.
+
+ 1% speed-up on V8v7 due to splay improving by 6.8%. Splay does the thing where it
+ sometimes swaps field insertion order, creating fake polymorphism.
+
+ * CMakeLists.txt:
+ * GNUmakefile.list.am:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * bytecode/PutByIdStatus.cpp:
+ (JSC::PutByIdStatus::computeFromLLInt):
+ (JSC::PutByIdStatus::computeFor):
+ (JSC::PutByIdStatus::computeForStubInfo):
+ (JSC::PutByIdStatus::dump):
+ * bytecode/PutByIdStatus.h:
+ (JSC::PutByIdStatus::PutByIdStatus):
+ (JSC::PutByIdStatus::isSimple):
+ (JSC::PutByIdStatus::numVariants):
+ (JSC::PutByIdStatus::variants):
+ (JSC::PutByIdStatus::at):
+ (JSC::PutByIdStatus::operator[]):
+ * bytecode/PutByIdVariant.cpp: Added.
+ (JSC::PutByIdVariant::dump):
+ (JSC::PutByIdVariant::dumpInContext):
+ * bytecode/PutByIdVariant.h: Added.
+ (JSC::PutByIdVariant::PutByIdVariant):
+ (JSC::PutByIdVariant::replace):
+ (JSC::PutByIdVariant::transition):
+ (JSC::PutByIdVariant::kind):
+ (JSC::PutByIdVariant::isSet):
+ (JSC::PutByIdVariant::operator!):
+ (JSC::PutByIdVariant::structure):
+ (JSC::PutByIdVariant::oldStructure):
+ (JSC::PutByIdVariant::newStructure):
+ (JSC::PutByIdVariant::structureChain):
+ (JSC::PutByIdVariant::offset):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::emitPrototypeChecks):
+ (JSC::DFG::ByteCodeParser::handleGetById):
+ (JSC::DFG::ByteCodeParser::emitPutById):
+ (JSC::DFG::ByteCodeParser::handlePutById):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCSEPhase.cpp:
+ (JSC::DFG::CSEPhase::checkStructureElimination):
+ (JSC::DFG::CSEPhase::structureTransitionWatchpointElimination):
+ (JSC::DFG::CSEPhase::putStructureStoreElimination):
+ (JSC::DFG::CSEPhase::getByOffsetLoadElimination):
+ (JSC::DFG::CSEPhase::putByOffsetStoreElimination):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants):
+ (JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGGraph.h:
+ * dfg/DFGNode.cpp:
+ (JSC::DFG::MultiPutByOffsetData::writesStructures):
+ (JSC::DFG::MultiPutByOffsetData::reallocatesStorage):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::convertToPutByOffset):
+ (JSC::DFG::Node::hasMultiPutByOffsetData):
+ (JSC::DFG::Node::multiPutByOffsetData):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGTypeCheckHoistingPhase.cpp:
+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::LowerDFGToLLVM::compileNode):
+ (JSC::FTL::LowerDFGToLLVM::compilePutStructure):
+ (JSC::FTL::LowerDFGToLLVM::compileAllocatePropertyStorage):
+ (JSC::FTL::LowerDFGToLLVM::compileReallocatePropertyStorage):
+ (JSC::FTL::LowerDFGToLLVM::compileGetByOffset):
+ (JSC::FTL::LowerDFGToLLVM::compileMultiGetByOffset):
+ (JSC::FTL::LowerDFGToLLVM::compilePutByOffset):
+ (JSC::FTL::LowerDFGToLLVM::compileMultiPutByOffset):
+ (JSC::FTL::LowerDFGToLLVM::loadProperty):
+ (JSC::FTL::LowerDFGToLLVM::storeProperty):
+ (JSC::FTL::LowerDFGToLLVM::addressOfProperty):
+ (JSC::FTL::LowerDFGToLLVM::storageForTransition):
+ (JSC::FTL::LowerDFGToLLVM::allocatePropertyStorage):
+ (JSC::FTL::LowerDFGToLLVM::reallocatePropertyStorage):
+ (JSC::FTL::LowerDFGToLLVM::emitStoreBarrier):
+ * tests/stress/fold-multi-put-by-offset-to-put-by-offset.js: Added.
+ * tests/stress/multi-put-by-offset-reallocation-butterfly-cse.js: Added.
+ * tests/stress/multi-put-by-offset-reallocation-cases.js: Added.
+
2014-02-24 peavo@outlook.com <peavo@outlook.com>
JSC regressions after r164494
diff --git a/Source/JavaScriptCore/GNUmakefile.list.am b/Source/JavaScriptCore/GNUmakefile.list.am
index f0c86cb..baa0ec5 100644
--- a/Source/JavaScriptCore/GNUmakefile.list.am
+++ b/Source/JavaScriptCore/GNUmakefile.list.am
@@ -182,6 +182,8 @@
Source/JavaScriptCore/bytecode/SpeculatedType.h \
Source/JavaScriptCore/bytecode/PutByIdStatus.cpp \
Source/JavaScriptCore/bytecode/PutByIdStatus.h \
+ Source/JavaScriptCore/bytecode/PutByIdVariant.cpp \
+ Source/JavaScriptCore/bytecode/PutByIdVariant.h \
Source/JavaScriptCore/bytecode/PutKind.h \
Source/JavaScriptCore/bytecode/ReduceWhitespace.cpp \
Source/JavaScriptCore/bytecode/ReduceWhitespace.h \
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
index 2ada47f..261cba9 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
@@ -337,6 +337,7 @@
<ClCompile Include="..\bytecode\ProfiledCodeBlockJettisoningWatchpoint.cpp" />
<ClCompile Include="..\bytecode\PreciseJumpTargets.cpp" />
<ClCompile Include="..\bytecode\PutByIdStatus.cpp" />
+ <ClCompile Include="..\bytecode\PutByIdVariant.cpp" />
<ClCompile Include="..\bytecode\ReduceWhitespace.cpp" />
<ClCompile Include="..\bytecode\SamplingTool.cpp" />
<ClCompile Include="..\bytecode\SpecialPointer.cpp" />
@@ -858,6 +859,7 @@
<ClInclude Include="..\bytecode\ProfiledCodeBlockJettisoningWatchpoint.h" />
<ClInclude Include="..\bytecode\PreciseJumpTargets.h" />
<ClInclude Include="..\bytecode\PutByIdStatus.h" />
+ <ClInclude Include="..\bytecode\PutByIdVariant.h" />
<ClInclude Include="..\bytecode\PutKind.h" />
<ClInclude Include="..\bytecode\ReduceWhitespace.h" />
<ClInclude Include="..\bytecode\SamplingTool.h" />
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index bfaf51a..293bd01 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -357,6 +357,8 @@
0F9332A314CA7DD70085F3C6 /* PutByIdStatus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */; };
0F9332A414CA7DD90085F3C6 /* PutByIdStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F9332A514CA7DDD0085F3C6 /* StructureSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93329B14CA7DC10085F3C6 /* StructureSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 0F93B4A918B92C4D00178A3F /* PutByIdVariant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */; };
+ 0F93B4AA18B92C4D00178A3F /* PutByIdVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93B4A818B92C4D00178A3F /* PutByIdVariant.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F963B3813FC6FE90002D9B2 /* ValueProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F96EBB316676EF6008BADE3 /* CodeBlockWithJITType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F96EBB116676EF4008BADE3 /* CodeBlockWithJITType.h */; settings = {ATTRIBUTES = (Private, ); }; };
0F9749711687ADE400A4FF6A /* JSCellInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F97496F1687ADE200A4FF6A /* JSCellInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -1832,6 +1834,8 @@
0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PutByIdStatus.cpp; sourceTree = "<group>"; };
0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PutByIdStatus.h; sourceTree = "<group>"; };
0F93329B14CA7DC10085F3C6 /* StructureSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StructureSet.h; sourceTree = "<group>"; };
+ 0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PutByIdVariant.cpp; sourceTree = "<group>"; };
+ 0F93B4A818B92C4D00178A3F /* PutByIdVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PutByIdVariant.h; sourceTree = "<group>"; };
0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValueProfile.h; sourceTree = "<group>"; };
0F96EBB116676EF4008BADE3 /* CodeBlockWithJITType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeBlockWithJITType.h; sourceTree = "<group>"; };
0F97496F1687ADE200A4FF6A /* JSCellInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCellInlines.h; sourceTree = "<group>"; };
@@ -4585,6 +4589,8 @@
0FC97F32182020D7002C9B26 /* ProfiledCodeBlockJettisoningWatchpoint.h */,
0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */,
0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */,
+ 0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */,
+ 0F93B4A818B92C4D00178A3F /* PutByIdVariant.h */,
0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */,
0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */,
0FF60AC016740F8100029779 /* ReduceWhitespace.h */,
@@ -5387,6 +5393,7 @@
860161E40F3A83C100F84710 /* MacroAssemblerX86.h in Headers */,
860161E50F3A83C100F84710 /* MacroAssemblerX86_64.h in Headers */,
860161E60F3A83C100F84710 /* MacroAssemblerX86Common.h in Headers */,
+ 0F93B4AA18B92C4D00178A3F /* PutByIdVariant.h in Headers */,
A700873A17CBE85300C3E643 /* MapConstructor.h in Headers */,
A78507D717CBC6FD0011F6E7 /* MapData.h in Headers */,
A74DEF92182D991400522C22 /* MapIteratorConstructor.h in Headers */,
@@ -6174,6 +6181,7 @@
0FC20CB51852E2C600C9E954 /* DFGStrengthReductionPhase.cpp in Sources */,
0F2FCCFE18A60070001A27F8 /* DFGThreadData.cpp in Sources */,
0FC097A1146B28CA00CF2442 /* DFGThunks.cpp in Sources */,
+ 0F93B4A918B92C4D00178A3F /* PutByIdVariant.cpp in Sources */,
0FD8A32717D51F5700CA2C40 /* DFGTierUpCheckInjectionPhase.cpp in Sources */,
0FD8A32917D51F5700CA2C40 /* DFGToFTLDeferredCompilationCallback.cpp in Sources */,
0FD8A32B17D51F5700CA2C40 /* DFGToFTLForOSREntryDeferredCompilationCallback.cpp in Sources */,
diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
index b1e1538..a06fb1e 100644
--- a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
+++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
@@ -30,8 +30,10 @@
#include "LLIntData.h"
#include "LowLevelInterpreter.h"
#include "JSCInlines.h"
+#include "PolymorphicPutByIdList.h"
#include "Structure.h"
#include "StructureChain.h"
+#include <wtf/ListDump.h>
namespace JSC {
@@ -56,15 +58,15 @@
Structure* structure = instruction[4].u.structure.get();
if (!structure)
- return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(NoInformation);
if (instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id)
|| instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_out_of_line)) {
PropertyOffset offset = structure->getConcurrently(*profiledBlock->vm(), uid);
if (!isValidOffset(offset))
- return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(NoInformation);
- return PutByIdStatus(SimpleReplace, structure, 0, 0, offset);
+ return PutByIdVariant::replace(structure, offset);
}
ASSERT(structure->transitionWatchpointSetHasBeenInvalidated());
@@ -81,14 +83,14 @@
PropertyOffset offset = newStructure->getConcurrently(*profiledBlock->vm(), uid);
if (!isValidOffset(offset))
- return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(NoInformation);
- return PutByIdStatus(
- SimpleTransition, structure, newStructure,
+ return PutByIdVariant::transition(
+ structure, newStructure,
chain ? adoptRef(new IntendedStructureChain(profiledBlock, structure, chain)) : 0,
offset);
#else
- return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(NoInformation);
#endif
}
@@ -102,7 +104,7 @@
#if ENABLE(DFG_JIT)
if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex)
|| hasExitSite(locker, profiledBlock, bytecodeIndex))
- return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(TakesSlowPath);
StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex));
PutByIdStatus result = computeForStubInfo(locker, profiledBlock, stubInfo, uid);
@@ -112,7 +114,7 @@
return result;
#else // ENABLE(JIT)
UNUSED_PARAM(map);
- return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(NoInformation);
#endif // ENABLE(JIT)
}
@@ -123,25 +125,22 @@
return PutByIdStatus();
if (stubInfo->resetByGC)
- return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(TakesSlowPath);
switch (stubInfo->accessType) {
case access_unset:
// If the JIT saw it but didn't optimize it, then assume that this takes slow path.
- return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(TakesSlowPath);
case access_put_by_id_replace: {
PropertyOffset offset =
stubInfo->u.putByIdReplace.baseObjectStructure->getConcurrently(
*profiledBlock->vm(), uid);
if (isValidOffset(offset)) {
- return PutByIdStatus(
- SimpleReplace,
- stubInfo->u.putByIdReplace.baseObjectStructure.get(),
- 0, 0,
- offset);
+ return PutByIdVariant::replace(
+ stubInfo->u.putByIdReplace.baseObjectStructure.get(), offset);
}
- return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(TakesSlowPath);
}
case access_put_by_id_transition_normal:
@@ -151,8 +150,7 @@
stubInfo->u.putByIdTransition.structure->getConcurrently(
*profiledBlock->vm(), uid);
if (isValidOffset(offset)) {
- return PutByIdStatus(
- SimpleTransition,
+ return PutByIdVariant::transition(
stubInfo->u.putByIdTransition.previousStructure.get(),
stubInfo->u.putByIdTransition.structure.get(),
stubInfo->u.putByIdTransition.chain ? adoptRef(new IntendedStructureChain(
@@ -160,13 +158,51 @@
stubInfo->u.putByIdTransition.chain.get())) : 0,
offset);
}
- return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(TakesSlowPath);
+ }
+
+ case access_put_by_id_list: {
+ PolymorphicPutByIdList* list = stubInfo->u.putByIdList.list;
+
+ PutByIdStatus result;
+ result.m_state = Simple;
+
+ for (unsigned i = 0; i < list->size(); ++i) {
+ const PutByIdAccess& access = list->at(i);
+
+ switch (access.type()) {
+ case PutByIdAccess::Replace: {
+ Structure* structure = access.structure();
+ PropertyOffset offset = structure->getConcurrently(*profiledBlock->vm(), uid);
+ if (!isValidOffset(offset))
+ return PutByIdStatus(TakesSlowPath);
+ result.m_variants.append(PutByIdVariant::replace(structure, offset));
+ break;
+ }
+
+ case PutByIdAccess::Transition: {
+ PropertyOffset offset =
+ access.newStructure()->getConcurrently(*profiledBlock->vm(), uid);
+ if (!isValidOffset(offset))
+ return PutByIdStatus(TakesSlowPath);
+ result.m_variants.append(PutByIdVariant::transition(
+ access.oldStructure(), access.newStructure(),
+ access.chain() ? adoptRef(new IntendedStructureChain(
+ profiledBlock, access.oldStructure(), access.chain())) : 0,
+ offset));
+ break;
+ }
+
+ default:
+ return PutByIdStatus(TakesSlowPath);
+ }
+ }
+
+ return result;
}
default:
- // FIXME: We should handle polymorphic PutById. We probably have some interesting things
- // we could do about it.
- return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+ return PutByIdStatus(TakesSlowPath);
}
}
#endif
@@ -223,7 +259,7 @@
// the specialized slot.
return PutByIdStatus(TakesSlowPath);
}
- return PutByIdStatus(SimpleReplace, structure, 0, 0, offset);
+ return PutByIdVariant::replace(structure, offset);
}
// Our hypothesis is that we're doing a transition. Before we prove that this is really
@@ -276,7 +312,26 @@
ASSERT(!transition->transitionDidInvolveSpecificValue());
ASSERT(isValidOffset(offset));
- return PutByIdStatus(SimpleTransition, structure, transition, chain.release(), offset);
+ return PutByIdVariant::transition(structure, transition, chain.release(), offset);
+}
+
+void PutByIdStatus::dump(PrintStream& out) const
+{
+ switch (m_state) {
+ case NoInformation:
+ out.print("(NoInformation)");
+ return;
+
+ case Simple:
+ out.print("(", listDump(m_variants), ")");
+ return;
+
+ case TakesSlowPath:
+ out.print("(TakesSlowPath)");
+ return;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.h b/Source/JavaScriptCore/bytecode/PutByIdStatus.h
index 1684a74..4bb8b98 100644
--- a/Source/JavaScriptCore/bytecode/PutByIdStatus.h
+++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,8 +27,7 @@
#define PutByIdStatus_h
#include "ExitingJITType.h"
-#include "IntendedStructureChain.h"
-#include "PropertyOffset.h"
+#include "PutByIdVariant.h"
#include "StructureStubInfo.h"
#include <wtf/text/StringImpl.h>
@@ -45,51 +44,27 @@
enum State {
// It's uncached so we have no information.
NoInformation,
- // It's cached as a direct store into an object property for cases where the object
- // already has the property.
- SimpleReplace,
- // It's cached as a transition from one structure that lacks the property to one that
- // includes the property, and a direct store to this new property.
- SimpleTransition,
+ // It's cached as a simple store of some kind.
+ Simple,
// It's known to often take slow path.
TakesSlowPath
};
PutByIdStatus()
: m_state(NoInformation)
- , m_oldStructure(0)
- , m_newStructure(0)
- , m_structureChain(0)
- , m_offset(invalidOffset)
{
}
explicit PutByIdStatus(State state)
: m_state(state)
- , m_oldStructure(0)
- , m_newStructure(0)
- , m_structureChain(0)
- , m_offset(invalidOffset)
{
ASSERT(m_state == NoInformation || m_state == TakesSlowPath);
}
- PutByIdStatus(
- State state,
- Structure* oldStructure,
- Structure* newStructure,
- PassRefPtr<IntendedStructureChain> structureChain,
- PropertyOffset offset)
- : m_state(state)
- , m_oldStructure(oldStructure)
- , m_newStructure(newStructure)
- , m_structureChain(structureChain)
- , m_offset(offset)
+ PutByIdStatus(const PutByIdVariant& variant)
+ : m_state(Simple)
{
- ASSERT((m_state == NoInformation || m_state == TakesSlowPath) == !m_oldStructure);
- ASSERT((m_state != SimpleTransition) == !m_newStructure);
- ASSERT(!((m_state != SimpleTransition) && m_structureChain));
- ASSERT((m_state == NoInformation || m_state == TakesSlowPath) == (m_offset == invalidOffset));
+ m_variants.append(variant);
}
static PutByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, StringImpl* uid);
@@ -101,14 +76,15 @@
bool isSet() const { return m_state != NoInformation; }
bool operator!() const { return m_state == NoInformation; }
- bool isSimpleReplace() const { return m_state == SimpleReplace; }
- bool isSimpleTransition() const { return m_state == SimpleTransition; }
+ bool isSimple() const { return m_state == Simple; }
bool takesSlowPath() const { return m_state == TakesSlowPath; }
- Structure* oldStructure() const { return m_oldStructure; }
- Structure* newStructure() const { return m_newStructure; }
- IntendedStructureChain* structureChain() const { return m_structureChain.get(); }
- PropertyOffset offset() const { return m_offset; }
+ size_t numVariants() const { return m_variants.size(); }
+ const Vector<PutByIdVariant, 1>& variants() const { return m_variants; }
+ const PutByIdVariant& at(size_t index) const { return m_variants[index]; }
+ const PutByIdVariant& operator[](size_t index) const { return at(index); }
+
+ void dump(PrintStream&) const;
private:
#if ENABLE(DFG_JIT)
@@ -120,10 +96,7 @@
static PutByIdStatus computeFromLLInt(CodeBlock*, unsigned bytecodeIndex, StringImpl* uid);
State m_state;
- Structure* m_oldStructure;
- Structure* m_newStructure;
- RefPtr<IntendedStructureChain> m_structureChain;
- PropertyOffset m_offset;
+ Vector<PutByIdVariant, 1> m_variants;
};
} // namespace JSC
diff --git a/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp b/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp
new file mode 100644
index 0000000..f83c102
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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 "PutByIdVariant.h"
+
+namespace JSC {
+
+void PutByIdVariant::dump(PrintStream& out) const
+{
+ dumpInContext(out, 0);
+}
+
+void PutByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+ switch (kind()) {
+ case NotSet:
+ out.print("<empty>");
+ return;
+
+ case Replace:
+ out.print(
+ "<Replace: ", pointerDumpInContext(structure(), context), ", ", offset(), ">");
+ return;
+
+ case Transition:
+ out.print(
+ "<Transition: ", pointerDumpInContext(oldStructure(), context), " -> ",
+ pointerDumpInContext(newStructure(), context), ", ",
+ pointerDumpInContext(structureChain(), context), ", ", offset(), ">");
+ return;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/PutByIdVariant.h b/Source/JavaScriptCore/bytecode/PutByIdVariant.h
new file mode 100644
index 0000000..eba95e8
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/PutByIdVariant.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 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 PutByIdVariant_h
+#define PutByIdVariant_h
+
+#include "IntendedStructureChain.h"
+#include "PropertyOffset.h"
+
+namespace JSC {
+
+class PutByIdVariant {
+public:
+ enum Kind {
+ NotSet,
+ Replace,
+ Transition
+ };
+
+ PutByIdVariant()
+ : m_kind(NotSet)
+ , m_oldStructure(0)
+ , m_newStructure(0)
+ , m_offset(invalidOffset)
+ {
+ }
+
+ static PutByIdVariant replace(Structure* structure, PropertyOffset offset)
+ {
+ PutByIdVariant result;
+ result.m_kind = Replace;
+ result.m_oldStructure = structure;
+ result.m_offset = offset;
+ return result;
+ }
+
+ static PutByIdVariant transition(
+ Structure* oldStructure, Structure* newStructure,
+ PassRefPtr<IntendedStructureChain> structureChain, PropertyOffset offset)
+ {
+ PutByIdVariant result;
+ result.m_kind = Transition;
+ result.m_oldStructure = oldStructure;
+ result.m_newStructure = newStructure;
+ result.m_structureChain = structureChain;
+ result.m_offset = offset;
+ return result;
+ }
+
+ Kind kind() const { return m_kind; }
+
+ bool isSet() const { return kind() != NotSet; }
+ bool operator!() const { return !isSet(); }
+
+ Structure* structure() const
+ {
+ ASSERT(kind() == Replace);
+ return m_oldStructure;
+ }
+
+ Structure* oldStructure() const
+ {
+ ASSERT(kind() == Transition || kind() == Replace);
+ return m_oldStructure;
+ }
+
+ Structure* newStructure() const
+ {
+ ASSERT(kind() == Transition);
+ return m_newStructure;
+ }
+
+ IntendedStructureChain* structureChain() const
+ {
+ ASSERT(kind() == Transition);
+ return m_structureChain.get();
+ }
+
+ PropertyOffset offset() const
+ {
+ ASSERT(isSet());
+ return m_offset;
+ }
+
+ void dump(PrintStream&) const;
+ void dumpInContext(PrintStream&, DumpContext*) const;
+
+private:
+ Kind m_kind;
+ Structure* m_oldStructure;
+ Structure* m_newStructure;
+ RefPtr<IntendedStructureChain> m_structureChain;
+ PropertyOffset m_offset;
+};
+
+} // namespace JSC
+
+#endif // PutByIdVariant_h
+
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 8fd5453..32ed82f 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -1640,7 +1640,7 @@
for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;)
set.addAll(node->multiGetByOffsetData().variants[i].structureSet());
- filter(value, set);
+ filter(node->child1(), set);
forNode(node).makeHeapTop();
break;
}
@@ -1648,7 +1648,60 @@
case PutByOffset: {
break;
}
-
+
+ case MultiPutByOffset: {
+ AbstractValue& value = forNode(node->child1());
+ ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this.
+
+ if (Structure* structure = value.bestProvenStructure()) {
+ bool done = false;
+ for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) {
+ const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i];
+ if (variant.oldStructure() != structure)
+ continue;
+
+ if (variant.kind() == PutByIdVariant::Replace) {
+ filter(node->child1(), structure);
+ m_state.setFoundConstants(true);
+ m_state.setHaveStructures(true);
+ done = true;
+ break;
+ }
+
+ ASSERT(variant.kind() == PutByIdVariant::Transition);
+ clobberStructures(clobberLimit);
+ forNode(node->child1()).set(m_graph, variant.newStructure());
+ m_state.setFoundConstants(true);
+ m_state.setHaveStructures(true);
+ done = true;
+ break;
+ }
+ if (done)
+ break;
+ }
+
+ clobberStructures(clobberLimit);
+
+ StructureSet newSet;
+ for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) {
+ const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i];
+ if (variant.kind() == PutByIdVariant::Replace) {
+ if (value.m_currentKnownStructure.contains(variant.structure()))
+ newSet.addAll(variant.structure());
+ continue;
+ }
+ ASSERT(variant.kind() == PutByIdVariant::Transition);
+ if (value.m_currentKnownStructure.contains(variant.oldStructure()))
+ newSet.addAll(variant.newStructure());
+ }
+
+ // Use filter(value, set) as a way of setting the structure set. This works because
+ // we would have already made the set be TOP before this. Filtering top is another
+ // way of setting.
+ filter(node->child1(), newSet);
+ break;
+ }
+
case CheckFunction: {
JSValue value = forNode(node->child1()).value();
if (value == node->function()) {
@@ -1685,18 +1738,20 @@
structure,
m_graph.identifiers()[node->identifierNumber()],
node->op() == PutByIdDirect);
- if (status.isSimpleReplace()) {
- filter(node->child1(), structure);
- m_state.setFoundConstants(true);
- m_state.setHaveStructures(true);
- break;
- }
- if (status.isSimpleTransition()) {
- clobberStructures(clobberLimit);
- forNode(node->child1()).set(m_graph, status.newStructure());
- m_state.setHaveStructures(true);
- m_state.setFoundConstants(true);
- break;
+ if (status.isSimple() && status.numVariants() == 1) {
+ if (status[0].kind() == PutByIdVariant::Replace) {
+ filter(node->child1(), structure);
+ m_state.setFoundConstants(true);
+ m_state.setHaveStructures(true);
+ break;
+ }
+ if (status[0].kind() == PutByIdVariant::Transition) {
+ clobberStructures(clobberLimit);
+ forNode(node->child1()).set(m_graph, status[0].newStructure());
+ m_state.setHaveStructures(true);
+ m_state.setFoundConstants(true);
+ break;
+ }
}
}
clobberWorld(node->origin.semantic, clobberLimit);
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 1804b15..b5b2521 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -179,7 +179,12 @@
void handleGetById(
int destinationOperand, SpeculatedType, Node* base, unsigned identifierNumber,
const GetByIdStatus&);
- Node* emitPrototypeChecks(const GetByIdVariant&);
+ void emitPutById(
+ Node* base, unsigned identifierNumber, Node* value, bool isDirect);
+ void handlePutById(
+ Node* base, unsigned identifierNumber, Node* value, const PutByIdStatus&,
+ bool isDirect);
+ Node* emitPrototypeChecks(Structure*, IntendedStructureChain*);
Node* getScope(bool skipTop, unsigned skipCount);
@@ -1844,15 +1849,16 @@
return result;
}
-Node* ByteCodeParser::emitPrototypeChecks(const GetByIdVariant& variant)
+Node* ByteCodeParser::emitPrototypeChecks(
+ Structure* structure, IntendedStructureChain* chain)
{
Node* base = 0;
- m_graph.chains().addLazily(variant.chain());
- Structure* currentStructure = variant.structureSet().singletonStructure();
+ m_graph.chains().addLazily(chain);
+ Structure* currentStructure = structure;
JSObject* currentObject = 0;
- for (unsigned i = 0; i < variant.chain()->size(); ++i) {
+ for (unsigned i = 0; i < chain->size(); ++i) {
currentObject = asObject(currentStructure->prototypeForLookup(m_inlineStackTop->m_codeBlock));
- currentStructure = variant.chain()->at(i);
+ currentStructure = chain->at(i);
base = cellConstantWithStructureCheck(currentObject, currentStructure);
}
RELEASE_ASSERT(base);
@@ -1878,12 +1884,18 @@
return;
}
+ if (m_graph.compilation())
+ m_graph.compilation()->noticeInlinedGetById();
+
// 1) Emit prototype structure checks for all chains. This could sort of maybe not be
// optimal, if there is some rarely executed case in the chain that requires a lot
// of checks and those checks are not watchpointable.
for (unsigned variantIndex = getByIdStatus.numVariants(); variantIndex--;) {
- if (getByIdStatus[variantIndex].chain())
- emitPrototypeChecks(getByIdStatus[variantIndex]);
+ if (getByIdStatus[variantIndex].chain()) {
+ emitPrototypeChecks(
+ getByIdStatus[variantIndex].structureSet().singletonStructure(),
+ getByIdStatus[variantIndex].chain());
+ }
}
// 2) Emit a MultiGetByOffset
@@ -1905,8 +1917,10 @@
addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structureSet())), base);
- if (variant.chain())
- base = emitPrototypeChecks(variant);
+ if (variant.chain()) {
+ base = emitPrototypeChecks(
+ variant.structureSet().singletonStructure(), variant.chain());
+ }
// Unless we want bugs like https://bugs.webkit.org/show_bug.cgi?id=88783, we need to
// ensure that the base of the original get_by_id is kept alive until we're done with
@@ -1928,6 +1942,123 @@
destinationOperand, prediction, base, identifierNumber, variant.offset());
}
+void ByteCodeParser::emitPutById(
+ Node* base, unsigned identifierNumber, Node* value, bool isDirect)
+{
+ if (isDirect)
+ addToGraph(PutByIdDirect, OpInfo(identifierNumber), base, value);
+ else
+ addToGraph(PutById, OpInfo(identifierNumber), base, value);
+}
+
+void ByteCodeParser::handlePutById(
+ Node* base, unsigned identifierNumber, Node* value,
+ const PutByIdStatus& putByIdStatus, bool isDirect)
+{
+ if (!putByIdStatus.isSimple()) {
+ if (!putByIdStatus.isSet())
+ addToGraph(ForceOSRExit);
+ emitPutById(base, identifierNumber, value, isDirect);
+ return;
+ }
+
+ if (putByIdStatus.numVariants() > 1) {
+ if (!isFTL(m_graph.m_plan.mode)) {
+ emitPutById(base, identifierNumber, value, isDirect);
+ return;
+ }
+
+ if (m_graph.compilation())
+ m_graph.compilation()->noticeInlinedPutById();
+
+ if (!isDirect) {
+ for (unsigned variantIndex = putByIdStatus.numVariants(); variantIndex--;) {
+ if (putByIdStatus[variantIndex].kind() != PutByIdVariant::Transition)
+ continue;
+ if (!putByIdStatus[variantIndex].structureChain())
+ continue;
+ emitPrototypeChecks(
+ putByIdStatus[variantIndex].oldStructure(),
+ putByIdStatus[variantIndex].structureChain());
+ }
+ }
+
+ MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
+ data->variants = putByIdStatus.variants();
+ data->identifierNumber = identifierNumber;
+ addToGraph(MultiPutByOffset, OpInfo(data), base, value);
+ return;
+ }
+
+ ASSERT(putByIdStatus.numVariants() == 1);
+ const PutByIdVariant& variant = putByIdStatus[0];
+
+ if (variant.kind() == PutByIdVariant::Replace) {
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structure())), base);
+ handlePutByOffset(base, identifierNumber, variant.offset(), value);
+ if (m_graph.compilation())
+ m_graph.compilation()->noticeInlinedPutById();
+ return;
+ }
+
+ ASSERT(variant.kind() == PutByIdVariant::Transition);
+ if (variant.structureChain() && !variant.structureChain()->isStillValid()) {
+ emitPutById(base, identifierNumber, value, isDirect);
+ return;
+ }
+
+ m_graph.chains().addLazily(variant.structureChain());
+
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.oldStructure())), base);
+ if (!isDirect)
+ emitPrototypeChecks(variant.oldStructure(), variant.structureChain());
+
+ ASSERT(variant.oldStructure()->transitionWatchpointSetHasBeenInvalidated());
+
+ Node* propertyStorage;
+ StructureTransitionData* transitionData = m_graph.addStructureTransitionData(
+ StructureTransitionData(variant.oldStructure(), variant.newStructure()));
+
+ if (variant.oldStructure()->outOfLineCapacity()
+ != variant.newStructure()->outOfLineCapacity()) {
+
+ // If we're growing the property storage then it must be because we're
+ // storing into the out-of-line storage.
+ ASSERT(!isInlineOffset(variant.offset()));
+
+ if (!variant.oldStructure()->outOfLineCapacity()) {
+ propertyStorage = addToGraph(
+ AllocatePropertyStorage, OpInfo(transitionData), base);
+ } else {
+ propertyStorage = addToGraph(
+ ReallocatePropertyStorage, OpInfo(transitionData),
+ base, addToGraph(GetButterfly, base));
+ }
+ } else {
+ if (isInlineOffset(variant.offset()))
+ propertyStorage = base;
+ else
+ propertyStorage = addToGraph(GetButterfly, base);
+ }
+
+ addToGraph(PutStructure, OpInfo(transitionData), base);
+
+ addToGraph(
+ PutByOffset,
+ OpInfo(m_graph.m_storageAccessData.size()),
+ propertyStorage,
+ base,
+ value);
+
+ StorageAccessData storageAccessData;
+ storageAccessData.offset = variant.offset();
+ storageAccessData.identifierNumber = identifierNumber;
+ m_graph.m_storageAccessData.append(storageAccessData);
+
+ if (m_graph.compilation())
+ m_graph.compilation()->noticeInlinedPutById();
+}
+
void ByteCodeParser::prepareToParseBlock()
{
for (unsigned i = 0; i < m_constants.size(); ++i)
@@ -2590,91 +2721,8 @@
m_inlineStackTop->m_profiledBlock, m_dfgCodeBlock,
m_inlineStackTop->m_stubInfos, m_dfgStubInfos,
currentCodeOrigin(), m_graph.identifiers()[identifierNumber]);
- bool canCountAsInlined = true;
- if (!putByIdStatus.isSet()) {
- addToGraph(ForceOSRExit);
- canCountAsInlined = false;
- }
- if (putByIdStatus.isSimpleReplace()) {
- addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(putByIdStatus.oldStructure())), base);
- handlePutByOffset(base, identifierNumber, putByIdStatus.offset(), value);
- } else if (
- putByIdStatus.isSimpleTransition()
- && (!putByIdStatus.structureChain()
- || putByIdStatus.structureChain()->isStillValid())) {
-
- m_graph.chains().addLazily(putByIdStatus.structureChain());
-
- addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(putByIdStatus.oldStructure())), base);
- if (!direct) {
- if (!putByIdStatus.oldStructure()->storedPrototype().isNull()) {
- cellConstantWithStructureCheck(
- putByIdStatus.oldStructure()->storedPrototype().asCell());
- }
-
- for (unsigned i = 0; i < putByIdStatus.structureChain()->size(); ++i) {
- JSValue prototype = putByIdStatus.structureChain()->at(i)->storedPrototype();
- if (prototype.isNull())
- continue;
- cellConstantWithStructureCheck(prototype.asCell());
- }
- }
- ASSERT(putByIdStatus.oldStructure()->transitionWatchpointSetHasBeenInvalidated());
-
- Node* propertyStorage;
- StructureTransitionData* transitionData =
- m_graph.addStructureTransitionData(
- StructureTransitionData(
- putByIdStatus.oldStructure(),
- putByIdStatus.newStructure()));
-
- if (putByIdStatus.oldStructure()->outOfLineCapacity()
- != putByIdStatus.newStructure()->outOfLineCapacity()) {
-
- // If we're growing the property storage then it must be because we're
- // storing into the out-of-line storage.
- ASSERT(!isInlineOffset(putByIdStatus.offset()));
-
- if (!putByIdStatus.oldStructure()->outOfLineCapacity()) {
- propertyStorage = addToGraph(
- AllocatePropertyStorage, OpInfo(transitionData), base);
- } else {
- propertyStorage = addToGraph(
- ReallocatePropertyStorage, OpInfo(transitionData),
- base, addToGraph(GetButterfly, base));
- }
- } else {
- if (isInlineOffset(putByIdStatus.offset()))
- propertyStorage = base;
- else
- propertyStorage = addToGraph(GetButterfly, base);
- }
-
- addToGraph(PutStructure, OpInfo(transitionData), base);
-
- addToGraph(
- PutByOffset,
- OpInfo(m_graph.m_storageAccessData.size()),
- propertyStorage,
- base,
- value);
-
- StorageAccessData storageAccessData;
- storageAccessData.offset = putByIdStatus.offset();
- storageAccessData.identifierNumber = identifierNumber;
- m_graph.m_storageAccessData.append(storageAccessData);
- } else {
- if (direct)
- addToGraph(PutByIdDirect, OpInfo(identifierNumber), base, value);
- else
- addToGraph(PutById, OpInfo(identifierNumber), base, value);
- canCountAsInlined = false;
- }
-
- if (canCountAsInlined && m_graph.compilation())
- m_graph.compilation()->noticeInlinedPutById();
-
+ handlePutById(base, identifierNumber, value, putByIdStatus, direct);
NEXT_OPCODE(op_put_by_id);
}
@@ -3269,11 +3317,11 @@
case GlobalProperty:
case GlobalPropertyWithVarInjectionChecks: {
PutByIdStatus status = PutByIdStatus::computeFor(*m_vm, globalObject, structure, uid, false);
- if (!status.isSimpleReplace()) {
+ if (status.numVariants() != 1 || status[0].kind() != PutByIdVariant::Replace) {
addToGraph(PutById, OpInfo(identifierNumber), get(VirtualRegister(scope)), get(VirtualRegister(value)));
break;
}
- Node* base = cellConstantWithStructureCheck(globalObject, status.oldStructure());
+ Node* base = cellConstantWithStructureCheck(globalObject, status[0].structure());
addToGraph(Phantom, get(VirtualRegister(scope)));
handlePutByOffset(base, identifierNumber, static_cast<PropertyOffset>(operand), get(VirtualRegister(value)));
// Keep scope alive until after put.
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 50c19c4..04a651f 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -495,7 +495,12 @@
case PutByOffset:
// Setting a property cannot change the structure.
break;
-
+
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().writesStructures())
+ return false;
+ break;
+
case PutByValDirect:
case PutByVal:
case PutByValAlias:
@@ -544,6 +549,11 @@
// Setting a property cannot change the structure.
break;
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().writesStructures())
+ return false;
+ break;
+
case PutByValDirect:
case PutByVal:
case PutByValAlias:
@@ -617,6 +627,7 @@
case NewStringObject:
case MakeRope:
case NewTypedArray:
+ case MultiPutByOffset:
return 0;
// This either exits, causes a GC (lazy string allocation), or clobbers
@@ -668,6 +679,14 @@
return 0;
}
break;
+
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().identifierNumber == identifierNumber) {
+ if (node->child1() == base)
+ return node->child2().node();
+ return 0;
+ }
+ break;
case PutByValDirect:
case PutByVal:
@@ -709,7 +728,12 @@
return 0;
}
break;
-
+
+ case MultiPutByOffset:
+ if (node->multiPutByOffsetData().identifierNumber == identifierNumber)
+ return 0;
+ break;
+
case PutByValDirect:
case PutByVal:
case PutByValAlias:
@@ -774,6 +798,11 @@
// But that seems like it would take Effort.
return 0;
+ case MultiPutByOffset:
+ //if (node->multiPutByOffsetData().reallocatesStorage())
+ // return 0;
+ break;
+
default:
if (m_graph.clobbersWorld(node))
return 0;
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index 94f2b40..5aefcfa 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -469,9 +469,23 @@
return;
case MultiGetByOffset:
+ read(JSCell_structure);
+ read(JSObject_butterfly);
read(AbstractHeap(NamedProperties, node->multiGetByOffsetData().identifierNumber));
return;
+ case MultiPutByOffset:
+ read(JSCell_structure);
+ read(JSObject_butterfly);
+ write(AbstractHeap(NamedProperties, node->multiPutByOffsetData().identifierNumber));
+ if (node->multiPutByOffsetData().writesStructures())
+ write(JSCell_structure);
+ if (node->multiPutByOffsetData().reallocatesStorage()) {
+ write(JSObject_butterfly);
+ clobberizeForAllocation(read, write);
+ }
+ return;
+
case PutByOffset:
write(AbstractHeap(NamedProperties, graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber));
return;
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index 54f4575..44d4136 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -174,6 +174,27 @@
}
break;
}
+
+ case MultiPutByOffset: {
+ Edge childEdge = node->child1();
+ Node* child = childEdge.node();
+ MultiPutByOffsetData& data = node->multiPutByOffsetData();
+
+ Structure* structure = m_state.forNode(child).bestProvenStructure();
+ if (!structure)
+ break;
+
+ for (unsigned i = data.variants.size(); i--;) {
+ const PutByIdVariant& variant = data.variants[i];
+ if (variant.oldStructure() != structure)
+ continue;
+
+ emitPutByOffset(indexInBlock, node, structure, variant, data.identifierNumber);
+ eliminated = true;
+ break;
+ }
+ break;
+ }
case GetById:
case GetByIdFlush: {
@@ -187,7 +208,7 @@
Structure* structure = m_state.forNode(child).bestProvenStructure();
if (!structure)
break;
-
+
GetByIdStatus status = GetByIdStatus::computeFor(
vm(), structure, m_graph.identifiers()[identifierNumber]);
@@ -215,9 +236,6 @@
if (!structure)
break;
- bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
- bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
-
PutByIdStatus status = PutByIdStatus::computeFor(
vm(),
m_graph.globalObjectFor(origin.semantic),
@@ -225,96 +243,13 @@
m_graph.identifiers()[identifierNumber],
node->op() == PutByIdDirect);
- if (!status.isSimpleReplace() && !status.isSimpleTransition())
+ if (!status.isSimple())
+ break;
+ if (status.numVariants() != 1)
break;
- ASSERT(status.oldStructure() == structure);
-
- // Now before we do anything else, push the CFA forward over the PutById
- // and make sure we signal to the loop that it should continue and not
- // do any eliminations.
- m_interpreter.execute(indexInBlock);
+ emitPutByOffset(indexInBlock, node, structure, status[0], identifierNumber);
eliminated = true;
-
- if (needsWatchpoint) {
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, StructureTransitionWatchpoint, origin,
- OpInfo(structure), childEdge);
- } else if (needsCellCheck) {
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, Phantom, origin, childEdge);
- }
-
- childEdge.setUseKind(KnownCellUse);
-
- StructureTransitionData* transitionData = 0;
- if (status.isSimpleTransition()) {
- transitionData = m_graph.addStructureTransitionData(
- StructureTransitionData(structure, status.newStructure()));
-
- if (node->op() == PutById) {
- if (!structure->storedPrototype().isNull()) {
- addStructureTransitionCheck(
- origin, indexInBlock,
- structure->storedPrototype().asCell());
- }
-
- m_graph.chains().addLazily(status.structureChain());
-
- for (unsigned i = 0; i < status.structureChain()->size(); ++i) {
- JSValue prototype = status.structureChain()->at(i)->storedPrototype();
- if (prototype.isNull())
- continue;
- ASSERT(prototype.isCell());
- addStructureTransitionCheck(
- origin, indexInBlock, prototype.asCell());
- }
- }
- }
-
- Edge propertyStorage;
-
- if (isInlineOffset(status.offset()))
- propertyStorage = childEdge;
- else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) {
- propertyStorage = Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, GetButterfly, origin, childEdge));
- } else if (!structure->outOfLineCapacity()) {
- ASSERT(status.newStructure()->outOfLineCapacity());
- ASSERT(!isInlineOffset(status.offset()));
- Node* allocatePropertyStorage = m_insertionSet.insertNode(
- indexInBlock, SpecNone, AllocatePropertyStorage,
- origin, OpInfo(transitionData), childEdge);
- m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
- propertyStorage = Edge(allocatePropertyStorage);
- } else {
- ASSERT(structure->outOfLineCapacity());
- ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
- ASSERT(!isInlineOffset(status.offset()));
-
- Node* reallocatePropertyStorage = m_insertionSet.insertNode(
- indexInBlock, SpecNone, ReallocatePropertyStorage, origin,
- OpInfo(transitionData), childEdge,
- Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, GetButterfly, origin, childEdge)));
- m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
- propertyStorage = Edge(reallocatePropertyStorage);
- }
-
- if (status.isSimpleTransition()) {
- Node* putStructure = m_graph.addNode(SpecNone, PutStructure, origin, OpInfo(transitionData), childEdge);
- m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
- m_insertionSet.insert(indexInBlock, putStructure);
- }
-
- node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
- m_insertionSet.insertNode(indexInBlock, SpecNone, ConditionalStoreBarrier, origin,
- Edge(node->child2().node(), KnownCellUse), Edge(node->child3().node(), UntypedUse));
-
- StorageAccessData storageAccessData;
- storageAccessData.offset = status.offset();
- storageAccessData.identifierNumber = identifierNumber;
- m_graph.m_storageAccessData.append(storageAccessData);
break;
}
@@ -445,6 +380,107 @@
m_graph.m_storageAccessData.append(storageAccessData);
}
+ void emitPutByOffset(unsigned indexInBlock, Node* node, Structure* structure, const PutByIdVariant& variant, unsigned identifierNumber)
+ {
+ NodeOrigin origin = node->origin;
+ Edge childEdge = node->child1();
+ Node* child = childEdge.node();
+
+ ASSERT(variant.oldStructure() == structure);
+
+ bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
+ bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
+
+ // Now before we do anything else, push the CFA forward over the PutById
+ // and make sure we signal to the loop that it should continue and not
+ // do any eliminations.
+ m_interpreter.execute(indexInBlock);
+
+ if (needsWatchpoint) {
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, StructureTransitionWatchpoint, origin,
+ OpInfo(structure), childEdge);
+ } else if (needsCellCheck) {
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, Phantom, origin, childEdge);
+ }
+
+ childEdge.setUseKind(KnownCellUse);
+
+ StructureTransitionData* transitionData = 0;
+ if (variant.kind() == PutByIdVariant::Transition) {
+ transitionData = m_graph.addStructureTransitionData(
+ StructureTransitionData(structure, variant.newStructure()));
+
+ if (node->op() == PutById) {
+ if (!structure->storedPrototype().isNull()) {
+ addStructureTransitionCheck(
+ origin, indexInBlock,
+ structure->storedPrototype().asCell());
+ }
+
+ m_graph.chains().addLazily(variant.structureChain());
+
+ for (unsigned i = 0; i < variant.structureChain()->size(); ++i) {
+ JSValue prototype = variant.structureChain()->at(i)->storedPrototype();
+ if (prototype.isNull())
+ continue;
+ ASSERT(prototype.isCell());
+ addStructureTransitionCheck(
+ origin, indexInBlock, prototype.asCell());
+ }
+ }
+ }
+
+ Edge propertyStorage;
+
+ if (isInlineOffset(variant.offset()))
+ propertyStorage = childEdge;
+ else if (
+ variant.kind() == PutByIdVariant::Replace
+ || structure->outOfLineCapacity() == variant.newStructure()->outOfLineCapacity()) {
+ propertyStorage = Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, origin, childEdge));
+ } else if (!structure->outOfLineCapacity()) {
+ ASSERT(variant.newStructure()->outOfLineCapacity());
+ ASSERT(!isInlineOffset(variant.offset()));
+ Node* allocatePropertyStorage = m_insertionSet.insertNode(
+ indexInBlock, SpecNone, AllocatePropertyStorage,
+ origin, OpInfo(transitionData), childEdge);
+ m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
+ propertyStorage = Edge(allocatePropertyStorage);
+ } else {
+ ASSERT(structure->outOfLineCapacity());
+ ASSERT(variant.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
+ ASSERT(!isInlineOffset(variant.offset()));
+
+ Node* reallocatePropertyStorage = m_insertionSet.insertNode(
+ indexInBlock, SpecNone, ReallocatePropertyStorage, origin,
+ OpInfo(transitionData), childEdge,
+ Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, origin, childEdge)));
+ m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
+ propertyStorage = Edge(reallocatePropertyStorage);
+ }
+
+ if (variant.kind() == PutByIdVariant::Transition) {
+ Node* putStructure = m_graph.addNode(SpecNone, PutStructure, origin, OpInfo(transitionData), childEdge);
+ m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse));
+ m_insertionSet.insert(indexInBlock, putStructure);
+ }
+
+ node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, ConditionalStoreBarrier, origin,
+ Edge(node->child2().node(), KnownCellUse),
+ Edge(node->child3().node(), UntypedUse));
+
+ StorageAccessData storageAccessData;
+ storageAccessData.offset = variant.offset();
+ storageAccessData.identifierNumber = identifierNumber;
+ m_graph.m_storageAccessData.append(storageAccessData);
+ }
+
void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell)
{
Node* weakConstant = m_insertionSet.insertNode(
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 06fd411..0e55504 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -891,6 +891,12 @@
break;
}
+ case MultiPutByOffset: {
+ fixEdge<CellUse>(node->child1());
+ insertStoreBarrier(m_indexInBlock, node->child1(), node->child2());
+ break;
+ }
+
case InstanceOf: {
// FIXME: This appears broken: CheckHasInstance already does an unconditional cell
// check. https://bugs.webkit.org/show_bug.cgi?id=107479
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp
index e05e6fa..ee6a9d6 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp
+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp
@@ -251,6 +251,12 @@
for (unsigned i = 0; i < data.variants.size(); ++i)
out.print(comma, inContext(data.variants[i], context));
}
+ if (node->hasMultiPutByOffsetData()) {
+ MultiPutByOffsetData& data = node->multiPutByOffsetData();
+ out.print(comma, "id", data.identifierNumber, "{", identifiers()[data.identifierNumber], "}");
+ for (unsigned i = 0; i < data.variants.size(); ++i)
+ out.print(comma, inContext(data.variants[i], context));
+ }
ASSERT(node->hasVariableAccessData(*this) == node->hasLocal(*this));
if (node->hasVariableAccessData(*this)) {
VariableAccessData* variableAccessData = node->tryGetVariableAccessData();
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index f394530..d0914d1 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -830,6 +830,7 @@
Bag<BranchData> m_branchData;
Bag<SwitchData> m_switchData;
Bag<MultiGetByOffsetData> m_multiGetByOffsetData;
+ Bag<MultiPutByOffsetData> m_multiPutByOffsetData;
Vector<InlineVariableData, 4> m_inlineVariableData;
OwnPtr<InlineCallFrameSet> m_inlineCallFrames;
HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>> m_bytecodeLiveness;
diff --git a/Source/JavaScriptCore/dfg/DFGNode.cpp b/Source/JavaScriptCore/dfg/DFGNode.cpp
index 3c15e67..8a8e692 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.cpp
+++ b/Source/JavaScriptCore/dfg/DFGNode.cpp
@@ -34,6 +34,30 @@
namespace JSC { namespace DFG {
+bool MultiPutByOffsetData::writesStructures() const
+{
+ for (unsigned i = variants.size(); i--;) {
+ if (variants[i].kind() == PutByIdVariant::Transition)
+ return true;
+ }
+ return false;
+}
+
+bool MultiPutByOffsetData::reallocatesStorage() const
+{
+ for (unsigned i = variants.size(); i--;) {
+ if (variants[i].kind() != PutByIdVariant::Transition)
+ continue;
+
+ if (variants[i].oldStructure()->outOfLineCapacity() ==
+ variants[i].newStructure()->outOfLineCapacity())
+ continue;
+
+ return true;
+ }
+ return false;
+}
+
void BranchTarget::dump(PrintStream& out) const
{
if (!block)
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 153b661..670f224 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -42,6 +42,7 @@
#include "GetByIdVariant.h"
#include "JSCJSValue.h"
#include "Operands.h"
+#include "PutByIdVariant.h"
#include "SpeculatedType.h"
#include "StructureSet.h"
#include "ValueProfile.h"
@@ -57,6 +58,14 @@
Vector<GetByIdVariant, 2> variants;
};
+struct MultiPutByOffsetData {
+ unsigned identifierNumber;
+ Vector<PutByIdVariant, 2> variants;
+
+ bool writesStructures() const;
+ bool reallocatesStorage() const;
+};
+
struct StructureTransitionData {
Structure* previousStructure;
Structure* newStructure;
@@ -460,7 +469,7 @@
void convertToPutByOffset(unsigned storageAccessDataIndex, Edge storage)
{
- ASSERT(m_op == PutById || m_op == PutByIdDirect);
+ ASSERT(m_op == PutById || m_op == PutByIdDirect || m_op == MultiPutByOffset);
m_opInfo = storageAccessDataIndex;
children.setChild3(children.child2());
children.setChild2(children.child1());
@@ -1091,6 +1100,16 @@
return *reinterpret_cast<MultiGetByOffsetData*>(m_opInfo);
}
+ bool hasMultiPutByOffsetData()
+ {
+ return op() == MultiPutByOffset;
+ }
+
+ MultiPutByOffsetData& multiPutByOffsetData()
+ {
+ return *reinterpret_cast<MultiPutByOffsetData*>(m_opInfo);
+ }
+
bool hasFunctionDeclIndex()
{
return op() == NewFunction
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 5aa1c3f..ce43d1a 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -175,6 +175,7 @@
macro(GetByOffset, NodeResultJS) \
macro(MultiGetByOffset, NodeResultJS) \
macro(PutByOffset, NodeMustGenerate) \
+ macro(MultiPutByOffset, NodeMustGenerate) \
macro(GetArrayLength, NodeResultInt32) \
macro(GetTypedArrayByteOffset, NodeResultInt32) \
macro(GetScope, NodeResultJS) \
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 3b694ae..28c80e4 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -552,6 +552,7 @@
case PutById:
case PutByIdDirect:
case PutByOffset:
+ case MultiPutByOffset:
case DFG::Jump:
case Branch:
case Switch:
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index 6c7df0b..1bc1fbc 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -252,6 +252,7 @@
case ConstantStoragePointer:
case Check:
case MultiGetByOffset:
+ case MultiPutByOffset:
return true;
case GetByVal:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 67472e6..57db500 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4689,6 +4689,7 @@
case CheckInBounds:
case ArithIMul:
case MultiGetByOffset:
+ case MultiPutByOffset:
RELEASE_ASSERT_NOT_REACHED();
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 7ee9871..8cfc700 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -5006,6 +5006,7 @@
case CheckInBounds:
case ArithIMul:
case MultiGetByOffset:
+ case MultiPutByOffset:
RELEASE_ASSERT_NOT_REACHED();
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
index fe56b5b..ad509cf 100644
--- a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
@@ -246,6 +246,7 @@
case HardPhantom:
case MovHint:
case MultiGetByOffset:
+ case MultiPutByOffset:
// Don't count these uses.
break;
@@ -346,6 +347,7 @@
case HardPhantom:
case MovHint:
case MultiGetByOffset:
+ case MultiPutByOffset:
// Don't count these uses.
break;
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index a6cebbe..8f6666a 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -138,6 +138,7 @@
case GetById:
case ToThis:
case MultiGetByOffset:
+ case MultiPutByOffset:
case ToPrimitive:
// These are OK.
break;
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index 965eaf7..eef3bbd 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -474,6 +474,9 @@
case PutByOffset:
compilePutByOffset();
break;
+ case MultiPutByOffset:
+ compileMultiPutByOffset();
+ break;
case GetGlobalVar:
compileGetGlobalVar();
break;
@@ -1656,8 +1659,8 @@
{
m_ftlState.jitCode->common.notifyCompilingStructureTransition(m_graph.m_plan, codeBlock(), m_node);
- m_out.store64(
- m_out.constIntPtr(m_node->structureTransitionData().newStructure),
+ m_out.storePtr(
+ weakPointer(m_node->structureTransitionData().newStructure),
lowCell(m_node->child1()), m_heaps.JSCell_structure);
}
@@ -2708,105 +2711,22 @@
void compileAllocatePropertyStorage()
{
StructureTransitionData& data = m_node->structureTransitionData();
-
LValue object = lowCell(m_node->child1());
- if (data.previousStructure->couldHaveIndexingHeader()) {
- setStorage(vmCall(
- m_out.operation(
- operationReallocateButterflyToHavePropertyStorageWithInitialCapacity),
- m_callFrame, object));
- return;
- }
-
- LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("AllocatePropertyStorage slow path"));
- LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("AllocatePropertyStorage continuation"));
-
- LBasicBlock lastNext = m_out.insertNewBlocksBefore(slowPath);
-
- LValue endOfStorage = allocateBasicStorageAndGetEnd(
- m_out.constIntPtr(initialOutOfLineCapacity * sizeof(JSValue)), slowPath);
-
- ValueFromBlock fastButterfly = m_out.anchor(
- m_out.add(m_out.constIntPtr(sizeof(IndexingHeader)), endOfStorage));
-
- m_out.jump(continuation);
-
- m_out.appendTo(slowPath, continuation);
-
- ValueFromBlock slowButterfly = m_out.anchor(vmCall(
- m_out.operation(operationAllocatePropertyStorageWithInitialCapacity), m_callFrame));
-
- m_out.jump(continuation);
-
- m_out.appendTo(continuation, lastNext);
-
- LValue result = m_out.phi(m_out.intPtr, fastButterfly, slowButterfly);
- m_out.storePtr(result, object, m_heaps.JSObject_butterfly);
-
- setStorage(result);
+ setStorage(allocatePropertyStorage(object, data.previousStructure));
}
void compileReallocatePropertyStorage()
{
StructureTransitionData& data = m_node->structureTransitionData();
-
- Structure* previous = data.previousStructure;
LValue object = lowCell(m_node->child1());
-
- size_t oldSize = previous->outOfLineCapacity() * sizeof(JSValue);
- size_t newSize = oldSize * outOfLineGrowthFactor;
-
- ASSERT(newSize == data.newStructure->outOfLineCapacity() * sizeof(JSValue));
+ LValue oldStorage = lowStorage(m_node->child2());
- if (previous->couldHaveIndexingHeader()) {
- LValue newAllocSize = m_out.constInt64(newSize / sizeof(JSValue));
- LValue result = vmCall(m_out.operation(operationReallocateButterflyToGrowPropertyStorage), m_callFrame, object, newAllocSize);
- setStorage(result);
- return;
- }
-
- LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("ReallocatePropertyStorage slow path"));
- LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("ReallocatePropertyStorage continuation"));
- LBasicBlock lastNext = m_out.insertNewBlocksBefore(slowPath);
-
- LValue endOfStorage =
- allocateBasicStorageAndGetEnd(m_out.constIntPtr(newSize), slowPath);
-
- ValueFromBlock fastButterfly = m_out.anchor(m_out.add(m_out.constIntPtr(sizeof(IndexingHeader)), endOfStorage));
-
- m_out.jump(continuation);
-
- m_out.appendTo(slowPath, continuation);
-
- LValue newAllocSize = m_out.constInt64(newSize / sizeof(JSValue));
-
- LValue storageLocation = vmCall(m_out.operation(operationAllocatePropertyStorage), m_callFrame, newAllocSize);
-
- ValueFromBlock slowButterfly = m_out.anchor(storageLocation);
-
- m_out.jump(continuation);
-
- m_out.appendTo(continuation, lastNext);
-
- LValue result = m_out.phi(m_out.intPtr, fastButterfly, slowButterfly);
- LValue oldStorage = m_out.loadPtr(object, m_heaps.JSObject_butterfly);
-
- ptrdiff_t headerSize = -sizeof(JSValue) - sizeof(void *);
- ptrdiff_t endStorage = headerSize - static_cast<ptrdiff_t>(oldSize);
-
- for (ptrdiff_t offset = headerSize; offset > endStorage; offset -= sizeof(void*)) {
- LValue loaded =
- m_out.loadPtr(m_out.address(m_heaps.properties.atAnyNumber(), oldStorage, offset));
- m_out.storePtr(loaded, m_out.address(m_heaps.properties.atAnyNumber(), result, offset));
- }
-
- m_out.storePtr(result, m_out.address(object, m_heaps.JSObject_butterfly));
-
- setStorage(result);
+ setStorage(
+ reallocatePropertyStorage(
+ object, oldStorage, data.previousStructure, data.newStructure));
}
-
void compileToString()
{
switch (m_node->child1().useKind()) {
@@ -3140,12 +3060,8 @@
StorageAccessData& data =
m_graph.m_storageAccessData[m_node->storageAccessDataIndex()];
- setJSValue(
- m_out.load64(
- m_out.address(
- m_heaps.properties[data.identifierNumber],
- lowStorage(m_node->child1()),
- offsetRelativeToBase(data.offset))));
+ setJSValue(loadProperty(
+ lowStorage(m_node->child1()), data.identifierNumber, data.offset));
}
void compileMultiGetByOffset()
@@ -3189,11 +3105,7 @@
propertyBase = base;
if (!isInlineOffset(variant.offset()))
propertyBase = m_out.loadPtr(propertyBase, m_heaps.JSObject_butterfly);
- result = m_out.load64(
- m_out.address(
- m_heaps.properties[data.identifierNumber],
- propertyBase,
- offsetRelativeToBase(variant.offset())));
+ result = loadProperty(propertyBase, data.identifierNumber, variant.offset());
}
results.append(m_out.anchor(result));
@@ -3213,12 +3125,66 @@
StorageAccessData& data =
m_graph.m_storageAccessData[m_node->storageAccessDataIndex()];
- m_out.store64(
+ storeProperty(
lowJSValue(m_node->child3()),
- m_out.address(
- m_heaps.properties[data.identifierNumber],
- lowStorage(m_node->child1()),
- offsetRelativeToBase(data.offset)));
+ lowStorage(m_node->child1()), data.identifierNumber, data.offset);
+ }
+
+ void compileMultiPutByOffset()
+ {
+ LValue base = lowCell(m_node->child1());
+ LValue value = lowJSValue(m_node->child2());
+
+ MultiPutByOffsetData& data = m_node->multiPutByOffsetData();
+
+ Vector<LBasicBlock, 2> blocks(data.variants.size());
+ for (unsigned i = data.variants.size(); i--;)
+ blocks[i] = FTL_NEW_BLOCK(m_out, ("MultiPutByOffset case ", i));
+ LBasicBlock exit = FTL_NEW_BLOCK(m_out, ("MultiPutByOffset fail"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("MultiPutByOffset continuation"));
+
+ Vector<SwitchCase, 2> cases;
+ for (unsigned i = data.variants.size(); i--;) {
+ PutByIdVariant variant = data.variants[i];
+ cases.append(
+ SwitchCase(weakPointer(variant.oldStructure()), blocks[i], Weight(1)));
+ }
+ m_out.switchInstruction(
+ m_out.loadPtr(base, m_heaps.JSCell_structure), cases, exit, Weight(0));
+
+ LBasicBlock lastNext = m_out.m_nextBlock;
+
+ for (unsigned i = data.variants.size(); i--;) {
+ m_out.appendTo(blocks[i], i + 1 < data.variants.size() ? blocks[i + 1] : exit);
+
+ PutByIdVariant variant = data.variants[i];
+
+ LValue storage;
+ if (variant.kind() == PutByIdVariant::Replace) {
+ if (isInlineOffset(variant.offset()))
+ storage = base;
+ else
+ storage = m_out.loadPtr(base, m_heaps.JSObject_butterfly);
+ } else {
+ m_graph.m_plan.transitions.addLazily(
+ codeBlock(), m_node->origin.semantic.codeOriginOwner(),
+ variant.oldStructure(), variant.newStructure());
+
+ storage = storageForTransition(
+ base, variant.offset(), variant.oldStructure(), variant.newStructure());
+ m_out.storePtr(
+ weakPointer(variant.newStructure()), base, m_heaps.JSCell_structure);
+ }
+
+ storeProperty(value, storage, data.identifierNumber, variant.offset());
+ m_out.jump(continuation);
+ }
+
+ m_out.appendTo(exit, continuation);
+ terminate(BadCache);
+ m_out.unreachable();
+
+ m_out.appendTo(continuation, lastNext);
}
void compileGetGlobalVar()
@@ -3748,6 +3714,137 @@
return m_out.booleanFalse;
}
+ LValue loadProperty(LValue storage, unsigned identifierNumber, PropertyOffset offset)
+ {
+ return m_out.load64(addressOfProperty(storage, identifierNumber, offset));
+ }
+
+ void storeProperty(
+ LValue value, LValue storage, unsigned identifierNumber, PropertyOffset offset)
+ {
+ m_out.store64(value, addressOfProperty(storage, identifierNumber, offset));
+ }
+
+ TypedPointer addressOfProperty(
+ LValue storage, unsigned identifierNumber, PropertyOffset offset)
+ {
+ return m_out.address(
+ m_heaps.properties[identifierNumber], storage, offsetRelativeToBase(offset));
+ }
+
+ LValue storageForTransition(
+ LValue object, PropertyOffset offset,
+ Structure* previousStructure, Structure* nextStructure)
+ {
+ if (isInlineOffset(offset))
+ return object;
+
+ if (previousStructure->outOfLineCapacity() == nextStructure->outOfLineCapacity())
+ return m_out.loadPtr(object, m_heaps.JSObject_butterfly);
+
+ LValue result;
+ if (!previousStructure->outOfLineCapacity())
+ result = allocatePropertyStorage(object, previousStructure);
+ else {
+ result = reallocatePropertyStorage(
+ object, m_out.loadPtr(object, m_heaps.JSObject_butterfly),
+ previousStructure, nextStructure);
+ }
+
+ emitStoreBarrier(object);
+
+ return result;
+ }
+
+ LValue allocatePropertyStorage(LValue object, Structure* previousStructure)
+ {
+ if (previousStructure->couldHaveIndexingHeader()) {
+ return vmCall(
+ m_out.operation(
+ operationReallocateButterflyToHavePropertyStorageWithInitialCapacity),
+ m_callFrame, object);
+ }
+
+ LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("allocatePropertyStorage slow path"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("allocatePropertyStorage continuation"));
+
+ LBasicBlock lastNext = m_out.insertNewBlocksBefore(slowPath);
+
+ LValue endOfStorage = allocateBasicStorageAndGetEnd(
+ m_out.constIntPtr(initialOutOfLineCapacity * sizeof(JSValue)), slowPath);
+
+ ValueFromBlock fastButterfly = m_out.anchor(
+ m_out.add(m_out.constIntPtr(sizeof(IndexingHeader)), endOfStorage));
+
+ m_out.jump(continuation);
+
+ m_out.appendTo(slowPath, continuation);
+
+ ValueFromBlock slowButterfly = m_out.anchor(vmCall(
+ m_out.operation(operationAllocatePropertyStorageWithInitialCapacity), m_callFrame));
+
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+
+ LValue result = m_out.phi(m_out.intPtr, fastButterfly, slowButterfly);
+ m_out.storePtr(result, object, m_heaps.JSObject_butterfly);
+
+ return result;
+ }
+
+ LValue reallocatePropertyStorage(
+ LValue object, LValue oldStorage, Structure* previous, Structure* next)
+ {
+ size_t oldSize = previous->outOfLineCapacity() * sizeof(JSValue);
+ size_t newSize = oldSize * outOfLineGrowthFactor;
+
+ ASSERT_UNUSED(next, newSize == next->outOfLineCapacity() * sizeof(JSValue));
+
+ if (previous->couldHaveIndexingHeader()) {
+ LValue newAllocSize = m_out.constInt64(newSize / sizeof(JSValue));
+ return vmCall(m_out.operation(operationReallocateButterflyToGrowPropertyStorage), m_callFrame, object, newAllocSize);
+ }
+
+ LBasicBlock slowPath = FTL_NEW_BLOCK(m_out, ("reallocatePropertyStorage slow path"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("reallocatePropertyStorage continuation"));
+ LBasicBlock lastNext = m_out.insertNewBlocksBefore(slowPath);
+
+ LValue endOfStorage =
+ allocateBasicStorageAndGetEnd(m_out.constIntPtr(newSize), slowPath);
+
+ ValueFromBlock fastButterfly = m_out.anchor(m_out.add(m_out.constIntPtr(sizeof(IndexingHeader)), endOfStorage));
+
+ m_out.jump(continuation);
+
+ m_out.appendTo(slowPath, continuation);
+
+ LValue newAllocSize = m_out.constInt64(newSize / sizeof(JSValue));
+
+ LValue storageLocation = vmCall(m_out.operation(operationAllocatePropertyStorage), m_callFrame, newAllocSize);
+
+ ValueFromBlock slowButterfly = m_out.anchor(storageLocation);
+
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+
+ LValue result = m_out.phi(m_out.intPtr, fastButterfly, slowButterfly);
+
+ ptrdiff_t headerSize = -sizeof(JSValue) - sizeof(void *);
+ ptrdiff_t endStorage = headerSize - static_cast<ptrdiff_t>(oldSize);
+
+ for (ptrdiff_t offset = headerSize; offset > endStorage; offset -= sizeof(void*)) {
+ LValue loaded =
+ m_out.loadPtr(m_out.address(m_heaps.properties.atAnyNumber(), oldStorage, offset));
+ m_out.storePtr(loaded, m_out.address(m_heaps.properties.atAnyNumber(), result, offset));
+ }
+
+ m_out.storePtr(result, m_out.address(object, m_heaps.JSObject_butterfly));
+
+ return result;
+ }
+
LValue getById(LValue base)
{
StringImpl* uid = m_graph.identifiers()[m_node->identifierNumber()];
@@ -5185,7 +5282,7 @@
return m_out.load8(m_out.baseIndex(m_heaps.MarkedBlock_markBits, markedBlock, markByteIndex, ScaleOne, MarkedBlock::offsetOfMarks()));
}
- void emitStoreBarrier(LValue base, LValue value, Edge& valueEdge)
+ void emitStoreBarrier(LValue base, LValue value, Edge valueEdge)
{
#if ENABLE(GGC)
LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("Store barrier continuation"));
diff --git a/Source/JavaScriptCore/tests/stress/fold-multi-put-by-offset-to-put-by-offset.js b/Source/JavaScriptCore/tests/stress/fold-multi-put-by-offset-to-put-by-offset.js
new file mode 100644
index 0000000..8da82cf
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/fold-multi-put-by-offset-to-put-by-offset.js
@@ -0,0 +1,40 @@
+function foo(o) {
+ o.f = (o.f | 0) + 42;
+}
+
+function callFoo(o) {
+ return foo(o);
+}
+
+noInline(callFoo);
+
+for (var i = 0; i < 10000; ++i) {
+ var object;
+ if ((i % 3) == 0)
+ object = {g:3};
+ else if ((i % 3) == 1)
+ object = {f:1, g:2};
+ else if ((i % 3) == 2)
+ object = {g:1, f:2};
+ callFoo(object);
+ if (object.f != 42 + (i % 3))
+ throw "Error: bad result for i = " + i + ": " + object.f;
+}
+
+function bar(o) {
+ var result = o.f;
+ foo(o);
+ return result;
+}
+
+noInline(bar);
+
+for (var i = 0; i < 100000; ++i) {
+ var o = {f:42};
+ var result = bar(o);
+ if (result != 42)
+ throw "Error: bad result at end: " + result;
+ if (o.f != 42 + 42)
+ throw "Error: bad o.f: " + o.f;
+}
+
diff --git a/Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-butterfly-cse.js b/Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-butterfly-cse.js
new file mode 100644
index 0000000..d283f3f
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-butterfly-cse.js
@@ -0,0 +1,102 @@
+var foos = [
+ function(o) { o[0] = 5; o.ff = 42; o[0] = 6; },
+ function(o) { o[0] = 5; o.ff = 42; o[0] = 6; },
+ function(o) { o[0] = 5; o.ff = 42; o[0] = 6; },
+ function(o) { o[0] = 5; o.ff = 42; o[0] = 6; },
+ function(o) { o[0] = 5; o.ff = 42; o[0] = 6; },
+ function(o) { o[0] = 5; o.ff = 42; o[0] = 6; },
+ function(o) { o[0] = 5; o.ff = 42; o[0] = 6; },
+ function(o) { o[0] = 5; o.ff = 42; o[0] = 6; }
+];
+
+if (foos.length != 8)
+ throw "Error";
+
+function bar(o, n) {
+ if (n == 0)
+ return;
+ o.na = 1;
+ if (n == 1)
+ return;
+ o.nb = 2;
+ if (n == 2)
+ return;
+ o.nc = 3;
+ if (n == 3)
+ return;
+ o.nd = 4;
+ if (n == 4)
+ return;
+ o.ne = 5;
+ if (n == 5)
+ return;
+ o.nf = 6;
+ if (n == 6)
+ return;
+ o.ng = 7;
+ if (n == 7)
+ return;
+ o.nh = 8;
+}
+
+function baz(o, n) {
+ if (n == 0)
+ return;
+ if (o.na != 1)
+ throw "Memory corruption; have o.na = " + o.na;
+ if (n == 1)
+ return;
+ if (o.nb != 2)
+ throw "Memory corruption";
+ if (n == 2)
+ return;
+ if (o.nc != 3)
+ throw "Memory corruption";
+ if (n == 3)
+ return;
+ if (o.nd != 4)
+ throw "Memory corruption";
+ if (n == 4)
+ return;
+ if (o.ne != 5)
+ throw "Memory corruption";
+ if (n == 5)
+ return;
+ if (o.nf != 6)
+ throw "Memory corruption";
+ if (n == 6)
+ return;
+ if (o.ng != 7)
+ throw "Memory corruption";
+ if (n == 7)
+ return;
+ if (o.nh != 8)
+ throw "Memory corruption";
+}
+
+for (var i = 0; i < 8; ++i)
+ noInline(foos[i]);
+noInline(bar);
+
+for (var i = 0; i < 100000; ++i) {
+ var o = {};
+ var p = {a:1, b:2, c:3, d:4, e:5, f:6};
+ o[0] = 0;
+ p[0] = 0;
+ bar(o, i % 8);
+ bar(p, i % 8);
+
+ foos[i % 8](o);
+ foos[i % 8](p);
+
+ if (o.ff != 42)
+ throw "Bad result in o: " + o.ff;
+ if (p.ff != 42)
+ throw "Bad result in o: " + p.ff;
+
+ if (p.a != 1 || p.b != 2 || p.c != 3 || p.d != 4 || p.e != 5 || p.f != 6)
+ throw "Memory corruption"
+ baz(o, i % 8);
+ baz(p, i % 8);
+}
+
diff --git a/Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-cases.js b/Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-cases.js
new file mode 100644
index 0000000..f8f386c
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-cases.js
@@ -0,0 +1,100 @@
+var foos = [
+ function(o) { o.ff = 42; },
+ function(o) { o.ff = 42; },
+ function(o) { o.ff = 42; },
+ function(o) { o.ff = 42; },
+ function(o) { o.ff = 42; },
+ function(o) { o.ff = 42; },
+ function(o) { o.ff = 42; },
+ function(o) { o.ff = 42; }
+];
+
+if (foos.length != 8)
+ throw "Error";
+
+function bar(o, n) {
+ if (n == 0)
+ return;
+ o.na = 1;
+ if (n == 1)
+ return;
+ o.nb = 2;
+ if (n == 2)
+ return;
+ o.nc = 3;
+ if (n == 3)
+ return;
+ o.nd = 4;
+ if (n == 4)
+ return;
+ o.ne = 5;
+ if (n == 5)
+ return;
+ o.nf = 6;
+ if (n == 6)
+ return;
+ o.ng = 7;
+ if (n == 7)
+ return;
+ o.nh = 8;
+}
+
+function baz(o, n) {
+ if (n == 0)
+ return;
+ if (o.na != 1)
+ throw "Memory corruption";
+ if (n == 1)
+ return;
+ if (o.nb != 2)
+ throw "Memory corruption";
+ if (n == 2)
+ return;
+ if (o.nc != 3)
+ throw "Memory corruption";
+ if (n == 3)
+ return;
+ if (o.nd != 4)
+ throw "Memory corruption";
+ if (n == 4)
+ return;
+ if (o.ne != 5)
+ throw "Memory corruption";
+ if (n == 5)
+ return;
+ if (o.nf != 6)
+ throw "Memory corruption";
+ if (n == 6)
+ return;
+ if (o.ng != 7)
+ throw "Memory corruption";
+ if (n == 7)
+ return;
+ if (o.nh != 8)
+ throw "Memory corruption";
+}
+
+for (var i = 0; i < 8; ++i)
+ noInline(foos[i]);
+noInline(bar);
+
+for (var i = 0; i < 100000; ++i) {
+ var o = {};
+ var p = {a:1, b:2, c:3, d:4, e:5, f:6};
+ bar(o, i % 8);
+ bar(p, i % 8);
+
+ foos[i % 8](o);
+ foos[i % 8](p);
+
+ if (o.ff != 42)
+ throw "Bad result in o: " + o.ff;
+ if (p.ff != 42)
+ throw "Bad result in o: " + p.ff;
+
+ if (p.a != 1 || p.b != 2 || p.c != 3 || p.d != 4 || p.e != 5 || p.f != 6)
+ throw "Memory corruption"
+ baz(o, i % 8);
+ baz(p, i % 8);
+}
+