WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
the patch
blah.patch (text/plain), 94.86 KB, created by
Filip Pizlo
on 2014-02-24 09:23:20 PST
(
hide
)
Description:
the patch
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2014-02-24 09:23:20 PST
Size:
94.86 KB
patch
obsolete
>Index: Source/JavaScriptCore/ChangeLog >=================================================================== >--- Source/JavaScriptCore/ChangeLog (revision 164575) >+++ Source/JavaScriptCore/ChangeLog (working copy) >@@ -1,3 +1,121 @@ >+2014-02-23 Filip Pizlo <fpizlo@apple.com> >+ >+ FTL should do polymorphic PutById inlining >+ https://bugs.webkit.org/show_bug.cgi?id=129210 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ 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/multi-put-by-offset-reallocation-cases.js: Added. >+ (foos): >+ (bar): >+ (baz): >+ (.o): >+ > 2014-02-23 Sam Weinig <sam@webkit.org> > > Update FeatureDefines.xcconfig >Index: Source/JavaScriptCore/CMakeLists.txt >=================================================================== >--- Source/JavaScriptCore/CMakeLists.txt (revision 164575) >+++ Source/JavaScriptCore/CMakeLists.txt (working copy) >@@ -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: Source/JavaScriptCore/GNUmakefile.list.am >=================================================================== >--- Source/JavaScriptCore/GNUmakefile.list.am (revision 164575) >+++ Source/JavaScriptCore/GNUmakefile.list.am (working copy) >@@ -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: Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (revision 164575) >+++ Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (working copy) >@@ -337,6 +337,7 @@ > <ClCompile Include="..\bytecode\ProfiledCodeBlockJettisoningWatchpoint.cpp" /> > <ClCompile Include="..\bytecode\PreciseJumpTargets.cpp" /> > <ClCompile Include="..\bytecode\PutByIdStatus.cpp" /> >+ <ClCompile Include="..\bytecode\PutByIdVariant.cpp" /> > <ClCompile Include="..\bytecode\ReduceWhitespace.cpp" /> > <ClCompile Include="..\bytecode\SamplingTool.cpp" /> > <ClCompile Include="..\bytecode\SpecialPointer.cpp" /> >@@ -858,6 +859,7 @@ > <ClInclude Include="..\bytecode\ProfiledCodeBlockJettisoningWatchpoint.h" /> > <ClInclude Include="..\bytecode\PreciseJumpTargets.h" /> > <ClInclude Include="..\bytecode\PutByIdStatus.h" /> >+ <ClInclude Include="..\bytecode\PutByIdVariant.h" /> > <ClInclude Include="..\bytecode\PutKind.h" /> > <ClInclude Include="..\bytecode\ReduceWhitespace.h" /> > <ClInclude Include="..\bytecode\SamplingTool.h" /> >Index: Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (revision 164575) >+++ Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (working copy) >@@ -357,6 +357,8 @@ > 0F9332A314CA7DD70085F3C6 /* PutByIdStatus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */; }; > 0F9332A414CA7DD90085F3C6 /* PutByIdStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F9332A514CA7DDD0085F3C6 /* StructureSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93329B14CA7DC10085F3C6 /* StructureSet.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0F93B4A918B92C4D00178A3F /* PutByIdVariant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */; }; >+ 0F93B4AA18B92C4D00178A3F /* PutByIdVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F93B4A818B92C4D00178A3F /* PutByIdVariant.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F963B3813FC6FE90002D9B2 /* ValueProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F96EBB316676EF6008BADE3 /* CodeBlockWithJITType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F96EBB116676EF4008BADE3 /* CodeBlockWithJITType.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F9749711687ADE400A4FF6A /* JSCellInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F97496F1687ADE200A4FF6A /* JSCellInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -1832,6 +1834,8 @@ > 0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PutByIdStatus.cpp; sourceTree = "<group>"; }; > 0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PutByIdStatus.h; sourceTree = "<group>"; }; > 0F93329B14CA7DC10085F3C6 /* StructureSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StructureSet.h; sourceTree = "<group>"; }; >+ 0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PutByIdVariant.cpp; sourceTree = "<group>"; }; >+ 0F93B4A818B92C4D00178A3F /* PutByIdVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PutByIdVariant.h; sourceTree = "<group>"; }; > 0F963B3613FC6FDE0002D9B2 /* ValueProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValueProfile.h; sourceTree = "<group>"; }; > 0F96EBB116676EF4008BADE3 /* CodeBlockWithJITType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeBlockWithJITType.h; sourceTree = "<group>"; }; > 0F97496F1687ADE200A4FF6A /* JSCellInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCellInlines.h; sourceTree = "<group>"; }; >@@ -4585,6 +4589,8 @@ > 0FC97F32182020D7002C9B26 /* ProfiledCodeBlockJettisoningWatchpoint.h */, > 0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */, > 0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */, >+ 0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */, >+ 0F93B4A818B92C4D00178A3F /* PutByIdVariant.h */, > 0F9FC8C114E1B5FB00D52AE0 /* PutKind.h */, > 0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */, > 0FF60AC016740F8100029779 /* ReduceWhitespace.h */, >@@ -5387,6 +5393,7 @@ > 860161E40F3A83C100F84710 /* MacroAssemblerX86.h in Headers */, > 860161E50F3A83C100F84710 /* MacroAssemblerX86_64.h in Headers */, > 860161E60F3A83C100F84710 /* MacroAssemblerX86Common.h in Headers */, >+ 0F93B4AA18B92C4D00178A3F /* PutByIdVariant.h in Headers */, > A700873A17CBE85300C3E643 /* MapConstructor.h in Headers */, > A78507D717CBC6FD0011F6E7 /* MapData.h in Headers */, > A74DEF92182D991400522C22 /* MapIteratorConstructor.h in Headers */, >@@ -6174,6 +6181,7 @@ > 0FC20CB51852E2C600C9E954 /* DFGStrengthReductionPhase.cpp in Sources */, > 0F2FCCFE18A60070001A27F8 /* DFGThreadData.cpp in Sources */, > 0FC097A1146B28CA00CF2442 /* DFGThunks.cpp in Sources */, >+ 0F93B4A918B92C4D00178A3F /* PutByIdVariant.cpp in Sources */, > 0FD8A32717D51F5700CA2C40 /* DFGTierUpCheckInjectionPhase.cpp in Sources */, > 0FD8A32917D51F5700CA2C40 /* DFGToFTLDeferredCompilationCallback.cpp in Sources */, > 0FD8A32B17D51F5700CA2C40 /* DFGToFTLForOSREntryDeferredCompilationCallback.cpp in Sources */, >Index: Source/JavaScriptCore/bytecode/PutByIdStatus.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdStatus.cpp (revision 164575) >+++ Source/JavaScriptCore/bytecode/PutByIdStatus.cpp (working copy) >@@ -30,8 +30,10 @@ > #include "LLIntData.h" > #include "LowLevelInterpreter.h" > #include "JSCInlines.h" >+#include "PolymorphicPutByIdList.h" > #include "Structure.h" > #include "StructureChain.h" >+#include <wtf/ListDump.h> > > namespace JSC { > >@@ -56,15 +58,15 @@ PutByIdStatus PutByIdStatus::computeFrom > > 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::computeFrom > > 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( > #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( > 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::computeForS > 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::computeForS > 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::computeForS > 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( > // 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( > 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: Source/JavaScriptCore/bytecode/PutByIdStatus.h >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdStatus.h (revision 164575) >+++ Source/JavaScriptCore/bytecode/PutByIdStatus.h (working copy) >@@ -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 >Index: Source/JavaScriptCore/bytecode/PutByIdVariant.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdVariant.cpp (revision 0) >+++ Source/JavaScriptCore/bytecode/PutByIdVariant.cpp (working copy) >@@ -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 >+ >Index: Source/JavaScriptCore/bytecode/PutByIdVariant.h >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdVariant.h (revision 0) >+++ Source/JavaScriptCore/bytecode/PutByIdVariant.h (working copy) >@@ -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: Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (working copy) >@@ -1640,7 +1640,7 @@ bool AbstractInterpreter<AbstractStateTy > 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<AbstractStateTy > 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<AbstractStateTy > 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: Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (working copy) >@@ -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( > 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 > 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 > 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: Source/JavaScriptCore/dfg/DFGCSEPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGCSEPhase.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGCSEPhase.cpp (working copy) >@@ -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: >Index: Source/JavaScriptCore/dfg/DFGClobberize.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGClobberize.h (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGClobberize.h (working copy) >@@ -469,9 +469,23 @@ void clobberize(Graph& graph, Node* node > 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: Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (working copy) >@@ -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: { >@@ -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: Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (working copy) >@@ -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: Source/JavaScriptCore/dfg/DFGGraph.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGGraph.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGGraph.cpp (working copy) >@@ -251,6 +251,12 @@ void Graph::dump(PrintStream& out, const > 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: Source/JavaScriptCore/dfg/DFGGraph.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGGraph.h (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGGraph.h (working copy) >@@ -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: Source/JavaScriptCore/dfg/DFGNode.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNode.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGNode.cpp (working copy) >@@ -34,6 +34,30 @@ > > namespace JSC { namespace DFG { > >+bool MultiPutByOffsetData::writesStructures() const >+{ >+ for (unsigned i = variants.size(); i--;) { >+ if (variants[i].kind() == PutByIdVariant::Transition) >+ return true; >+ } >+ return false; >+} >+ >+bool MultiPutByOffsetData::reallocatesStorage() const >+{ >+ for (unsigned i = variants.size(); i--;) { >+ if (variants[i].kind() != PutByIdVariant::Transition) >+ continue; >+ >+ if (variants[i].oldStructure()->outOfLineCapacity() == >+ variants[i].newStructure()->outOfLineCapacity()) >+ continue; >+ >+ return true; >+ } >+ return false; >+} >+ > void BranchTarget::dump(PrintStream& out) const > { > if (!block) >Index: Source/JavaScriptCore/dfg/DFGNode.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNode.h (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGNode.h (working copy) >@@ -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: Source/JavaScriptCore/dfg/DFGNodeType.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNodeType.h (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGNodeType.h (working copy) >@@ -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: Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (working copy) >@@ -552,6 +552,7 @@ private: > case PutById: > case PutByIdDirect: > case PutByOffset: >+ case MultiPutByOffset: > case DFG::Jump: > case Branch: > case Switch: >Index: Source/JavaScriptCore/dfg/DFGSafeToExecute.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSafeToExecute.h (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGSafeToExecute.h (working copy) >@@ -252,6 +252,7 @@ bool safeToExecute(AbstractStateType& st > case ConstantStoragePointer: > case Check: > case MultiGetByOffset: >+ case MultiPutByOffset: > return true; > > case GetByVal: >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (working copy) >@@ -4689,6 +4689,7 @@ void SpeculativeJIT::compile(Node* node) > case CheckInBounds: > case ArithIMul: > case MultiGetByOffset: >+ case MultiPutByOffset: > RELEASE_ASSERT_NOT_REACHED(); > break; > } >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (working copy) >@@ -5006,6 +5006,7 @@ void SpeculativeJIT::compile(Node* node) > case CheckInBounds: > case ArithIMul: > case MultiGetByOffset: >+ case MultiPutByOffset: > RELEASE_ASSERT_NOT_REACHED(); > break; > } >Index: Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp (revision 164575) >+++ Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp (working copy) >@@ -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: Source/JavaScriptCore/ftl/FTLCapabilities.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp (revision 164575) >+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp (working copy) >@@ -138,6 +138,7 @@ inline CapabilityLevel canCompile(Node* > case GetById: > case ToThis: > case MultiGetByOffset: >+ case MultiPutByOffset: > case ToPrimitive: > // These are OK. > break; >Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (revision 164575) >+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (working copy) >@@ -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")); >Index: Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-cases.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-cases.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/multi-put-by-offset-reallocation-cases.js (working copy) >@@ -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); >+} >+ >Index: LayoutTests/ChangeLog >=================================================================== >--- LayoutTests/ChangeLog (revision 164575) >+++ LayoutTests/ChangeLog (working copy) >@@ -1,3 +1,17 @@ >+2014-02-23 Filip Pizlo <fpizlo@apple.com> >+ >+ FTL should do polymorphic PutById inlining >+ https://bugs.webkit.org/show_bug.cgi?id=129210 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ 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-23 Dean Jackson <dino@apple.com> > > [WebGL] Allow ANGLE to initialize unused varyings >Index: LayoutTests/js/regress/polymorphic-put-by-id-expected.txt >=================================================================== >--- LayoutTests/js/regress/polymorphic-put-by-id-expected.txt (revision 0) >+++ LayoutTests/js/regress/polymorphic-put-by-id-expected.txt (working copy) >@@ -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 >+ >Index: LayoutTests/js/regress/polymorphic-put-by-id.html >=================================================================== >--- LayoutTests/js/regress/polymorphic-put-by-id.html (revision 0) >+++ LayoutTests/js/regress/polymorphic-put-by-id.html (working copy) >@@ -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> >Index: LayoutTests/js/regress/script-tests/polymorphic-put-by-id.js >=================================================================== >--- LayoutTests/js/regress/script-tests/polymorphic-put-by-id.js (revision 0) >+++ LayoutTests/js/regress/script-tests/polymorphic-put-by-id.js (working copy) >@@ -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;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
oliver
:
review+
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 129210
:
224977
|
224980
|
224990
|
224991
|
225023
|
225025
|
225026
|
225071
| 225073