FTL should do polymorphic PutById inlining
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 25 Feb 2014 02:02:50 +0000 (02:02 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 25 Feb 2014 02:02:50 +0000 (02:02 +0000)
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: https://svn.webkit.org/repository/webkit/trunk@164620 268f45cc-cd09-0410-ab3c-d52691b4dbfc

34 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/polymorphic-put-by-id-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/polymorphic-put-by-id.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/polymorphic-put-by-id.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/GNUmakefile.list.am
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
Source/JavaScriptCore/bytecode/PutByIdStatus.h
Source/JavaScriptCore/bytecode/PutByIdVariant.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/PutByIdVariant.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGGraph.h
Source/JavaScriptCore/dfg/DFGNode.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/tests/stress/fold-multi-put-by-offset-to-put-by-offset.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-butterfly-cse.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-cases.js [new file with mode: 0644]

index 06dabdd..dea2576 100644 (file)
@@ -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 (file)
index 0000000..182d079
--- /dev/null
@@ -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 (file)
index 0000000..e2df520
--- /dev/null
@@ -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 (file)
index 0000000..89dd793
--- /dev/null
@@ -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;
index 715e5d2..0db0cdc 100644 (file)
@@ -82,6 +82,7 @@ set(JavaScriptCore_SOURCES
     bytecode/PreciseJumpTargets.cpp
     bytecode/ProfiledCodeBlockJettisoningWatchpoint.cpp
     bytecode/PutByIdStatus.cpp
+    bytecode/PutByIdVariant.cpp
     bytecode/ReduceWhitespace.cpp
     bytecode/SamplingTool.cpp
     bytecode/SpecialPointer.cpp
index 2190df2..55d74ee 100644 (file)
@@ -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
index f0c86cb..baa0ec5 100644 (file)
@@ -182,6 +182,8 @@ javascriptcore_sources += \
        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 \
index 2ada47f..261cba9 100644 (file)
     <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" />
     <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" />
index bfaf51a..293bd01 100644 (file)
                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, ); }; };
                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>"; };
                                0FC97F32182020D7002C9B26 /* ProfiledCodeBlockJettisoningWatchpoint.h */,
                                0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */,
                                0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */,
+                               0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */,
+                               0F93B4A818B92C4D00178A3F /* PutByIdVariant.h */,
                                0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */,
                                0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */,
                                0FF60AC016740F8100029779 /* ReduceWhitespace.h */,
                                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 */,
                                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 */,
index b1e1538..a06fb1e 100644 (file)
 #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 @@ PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
 
     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 @@ PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
     
     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 @@ PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& m
 #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 @@ PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& m
     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 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJITLocker&, Code
         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 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJITLocker&, Code
             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 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(const ConcurrentJITLocker&, Code
                     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 @@ PutByIdStatus PutByIdStatus::computeFor(VM& vm, JSGlobalObject* globalObject, St
             // 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 @@ PutByIdStatus PutByIdStatus::computeFor(VM& vm, JSGlobalObject* globalObject, St
     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
index 1684a74..4bb8b98 100644 (file)
@@ -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 @@ public:
     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 @@ public:
     
     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 @@ private:
     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 (file)
index 0000000..f83c102
--- /dev/null
@@ -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 (file)
index 0000000..eba95e8
--- /dev/null
@@ -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
+
index 8fd5453..32ed82f 100644 (file)
@@ -1640,7 +1640,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         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 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     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 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 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);
index 1804b15..b5b2521 100644 (file)
@@ -179,7 +179,12 @@ private:
     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 @@ Node* ByteCodeParser::handlePutByOffset(Node* base, unsigned identifier, Propert
     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 @@ void ByteCodeParser::handleGetById(
             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 @@ void ByteCodeParser::handleGetById(
                 
     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 @@ void ByteCodeParser::handleGetById(
         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 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 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 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             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.
index 50c19c4..04a651f 100644 (file)
@@ -495,7 +495,12 @@ private:
             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 @@ private:
                 // 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 @@ private:
             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 @@ private:
                     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 @@ private:
                     return 0;
                 }
                 break;
-                    
+                
+            case MultiPutByOffset:
+                if (node->multiPutByOffsetData().identifierNumber == identifierNumber)
+                    return 0;
+                break;
+                
             case PutByValDirect:
             case PutByVal:
             case PutByValAlias:
@@ -774,6 +798,11 @@ private:
                 // 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;
index 94f2b40..5aefcfa 100644 (file)
@@ -469,9 +469,23 @@ void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write
         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;
index 54f4575..44d4136 100644 (file)
@@ -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 @@ private:
                 }
                 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 @@ private:
                 Structure* structure = m_state.forNode(child).bestProvenStructure();
                 if (!structure)
                     break;
-                
+
                 GetByIdStatus status = GetByIdStatus::computeFor(
                     vm(), structure, m_graph.identifiers()[identifierNumber]);
                 
@@ -215,9 +236,6 @@ private:
                 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 @@ private:
                     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 @@ private:
         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(
index 06fd411..0e55504 100644 (file)
@@ -891,6 +891,12 @@ private:
             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
index e05e6fa..ee6a9d6 100644 (file)
@@ -251,6 +251,12 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
         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();
index f394530..d0914d1 100644 (file)
@@ -830,6 +830,7 @@ public:
     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;
index 3c15e67..8a8e692 100644 (file)
 
 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)
index 153b661..670f224 100644 (file)
@@ -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 @@ struct MultiGetByOffsetData {
     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 @@ struct Node {
     
     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 @@ struct Node {
         return *reinterpret_cast<MultiGetByOffsetData*>(m_opInfo);
     }
     
+    bool hasMultiPutByOffsetData()
+    {
+        return op() == MultiPutByOffset;
+    }
+    
+    MultiPutByOffsetData& multiPutByOffsetData()
+    {
+        return *reinterpret_cast<MultiPutByOffsetData*>(m_opInfo);
+    }
+    
     bool hasFunctionDeclIndex()
     {
         return op() == NewFunction
index 5aa1c3f..ce43d1a 100644 (file)
@@ -175,6 +175,7 @@ namespace JSC { namespace DFG {
     macro(GetByOffset, NodeResultJS) \
     macro(MultiGetByOffset, NodeResultJS) \
     macro(PutByOffset, NodeMustGenerate) \
+    macro(MultiPutByOffset, NodeMustGenerate) \
     macro(GetArrayLength, NodeResultInt32) \
     macro(GetTypedArrayByteOffset, NodeResultInt32) \
     macro(GetScope, NodeResultJS) \
index 3b694ae..28c80e4 100644 (file)
@@ -552,6 +552,7 @@ private:
         case PutById:
         case PutByIdDirect:
         case PutByOffset:
+        case MultiPutByOffset:
         case DFG::Jump:
         case Branch:
         case Switch:
index 6c7df0b..1bc1fbc 100644 (file)
@@ -252,6 +252,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case ConstantStoragePointer:
     case Check:
     case MultiGetByOffset:
+    case MultiPutByOffset:
         return true;
         
     case GetByVal:
index 67472e6..57db500 100644 (file)
@@ -4689,6 +4689,7 @@ void SpeculativeJIT::compile(Node* node)
     case CheckInBounds:
     case ArithIMul:
     case MultiGetByOffset:
+    case MultiPutByOffset:
         RELEASE_ASSERT_NOT_REACHED();
         break;
     }
index 7ee9871..8cfc700 100644 (file)
@@ -5006,6 +5006,7 @@ void SpeculativeJIT::compile(Node* node)
     case CheckInBounds:
     case ArithIMul:
     case MultiGetByOffset:
+    case MultiPutByOffset:
         RELEASE_ASSERT_NOT_REACHED();
         break;
     }
index fe56b5b..ad509cf 100644 (file)
@@ -246,6 +246,7 @@ private:
                 case HardPhantom:
                 case MovHint:
                 case MultiGetByOffset:
+                case MultiPutByOffset:
                     // Don't count these uses.
                     break;
                     
@@ -346,6 +347,7 @@ private:
                 case HardPhantom:
                 case MovHint:
                 case MultiGetByOffset:
+                case MultiPutByOffset:
                     // Don't count these uses.
                     break;
                     
index a6cebbe..8f6666a 100644 (file)
@@ -138,6 +138,7 @@ inline CapabilityLevel canCompile(Node* node)
     case GetById:
     case ToThis:
     case MultiGetByOffset:
+    case MultiPutByOffset:
     case ToPrimitive:
         // These are OK.
         break;
index 965eaf7..eef3bbd 100644 (file)
@@ -474,6 +474,9 @@ private:
         case PutByOffset:
             compilePutByOffset();
             break;
+        case MultiPutByOffset:
+            compileMultiPutByOffset();
+            break;
         case GetGlobalVar:
             compileGetGlobalVar();
             break;
@@ -1656,8 +1659,8 @@ private:
     {
         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 @@ private:
     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));
-        
-        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;
-        }
+        LValue oldStorage = lowStorage(m_node->child2());
         
-        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 @@ private:
         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 @@ private:
                     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 @@ private:
         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 @@ private:
         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 @@ private:
         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 (file)
index 0000000..8da82cf
--- /dev/null
@@ -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 (file)
index 0000000..d283f3f
--- /dev/null
@@ -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 (file)
index 0000000..f8f386c
--- /dev/null
@@ -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);
+}
+