WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
patch for landing
blah.patch (text/plain), 230.96 KB, created by
Filip Pizlo
on 2015-09-21 12:09:40 PDT
(
hide
)
Description:
patch for landing
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2015-09-21 12:09:40 PDT
Size:
230.96 KB
patch
obsolete
>Index: Source/JavaScriptCore/ChangeLog >=================================================================== >--- Source/JavaScriptCore/ChangeLog (revision 190059) >+++ Source/JavaScriptCore/ChangeLog (working copy) >@@ -1,3 +1,333 @@ >+2015-09-17 Filip Pizlo <fpizlo@apple.com> >+ >+ JSC should infer property types >+ https://bugs.webkit.org/show_bug.cgi?id=148610 >+ >+ Reviewed by Geoffrey Garen. >+ >+ This change brings recursive type inference to JavaScript object properties in JSC. We check that a >+ value being stored into a property obeys a property's type before we do the store. If it doesn't, >+ we broaden the property's type to include the new value. If optimized code was relying on the old >+ type, we deoptimize that code. >+ >+ The type system that this supports includes important primitive types like Int32 and Boolean. But >+ it goes further and also includes a type kind called ObjectWithStructure, which means that we >+ expect the property to always point to objects with a particular structure. This only works for >+ leaf structures (i.e. structures that have a valid transition watchpoint set). Invalidation of the >+ transition set causes the property type to become Object (meaning an object with any structure). >+ This capability gives us recursive type inference. It's possible for an expression like "o.f.g.h" >+ to execute without any type checks if .f and .g are both ObjectWithStructure. >+ >+ The type inference of a property is tracked by an InferredType instance, which is a JSCell. This >+ means that it manages its own memory. That's convenient. For example, when the DFG is interested in >+ one of these, it can just list the InferredType as a weak reference in addition to setting a >+ watchpoint. This ensures that even if the InferredType is dropped by the owning structure, the DFG >+ won't read a dangling pointer. A mapping from property name to InferredType is implemented by >+ InferredTypeTable, which is also a JSCell. Each Structure may point to some InferredTypeTable. >+ >+ This feature causes programs to be happier (run faster without otherwise doing bad things like >+ using lots of memory) when four conditions hold: >+ >+ 1) A property converges to one of the types that we support. >+ 2) The property is loaded from more frequently than it is stored to. >+ 3) The stores are all cached, so that we statically emit a type check. >+ 4) We don't allocate a lot of meta-data for the property's type. >+ >+ We maximize the likelihood of (1) by having a rich type system. But having a rich type system means >+ that a reflective put to a property has to have a large switch over the inferred type to decide how >+ to do the type check. That's why we need (3). We ensure (3) by having every reflective property >+ store (i.e. putDirectInternal in any context that isn't PutById) force the inferred type to become >+ Top. We don't really worry about ensuring (2); this is statistically true for most programs >+ already. >+ >+ Probably the most subtle trickery goes into (4). Logically we'd like to say that each >+ (Structure, Property) maps to its own InferredType. If structure S1 has a transition edge to S2, >+ then we could ensure that the InferredType I1 where (S1, Property)->I1 has a data flow constraint >+ to I2 where (S2, Property)->I2. That would work, but it would involve a lot of memory. And when I1 >+ gets invalidated in some way, it would have to tell I2 about it, and then I2 might tell other >+ InferredType objects downstream. That's madness. So, the first major compromise that we make here >+ is to say that if some property has some InferredType at some Structure, then anytime we >+ transition from that Structure, the new Structure shares the same InferredType for that property. >+ This unifies the type of the property over the entire transition tree starting at the Structure at >+ which the property was added. But this would still mean that each Structure would have its own >+ InferredTypeTable. We don't want that because experience with PropertyTable shows that this can be >+ a major memory hog. So, we don't create an InferredTypeTable until someone adds a property that is >+ subject to type inference (i.e. it was added non-reflectively), and we share that InferredTypeTable >+ with the entire structure transition tree rooted at the Structure that had the first inferred >+ property. We also drop the InferredTypeTable anytime that we do a dictionary transition, and we >+ don't allow further property type inference if a structure had ever been a dictionary. >+ >+ This is a 3% speed-up on Octane and a 12% speed-up on Kraken on my setup. It's not a significant >+ slow-down on any benchmark I ran. >+ >+ * CMakeLists.txt: >+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj: >+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters: >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ * assembler/MacroAssemblerARM64.h: >+ (JSC::MacroAssemblerARM64::branchTest64): >+ * assembler/MacroAssemblerX86_64.h: >+ (JSC::MacroAssemblerX86_64::branchTest64): >+ (JSC::MacroAssemblerX86_64::test64): >+ * bytecode/PolymorphicAccess.cpp: >+ (JSC::AccessCase::generate): >+ * bytecode/PutByIdFlags.cpp: >+ (WTF::printInternal): >+ * bytecode/PutByIdFlags.h: >+ (JSC::encodeStructureID): >+ (JSC::decodeStructureID): >+ * bytecode/PutByIdStatus.cpp: >+ (JSC::PutByIdStatus::computeFromLLInt): >+ (JSC::PutByIdStatus::computeFor): >+ (JSC::PutByIdStatus::computeForStubInfo): >+ * bytecode/PutByIdVariant.cpp: >+ (JSC::PutByIdVariant::operator=): >+ (JSC::PutByIdVariant::replace): >+ (JSC::PutByIdVariant::transition): >+ (JSC::PutByIdVariant::setter): >+ (JSC::PutByIdVariant::attemptToMerge): >+ (JSC::PutByIdVariant::dumpInContext): >+ * bytecode/PutByIdVariant.h: >+ (JSC::PutByIdVariant::newStructure): >+ (JSC::PutByIdVariant::requiredType): >+ * bytecode/UnlinkedCodeBlock.h: >+ (JSC::UnlinkedInstruction::UnlinkedInstruction): >+ * bytecode/Watchpoint.h: >+ (JSC::InlineWatchpointSet::touch): >+ (JSC::InlineWatchpointSet::isBeingWatched): >+ * bytecompiler/BytecodeGenerator.cpp: >+ (JSC::BytecodeGenerator::addConstantValue): >+ (JSC::BytecodeGenerator::emitPutById): >+ (JSC::BytecodeGenerator::emitDirectPutById): >+ * dfg/DFGAbstractInterpreter.h: >+ (JSC::DFG::AbstractInterpreter::filter): >+ (JSC::DFG::AbstractInterpreter::filterByValue): >+ * dfg/DFGAbstractInterpreterInlines.h: >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::filter): >+ * dfg/DFGAbstractValue.cpp: >+ (JSC::DFG::AbstractValue::setType): >+ (JSC::DFG::AbstractValue::set): >+ (JSC::DFG::AbstractValue::fixTypeForRepresentation): >+ (JSC::DFG::AbstractValue::mergeOSREntryValue): >+ (JSC::DFG::AbstractValue::isType): >+ (JSC::DFG::AbstractValue::filter): >+ (JSC::DFG::AbstractValue::filterValueByType): >+ * dfg/DFGAbstractValue.h: >+ (JSC::DFG::AbstractValue::setType): >+ (JSC::DFG::AbstractValue::isType): >+ (JSC::DFG::AbstractValue::validate): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::handleConstantInternalFunction): >+ (JSC::DFG::ByteCodeParser::handleGetByOffset): >+ (JSC::DFG::ByteCodeParser::handlePutByOffset): >+ (JSC::DFG::ByteCodeParser::load): >+ (JSC::DFG::ByteCodeParser::store): >+ (JSC::DFG::ByteCodeParser::handleGetById): >+ (JSC::DFG::ByteCodeParser::handlePutById): >+ * dfg/DFGClobbersExitState.cpp: >+ (JSC::DFG::clobbersExitState): >+ * dfg/DFGConstantFoldingPhase.cpp: >+ (JSC::DFG::ConstantFoldingPhase::foldConstants): >+ (JSC::DFG::ConstantFoldingPhase::emitGetByOffset): >+ (JSC::DFG::ConstantFoldingPhase::emitPutByOffset): >+ (JSC::DFG::ConstantFoldingPhase::addBaseCheck): >+ * dfg/DFGDesiredInferredType.h: Added. >+ (JSC::DFG::DesiredInferredType::DesiredInferredType): >+ (JSC::DFG::DesiredInferredType::operator bool): >+ (JSC::DFG::DesiredInferredType::object): >+ (JSC::DFG::DesiredInferredType::expected): >+ (JSC::DFG::DesiredInferredType::isStillValid): >+ (JSC::DFG::DesiredInferredType::add): >+ (JSC::DFG::DesiredInferredType::operator==): >+ (JSC::DFG::DesiredInferredType::operator!=): >+ (JSC::DFG::DesiredInferredType::isHashTableDeletedValue): >+ (JSC::DFG::DesiredInferredType::hash): >+ (JSC::DFG::DesiredInferredType::dumpInContext): >+ (JSC::DFG::DesiredInferredType::dump): >+ (JSC::DFG::DesiredInferredTypeHash::hash): >+ (JSC::DFG::DesiredInferredTypeHash::equal): >+ * dfg/DFGDesiredWatchpoints.cpp: >+ (JSC::DFG::AdaptiveStructureWatchpointAdaptor::add): >+ (JSC::DFG::InferredTypeAdaptor::add): >+ (JSC::DFG::DesiredWatchpoints::DesiredWatchpoints): >+ (JSC::DFG::DesiredWatchpoints::~DesiredWatchpoints): >+ (JSC::DFG::DesiredWatchpoints::addLazily): >+ (JSC::DFG::DesiredWatchpoints::consider): >+ (JSC::DFG::DesiredWatchpoints::reallyAdd): >+ (JSC::DFG::DesiredWatchpoints::areStillValid): >+ (JSC::DFG::DesiredWatchpoints::dumpInContext): >+ * dfg/DFGDesiredWatchpoints.h: >+ (JSC::DFG::AdaptiveStructureWatchpointAdaptor::dumpInContext): >+ (JSC::DFG::InferredTypeAdaptor::hasBeenInvalidated): >+ (JSC::DFG::InferredTypeAdaptor::dumpInContext): >+ (JSC::DFG::DesiredWatchpoints::isWatched): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGGraph.cpp: >+ (JSC::DFG::Graph::dump): >+ (JSC::DFG::Graph::isSafeToLoad): >+ (JSC::DFG::Graph::inferredTypeFor): >+ (JSC::DFG::Graph::livenessFor): >+ (JSC::DFG::Graph::tryGetConstantProperty): >+ (JSC::DFG::Graph::inferredValueForProperty): >+ (JSC::DFG::Graph::tryGetConstantClosureVar): >+ * dfg/DFGGraph.h: >+ (JSC::DFG::Graph::registerInferredType): >+ (JSC::DFG::Graph::inferredTypeForProperty): >+ * dfg/DFGInferredTypeCheck.cpp: Added. >+ (JSC::DFG::insertInferredTypeCheck): >+ * dfg/DFGInferredTypeCheck.h: Added. >+ * dfg/DFGNode.h: >+ * dfg/DFGObjectAllocationSinkingPhase.cpp: >+ * dfg/DFGPropertyTypeKey.h: Added. >+ (JSC::DFG::PropertyTypeKey::PropertyTypeKey): >+ (JSC::DFG::PropertyTypeKey::operator bool): >+ (JSC::DFG::PropertyTypeKey::structure): >+ (JSC::DFG::PropertyTypeKey::uid): >+ (JSC::DFG::PropertyTypeKey::operator==): >+ (JSC::DFG::PropertyTypeKey::operator!=): >+ (JSC::DFG::PropertyTypeKey::hash): >+ (JSC::DFG::PropertyTypeKey::isHashTableDeletedValue): >+ (JSC::DFG::PropertyTypeKey::dumpInContext): >+ (JSC::DFG::PropertyTypeKey::dump): >+ (JSC::DFG::PropertyTypeKey::deletedUID): >+ (JSC::DFG::PropertyTypeKeyHash::hash): >+ (JSC::DFG::PropertyTypeKeyHash::equal): >+ * dfg/DFGSafeToExecute.h: >+ (JSC::DFG::SafeToExecuteEdge::operator()): >+ (JSC::DFG::safeToExecute): >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::compileTypeOf): >+ (JSC::DFG::SpeculativeJIT::compileCheckStructure): >+ (JSC::DFG::SpeculativeJIT::compileAllocatePropertyStorage): >+ (JSC::DFG::SpeculativeJIT::speculateCell): >+ (JSC::DFG::SpeculativeJIT::speculateCellOrOther): >+ (JSC::DFG::SpeculativeJIT::speculateObject): >+ (JSC::DFG::SpeculativeJIT::speculate): >+ * dfg/DFGSpeculativeJIT.h: >+ * dfg/DFGSpeculativeJIT32_64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGStoreBarrierInsertionPhase.cpp: >+ * dfg/DFGStructureAbstractValue.h: >+ (JSC::DFG::StructureAbstractValue::at): >+ (JSC::DFG::StructureAbstractValue::operator[]): >+ (JSC::DFG::StructureAbstractValue::onlyStructure): >+ (JSC::DFG::StructureAbstractValue::forEach): >+ * dfg/DFGUseKind.cpp: >+ (WTF::printInternal): >+ * dfg/DFGUseKind.h: >+ (JSC::DFG::typeFilterFor): >+ * dfg/DFGValidate.cpp: >+ (JSC::DFG::Validate::validate): >+ * ftl/FTLCapabilities.cpp: >+ (JSC::FTL::canCompile): >+ * ftl/FTLLowerDFGToLLVM.cpp: >+ (JSC::FTL::DFG::LowerDFGToLLVM::compileCheckStructure): >+ (JSC::FTL::DFG::LowerDFGToLLVM::compileCheckCell): >+ (JSC::FTL::DFG::LowerDFGToLLVM::compileMultiPutByOffset): >+ (JSC::FTL::DFG::LowerDFGToLLVM::numberOrNotCellToInt32): >+ (JSC::FTL::DFG::LowerDFGToLLVM::checkInferredType): >+ (JSC::FTL::DFG::LowerDFGToLLVM::loadProperty): >+ (JSC::FTL::DFG::LowerDFGToLLVM::speculate): >+ (JSC::FTL::DFG::LowerDFGToLLVM::speculateCell): >+ (JSC::FTL::DFG::LowerDFGToLLVM::speculateCellOrOther): >+ (JSC::FTL::DFG::LowerDFGToLLVM::speculateMachineInt): >+ (JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit): >+ * jit/AssemblyHelpers.cpp: >+ (JSC::AssemblyHelpers::decodedCodeMapFor): >+ (JSC::AssemblyHelpers::branchIfNotType): >+ (JSC::AssemblyHelpers::purifyNaN): >+ * jit/AssemblyHelpers.h: >+ (JSC::AssemblyHelpers::branchIfEqual): >+ (JSC::AssemblyHelpers::branchIfNotCell): >+ (JSC::AssemblyHelpers::branchIfCell): >+ (JSC::AssemblyHelpers::branchIfNotOther): >+ (JSC::AssemblyHelpers::branchIfInt32): >+ (JSC::AssemblyHelpers::branchIfNotInt32): >+ (JSC::AssemblyHelpers::branchIfNumber): >+ (JSC::AssemblyHelpers::branchIfNotNumber): >+ (JSC::AssemblyHelpers::branchIfEmpty): >+ (JSC::AssemblyHelpers::branchStructure): >+ * jit/Repatch.cpp: >+ (JSC::tryCachePutByID): >+ * llint/LLIntSlowPaths.cpp: >+ (JSC::LLInt::LLINT_SLOW_PATH_DECL): >+ * llint/LowLevelInterpreter.asm: >+ * llint/LowLevelInterpreter32_64.asm: >+ * llint/LowLevelInterpreter64.asm: >+ * runtime/InferredType.cpp: Added. >+ (JSC::InferredType::create): >+ (JSC::InferredType::destroy): >+ (JSC::InferredType::createStructure): >+ (JSC::InferredType::visitChildren): >+ (JSC::InferredType::kindForFlags): >+ (JSC::InferredType::Descriptor::forValue): >+ (JSC::InferredType::Descriptor::forFlags): >+ (JSC::InferredType::Descriptor::putByIdFlags): >+ (JSC::InferredType::Descriptor::merge): >+ (JSC::InferredType::Descriptor::removeStructure): >+ (JSC::InferredType::Descriptor::subsumes): >+ (JSC::InferredType::Descriptor::dumpInContext): >+ (JSC::InferredType::Descriptor::dump): >+ (JSC::InferredType::InferredType): >+ (JSC::InferredType::~InferredType): >+ (JSC::InferredType::canWatch): >+ (JSC::InferredType::addWatchpoint): >+ (JSC::InferredType::dump): >+ (JSC::InferredType::willStoreValueSlow): >+ (JSC::InferredType::makeTopSlow): >+ (JSC::InferredType::set): >+ (JSC::InferredType::removeStructure): >+ (JSC::InferredType::InferredStructureWatchpoint::fireInternal): >+ (JSC::InferredType::InferredStructureFinalizer::finalizeUnconditionally): >+ (JSC::InferredType::InferredStructure::InferredStructure): >+ (WTF::printInternal): >+ * runtime/InferredType.h: Added. >+ * runtime/InferredTypeTable.cpp: Added. >+ (JSC::InferredTypeTable::create): >+ (JSC::InferredTypeTable::destroy): >+ (JSC::InferredTypeTable::createStructure): >+ (JSC::InferredTypeTable::visitChildren): >+ (JSC::InferredTypeTable::get): >+ (JSC::InferredTypeTable::willStoreValue): >+ (JSC::InferredTypeTable::makeTop): >+ (JSC::InferredTypeTable::InferredTypeTable): >+ (JSC::InferredTypeTable::~InferredTypeTable): >+ * runtime/InferredTypeTable.h: Added. >+ * runtime/JSObject.h: >+ (JSC::JSObject::putDirectInternal): >+ (JSC::JSObject::putDirectWithoutTransition): >+ * runtime/Structure.cpp: >+ (JSC::Structure::materializePropertyMap): >+ (JSC::Structure::addPropertyTransition): >+ (JSC::Structure::removePropertyTransition): >+ (JSC::Structure::startWatchingInternalProperties): >+ (JSC::Structure::willStoreValueSlow): >+ (JSC::Structure::visitChildren): >+ (JSC::Structure::prototypeChainMayInterceptStoreTo): >+ * runtime/Structure.h: >+ (JSC::PropertyMapEntry::PropertyMapEntry): >+ * runtime/StructureInlines.h: >+ (JSC::Structure::get): >+ * runtime/VM.cpp: >+ (JSC::VM::VM): >+ * runtime/VM.h: >+ * tests/stress/prop-type-boolean-then-string.js: Added. >+ * tests/stress/prop-type-int32-then-string.js: Added. >+ * tests/stress/prop-type-number-then-string.js: Added. >+ * tests/stress/prop-type-object-or-other-then-string.js: Added. >+ * tests/stress/prop-type-object-then-string.js: Added. >+ * tests/stress/prop-type-other-then-string.js: Added. >+ * tests/stress/prop-type-string-then-object.js: Added. >+ * tests/stress/prop-type-struct-or-other-then-string.js: Added. >+ * tests/stress/prop-type-struct-then-object.js: Added. >+ * tests/stress/prop-type-struct-then-object-opt.js: Added. >+ * tests/stress/prop-type-struct-then-object-opt-fold.js: Added. >+ * tests/stress/prop-type-struct-then-object-opt-multi.js: Added. >+ > 2015-09-20 Sukolsak Sakshuwong <sukolsak@gmail.com> > > Implement SetLocal and SetGlobal expressions in WebAssembly >Index: Source/JavaScriptCore/CMakeLists.txt >=================================================================== >--- Source/JavaScriptCore/CMakeLists.txt (revision 190059) >+++ Source/JavaScriptCore/CMakeLists.txt (working copy) >@@ -192,6 +192,7 @@ set(JavaScriptCore_SOURCES > dfg/DFGGraphSafepoint.cpp > dfg/DFGHeapLocation.cpp > dfg/DFGInPlaceAbstractState.cpp >+ dfg/DFGInferredTypeCheck.cpp > dfg/DFGInsertionSet.cpp > dfg/DFGIntegerCheckCombiningPhase.cpp > dfg/DFGIntegerRangeOptimizationPhase.cpp >@@ -501,6 +502,8 @@ set(JavaScriptCore_RUNTIME_SOURCES > runtime/Identifier.cpp > runtime/IndexingType.cpp > runtime/InferredValue.cpp >+ runtime/InferredType.cpp >+ runtime/InferredTypeTable.cpp > runtime/InitializeThreading.cpp > runtime/InspectorInstrumentationObject.cpp > runtime/InternalFunction.cpp >Index: Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (revision 190059) >+++ Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (working copy) >@@ -428,6 +428,7 @@ > <ClCompile Include="..\dfg\DFGGraphSafepoint.cpp" /> > <ClCompile Include="..\dfg\DFGHeapLocation.cpp" /> > <ClCompile Include="..\dfg\DFGInPlaceAbstractState.cpp" /> >+ <ClCompile Include="..\dfg\DFGInferredTypeCheck.cpp" /> > <ClCompile Include="..\dfg\DFGInsertionSet.cpp" /> > <ClCompile Include="..\dfg\DFGIntegerCheckCombiningPhase.cpp" /> > <ClCompile Include="..\dfg\DFGIntegerRangeOptimizationPhase.cpp" /> >@@ -750,6 +751,8 @@ > <ClCompile Include="..\runtime\Identifier.cpp" /> > <ClCompile Include="..\runtime\IndexingType.cpp" /> > <ClCompile Include="..\runtime\InferredValue.cpp" /> >+ <ClCompile Include="..\runtime\InferredType.cpp" /> >+ <ClCompile Include="..\runtime\InferredTypeTable.cpp" /> > <ClCompile Include="..\runtime\InitializeThreading.cpp" /> > <ClCompile Include="..\runtime\InspectorInstrumentationObject.cpp" /> > <ClCompile Include="..\runtime\InternalFunction.cpp" /> >@@ -1137,6 +1140,7 @@ > <ClInclude Include="..\dfg\DFGCSEPhase.h" /> > <ClInclude Include="..\dfg\DFGDCEPhase.h" /> > <ClInclude Include="..\dfg\DFGDesiredIdentifiers.h" /> >+ <ClInclude Include="..\dfg\DFGDesiredInferredType.h" /> > <ClInclude Include="..\dfg\DFGDesiredTransitions.h" /> > <ClInclude Include="..\dfg\DFGDesiredWatchpoints.h" /> > <ClInclude Include="..\dfg\DFGDesiredWeakReferences.h" /> >@@ -1166,6 +1170,7 @@ > <ClInclude Include="..\dfg\DFGGraphSafepoint.h" /> > <ClInclude Include="..\dfg\DFGHeapLocation.h" /> > <ClInclude Include="..\dfg\DFGInPlaceAbstractState.h" /> >+ <ClInclude Include="..\dfg\DFGInferredTypeCheck.h" /> > <ClInclude Include="..\dfg\DFGInsertionSet.h" /> > <ClInclude Include="..\dfg\DFGIntegerCheckCombiningPhase.h" /> > <ClInclude Include="..\dfg\DFGIntegerRangeOptimizationPhase.h" /> >@@ -1217,6 +1222,7 @@ > <ClInclude Include="..\dfg\DFGPredictionInjectionPhase.h" /> > <ClInclude Include="..\dfg\DFGPredictionPropagationPhase.h" /> > <ClInclude Include="..\dfg\DFGPromotedHeapLocation.h" /> >+ <ClInclude Include="..\dfg\DFGPropertyTypeKey.h" /> > <ClInclude Include="..\dfg\DFGPureValue.h" /> > <ClInclude Include="..\dfg\DFGPutStackSinkingPhase.h" /> > <ClInclude Include="..\dfg\DFGRegisterBank.h" /> >@@ -1591,6 +1597,8 @@ > <ClInclude Include="..\runtime\IndexingHeaderInlines.h" /> > <ClInclude Include="..\runtime\IndexingType.h" /> > <ClInclude Include="..\runtime\InferredValue.h" /> >+ <ClInclude Include="..\runtime\InferredType.h" /> >+ <ClInclude Include="..\runtime\InferredTypeTable.h" /> > <ClInclude Include="..\runtime\InitializeThreading.h" /> > <ClInclude Include="..\runtime\Int16Array.h" /> > <ClInclude Include="..\runtime\Int32Array.h" /> >Index: Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters (revision 190059) >+++ Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters (working copy) >@@ -1269,6 +1269,9 @@ > <ClCompile Include="..\dfg\DFGInPlaceAbstractState.cpp"> > <Filter>dfg</Filter> > </ClCompile> >+ <ClCompile Include="..\dfg\DFGInferredTypeCheck.cpp"> >+ <Filter>dfg</Filter> >+ </ClCompile> > <ClCompile Include="..\dfg\DFGInvalidationPointInjectionPhase.cpp"> > <Filter>dfg</Filter> > </ClCompile> >@@ -1866,6 +1869,8 @@ > <ClCompile Include="..\runtime\DirectArguments.cpp" /> > <ClCompile Include="..\runtime\DirectArgumentsOffset.cpp" /> > <ClCompile Include="..\runtime\InferredValue.cpp" /> >+ <ClCompile Include="..\runtime\InferredType.cpp" /> >+ <ClCompile Include="..\runtime\InferredTypeTable.cpp" /> > <ClCompile Include="..\runtime\ScopeOffset.cpp" /> > <ClCompile Include="..\runtime\ScopedArguments.cpp" /> > <ClCompile Include="..\runtime\ScopedArgumentsTable.cpp" /> >@@ -3676,6 +3681,9 @@ > <ClInclude Include="..\dfg\DFGDesiredIdentifiers.h"> > <Filter>dfg</Filter> > </ClInclude> >+ <ClInclude Include="..\dfg\DFGDesiredInferredType.h"> >+ <Filter>dfg</Filter> >+ </ClInclude> > <ClInclude Include="..\dfg\DFGDesiredTransitions.h"> > <Filter>dfg</Filter> > </ClInclude> >@@ -3742,6 +3750,9 @@ > <ClInclude Include="..\dfg\DFGInPlaceAbstractState.h"> > <Filter>dfg</Filter> > </ClInclude> >+ <ClInclude Include="..\dfg\DFGInferredTypeCheck.h"> >+ <Filter>dfg</Filter> >+ </ClInclude> > <ClInclude Include="..\dfg\DFGInvalidationPointInjectionPhase.h"> > <Filter>dfg</Filter> > </ClInclude> >@@ -4424,6 +4435,9 @@ > <ClInclude Include="..\dfg\DFGPromotedHeapLocation.h"> > <Filter>dfg</Filter> > </ClInclude> >+ <ClInclude Include="..\dfg\DFGPropertyTypeKey.h"> >+ <Filter>dfg</Filter> >+ </ClInclude> > <ClInclude Include="..\ftl\FTLExitPropertyValue.h"> > <Filter>ftl</Filter> > </ClInclude> >@@ -4467,6 +4481,8 @@ > <ClInclude Include="..\runtime\GenericArgumentsInlines.h" /> > <ClInclude Include="..\runtime\GenericOffset.h" /> > <ClInclude Include="..\runtime\InferredValue.h" /> >+ <ClInclude Include="..\runtime\InferredType.h" /> >+ <ClInclude Include="..\runtime\InferredTypeTable.h" /> > <ClInclude Include="..\runtime\ScopeOffset.h" /> > <ClInclude Include="..\runtime\ScopedArguments.h" /> > <ClInclude Include="..\runtime\ScopedArgumentsTable.h" /> >Index: Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (revision 190059) >+++ Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (working copy) >@@ -86,6 +86,8 @@ > 0F04396E1B03DC0B009598B7 /* DFGCombinedLiveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F04396C1B03DC0B009598B7 /* DFGCombinedLiveness.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F05C3B41683CF9200BAF45B /* DFGArrayifySlowPathGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F05C3B21683CF8F00BAF45B /* DFGArrayifySlowPathGenerator.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F0776BF14FF002B00102332 /* JITCompilationEffort.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0776BD14FF002800102332 /* JITCompilationEffort.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0F0A75221B94BFA900110660 /* InferredType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F0A75201B94BFA900110660 /* InferredType.cpp */; }; >+ 0F0A75231B94BFA900110660 /* InferredType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0A75211B94BFA900110660 /* InferredType.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F0B839C14BCF46300885B4F /* LLIntThunks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F0B839714BCF45A00885B4F /* LLIntThunks.cpp */; }; > 0F0B839D14BCF46600885B4F /* LLIntThunks.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0B839814BCF45A00885B4F /* LLIntThunks.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F0B83A714BCF50700885B4F /* CodeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F0B83A514BCF50400885B4F /* CodeType.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -543,6 +545,8 @@ > 0FC712E317CD8793008CC93C /* JITToDFGDeferredCompilationCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC712E117CD878F008CC93C /* JITToDFGDeferredCompilationCallback.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FC8150A14043BF500CFA603 /* WriteBarrierSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC8150914043BD200CFA603 /* WriteBarrierSupport.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FC8150B14043C0E00CFA603 /* WriteBarrierSupport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC8150814043BCA00CFA603 /* WriteBarrierSupport.cpp */; }; >+ 0FC841681BA8C3210061837D /* DFGInferredTypeCheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC841661BA8C3210061837D /* DFGInferredTypeCheck.cpp */; }; >+ 0FC841691BA8C3210061837D /* DFGInferredTypeCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC841671BA8C3210061837D /* DFGInferredTypeCheck.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FC97F33182020D7002C9B26 /* CodeBlockJettisoningWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC97F2F182020D7002C9B26 /* CodeBlockJettisoningWatchpoint.cpp */; }; > 0FC97F34182020D7002C9B26 /* CodeBlockJettisoningWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC97F30182020D7002C9B26 /* CodeBlockJettisoningWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FC97F3D18202119002C9B26 /* DFGInvalidationPointInjectionPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FC97F3718202119002C9B26 /* DFGInvalidationPointInjectionPhase.cpp */; }; >@@ -742,6 +746,10 @@ > 0FFB921C16D02F110055A5DB /* DFGOSRExitCompilationInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 65987F2C167FE84B003C2F8D /* DFGOSRExitCompilationInfo.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FFB921D16D02F300055A5DB /* DFGSlowPathGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F1E3A501537C2CB000F9456 /* DFGSlowPathGenerator.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FFB922016D033B70055A5DB /* NodeConstructors.h in Headers */ = {isa = PBXBuildFile; fileRef = 930DAD030FB1EB1A0082D205 /* NodeConstructors.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0FFC92111B94D4DF0071DD66 /* InferredTypeTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFC920F1B94D4DF0071DD66 /* InferredTypeTable.cpp */; }; >+ 0FFC92121B94D4DF0071DD66 /* InferredTypeTable.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFC92101B94D4DF0071DD66 /* InferredTypeTable.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0FFC92141B94E83E0071DD66 /* DFGDesiredInferredType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFC92131B94E83E0071DD66 /* DFGDesiredInferredType.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0FFC92161B94FB3E0071DD66 /* DFGPropertyTypeKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFC92151B94FB3E0071DD66 /* DFGPropertyTypeKey.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FFC99D1184EC8AD009C10AB /* ConstantMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFC99D0184EC8AD009C10AB /* ConstantMode.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FFC99D4184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFC99D2184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp */; }; > 0FFC99D5184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFC99D3184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -1924,6 +1932,8 @@ > 0F04396C1B03DC0B009598B7 /* DFGCombinedLiveness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGCombinedLiveness.h; path = dfg/DFGCombinedLiveness.h; sourceTree = "<group>"; }; > 0F05C3B21683CF8F00BAF45B /* DFGArrayifySlowPathGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGArrayifySlowPathGenerator.h; path = dfg/DFGArrayifySlowPathGenerator.h; sourceTree = "<group>"; }; > 0F0776BD14FF002800102332 /* JITCompilationEffort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITCompilationEffort.h; sourceTree = "<group>"; }; >+ 0F0A75201B94BFA900110660 /* InferredType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InferredType.cpp; sourceTree = "<group>"; }; >+ 0F0A75211B94BFA900110660 /* InferredType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InferredType.h; sourceTree = "<group>"; }; > 0F0B839714BCF45A00885B4F /* LLIntThunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LLIntThunks.cpp; path = llint/LLIntThunks.cpp; sourceTree = "<group>"; }; > 0F0B839814BCF45A00885B4F /* LLIntThunks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntThunks.h; path = llint/LLIntThunks.h; sourceTree = "<group>"; }; > 0F0B83A514BCF50400885B4F /* CodeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeType.h; sourceTree = "<group>"; }; >@@ -2390,6 +2400,8 @@ > 0FC712E117CD878F008CC93C /* JITToDFGDeferredCompilationCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JITToDFGDeferredCompilationCallback.h; sourceTree = "<group>"; }; > 0FC8150814043BCA00CFA603 /* WriteBarrierSupport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WriteBarrierSupport.cpp; sourceTree = "<group>"; }; > 0FC8150914043BD200CFA603 /* WriteBarrierSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WriteBarrierSupport.h; sourceTree = "<group>"; }; >+ 0FC841661BA8C3210061837D /* DFGInferredTypeCheck.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGInferredTypeCheck.cpp; path = dfg/DFGInferredTypeCheck.cpp; sourceTree = "<group>"; }; >+ 0FC841671BA8C3210061837D /* DFGInferredTypeCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGInferredTypeCheck.h; path = dfg/DFGInferredTypeCheck.h; sourceTree = "<group>"; }; > 0FC97F2F182020D7002C9B26 /* CodeBlockJettisoningWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CodeBlockJettisoningWatchpoint.cpp; sourceTree = "<group>"; }; > 0FC97F30182020D7002C9B26 /* CodeBlockJettisoningWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeBlockJettisoningWatchpoint.h; sourceTree = "<group>"; }; > 0FC97F3718202119002C9B26 /* DFGInvalidationPointInjectionPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGInvalidationPointInjectionPhase.cpp; path = dfg/DFGInvalidationPointInjectionPhase.cpp; sourceTree = "<group>"; }; >@@ -2577,6 +2589,10 @@ > 0FF9CE721B9CD6D0004EDCA6 /* PolymorphicAccess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PolymorphicAccess.h; sourceTree = "<group>"; }; > 0FFB6C361AF48DDC00DB1BF7 /* TypeofType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TypeofType.cpp; sourceTree = "<group>"; }; > 0FFB6C371AF48DDC00DB1BF7 /* TypeofType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TypeofType.h; sourceTree = "<group>"; }; >+ 0FFC920F1B94D4DF0071DD66 /* InferredTypeTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InferredTypeTable.cpp; sourceTree = "<group>"; }; >+ 0FFC92101B94D4DF0071DD66 /* InferredTypeTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InferredTypeTable.h; sourceTree = "<group>"; }; >+ 0FFC92131B94E83E0071DD66 /* DFGDesiredInferredType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDesiredInferredType.h; path = dfg/DFGDesiredInferredType.h; sourceTree = "<group>"; }; >+ 0FFC92151B94FB3E0071DD66 /* DFGPropertyTypeKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPropertyTypeKey.h; path = dfg/DFGPropertyTypeKey.h; sourceTree = "<group>"; }; > 0FFC99D0184EC8AD009C10AB /* ConstantMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConstantMode.h; sourceTree = "<group>"; }; > 0FFC99D2184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBufferNeuteringWatchpoint.cpp; sourceTree = "<group>"; }; > 0FFC99D3184EE318009C10AB /* ArrayBufferNeuteringWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayBufferNeuteringWatchpoint.h; sourceTree = "<group>"; }; >@@ -4671,8 +4687,8 @@ > BC02E98B0E183E38000F9297 /* ErrorInstance.h */, > BC02E9060E1839DB000F9297 /* ErrorPrototype.cpp */, > BC02E9070E1839DB000F9297 /* ErrorPrototype.h */, >- FE1C0FFC1B193E9800B53FCA /* Exception.h */, > FE1C0FFE1B194FD100B53FCA /* Exception.cpp */, >+ FE1C0FFC1B193E9800B53FCA /* Exception.h */, > 0F12DE0D1979D5FD0006FF4E /* ExceptionFuzz.cpp */, > 0F12DE0E1979D5FD0006FF4E /* ExceptionFuzz.h */, > 1429D8770ED21ACD00B89619 /* ExceptionHelpers.cpp */, >@@ -4706,6 +4722,10 @@ > 0FB7F38E15ED8E3800F167B2 /* IndexingHeaderInlines.h */, > 0F13E04C16164A1B00DC8DE7 /* IndexingType.cpp */, > 0FB7F38F15ED8E3800F167B2 /* IndexingType.h */, >+ 0F0A75201B94BFA900110660 /* InferredType.cpp */, >+ 0F0A75211B94BFA900110660 /* InferredType.h */, >+ 0FFC920F1B94D4DF0071DD66 /* InferredTypeTable.cpp */, >+ 0FFC92101B94D4DF0071DD66 /* InferredTypeTable.h */, > 0FF8BDE81AD4CF7100DFE884 /* InferredValue.cpp */, > 0FF8BDE91AD4CF7100DFE884 /* InferredValue.h */, > E178636C0D9BEEC300D74E75 /* InitializeThreading.cpp */, >@@ -4756,10 +4776,10 @@ > 0F2B66BC17B6B5AB00A7AE3F /* JSArrayBufferViewInlines.h */, > A7BDAEC417F4EA1400F6140C /* JSArrayIterator.cpp */, > A7BDAEC517F4EA1400F6140C /* JSArrayIterator.h */, >- 86FA9E8F142BBB2D001773B7 /* JSBoundFunction.cpp */, >- 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */, > 8B0F424B1ABD6DE2003917EA /* JSArrowFunction.cpp */, > 8B0F424A1ABD6D2F003917EA /* JSArrowFunction.h */, >+ 86FA9E8F142BBB2D001773B7 /* JSBoundFunction.cpp */, >+ 86FA9E90142BBB2E001773B7 /* JSBoundFunction.h */, > 657CF45619BF6662004ACBF2 /* JSCallee.cpp */, > 657CF45719BF6662004ACBF2 /* JSCallee.h */, > BC7F8FBA0E19D1EF008632C0 /* JSCell.cpp */, >@@ -5062,9 +5082,9 @@ > 0FE050241AA9095600D33B33 /* VarOffset.h */, > E18E3A570DF9278C00D90B34 /* VM.cpp */, > E18E3A560DF9278C00D90B34 /* VM.h */, >- FE90BB3A1B7CF64E006B3F03 /* VMInlines.h */, > FE5932A5183C5A2600A1ECCC /* VMEntryScope.cpp */, > FE5932A6183C5A2600A1ECCC /* VMEntryScope.h */, >+ FE90BB3A1B7CF64E006B3F03 /* VMInlines.h */, > FED94F2B171E3E2300BE77A4 /* Watchdog.cpp */, > FED94F2C171E3E2300BE77A4 /* Watchdog.h */, > 14BFCE6810CDB1FC00364CCE /* WeakGCMap.h */, >@@ -5216,6 +5236,7 @@ > 0F2FC77116E12F6F0038D976 /* DFGDCEPhase.h */, > 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */, > 0F8F2B98172F04FD007DBDA5 /* DFGDesiredIdentifiers.h */, >+ 0FFC92131B94E83E0071DD66 /* DFGDesiredInferredType.h */, > C2C0F7CB17BBFC5B00464FE4 /* DFGDesiredTransitions.cpp */, > C2C0F7CC17BBFC5B00464FE4 /* DFGDesiredTransitions.h */, > 0FE853491723CDA500B618F5 /* DFGDesiredWatchpoints.cpp */, >@@ -5262,6 +5283,8 @@ > 0F2FCCF318A60070001A27F8 /* DFGGraphSafepoint.h */, > 0FB1765C196B8F9E0091052A /* DFGHeapLocation.cpp */, > 0FB1765D196B8F9E0091052A /* DFGHeapLocation.h */, >+ 0FC841661BA8C3210061837D /* DFGInferredTypeCheck.cpp */, >+ 0FC841671BA8C3210061837D /* DFGInferredTypeCheck.h */, > 0FB14E201812570B009B6B4D /* DFGInlineCacheWrapper.h */, > 0FB14E2218130955009B6B4D /* DFGInlineCacheWrapperInlines.h */, > A704D90017A0BAA8006BA554 /* DFGInPlaceAbstractState.cpp */, >@@ -5294,10 +5317,10 @@ > 0FB4B51D16B62772003F696B /* DFGLongLivedState.h */, > A767B5B317A0B9650063D940 /* DFGLoopPreHeaderCreationPhase.cpp */, > A767B5B417A0B9650063D940 /* DFGLoopPreHeaderCreationPhase.h */, >- 0F5874EB194FEB1200AAB2C1 /* DFGMayExit.cpp */, >- 0F5874EC194FEB1200AAB2C1 /* DFGMayExit.h */, > 79F8FC1C1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.cpp */, > 79F8FC1D1B9FED0F00CA66AB /* DFGMaximalFlushInsertionPhase.h */, >+ 0F5874EB194FEB1200AAB2C1 /* DFGMayExit.cpp */, >+ 0F5874EC194FEB1200AAB2C1 /* DFGMayExit.h */, > 0F1725FE1B48719A00AC3A55 /* DFGMinifiedGraph.cpp */, > 0F2BDC3D1522801700CD8910 /* DFGMinifiedGraph.h */, > 0FB4B51016B3A964003F696B /* DFGMinifiedID.h */, >@@ -5365,6 +5388,7 @@ > 0F3E01A919D353A500F61B7F /* DFGPrePostNumbering.h */, > 0F2B9CE019D0BA7D00B1D1B5 /* DFGPromotedHeapLocation.cpp */, > 0F2B9CE119D0BA7D00B1D1B5 /* DFGPromotedHeapLocation.h */, >+ 0FFC92151B94FB3E0071DD66 /* DFGPropertyTypeKey.h */, > 0FB1765E196B8F9E0091052A /* DFGPureValue.cpp */, > 0FB1765F196B8F9E0091052A /* DFGPureValue.h */, > 0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */, >@@ -5957,6 +5981,7 @@ > FE5068651AE246390009DAB7 /* DeferredSourceDump.h in Headers */, > C442CB251A6CDB8C005D3D7C /* JSInputs.json in Headers */, > FE1C0FFD1B193E9800B53FCA /* Exception.h in Headers */, >+ 0FFC92141B94E83E0071DD66 /* DFGDesiredInferredType.h in Headers */, > 79EE0C001B4AFB85000385C9 /* VariableEnvironment.h in Headers */, > 52678F911A04177C006A306D /* ControlFlowProfiler.h in Headers */, > 52678F8F1A031009006A306D /* BasicBlockLocation.h in Headers */, >@@ -6071,6 +6096,7 @@ > 0FBD7E691447999600481315 /* CodeOrigin.h in Headers */, > 7B0247591B868EB700542440 /* WASMFunctionSyntaxChecker.h in Headers */, > 0F21C27D14BE727A00ADC64B /* CodeSpecializationKind.h in Headers */, >+ 0F0A75231B94BFA900110660 /* InferredType.h in Headers */, > 0F0B83A714BCF50700885B4F /* CodeType.h in Headers */, > BC18C3F30E16F5CD00B34460 /* CommonIdentifiers.h in Headers */, > 709FB86C1AE335C60039D069 /* WeakSetPrototype.h in Headers */, >@@ -6351,12 +6377,14 @@ > 0F25F1B4181635F300522F39 /* FTLSlowPathCallKey.h in Headers */, > 0F9D339B1803ADB70073C2BC /* FTLStackMaps.h in Headers */, > 0FEA0A12170513DB00BB722C /* FTLState.h in Headers */, >+ 0FC841691BA8C3210061837D /* DFGInferredTypeCheck.h in Headers */, > A7FCC26D17A0B6AA00786D1A /* FTLSwitchCase.h in Headers */, > 0F235BE217178E1C00690C7F /* FTLThunks.h in Headers */, > 0FEA0A201708B00700BB722C /* FTLTypedPointer.h in Headers */, > 0F6B1CC61862C47800845D97 /* FTLUnwindInfo.h in Headers */, > 0FDB2CCA173DA523007B3C1B /* FTLValueFromBlock.h in Headers */, > 0FE7211E193B9C590031F6ED /* DFGTransition.h in Headers */, >+ 0FFC92121B94D4DF0071DD66 /* InferredTypeTable.h in Headers */, > 0F5A6284188C98D40072C9DF /* FTLValueRange.h in Headers */, > 0F0332C618B53FA9005F979A /* FTLWeight.h in Headers */, > 0F0332C818B546EC005F979A /* FTLWeightedTarget.h in Headers */, >@@ -6521,6 +6549,7 @@ > BC18C41F0E16F5CD00B34460 /* JSFunction.h in Headers */, > A72028BA1797603D0098028C /* JSFunctionInlines.h in Headers */, > 0F2B66F117B6B5AB00A7AE3F /* JSGenericTypedArrayView.h in Headers */, >+ 0FFC92161B94FB3E0071DD66 /* DFGPropertyTypeKey.h in Headers */, > 0F2B66F217B6B5AB00A7AE3F /* JSGenericTypedArrayViewConstructor.h in Headers */, > 0F2B66F317B6B5AB00A7AE3F /* JSGenericTypedArrayViewConstructorInlines.h in Headers */, > 0F2B66F417B6B5AB00A7AE3F /* JSGenericTypedArrayViewInlines.h in Headers */, >@@ -7463,6 +7492,7 @@ > 1428082E107EC0570013E7B2 /* ConstructData.cpp in Sources */, > A57D23F11891B5B40031C7FA /* ContentSearchUtilities.cpp in Sources */, > C240305514B404E60079EB64 /* CopiedSpace.cpp in Sources */, >+ 0FFC92111B94D4DF0071DD66 /* InferredTypeTable.cpp in Sources */, > 2AACE63C18CA5A0300ED0191 /* GCActivityCallback.cpp in Sources */, > C2239D1716262BDD005AC5FD /* CopyVisitor.cpp in Sources */, > 0F2B66DE17B6B5AB00A7AE3F /* DataView.cpp in Sources */, >@@ -7847,6 +7877,7 @@ > 0FF054F91AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp in Sources */, > 86E3C61A167BABEE006D760A /* JSValue.mm in Sources */, > 0FF8BDEA1AD4CF7100DFE884 /* InferredValue.cpp in Sources */, >+ 0FC841681BA8C3210061837D /* DFGInferredTypeCheck.cpp in Sources */, > 0F8F14351ADF090100ED792C /* DFGMovHintRemovalPhase.cpp in Sources */, > 14BD5A320A3E91F600BAF59C /* JSValueRef.cpp in Sources */, > 147F39D7107EC37600427A48 /* JSEnvironmentRecord.cpp in Sources */, >@@ -7881,6 +7912,7 @@ > 86C568E011A213EE0007F7F0 /* MacroAssemblerARM.cpp in Sources */, > E33F50741B8421C000413856 /* JSInternalPromisePrototype.cpp in Sources */, > A729009C17976C6000317298 /* MacroAssemblerARMv7.cpp in Sources */, >+ 0F0A75221B94BFA900110660 /* InferredType.cpp in Sources */, > A53CE08518BC1A5600BEDF76 /* ConsolePrototype.cpp in Sources */, > A7A4AE0817973B26005612B1 /* MacroAssemblerX86Common.cpp in Sources */, > A5B6A74D18C6DBA600F11E91 /* ConsoleClient.cpp in Sources */, >Index: Source/JavaScriptCore/assembler/MacroAssemblerARM64.h >=================================================================== >--- Source/JavaScriptCore/assembler/MacroAssemblerARM64.h (revision 190059) >+++ Source/JavaScriptCore/assembler/MacroAssemblerARM64.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2012, 2014 Apple Inc. All rights reserved. >+ * Copyright (C) 2012, 2014, 2015 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -1853,6 +1853,12 @@ public: > return Jump(makeBranch(cond)); > } > >+ Jump branchTest64(ResultCondition cond, RegisterID reg, TrustedImm64 mask) >+ { >+ move(mask, getCachedDataTempRegisterIDAndInvalidate()); >+ return branchTest64(cond, reg, dataTempRegister); >+ } >+ > Jump branchTest64(ResultCondition cond, Address address, RegisterID mask) > { > load64(address, getCachedDataTempRegisterIDAndInvalidate()); >Index: Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h >=================================================================== >--- Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h (revision 190059) >+++ Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h (working copy) >@@ -605,6 +605,12 @@ public: > return Jump(m_assembler.jCC(x86Condition(cond))); > } > >+ Jump branchTest64(ResultCondition cond, RegisterID reg, TrustedImm64 mask) >+ { >+ move(mask, scratchRegister); >+ return branchTest64(cond, reg, scratchRegister); >+ } >+ > void test64(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest) > { > if (mask.m_value == -1) >Index: Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp (revision 190059) >+++ Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp (working copy) >@@ -691,6 +691,15 @@ void AccessCase::generate(AccessGenerati > } > > case Replace: { >+ if (InferredType* type = structure()->inferredTypeFor(ident.impl())) { >+ if (verbose) >+ dataLog("Have type: ", type->descriptor(), "\n"); >+ state.failAndRepatch.append( >+ jit.branchIfNotType( >+ valueRegs, scratchGPR, type->descriptor(), CCallHelpers::DoNotHaveTagRegisters)); >+ } else if (verbose) >+ dataLog("Don't have type.\n"); >+ > if (isInlineOffset(m_offset)) { > jit.storeValue( > valueRegs, >@@ -714,6 +723,15 @@ void AccessCase::generate(AccessGenerati > RELEASE_ASSERT(GPRInfo::numberOfRegisters >= 6 || !structure()->outOfLineCapacity() || structure()->outOfLineCapacity() == newStructure()->outOfLineCapacity()); > RELEASE_ASSERT(!structure()->couldHaveIndexingHeader()); > >+ if (InferredType* type = newStructure()->inferredTypeFor(ident.impl())) { >+ if (verbose) >+ dataLog("Have type: ", type->descriptor(), "\n"); >+ state.failAndRepatch.append( >+ jit.branchIfNotType( >+ valueRegs, scratchGPR, type->descriptor(), CCallHelpers::DoNotHaveTagRegisters)); >+ } else if (verbose) >+ dataLog("Don't have type.\n"); >+ > CCallHelpers::JumpList slowPath; > > ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters); >Index: Source/JavaScriptCore/bytecode/PutByIdFlags.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdFlags.cpp (revision 190059) >+++ Source/JavaScriptCore/bytecode/PutByIdFlags.cpp (working copy) >@@ -26,6 +26,7 @@ > #include "config.h" > #include "PutByIdFlags.h" > >+#include "InferredType.h" > #include <wtf/CommaPrinter.h> > #include <wtf/PrintStream.h> > #include <wtf/StringPrintStream.h> >@@ -35,16 +36,14 @@ namespace WTF { > using namespace JSC; > > void printInternal(PrintStream& out, PutByIdFlags flags) { >- StringPrintStream stringOut; > CommaPrinter comma("|"); > if (flags & PutByIdIsDirect) >- stringOut.print(comma, "IsDirect"); >+ out.print(comma, "IsDirect"); > >- CString string = stringOut.toCString(); >- if (!string.length()) >- out.print("None"); >- else >- out.print(string); >+ InferredType::Kind kind = InferredType::kindForFlags(flags); >+ out.print(comma, kind); >+ if (InferredType::hasStructure(kind)) >+ out.print(":", bitwise_cast<int32_t>(decodeStructureID(flags))); > } > > } // namespace WTF >Index: Source/JavaScriptCore/bytecode/PutByIdFlags.h >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdFlags.h (revision 190059) >+++ Source/JavaScriptCore/bytecode/PutByIdFlags.h (working copy) >@@ -26,13 +26,70 @@ > #ifndef PutByIdFlags_h > #define PutByIdFlags_h > >+#include "StructureIDTable.h" >+ > namespace JSC { > >-enum PutByIdFlags { >+enum PutByIdFlags : intptr_t { > PutByIdNone = 0, >- PutByIdIsDirect = 1 >+ >+ // This flag indicates that the put_by_id is direct. That means that we store the property without >+ // checking if the prototype chain has a setter. >+ PutByIdIsDirect = 0x1, >+ PutByIdPersistentFlagsMask = 0x1, >+ >+ // NOTE: The values below must be in sync with what is in LowLevelInterpreter.asm. >+ >+ // Determining the required inferred type involves first checking the primary type mask, and then >+ // using that to figure out the meaning of the secondary mask: >+ // switch (flags & PutByIdPrimaryTypeMask) { >+ // case PutByIdPrimaryTypeSecondary: >+ // switch (flags & PutByIdSecondaryTypeMask) { >+ // ... >+ // } >+ // break; >+ // case PutByIdPrimaryTypeObjectWithStructure: >+ // case PutByIdPrimaryTypeObjectWithStructureOrOther: >+ // StructureID structureID = decodeStructureID(flags); >+ // break; >+ // } >+ PutByIdPrimaryTypeMask = 0x6, >+ PutByIdPrimaryTypeSecondary = 0x0, // Need to check the secondary type mask for the type. >+ PutByIdPrimaryTypeObjectWithStructure = 0x2, // Secondary type has structure ID. >+ PutByIdPrimaryTypeObjectWithStructureOrOther = 0x4, // Secondary type has structure ID. >+ >+ PutByIdSecondaryTypeMask = -0x8, >+ PutByIdSecondaryTypeBottom = 0x0, >+ PutByIdSecondaryTypeBoolean = 0x8, >+ PutByIdSecondaryTypeOther = 0x10, >+ PutByIdSecondaryTypeInt32 = 0x18, >+ PutByIdSecondaryTypeNumber = 0x20, >+ PutByIdSecondaryTypeString = 0x28, >+ PutByIdSecondaryTypeObject = 0x30, >+ PutByIdSecondaryTypeObjectOrOther = 0x38, >+ PutByIdSecondaryTypeTop = 0x40 > }; > >+inline PutByIdFlags encodeStructureID(StructureID id) >+{ >+#if USE(JSVALUE64) >+ return static_cast<PutByIdFlags>(static_cast<PutByIdFlags>(id) << 3); >+#else >+ PutByIdFlags result = bitwise_cast<PutByIdFlags>(id); >+ ASSERT(!(result & ~PutByIdSecondaryTypeMask)); >+ return result; >+#endif >+} >+ >+inline StructureID decodeStructureID(PutByIdFlags flags) >+{ >+#if USE(JSVALUE64) >+ return static_cast<StructureID>(flags >> 3); >+#else >+ return bitwise_cast<StructureID>(flags & PutByIdSecondaryTypeMask); >+#endif >+} >+ > } // namespace JSC > > namespace WTF { >Index: Source/JavaScriptCore/bytecode/PutByIdStatus.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdStatus.cpp (revision 190059) >+++ Source/JavaScriptCore/bytecode/PutByIdStatus.cpp (working copy) >@@ -83,7 +83,7 @@ PutByIdStatus PutByIdStatus::computeFrom > if (!isValidOffset(offset)) > return PutByIdStatus(NoInformation); > >- return PutByIdVariant::replace(structure, offset); >+ return PutByIdVariant::replace(structure, offset, structure->inferredTypeDescriptorFor(uid)); > } > > Structure* newStructure = vm.heap.structureIDTable().get(newStructureID); >@@ -103,7 +103,8 @@ PutByIdStatus PutByIdStatus::computeFrom > return PutByIdStatus(NoInformation); > } > >- return PutByIdVariant::transition(structure, newStructure, conditionSet, offset); >+ return PutByIdVariant::transition( >+ structure, newStructure, conditionSet, offset, newStructure->inferredTypeDescriptorFor(uid)); > } > > PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid) >@@ -162,7 +163,7 @@ PutByIdStatus PutByIdStatus::computeForS > stubInfo->u.byIdSelf.baseObjectStructure->getConcurrently(uid); > if (isValidOffset(offset)) { > return PutByIdVariant::replace( >- stubInfo->u.byIdSelf.baseObjectStructure.get(), offset); >+ stubInfo->u.byIdSelf.baseObjectStructure.get(), offset, InferredType::Top); > } > return PutByIdStatus(TakesSlowPath); > } >@@ -193,7 +194,8 @@ PutByIdStatus PutByIdStatus::computeForS > PropertyOffset offset = structure->getConcurrently(uid); > if (!isValidOffset(offset)) > return PutByIdStatus(slowPathState); >- variant = PutByIdVariant::replace(structure, offset); >+ variant = PutByIdVariant::replace( >+ structure, offset, structure->inferredTypeDescriptorFor(uid)); > break; > } > >@@ -206,7 +208,8 @@ PutByIdStatus PutByIdStatus::computeForS > if (!conditionSet.structuresEnsureValidity()) > return PutByIdStatus(slowPathState); > variant = PutByIdVariant::transition( >- access.structure(), access.newStructure(), conditionSet, offset); >+ access.structure(), access.newStructure(), conditionSet, offset, >+ access.newStructure()->inferredTypeDescriptorFor(uid)); > break; > } > >@@ -329,8 +332,10 @@ PutByIdStatus PutByIdStatus::computeFor( > // So, better leave this alone and take slow path. > return PutByIdStatus(TakesSlowPath); > } >- >- if (!result.appendVariant(PutByIdVariant::replace(structure, offset))) >+ >+ PutByIdVariant variant = >+ PutByIdVariant::replace(structure, offset, structure->inferredTypeDescriptorFor(uid)); >+ if (!result.appendVariant(variant)) > return PutByIdStatus(TakesSlowPath); > continue; > } >@@ -356,13 +361,16 @@ PutByIdStatus PutByIdStatus::computeFor( > } > > // We only optimize if there is already a structure that the transition is cached to. >- Structure* transition = Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset); >+ Structure* transition = >+ Structure::addPropertyTransitionToExistingStructureConcurrently(structure, uid, 0, offset); > if (!transition) > return PutByIdStatus(TakesSlowPath); > ASSERT(isValidOffset(offset)); > > bool didAppend = result.appendVariant( >- PutByIdVariant::transition(structure, transition, conditionSet, offset)); >+ PutByIdVariant::transition( >+ structure, transition, conditionSet, offset, >+ transition->inferredTypeDescriptorFor(uid))); > if (!didAppend) > return PutByIdStatus(TakesSlowPath); > } >Index: Source/JavaScriptCore/bytecode/PutByIdVariant.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdVariant.cpp (revision 190059) >+++ Source/JavaScriptCore/bytecode/PutByIdVariant.cpp (working copy) >@@ -45,6 +45,7 @@ PutByIdVariant& PutByIdVariant::operator > m_newStructure = other.m_newStructure; > m_conditionSet = other.m_conditionSet; > m_offset = other.m_offset; >+ m_requiredType = other.m_requiredType; > if (other.m_callLinkStatus) > m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus); > else >@@ -52,18 +53,21 @@ PutByIdVariant& PutByIdVariant::operator > return *this; > } > >-PutByIdVariant PutByIdVariant::replace(const StructureSet& structure, PropertyOffset offset) >+PutByIdVariant PutByIdVariant::replace( >+ const StructureSet& structure, PropertyOffset offset, const InferredType::Descriptor& requiredType) > { > PutByIdVariant result; > result.m_kind = Replace; > result.m_oldStructure = structure; > result.m_offset = offset; >+ result.m_requiredType = requiredType; > return result; > } > > PutByIdVariant PutByIdVariant::transition( > const StructureSet& oldStructure, Structure* newStructure, >- const ObjectPropertyConditionSet& conditionSet, PropertyOffset offset) >+ const ObjectPropertyConditionSet& conditionSet, PropertyOffset offset, >+ const InferredType::Descriptor& requiredType) > { > PutByIdVariant result; > result.m_kind = Transition; >@@ -71,6 +75,7 @@ PutByIdVariant PutByIdVariant::transitio > result.m_newStructure = newStructure; > result.m_conditionSet = conditionSet; > result.m_offset = offset; >+ result.m_requiredType = requiredType; > return result; > } > >@@ -85,6 +90,7 @@ PutByIdVariant PutByIdVariant::setter( > result.m_conditionSet = conditionSet; > result.m_offset = offset; > result.m_callLinkStatus = WTF::move(callLinkStatus); >+ result.m_requiredType = InferredType::Top; > return result; > } > >@@ -134,9 +140,12 @@ bool PutByIdVariant::attemptToMerge(cons > { > if (m_offset != other.m_offset) > return false; >+ >+ if (m_requiredType != other.m_requiredType) >+ return false; > > switch (m_kind) { >- case Replace: >+ case Replace: { > switch (other.m_kind) { > case Replace: { > ASSERT(m_conditionSet.isEmpty()); >@@ -158,6 +167,7 @@ bool PutByIdVariant::attemptToMerge(cons > default: > return false; > } >+ } > > case Transition: > switch (other.m_kind) { >@@ -210,14 +220,16 @@ void PutByIdVariant::dumpInContext(Print > > case Replace: > out.print( >- "<Replace: ", inContext(structure(), context), ", offset = ", offset(), ">"); >+ "<Replace: ", inContext(structure(), context), ", offset = ", offset(), ", ", >+ inContext(requiredType(), context), ">"); > return; > > case Transition: > out.print( > "<Transition: ", inContext(oldStructure(), context), " -> ", > pointerDumpInContext(newStructure(), context), ", [", >- inContext(m_conditionSet, context), "], offset = ", offset(), ">"); >+ inContext(m_conditionSet, context), "], offset = ", offset(), ", ", >+ inContext(requiredType(), context), ">"); > return; > > case Setter: >Index: Source/JavaScriptCore/bytecode/PutByIdVariant.h >=================================================================== >--- Source/JavaScriptCore/bytecode/PutByIdVariant.h (revision 190059) >+++ Source/JavaScriptCore/bytecode/PutByIdVariant.h (working copy) >@@ -53,11 +53,11 @@ public: > PutByIdVariant(const PutByIdVariant&); > PutByIdVariant& operator=(const PutByIdVariant&); > >- static PutByIdVariant replace(const StructureSet&, PropertyOffset); >+ static PutByIdVariant replace(const StructureSet&, PropertyOffset, const InferredType::Descriptor&); > > static PutByIdVariant transition( > const StructureSet& oldStructure, Structure* newStructure, >- const ObjectPropertyConditionSet&, PropertyOffset); >+ const ObjectPropertyConditionSet&, PropertyOffset, const InferredType::Descriptor&); > > static PutByIdVariant setter( > const StructureSet&, PropertyOffset, const ObjectPropertyConditionSet&, >@@ -99,6 +99,11 @@ public: > return m_newStructure; > } > >+ InferredType::Descriptor requiredType() const >+ { >+ return m_requiredType; >+ } >+ > bool writesStructures() const; > bool reallocatesStorage() const; > bool makesCalls() const; >@@ -130,6 +135,7 @@ private: > Structure* m_newStructure; > ObjectPropertyConditionSet m_conditionSet; > PropertyOffset m_offset; >+ InferredType::Descriptor m_requiredType; > std::unique_ptr<CallLinkStatus> m_callLinkStatus; > }; > >Index: Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h >=================================================================== >--- Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h (revision 190059) >+++ Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h (working copy) >@@ -36,7 +36,6 @@ > #include "JSCell.h" > #include "JSString.h" > #include "ParserModes.h" >-#include "PutByIdFlags.h" > #include "RegExp.h" > #include "SpecialPointer.h" > #include "UnlinkedFunctionExecutable.h" >@@ -98,12 +97,10 @@ struct UnlinkedInstruction { > UnlinkedInstruction() { u.operand = 0; } > UnlinkedInstruction(OpcodeID opcode) { u.opcode = opcode; } > UnlinkedInstruction(int operand) { u.operand = operand; } >- UnlinkedInstruction(PutByIdFlags flags) { u.putByIdFlags = flags; } > union { > OpcodeID opcode; > int32_t operand; > unsigned index; >- PutByIdFlags putByIdFlags; > } u; > }; > >Index: Source/JavaScriptCore/bytecode/Watchpoint.h >=================================================================== >--- Source/JavaScriptCore/bytecode/Watchpoint.h (revision 190059) >+++ Source/JavaScriptCore/bytecode/Watchpoint.h (working copy) >@@ -336,7 +336,39 @@ public: > { > touch(StringFireDetail(reason)); > } >- >+ >+ // Note that for any watchpoint that is visible from the DFG, it would be incorrect to write code like: >+ // >+ // if (w.isBeingWatched()) >+ // w.fireAll() >+ // >+ // Concurrently to this, the DFG could do: >+ // >+ // if (w.isStillValid()) >+ // perform optimizations; >+ // if (!w.isStillValid()) >+ // retry compilation; >+ // >+ // Note that the DFG algorithm is widespread, and sound, because fireAll() and invalidate() will leave >+ // the watchpoint in a !isStillValid() state. Hence, if fireAll() or invalidate() interleaved between >+ // the first isStillValid() check and the second one, then it would simply cause the DFG to retry >+ // compilation later. >+ // >+ // But, if you change some piece of state that the DFG might optimize for, but invalidate the >+ // watchpoint by doing: >+ // >+ // if (w.isBeingWatched()) >+ // w.fireAll() >+ // >+ // then the DFG would never know that you invalidated state between the two checks. >+ // >+ // There are two ways to work around this: >+ // >+ // - Call fireAll() without a isBeingWatched() check. Then, the DFG will know that the watchpoint has >+ // been invalidated when it does its second check. >+ // >+ // - Do not expose the watchpoint set to the DFG directly, and have your own way of validating whether >+ // the assumptions that the DFG thread used are still valid when the DFG code is installed. > bool isBeingWatched() const > { > if (isFat()) >Index: Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >=================================================================== >--- Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (revision 190059) >+++ Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (working copy) >@@ -2114,7 +2114,7 @@ RegisterID* BytecodeGenerator::emitPutBy > instructions().append(0); // offset > instructions().append(0); // new structure > instructions().append(0); // structure chain >- instructions().append(PutByIdNone); // is not direct >+ instructions().append(static_cast<int>(PutByIdNone)); // is not direct > > return value; > } >@@ -2136,7 +2136,7 @@ RegisterID* BytecodeGenerator::emitDirec > instructions().append(0); // offset > instructions().append(0); // new structure > instructions().append(0); // structure chain (unused if direct) >- instructions().append((putType == PropertyNode::KnownDirect || property != m_vm->propertyNames->underscoreProto) ? PutByIdIsDirect : PutByIdNone); >+ instructions().append(static_cast<int>((putType == PropertyNode::KnownDirect || property != m_vm->propertyNames->underscoreProto) ? PutByIdIsDirect : PutByIdNone)); > return value; > } > >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (working copy) >@@ -116,9 +116,9 @@ public: > void dump(PrintStream& out); > > template<typename T> >- FiltrationResult filter(T node, const StructureSet& set) >+ FiltrationResult filter(T node, const StructureSet& set, SpeculatedType admittedTypes = SpecNone) > { >- return filter(forNode(node), set); >+ return filter(forNode(node), set, admittedTypes); > } > > template<typename T> >@@ -139,7 +139,7 @@ public: > return filterByValue(forNode(node), value); > } > >- FiltrationResult filter(AbstractValue&, const StructureSet&); >+ FiltrationResult filter(AbstractValue&, const StructureSet&, SpeculatedType admittedTypes = SpecNone); > FiltrationResult filterArrayModes(AbstractValue&, ArrayModes); > FiltrationResult filter(AbstractValue&, SpeculatedType); > FiltrationResult filterByValue(AbstractValue&, FrozenValue); >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (working copy) >@@ -1829,10 +1829,10 @@ bool AbstractInterpreter<AbstractStateTy > } > > AbstractValue& value = forNode(node->child1()); >- if (!value.m_structure.isTop() && !value.m_structure.isClobbered() >+ if (value.m_structure.isFinite() > && (node->child1().useKind() == CellUse || !(value.m_type & ~SpecCell))) { >- GetByIdStatus status = GetByIdStatus::computeFor( >- value.m_structure.set(), m_graph.identifiers()[node->identifierNumber()]); >+ UniquedStringImpl* uid = m_graph.identifiers()[node->identifierNumber()]; >+ GetByIdStatus status = GetByIdStatus::computeFor(value.m_structure.set(), uid); > if (status.isSimple()) { > // Figure out what the result is going to be - is it TOP, a constant, or maybe > // something more subtle? >@@ -1841,22 +1841,12 @@ bool AbstractInterpreter<AbstractStateTy > // This thing won't give us a variant that involves prototypes. If it did, we'd > // have more work to do here. > DFG_ASSERT(m_graph, node, status[i].conditionSet().isEmpty()); >- >- JSValue constantResult = >- m_graph.tryGetConstantProperty(value, status[i].offset()); >- if (!constantResult) { >- result.makeHeapTop(); >- break; >- } >- >- AbstractValue thisResult; >- thisResult.set( >- m_graph, *m_graph.freeze(constantResult), >- m_state.structureClobberState()); >- result.merge(thisResult); >+ >+ result.merge( >+ m_graph.inferredValueForProperty( >+ value, uid, status[i].offset(), m_state.structureClobberState())); > } >- if (status.numVariants() == 1 || isFTL(m_graph.m_plan.mode)) >- m_state.setFoundConstants(true); >+ m_state.setFoundConstants(true); > forNode(node) = result; > break; > } >@@ -1880,7 +1870,6 @@ bool AbstractInterpreter<AbstractStateTy > > case CheckStructure: { > AbstractValue& value = forNode(node->child1()); >- ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this. > > StructureSet& set = node->structureSet(); > >@@ -1888,12 +1877,24 @@ bool AbstractInterpreter<AbstractStateTy > // that includes the set we're testing. In that case we could make the structure check > // more efficient. We currently don't. > >- if (value.m_structure.isSubsetOf(set)) { >+ if (value.m_structure.isSubsetOf(set)) > m_state.setFoundConstants(true); >+ >+ SpeculatedType admittedTypes = SpecNone; >+ switch (node->child1().useKind()) { >+ case CellUse: >+ case KnownCellUse: >+ admittedTypes = SpecNone; >+ break; >+ case CellOrOtherUse: >+ admittedTypes = SpecOther; >+ break; >+ default: >+ DFG_CRASH(m_graph, node, "Bad use kind"); > break; > } >- >- filter(value, set); >+ >+ filter(value, set, admittedTypes); > break; > } > >@@ -2084,13 +2085,29 @@ bool AbstractInterpreter<AbstractStateTy > > case GetByOffset: { > StorageAccessData& data = node->storageAccessData(); >- JSValue result = m_graph.tryGetConstantProperty(forNode(node->child2()), data.offset); >- if (result) { >- setConstant(node, *m_graph.freeze(result)); >- break; >- } >- >- forNode(node).makeHeapTop(); >+ UniquedStringImpl* uid = m_graph.identifiers()[data.identifierNumber]; >+ >+ // FIXME: The part of this that handles inferred property types relies on AI knowing the structure >+ // right now. That's probably not optimal. In some cases, we may perform an optimization (usually >+ // by something other than AI, maybe by CSE for example) that obscures AI's view of the structure >+ // at the point where GetByOffset runs. Currently, when that happens, we'll have to rely entirely >+ // on the type that ByteCodeParser was able to prove. >+ AbstractValue value = m_graph.inferredValueForProperty( >+ forNode(node->child2()), uid, data.offset, m_state.structureClobberState()); >+ >+ // It's possible that the type that ByteCodeParser came up with is better. >+ AbstractValue typeFromParsing; >+ typeFromParsing.set(m_graph, data.inferredType, m_state.structureClobberState()); >+ value.filter(typeFromParsing); >+ >+ // If we decide that there does not exist any value that this can return, then it's probably >+ // because the compilation was already invalidated. >+ if (value.isClear()) >+ m_state.setIsValid(false); >+ >+ forNode(node) = value; >+ if (value.m_value) >+ m_state.setFoundConstants(true); > break; > } > >@@ -2120,6 +2137,8 @@ bool AbstractInterpreter<AbstractStateTy > // because of the effect on compile times, but this node is FTL-only. > m_state.setFoundConstants(true); > >+ UniquedStringImpl* uid = m_graph.identifiers()[node->multiGetByOffsetData().identifierNumber]; >+ > AbstractValue base = forNode(node->child1()); > StructureSet baseSet; > AbstractValue result; >@@ -2129,18 +2148,29 @@ bool AbstractInterpreter<AbstractStateTy > if (set.isEmpty()) > continue; > baseSet.merge(set); >- >- if (getCase.method().kind() != GetByOffsetMethod::Constant) { >- result.makeHeapTop(); >- continue; >+ >+ switch (getCase.method().kind()) { >+ case GetByOffsetMethod::Constant: { >+ AbstractValue thisResult; >+ thisResult.set( >+ m_graph, >+ *getCase.method().constant(), >+ m_state.structureClobberState()); >+ result.merge(thisResult); >+ break; > } >- >- AbstractValue thisResult; >- thisResult.set( >- m_graph, >- *getCase.method().constant(), >- m_state.structureClobberState()); >- result.merge(thisResult); >+ >+ case GetByOffsetMethod::Load: { >+ result.merge( >+ m_graph.inferredValueForProperty( >+ set, uid, m_state.structureClobberState())); >+ break; >+ } >+ >+ default: { >+ result.makeHeapTop(); >+ break; >+ } } > } > > if (forNode(node->child1()).changeStructure(m_graph, baseSet) == Contradiction) >@@ -2163,6 +2193,8 @@ bool AbstractInterpreter<AbstractStateTy > m_state.setFoundConstants(true); > > AbstractValue base = forNode(node->child1()); >+ AbstractValue originalValue = forNode(node->child2()); >+ AbstractValue resultingValue; > > for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) { > const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i]; >@@ -2170,6 +2202,11 @@ bool AbstractInterpreter<AbstractStateTy > thisSet.filter(base); > if (thisSet.isEmpty()) > continue; >+ >+ AbstractValue thisValue = originalValue; >+ thisValue.filter(m_graph, variant.requiredType()); >+ resultingValue.merge(thisValue); >+ > if (variant.kind() == PutByIdVariant::Transition) { > if (thisSet.onlyStructure() != variant.newStructure()) { > transitions.append( >@@ -2185,6 +2222,9 @@ bool AbstractInterpreter<AbstractStateTy > observeTransitions(clobberLimit, transitions); > if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction) > m_state.setIsValid(false); >+ forNode(node->child2()) = resultingValue; >+ if (!!originalValue && !resultingValue) >+ m_state.setIsValid(false); > break; > } > >@@ -2264,13 +2304,13 @@ bool AbstractInterpreter<AbstractStateTy > case PutByIdFlush: > case PutByIdDirect: { > AbstractValue& value = forNode(node->child1()); >- if (!value.m_structure.isTop() && !value.m_structure.isClobbered()) { >+ if (value.m_structure.isFinite()) { > PutByIdStatus status = PutByIdStatus::computeFor( > m_graph.globalObjectFor(node->origin.semantic), > value.m_structure.set(), > m_graph.identifiers()[node->identifierNumber()], > node->op() == PutByIdDirect); >- >+ > if (status.isSimple()) { > StructureSet newSet; > TransitionVector transitions; >@@ -2615,9 +2655,9 @@ void AbstractInterpreter<AbstractStateTy > > template<typename AbstractStateType> > FiltrationResult AbstractInterpreter<AbstractStateType>::filter( >- AbstractValue& value, const StructureSet& set) >+ AbstractValue& value, const StructureSet& set, SpeculatedType admittedTypes) > { >- if (value.filter(m_graph, set) == FiltrationOK) >+ if (value.filter(m_graph, set, admittedTypes) == FiltrationOK) > return FiltrationOK; > m_state.setIsValid(false); > return Contradiction; >Index: Source/JavaScriptCore/dfg/DFGAbstractValue.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractValue.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGAbstractValue.cpp (working copy) >@@ -117,6 +117,56 @@ void AbstractValue::setType(Graph& graph > checkConsistency(); > } > >+void AbstractValue::set(Graph& graph, const InferredType::Descriptor& descriptor) >+{ >+ switch (descriptor.kind()) { >+ case InferredType::Bottom: >+ clear(); >+ return; >+ case InferredType::Boolean: >+ setType(SpecBoolean); >+ return; >+ case InferredType::Other: >+ setType(SpecOther); >+ return; >+ case InferredType::Int32: >+ setType(SpecInt32); >+ return; >+ case InferredType::Number: >+ setType(SpecBytecodeNumber); >+ return; >+ case InferredType::String: >+ set(graph, graph.m_vm.stringStructure.get()); >+ return; >+ case InferredType::ObjectWithStructure: >+ set(graph, descriptor.structure()); >+ return; >+ case InferredType::ObjectWithStructureOrOther: >+ set(graph, descriptor.structure()); >+ merge(SpecOther); >+ return; >+ case InferredType::Object: >+ setType(graph, SpecObject); >+ return; >+ case InferredType::ObjectOrOther: >+ setType(graph, SpecObject | SpecOther); >+ return; >+ case InferredType::Top: >+ makeHeapTop(); >+ return; >+ } >+ >+ RELEASE_ASSERT_NOT_REACHED(); >+} >+ >+void AbstractValue::set( >+ Graph& graph, const InferredType::Descriptor& descriptor, StructureClobberState clobberState) >+{ >+ set(graph, descriptor); >+ if (clobberState == StructuresAreClobbered) >+ clobberStructures(); >+} >+ > void AbstractValue::fixTypeForRepresentation(Graph& graph, NodeFlags representation, Node* node) > { > if (representation == NodeResultDouble) { >@@ -189,8 +239,22 @@ bool AbstractValue::mergeOSREntryValue(G > return oldMe != *this; > } > >-FiltrationResult AbstractValue::filter(Graph& graph, const StructureSet& other) >+bool AbstractValue::isType(Graph& graph, const InferredType::Descriptor& inferredType) const > { >+ AbstractValue typeValue; >+ typeValue.set(graph, inferredType); >+ >+ AbstractValue mergedValue = *this; >+ mergedValue.merge(typeValue); >+ >+ return mergedValue == typeValue; >+} >+ >+FiltrationResult AbstractValue::filter( >+ Graph& graph, const StructureSet& other, SpeculatedType admittedTypes) >+{ >+ ASSERT(!(admittedTypes & SpecCell)); >+ > if (isClear()) > return FiltrationOK; > >@@ -198,7 +262,7 @@ FiltrationResult AbstractValue::filter(G > // having structures, array modes, or a specific value. > // https://bugs.webkit.org/show_bug.cgi?id=109663 > >- m_type &= other.speculationFromStructures(); >+ m_type &= other.speculationFromStructures() | admittedTypes; > m_arrayModes &= other.arrayModesFromStructures(); > m_structure.filter(other); > >@@ -315,6 +379,13 @@ FiltrationResult AbstractValue::filter(c > return Contradiction; > } > >+FiltrationResult AbstractValue::filter(Graph& graph, const InferredType::Descriptor& descriptor) >+{ >+ AbstractValue filterValue; >+ filterValue.set(graph, descriptor); >+ return filter(filterValue); >+} >+ > void AbstractValue::filterValueByType() > { > // We could go further, and ensure that if the futurePossibleStructure contravenes >Index: Source/JavaScriptCore/dfg/DFGAbstractValue.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractValue.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGAbstractValue.h (working copy) >@@ -34,6 +34,7 @@ > #include "DFGNodeFlags.h" > #include "DFGStructureAbstractValue.h" > #include "DFGStructureClobberState.h" >+#include "InferredType.h" > #include "JSCell.h" > #include "SpeculatedType.h" > #include "DumpContext.h" >@@ -214,7 +215,10 @@ struct AbstractValue { > m_value = JSValue(); > checkConsistency(); > } >- >+ >+ void set(Graph&, const InferredType::Descriptor&); >+ void set(Graph&, const InferredType::Descriptor&, StructureClobberState); >+ > void fixTypeForRepresentation(Graph&, NodeFlags representation, Node* = nullptr); > void fixTypeForRepresentation(Graph&, Node*); > >@@ -280,17 +284,27 @@ struct AbstractValue { > { > return !(m_type & ~desiredType); > } >+ >+ bool isType(Graph&, const InferredType::Descriptor&) const; >+ >+ // Filters the value using the given structure set. If the admittedTypes argument is not passed, this >+ // implicitly filters by the types implied by the structure set, which are usually a subset of >+ // SpecCell. Hence, after this call, the value will no longer have any non-cell members. But, you can >+ // use admittedTypes to preserve some non-cell types. Note that it's wrong for admittedTypes to overlap >+ // with SpecCell. >+ FiltrationResult filter(Graph&, const StructureSet&, SpeculatedType admittedTypes = SpecNone); > >- FiltrationResult filter(Graph&, const StructureSet&); > FiltrationResult filterArrayModes(ArrayModes); > FiltrationResult filter(SpeculatedType); > FiltrationResult filterByValue(const FrozenValue& value); > FiltrationResult filter(const AbstractValue&); >+ >+ FiltrationResult filter(Graph&, const InferredType::Descriptor&); > > FiltrationResult changeStructure(Graph&, const StructureSet&); > > bool contains(Structure*) const; >- >+ > bool validate(JSValue value) const > { > if (isHeapTop()) >Index: Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (working copy) >@@ -206,9 +206,8 @@ private: > bool handleTypedArrayConstructor(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, TypedArrayType, const ChecksFunctor& insertChecks); > template<typename ChecksFunctor> > bool handleConstantInternalFunction(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, CodeSpecializationKind, const ChecksFunctor& insertChecks); >- Node* handlePutByOffset(Node* base, unsigned identifier, PropertyOffset, Node* value); >- Node* handleGetByOffset(SpeculatedType, Node* base, unsigned identifierNumber, PropertyOffset, NodeType = GetByOffset); >- Node* handleGetByOffset(SpeculatedType, Node* base, UniquedStringImpl*, PropertyOffset, NodeType = GetByOffset); >+ Node* handlePutByOffset(Node* base, unsigned identifier, PropertyOffset, const InferredType::Descriptor&, Node* value); >+ Node* handleGetByOffset(SpeculatedType, Node* base, unsigned identifierNumber, PropertyOffset, const InferredType::Descriptor&, NodeType = GetByOffset); > > // Create a presence ObjectPropertyCondition based on some known offset and structure set. Does not > // check the validity of the condition, but it may return a null one if it encounters a contradiction. >@@ -2306,7 +2305,9 @@ bool ByteCodeParser::handleConstantInter > return false; > } > >-Node* ByteCodeParser::handleGetByOffset(SpeculatedType prediction, Node* base, unsigned identifierNumber, PropertyOffset offset, NodeType op) >+Node* ByteCodeParser::handleGetByOffset( >+ SpeculatedType prediction, Node* base, unsigned identifierNumber, PropertyOffset offset, >+ const InferredType::Descriptor& inferredType, NodeType op) > { > Node* propertyStorage; > if (isInlineOffset(offset)) >@@ -2317,13 +2318,17 @@ Node* ByteCodeParser::handleGetByOffset( > StorageAccessData* data = m_graph.m_storageAccessData.add(); > data->offset = offset; > data->identifierNumber = identifierNumber; >+ data->inferredType = inferredType; >+ m_graph.registerInferredType(inferredType); > > Node* getByOffset = addToGraph(op, OpInfo(data), OpInfo(prediction), propertyStorage, base); > > return getByOffset; > } > >-Node* ByteCodeParser::handlePutByOffset(Node* base, unsigned identifier, PropertyOffset offset, Node* value) >+Node* ByteCodeParser::handlePutByOffset( >+ Node* base, unsigned identifier, PropertyOffset offset, const InferredType::Descriptor& inferredType, >+ Node* value) > { > Node* propertyStorage; > if (isInlineOffset(offset)) >@@ -2334,6 +2339,8 @@ Node* ByteCodeParser::handlePutByOffset( > StorageAccessData* data = m_graph.m_storageAccessData.add(); > data->offset = offset; > data->identifierNumber = identifier; >+ data->inferredType = inferredType; >+ m_graph.registerInferredType(inferredType); > > Node* result = addToGraph(PutByOffset, OpInfo(data), propertyStorage, base, value); > >@@ -2448,7 +2455,8 @@ Node* ByteCodeParser::load( > return addToGraph(JSConstant, OpInfo(method.constant())); > case GetByOffsetMethod::LoadFromPrototype: { > Node* baseNode = addToGraph(JSConstant, OpInfo(method.prototype())); >- return handleGetByOffset(prediction, baseNode, identifierNumber, method.offset(), op); >+ return handleGetByOffset( >+ prediction, baseNode, identifierNumber, method.offset(), InferredType::Top, op); > } > case GetByOffsetMethod::Load: > // Will never see this from planLoad(). >@@ -2554,13 +2562,13 @@ Node* ByteCodeParser::load( > > bool needStructureCheck = true; > >+ UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber]; >+ > if (JSObject* knownBase = base->dynamicCastConstant<JSObject*>()) { > // Try to optimize away the structure check. Note that it's not worth doing anything about this > // if the base's structure is watched. > Structure* structure = base->constant()->structure(); > if (!structure->dfgShouldWatch()) { >- UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber]; >- > if (!variant.conditionSet().isEmpty()) { > // This means that we're loading from a prototype. We expect the base not to have the > // property. We can only use ObjectPropertyCondition if all of the structures in the >@@ -2625,9 +2633,18 @@ Node* ByteCodeParser::load( > if (constant) > return weakJSConstant(constant); > } >+ >+ InferredType::Descriptor inferredType; >+ if (needStructureCheck) { >+ for (Structure* structure : variant.structureSet()) { >+ InferredType::Descriptor thisType = m_graph.inferredTypeForProperty(structure, uid); >+ inferredType.merge(thisType); >+ } >+ } else >+ inferredType = InferredType::Top; > > loadedValue = handleGetByOffset( >- loadPrediction, base, identifierNumber, variant.offset(), loadOp); >+ loadPrediction, base, identifierNumber, variant.offset(), inferredType, loadOp); > } > > return loadedValue; >@@ -2638,7 +2655,7 @@ Node* ByteCodeParser::store(Node* base, > RELEASE_ASSERT(variant.kind() == PutByIdVariant::Replace); > > checkPresenceLike(base, m_graph.identifiers()[identifier], variant.offset(), variant.structure()); >- return handlePutByOffset(base, identifier, variant.offset(), value); >+ return handlePutByOffset(base, identifier, variant.offset(), variant.requiredType(), value); > } > > void ByteCodeParser::handleGetById( >@@ -2797,6 +2814,9 @@ void ByteCodeParser::handlePutById( > > if (m_graph.compilation()) > m_graph.compilation()->noticeInlinedPutById(); >+ >+ for (const PutByIdVariant& variant : putByIdStatus.variants()) >+ m_graph.registerInferredType(variant.requiredType()); > > MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add(); > data->variants = putByIdStatus.variants(); >@@ -2853,6 +2873,8 @@ void ByteCodeParser::handlePutById( > StorageAccessData* data = m_graph.m_storageAccessData.add(); > data->offset = variant.offset(); > data->identifierNumber = identifierNumber; >+ data->inferredType = variant.requiredType(); >+ m_graph.registerInferredType(data->inferredType); > > addToGraph( > PutByOffset, >Index: Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp (working copy) >@@ -64,6 +64,8 @@ bool clobbersExitState(Graph& graph, Nod > case PhantomCreateActivation: > case MaterializeCreateActivation: > case CountExecution: >+ case AllocatePropertyStorage: >+ case ReallocatePropertyStorage: > // These do clobber memory, but nothing that is observable. It may be nice to separate the > // heaps into those that are observable and those that aren't, but we don't do that right now. > // FIXME: https://bugs.webkit.org/show_bug.cgi?id=148440 >Index: Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (working copy) >@@ -33,6 +33,7 @@ > #include "DFGBasicBlock.h" > #include "DFGGraph.h" > #include "DFGInPlaceAbstractState.h" >+#include "DFGInferredTypeCheck.h" > #include "DFGInsertionSet.h" > #include "DFGPhase.h" > #include "GetByIdStatus.h" >@@ -373,7 +374,8 @@ private: > && variant.oldStructure().onlyStructure() == variant.newStructure()) { > variant = PutByIdVariant::replace( > variant.oldStructure(), >- variant.offset()); >+ variant.offset(), >+ variant.requiredType()); > changed = true; > } > } >@@ -448,6 +450,7 @@ private: > ASSERT(childEdge.useKind() == CellUse); > > AbstractValue baseValue = m_state.forNode(child); >+ AbstractValue valueValue = m_state.forNode(node->child2()); > > m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before. > alreadyHandled = true; // Don't allow the default constant folder to do things to this. >@@ -463,7 +466,7 @@ private: > > if (!status.isSimple()) > break; >- >+ > ASSERT(status.numVariants()); > > if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode)) >@@ -640,7 +643,9 @@ private: > emitGetByOffset(indexInBlock, node, childEdge, identifierNumber, variant.offset()); > } > >- void emitGetByOffset(unsigned indexInBlock, Node* node, Edge childEdge, unsigned identifierNumber, PropertyOffset offset) >+ void emitGetByOffset( >+ unsigned indexInBlock, Node* node, Edge childEdge, unsigned identifierNumber, >+ PropertyOffset offset, const InferredType::Descriptor& inferredType = InferredType::Top) > { > childEdge.setUseKind(KnownCellUse); > >@@ -656,6 +661,7 @@ private: > StorageAccessData& data = *m_graph.m_storageAccessData.add(); > data.offset = offset; > data.identifierNumber = identifierNumber; >+ data.inferredType = inferredType; > > node->convertToGetByOffset(data, propertyStorage); > } >@@ -664,8 +670,10 @@ private: > { > NodeOrigin origin = node->origin; > Edge childEdge = node->child1(); >- >+ > addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure()); >+ insertInferredTypeCheck( >+ m_insertionSet, indexInBlock, origin, node->child2().node(), variant.requiredType()); > > node->child1().setUseKind(KnownCellUse); > childEdge.setUseKind(KnownCellUse); >@@ -730,6 +738,7 @@ private: > // Arises when we prune MultiGetByOffset. We could have a > // MultiGetByOffset with a single variant that checks for structure S, > // and the input has structures S and T, for example. >+ ASSERT(node->child1()); > m_insertionSet.insertNode( > indexInBlock, SpecNone, CheckStructure, node->origin, > OpInfo(m_graph.addStructureSet(set)), node->child1()); >Index: Source/JavaScriptCore/dfg/DFGDesiredInferredType.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGDesiredInferredType.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGDesiredInferredType.h (working copy) >@@ -0,0 +1,132 @@ >+/* >+ * Copyright (C) 2015 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 DFGDesiredInferredType_h >+#define DFGDesiredInferredType_h >+ >+#if ENABLE(DFG_JIT) >+ >+#include "InferredType.h" >+#include <wtf/HashMap.h> >+ >+namespace JSC { namespace DFG { >+ >+// This documents the DFG's expectation for an InferredType: specifically, that we want it to have a live >+// InferredType object and that this object has the type that we originally saw. >+ >+class DesiredInferredType { >+public: >+ DesiredInferredType() >+ : m_object(nullptr) >+ { >+ } >+ >+ DesiredInferredType(InferredType* object, const InferredType::Descriptor& expected) >+ : m_object(object) >+ , m_expected(expected) >+ { >+ } >+ >+ DesiredInferredType(WTF::HashTableDeletedValueType) >+ : m_object(nullptr) >+ , m_expected(InferredType::Top) >+ { >+ } >+ >+ explicit operator bool() const { return m_object && m_expected; } >+ >+ InferredType* object() const { return m_object; } >+ InferredType::Descriptor expected() const { return m_expected; } >+ >+ bool isStillValid() const >+ { >+ return m_object->canWatch(m_expected); >+ } >+ >+ void add(Watchpoint* watchpoint) const >+ { >+ m_object->addWatchpoint(watchpoint); >+ } >+ >+ bool operator==(const DesiredInferredType& other) const >+ { >+ return m_object == other.m_object >+ && m_expected == other.m_expected; >+ } >+ >+ bool operator!=(const DesiredInferredType& other) const >+ { >+ return !(*this == other); >+ } >+ >+ bool isHashTableDeletedValue() const >+ { >+ return !m_object && m_expected == InferredType::Top; >+ } >+ >+ unsigned hash() const >+ { >+ return WTF::PtrHash<InferredType*>::hash(m_object) + m_expected.hash() * 7; >+ } >+ >+ void dumpInContext(PrintStream& out, DumpContext* context) const >+ { >+ out.print(inContext(m_expected, context), " for ", RawPointer(m_object)); >+ } >+ >+ void dump(PrintStream& out) const >+ { >+ dumpInContext(out, nullptr); >+ } >+ >+private: >+ InferredType* m_object; >+ InferredType::Descriptor m_expected; >+}; >+ >+struct DesiredInferredTypeHash { >+ static unsigned hash(const DesiredInferredType& key) { return key.hash(); } >+ static bool equal(const DesiredInferredType& a, const DesiredInferredType& b) { return a == b; } >+ static const bool safeToCompareToEmptyOrDeleted = true; >+}; >+ >+} } // namespace JSC::DFG >+ >+namespace WTF { >+ >+template<typename T> struct DefaultHash; >+template<> struct DefaultHash<JSC::DFG::DesiredInferredType> { >+ typedef JSC::DFG::DesiredInferredTypeHash Hash; >+}; >+ >+template<typename T> struct HashTraits; >+template<> struct HashTraits<JSC::DFG::DesiredInferredType> : SimpleClassHashTraits<JSC::DFG::DesiredInferredType> { }; >+ >+} // namespace WTF >+ >+#endif // ENABLE(DFG_JIT) >+ >+#endif // DFGDesiredInferredType_h >+ >Index: Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp (working copy) >@@ -65,6 +65,11 @@ void AdaptiveStructureWatchpointAdaptor: > } > } > >+void InferredTypeAdaptor::add(CodeBlock* codeBlock, const DesiredInferredType& key, CommonData& common) >+{ >+ key.add(common.watchpoints.add(codeBlock)); >+} >+ > DesiredWatchpoints::DesiredWatchpoints() { } > DesiredWatchpoints::~DesiredWatchpoints() { } > >@@ -93,6 +98,11 @@ void DesiredWatchpoints::addLazily(const > m_adaptiveStructureSets.addLazily(key); > } > >+void DesiredWatchpoints::addLazily(const DesiredInferredType& key) >+{ >+ m_inferredTypes.addLazily(key); >+} >+ > bool DesiredWatchpoints::consider(Structure* structure) > { > if (!structure->dfgShouldWatch()) >@@ -108,6 +118,7 @@ void DesiredWatchpoints::reallyAdd(CodeB > m_inferredValues.reallyAdd(codeBlock, commonData); > m_bufferViews.reallyAdd(codeBlock, commonData); > m_adaptiveStructureSets.reallyAdd(codeBlock, commonData); >+ m_inferredTypes.reallyAdd(codeBlock, commonData); > } > > bool DesiredWatchpoints::areStillValid() const >@@ -116,7 +127,8 @@ bool DesiredWatchpoints::areStillValid() > && m_inlineSets.areStillValid() > && m_inferredValues.areStillValid() > && m_bufferViews.areStillValid() >- && m_adaptiveStructureSets.areStillValid(); >+ && m_adaptiveStructureSets.areStillValid() >+ && m_inferredTypes.areStillValid(); > } > > void DesiredWatchpoints::dumpInContext(PrintStream& out, DumpContext* context) const >@@ -127,6 +139,7 @@ void DesiredWatchpoints::dumpInContext(P > out.print(" Inferred values: ", inContext(m_inferredValues, context), "\n"); > out.print(" Buffer views: ", inContext(m_bufferViews, context), "\n"); > out.print(" Object property conditions: ", inContext(m_adaptiveStructureSets, context), "\n"); >+ out.print(" Inferred types: ", inContext(m_inferredTypes, context), "\n"); > } > > } } // namespace JSC::DFG >Index: Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h (working copy) >@@ -30,6 +30,7 @@ > > #include "CodeOrigin.h" > #include "DFGCommonData.h" >+#include "DFGDesiredInferredType.h" > #include "InferredValue.h" > #include "JSArrayBufferView.h" > #include "ObjectPropertyCondition.h" >@@ -91,6 +92,18 @@ struct AdaptiveStructureWatchpointAdapto > } > }; > >+struct InferredTypeAdaptor { >+ static void add(CodeBlock*, const DesiredInferredType&, CommonData&); >+ static bool hasBeenInvalidated(const DesiredInferredType& key) >+ { >+ return !key.isStillValid(); >+ } >+ static void dumpInContext(PrintStream& out, const DesiredInferredType& key, DumpContext* context) >+ { >+ out.print(inContext(key, context)); >+ } >+}; >+ > template<typename WatchpointSetType, typename Adaptor = SetPointerAdaptor<WatchpointSetType>> > class GenericDesiredWatchpoints { > #if !ASSERT_DISABLED >@@ -159,6 +172,10 @@ public: > // It's recommended that you don't call this directly. Use Graph::watchCondition(), which does > // the required GC magic as well as some other bookkeeping. > void addLazily(const ObjectPropertyCondition&); >+ >+ // It's recommended that you don't call this directly. Use Graph::inferredTypeFor(), which does >+ // the required GC magic. >+ void addLazily(const DesiredInferredType&); > > bool consider(Structure*); > >@@ -186,6 +203,10 @@ public: > { > return m_adaptiveStructureSets.isWatched(key); > } >+ bool isWatched(const DesiredInferredType& key) >+ { >+ return m_inferredTypes.isWatched(key); >+ } > > void dumpInContext(PrintStream&, DumpContext*) const; > void dump(PrintStream&) const; >@@ -196,6 +217,7 @@ private: > GenericDesiredWatchpoints<InferredValue*, InferredValueAdaptor> m_inferredValues; > GenericDesiredWatchpoints<JSArrayBufferView*, ArrayBufferViewWatchpointAdaptor> m_bufferViews; > GenericDesiredWatchpoints<ObjectPropertyCondition, AdaptiveStructureWatchpointAdaptor> m_adaptiveStructureSets; >+ GenericDesiredWatchpoints<DesiredInferredType, InferredTypeAdaptor> m_inferredTypes; > }; > > } } // namespace JSC::DFG >Index: Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (working copy) >@@ -30,6 +30,7 @@ > > #include "ArrayPrototype.h" > #include "DFGGraph.h" >+#include "DFGInferredTypeCheck.h" > #include "DFGInsertionSet.h" > #include "DFGPhase.h" > #include "DFGPredictionPropagationPhase.h" >@@ -1092,6 +1093,9 @@ private: > if (!node->child1()->hasStorageResult()) > fixEdge<KnownCellUse>(node->child1()); > fixEdge<KnownCellUse>(node->child2()); >+ insertInferredTypeCheck( >+ m_insertionSet, m_indexInBlock, node->origin, node->child3().node(), >+ node->storageAccessData().inferredType); > speculateForBarrier(node->child3()); > break; > } >Index: Source/JavaScriptCore/dfg/DFGGraph.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGGraph.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGGraph.cpp (working copy) >@@ -261,12 +261,16 @@ void Graph::dump(PrintStream& out, const > StorageAccessData& storageAccessData = node->storageAccessData(); > out.print(comma, "id", storageAccessData.identifierNumber, "{", identifiers()[storageAccessData.identifierNumber], "}"); > out.print(", ", static_cast<ptrdiff_t>(storageAccessData.offset)); >+ out.print(", inferredType = ", inContext(storageAccessData.inferredType, context)); > } > if (node->hasMultiGetByOffsetData()) { > MultiGetByOffsetData& data = node->multiGetByOffsetData(); > out.print(comma, "id", data.identifierNumber, "{", identifiers()[data.identifierNumber], "}"); >- for (unsigned i = 0; i < data.cases.size(); ++i) >+ for (unsigned i = 0; i < data.cases.size(); ++i) { > out.print(comma, inContext(data.cases[i], context)); >+ if (data.cases[i].method().kind() == GetByOffsetMethod::Load) >+ out.print(" (inferred value = ", inContext(inferredValueForProperty(data.cases[i].set(), identifiers()[data.identifierNumber]), context), ")"); >+ } > } > if (node->hasMultiPutByOffsetData()) { > MultiPutByOffsetData& data = node->multiPutByOffsetData(); >@@ -887,6 +891,38 @@ bool Graph::isSafeToLoad(JSObject* base, > return m_safeToLoad.contains(std::make_pair(base, offset)); > } > >+InferredType::Descriptor Graph::inferredTypeFor(const PropertyTypeKey& key) >+{ >+ assertIsRegistered(key.structure()); >+ >+ auto iter = m_inferredTypes.find(key); >+ if (iter != m_inferredTypes.end()) >+ return iter->value; >+ >+ InferredType* typeObject = key.structure()->inferredTypeFor(key.uid()); >+ if (!typeObject) { >+ m_inferredTypes.add(key, InferredType::Top); >+ return InferredType::Top; >+ } >+ >+ InferredType::Descriptor typeDescriptor = typeObject->descriptor(); >+ if (typeDescriptor.kind() == InferredType::Top) { >+ m_inferredTypes.add(key, InferredType::Top); >+ return InferredType::Top; >+ } >+ >+ m_inferredTypes.add(key, typeDescriptor); >+ >+ m_plan.weakReferences.addLazily(typeObject); >+ registerInferredType(typeDescriptor); >+ >+ // Note that we may already be watching this desired inferred type, because multiple structures may >+ // point to the same InferredType instance. >+ m_plan.watchpoints.addLazily(DesiredInferredType(typeObject, typeDescriptor)); >+ >+ return typeDescriptor; >+} >+ > FullBytecodeLiveness& Graph::livenessFor(CodeBlock* codeBlock) > { > HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>>::iterator iter = m_bytecodeLiveness.find(codeBlock); >@@ -1061,7 +1097,7 @@ JSValue Graph::tryGetConstantProperty(JS > JSValue Graph::tryGetConstantProperty( > JSValue base, const StructureAbstractValue& structure, PropertyOffset offset) > { >- if (structure.isTop() || structure.isClobbered()) { >+ if (structure.isInfinite()) { > // FIXME: If we just converted the offset to a uid, we could do ObjectPropertyCondition > // watching to constant-fold the property. > // https://bugs.webkit.org/show_bug.cgi?id=147271 >@@ -1076,6 +1112,37 @@ JSValue Graph::tryGetConstantProperty(co > return tryGetConstantProperty(base.m_value, base.m_structure, offset); > } > >+AbstractValue Graph::inferredValueForProperty( >+ const StructureSet& base, UniquedStringImpl* uid, StructureClobberState clobberState) >+{ >+ AbstractValue result; >+ base.forEach( >+ [&] (Structure* structure) { >+ AbstractValue value; >+ value.set(*this, inferredTypeForProperty(structure, uid)); >+ result.merge(value); >+ }); >+ if (clobberState == StructuresAreClobbered) >+ result.clobberStructures(); >+ return result; >+} >+ >+AbstractValue Graph::inferredValueForProperty( >+ const AbstractValue& base, UniquedStringImpl* uid, PropertyOffset offset, >+ StructureClobberState clobberState) >+{ >+ if (JSValue value = tryGetConstantProperty(base, offset)) { >+ AbstractValue result; >+ result.set(*this, *freeze(value), clobberState); >+ return result; >+ } >+ >+ if (base.m_structure.isFinite()) >+ return inferredValueForProperty(base.m_structure.set(), uid, clobberState); >+ >+ return AbstractValue::heapTop(); >+} >+ > JSValue Graph::tryGetConstantClosureVar(JSValue base, ScopeOffset offset) > { > // This has an awesome concurrency story. See comment for GetGlobalVar in ByteCodeParser. >Index: Source/JavaScriptCore/dfg/DFGGraph.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGGraph.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGGraph.h (working copy) >@@ -41,6 +41,7 @@ > #include "DFGNodeAllocator.h" > #include "DFGPlan.h" > #include "DFGPrePostNumbering.h" >+#include "DFGPropertyTypeKey.h" > #include "DFGScannable.h" > #include "FullBytecodeLiveness.h" > #include "JSStack.h" >@@ -675,6 +676,27 @@ public: > // Checks if it's known that loading from the given object at the given offset is fine. This is > // computed by tracking which conditions we track with watchCondition(). > bool isSafeToLoad(JSObject* base, PropertyOffset); >+ >+ void registerInferredType(const InferredType::Descriptor& type) >+ { >+ if (type.structure()) >+ registerStructure(type.structure()); >+ } >+ >+ // Tells us what inferred type we are able to prove the property to have now and in the future. >+ InferredType::Descriptor inferredTypeFor(const PropertyTypeKey&); >+ InferredType::Descriptor inferredTypeForProperty(Structure* structure, UniquedStringImpl* uid) >+ { >+ return inferredTypeFor(PropertyTypeKey(structure, uid)); >+ } >+ >+ AbstractValue inferredValueForProperty( >+ const StructureSet& base, UniquedStringImpl* uid, StructureClobberState = StructuresAreWatched); >+ >+ // This uses either constant property inference or property type inference to derive a good abstract >+ // value for some property accessed with the given abstract value base. >+ AbstractValue inferredValueForProperty( >+ const AbstractValue& base, UniquedStringImpl* uid, PropertyOffset, StructureClobberState); > > FullBytecodeLiveness& livenessFor(CodeBlock*); > FullBytecodeLiveness& livenessFor(InlineCallFrame*); >@@ -856,6 +878,7 @@ public: > HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>> m_bytecodeLiveness; > HashMap<CodeBlock*, std::unique_ptr<BytecodeKills>> m_bytecodeKills; > HashSet<std::pair<JSObject*, PropertyOffset>> m_safeToLoad; >+ HashMap<PropertyTypeKey, InferredType::Descriptor> m_inferredTypes; > Dominators m_dominators; > PrePostNumbering m_prePostNumbering; > NaturalLoops m_naturalLoops; >Index: Source/JavaScriptCore/dfg/DFGInferredTypeCheck.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGInferredTypeCheck.cpp (revision 0) >+++ Source/JavaScriptCore/dfg/DFGInferredTypeCheck.cpp (working copy) >@@ -0,0 +1,98 @@ >+/* >+ * Copyright (C) 2015 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 "DFGInferredTypeCheck.h" >+ >+#if ENABLE(DFG_JIT) >+ >+#include "JSCInlines.h" >+ >+namespace JSC { namespace DFG { >+ >+void insertInferredTypeCheck( >+ InsertionSet& insertionSet, unsigned nodeIndex, NodeOrigin origin, Node* baseNode, >+ const InferredType::Descriptor& type) >+{ >+ insertionSet.graph().registerInferredType(type); >+ >+ switch (type.kind()) { >+ case InferredType::Bottom: >+ insertionSet.insertNode(nodeIndex, SpecNone, ForceOSRExit, origin); >+ return; >+ >+ case InferredType::Boolean: >+ insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, BooleanUse)); >+ return; >+ >+ case InferredType::Other: >+ insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, OtherUse)); >+ return; >+ >+ case InferredType::Int32: >+ insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, Int32Use)); >+ return; >+ >+ case InferredType::Number: >+ insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, NumberUse)); >+ return; >+ >+ case InferredType::String: >+ insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, StringUse)); >+ return; >+ >+ case InferredType::ObjectWithStructure: >+ insertionSet.insertNode( >+ nodeIndex, SpecNone, CheckStructure, origin, >+ OpInfo(insertionSet.graph().addStructureSet(type.structure())), >+ Edge(baseNode, CellUse)); >+ return; >+ >+ case InferredType::ObjectWithStructureOrOther: >+ insertionSet.insertNode( >+ nodeIndex, SpecNone, CheckStructure, origin, >+ OpInfo(insertionSet.graph().addStructureSet(type.structure())), >+ Edge(baseNode, CellOrOtherUse)); >+ return; >+ >+ case InferredType::Object: >+ insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, ObjectUse)); >+ return; >+ >+ case InferredType::ObjectOrOther: >+ insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, ObjectOrOtherUse)); >+ return; >+ >+ case InferredType::Top: >+ return; >+ } >+ >+ DFG_CRASH(insertionSet.graph(), baseNode, "Bad inferred type"); >+} >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >Index: Source/JavaScriptCore/dfg/DFGInferredTypeCheck.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGInferredTypeCheck.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGInferredTypeCheck.h (working copy) >@@ -0,0 +1,45 @@ >+/* >+ * Copyright (C) 2015 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 DFGInferredTypeCheck_h >+#define DFGInferredTypeCheck_h >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DFGInsertionSet.h" >+#include "InferredType.h" >+ >+namespace JSC { namespace DFG { >+ >+// Inserts a type check that ensures that baseNode has the given inferred type. >+void insertInferredTypeCheck( >+ InsertionSet&, unsigned nodeIndex, NodeOrigin, Node* baseNode, const InferredType::Descriptor&); >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >+#endif // DFGInferredTypeCheck_h >+ >Index: Source/JavaScriptCore/dfg/DFGNode.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNode.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGNode.h (working copy) >@@ -64,6 +64,11 @@ struct BasicBlock; > struct StorageAccessData { > PropertyOffset offset; > unsigned identifierNumber; >+ >+ // This needs to know the inferred type. For puts, this is necessary because we need to remember >+ // what check is needed. For gets, this is necessary because otherwise AI might forget what type is >+ // guaranteed. >+ InferredType::Descriptor inferredType; > }; > > struct MultiPutByOffsetData { >Index: Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp (working copy) >@@ -2135,14 +2135,20 @@ private: > for (Structure* structure : structures) { > PropertyOffset offset = structure->getConcurrently(uid); > if (offset != currentOffset) { >+ // Because our analysis treats MultiPutByOffset like an escape, we only have to >+ // deal with storing results that would have been previously stored by PutByOffset >+ // nodes. Those nodes were guarded by the appropriate type checks. This means that >+ // at this point, we can simply trust that the incoming value has the right type >+ // for whatever structure we are using. > data->variants.append( >- PutByIdVariant::replace(currentSet, currentOffset)); >+ PutByIdVariant::replace(currentSet, currentOffset, InferredType::Top)); > currentOffset = offset; > currentSet.clear(); > } > currentSet.add(structure); > } >- data->variants.append(PutByIdVariant::replace(currentSet, currentOffset)); >+ data->variants.append( >+ PutByIdVariant::replace(currentSet, currentOffset, InferredType::Top)); > } > > return m_graph.addNode( >Index: Source/JavaScriptCore/dfg/DFGPropertyTypeKey.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGPropertyTypeKey.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGPropertyTypeKey.h (working copy) >@@ -0,0 +1,129 @@ >+/* >+ * Copyright (C) 2015 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 DFGPropertyTypeKey_h >+#define DFGPropertyTypeKey_h >+ >+#if ENABLE(DFG_JIT) >+ >+#include "Structure.h" >+#include <wtf/HashMap.h> >+ >+namespace JSC { namespace DFG { >+ >+// This object is a key for finding a property's type. It's a tuple of Structure* and UniquedStringImpl*. >+ >+class PropertyTypeKey { >+public: >+ PropertyTypeKey() >+ : m_structure(nullptr) >+ , m_uid(nullptr) >+ { >+ } >+ >+ PropertyTypeKey(Structure* structure, UniquedStringImpl* uid) >+ : m_structure(structure) >+ , m_uid(uid) >+ { >+ } >+ >+ PropertyTypeKey(WTF::HashTableDeletedValueType) >+ : m_structure(nullptr) >+ , m_uid(deletedUID()) >+ { >+ } >+ >+ explicit operator bool() const { return m_structure && m_uid; } >+ >+ Structure* structure() const { return m_structure; } >+ UniquedStringImpl* uid() const { return m_uid; } >+ >+ bool operator==(const PropertyTypeKey& other) const >+ { >+ return m_structure == other.m_structure >+ && m_uid == other.m_uid; >+ } >+ >+ bool operator!=(const PropertyTypeKey& other) const >+ { >+ return !(*this == other); >+ } >+ >+ unsigned hash() const >+ { >+ return WTF::PtrHash<Structure*>::hash(m_structure) + WTF::PtrHash<UniquedStringImpl*>::hash(m_uid); >+ } >+ >+ bool isHashTableDeletedValue() const >+ { >+ return !m_structure && m_uid == deletedUID(); >+ } >+ >+ void dumpInContext(PrintStream& out, DumpContext* context) const >+ { >+ out.print(pointerDumpInContext(m_structure, context), "+", m_uid); >+ } >+ >+ void dump(PrintStream& out) const >+ { >+ dumpInContext(out, nullptr); >+ } >+ >+private: >+ static UniquedStringImpl* deletedUID() >+ { >+ return bitwise_cast<UniquedStringImpl*>(static_cast<intptr_t>(1)); >+ } >+ >+ Structure* m_structure; >+ UniquedStringImpl* m_uid; >+}; >+ >+struct PropertyTypeKeyHash { >+ static unsigned hash(const PropertyTypeKey& key) { return key.hash(); } >+ static bool equal(const PropertyTypeKey& a, const PropertyTypeKey& b) { return a == b; } >+ static const bool safeToCompareToEmptyOrDeleted = true; >+}; >+ >+} } // namespace JSC::DFG >+ >+namespace WTF { >+ >+template<typename T> struct DefaultHash; >+template<> struct DefaultHash<JSC::DFG::PropertyTypeKey> { >+ typedef JSC::DFG::PropertyTypeKeyHash Hash; >+}; >+ >+template<typename T> struct HashTraits; >+template<> struct HashTraits<JSC::DFG::PropertyTypeKey> : SimpleClassHashTraits<JSC::DFG::PropertyTypeKey> { >+ static const bool emptyValueIsZero = false; >+}; >+ >+} // namespace WTF >+ >+#endif // ENABLE(DFG_JIT) >+ >+#endif // DFGPropertyTypeKey_h >+ >Index: Source/JavaScriptCore/dfg/DFGSafeToExecute.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSafeToExecute.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGSafeToExecute.h (working copy) >@@ -53,6 +53,7 @@ public: > case RealNumberUse: > case BooleanUse: > case CellUse: >+ case CellOrOtherUse: > case ObjectUse: > case FunctionUse: > case FinalObjectUse: >@@ -127,6 +128,9 @@ bool safeToExecute(AbstractStateType& st > if (!safeToExecuteEdge.result()) > return false; > >+ // NOTE: This tends to lie when it comes to effectful nodes, because it knows that they aren't going to >+ // get hoisted anyway. >+ > switch (node->op()) { > case JSConstant: > case DoubleConstant: >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (working copy) >@@ -3901,36 +3901,7 @@ void SpeculativeJIT::compile(Node* node) > } > > case CheckStructure: { >- SpeculateCellOperand base(this, node->child1()); >- >- ASSERT(node->structureSet().size()); >- >- if (node->structureSet().size() == 1) { >- speculationCheck( >- BadCache, JSValueSource::unboxedCell(base.gpr()), 0, >- m_jit.branchWeakPtr( >- JITCompiler::NotEqual, >- JITCompiler::Address(base.gpr(), JSCell::structureIDOffset()), >- node->structureSet()[0])); >- } else { >- GPRTemporary structure(this); >- >- m_jit.loadPtr(JITCompiler::Address(base.gpr(), JSCell::structureIDOffset()), structure.gpr()); >- >- JITCompiler::JumpList done; >- >- for (size_t i = 0; i < node->structureSet().size() - 1; ++i) >- done.append(m_jit.branchWeakPtr(JITCompiler::Equal, structure.gpr(), node->structureSet()[i])); >- >- speculationCheck( >- BadCache, JSValueSource::unboxedCell(base.gpr()), 0, >- m_jit.branchWeakPtr( >- JITCompiler::NotEqual, structure.gpr(), node->structureSet().last())); >- >- done.link(&m_jit); >- } >- >- noResult(node); >+ compileCheckStructure(node); > break; > } > >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (working copy) >@@ -3916,38 +3916,7 @@ void SpeculativeJIT::compile(Node* node) > } > > case CheckStructure: { >- SpeculateCellOperand base(this, node->child1()); >- >- ASSERT(node->structureSet().size()); >- >- ExitKind exitKind; >- if (node->child1()->hasConstant()) >- exitKind = BadConstantCache; >- else >- exitKind = BadCache; >- >- if (node->structureSet().size() == 1) { >- speculationCheck( >- exitKind, JSValueSource::unboxedCell(base.gpr()), 0, >- m_jit.branchWeakStructure( >- JITCompiler::NotEqual, >- JITCompiler::Address(base.gpr(), JSCell::structureIDOffset()), >- node->structureSet()[0])); >- } else { >- JITCompiler::JumpList done; >- >- for (size_t i = 0; i < node->structureSet().size() - 1; ++i) >- done.append(m_jit.branchWeakStructure(JITCompiler::Equal, MacroAssembler::Address(base.gpr(), JSCell::structureIDOffset()), node->structureSet()[i])); >- >- speculationCheck( >- exitKind, JSValueSource::unboxedCell(base.gpr()), 0, >- m_jit.branchWeakStructure( >- JITCompiler::NotEqual, MacroAssembler::Address(base.gpr(), JSCell::structureIDOffset()), node->structureSet().last())); >- >- done.link(&m_jit); >- } >- >- noResult(node); >+ compileCheckStructure(node); > break; > } > >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (working copy) >@@ -5232,6 +5232,81 @@ void SpeculativeJIT::compileTypeOf(Node* > cellResult(resultGPR, node); > } > >+void SpeculativeJIT::compileCheckStructure(Node* node, GPRReg cellGPR, GPRReg tempGPR) >+{ >+ ASSERT(node->structureSet().size()); >+ >+ if (node->structureSet().size() == 1) { >+ speculationCheck( >+ BadCache, JSValueSource::unboxedCell(cellGPR), 0, >+ m_jit.branchWeakStructure( >+ JITCompiler::NotEqual, >+ JITCompiler::Address(cellGPR, JSCell::structureIDOffset()), >+ node->structureSet()[0])); >+ } else { >+ std::unique_ptr<GPRTemporary> structure; >+ GPRReg structureGPR; >+ >+ if (tempGPR == InvalidGPRReg) { >+ structure = std::make_unique<GPRTemporary>(this); >+ structureGPR = structure->gpr(); >+ } else >+ structureGPR = tempGPR; >+ >+ m_jit.load32(JITCompiler::Address(cellGPR, JSCell::structureIDOffset()), structureGPR); >+ >+ JITCompiler::JumpList done; >+ >+ for (size_t i = 0; i < node->structureSet().size() - 1; ++i) { >+ done.append( >+ m_jit.branchWeakStructure(JITCompiler::Equal, structureGPR, node->structureSet()[i])); >+ } >+ >+ speculationCheck( >+ BadCache, JSValueSource::unboxedCell(cellGPR), 0, >+ m_jit.branchWeakStructure( >+ JITCompiler::NotEqual, structureGPR, node->structureSet().last())); >+ >+ done.link(&m_jit); >+ } >+} >+ >+void SpeculativeJIT::compileCheckStructure(Node* node) >+{ >+ switch (node->child1().useKind()) { >+ case CellUse: >+ case KnownCellUse: { >+ SpeculateCellOperand cell(this, node->child1()); >+ compileCheckStructure(node, cell.gpr(), InvalidGPRReg); >+ noResult(node); >+ return; >+ } >+ >+ case CellOrOtherUse: { >+ JSValueOperand value(this, node->child1(), ManualOperandSpeculation); >+ GPRTemporary temp(this); >+ >+ JSValueRegs valueRegs = value.jsValueRegs(); >+ GPRReg tempGPR = temp.gpr(); >+ >+ JITCompiler::Jump cell = m_jit.branchIfCell(valueRegs); >+ DFG_TYPE_CHECK( >+ valueRegs, node->child1(), SpecCell | SpecOther, >+ m_jit.branchIfNotOther(valueRegs, tempGPR)); >+ JITCompiler::Jump done = m_jit.jump(); >+ cell.link(&m_jit); >+ compileCheckStructure(node, valueRegs.payloadGPR(), tempGPR); >+ done.link(&m_jit); >+ noResult(node); >+ return; >+ } >+ >+ default: >+ DFG_CRASH(m_jit.graph(), node, "Bad use kind"); >+ return; >+ } >+} >+ > void SpeculativeJIT::compileAllocatePropertyStorage(Node* node) > { > if (node->transition()->previous->couldHaveIndexingHeader()) { >@@ -5636,6 +5711,22 @@ void SpeculativeJIT::speculateCell(Edge > (SpeculateCellOperand(this, edge)).gpr(); > } > >+void SpeculativeJIT::speculateCellOrOther(Edge edge) >+{ >+ if (!needsTypeCheck(edge, SpecCell | SpecOther)) >+ return; >+ >+ JSValueOperand operand(this, edge, ManualOperandSpeculation); >+ GPRTemporary temp(this); >+ GPRReg tempGPR = temp.gpr(); >+ >+ MacroAssembler::Jump ok = m_jit.branchIfCell(operand.jsValueRegs()); >+ DFG_TYPE_CHECK( >+ operand.jsValueRegs(), edge, SpecCell | SpecOther, >+ m_jit.branchIfNotOther(operand.jsValueRegs(), tempGPR)); >+ ok.link(&m_jit); >+} >+ > void SpeculativeJIT::speculateObject(Edge edge) > { > if (!needsTypeCheck(edge, SpecObject)) >@@ -5916,6 +6007,9 @@ void SpeculativeJIT::speculate(Node*, Ed > case CellUse: > speculateCell(edge); > break; >+ case CellOrOtherUse: >+ speculateCellOrOther(edge); >+ break; > case ObjectUse: > speculateObject(edge); > break; >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (working copy) >@@ -2215,6 +2215,8 @@ public: > void compileIsObjectOrNull(Node*); > void compileIsFunction(Node*); > void compileTypeOf(Node*); >+ void compileCheckStructure(Node*, GPRReg cellGPR, GPRReg tempGPR); >+ void compileCheckStructure(Node*); > > void moveTrueTo(GPRReg); > void moveFalseTo(GPRReg); >@@ -2374,6 +2376,7 @@ public: > void speculateDoubleRepReal(Edge); > void speculateBoolean(Edge); > void speculateCell(Edge); >+ void speculateCellOrOther(Edge); > void speculateObject(Edge); > void speculateFunction(Edge); > void speculateFinalObject(Edge); >Index: Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp (working copy) >@@ -314,7 +314,7 @@ private: > case AllocatePropertyStorage: > case ReallocatePropertyStorage: > // These allocate but then run their own barrier. >- insertBarrierWithInvalidExit(m_nodeIndex + 1, Edge(m_node->child1().node(), KnownCellUse)); >+ insertBarrier(m_nodeIndex + 1, Edge(m_node->child1().node(), KnownCellUse)); > m_node->setEpoch(Epoch()); > break; > >@@ -481,11 +481,6 @@ private: > insertBarrier(m_nodeIndex, base); > } > >- void insertBarrierWithInvalidExit(unsigned nodeIndex, Edge base) >- { >- insertBarrier(nodeIndex, base, false); >- } >- > void insertBarrier(unsigned nodeIndex, Edge base, bool exitOK = true) > { > // If we're in global mode, we should only insert the barriers once we have converged. >Index: Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h (working copy) >@@ -192,7 +192,7 @@ public: > } > > Structure* operator[](size_t i) const { return at(i); } >- >+ > // In most cases, what you really want to do is verify whether the set is top or clobbered, and > // if not, enumerate the set of structures. Use this only in cases where the singleton case is > // meaningfully special, like for transitions. >@@ -202,6 +202,13 @@ public: > return nullptr; > return m_set.onlyStructure(); > } >+ >+ template<typename Functor> >+ void forEach(const Functor& functor) const >+ { >+ ASSERT(!isTop()); >+ m_set.forEach(functor); >+ } > > void dumpInContext(PrintStream&, DumpContext*) const; > void dump(PrintStream&) const; >Index: Source/JavaScriptCore/dfg/DFGUseKind.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGUseKind.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGUseKind.cpp (working copy) >@@ -79,6 +79,9 @@ void printInternal(PrintStream& out, Use > case KnownCellUse: > out.print("KnownCell"); > return; >+ case CellOrOtherUse: >+ out.print("CellOrOther"); >+ return; > case ObjectUse: > out.print("Object"); > return; >Index: Source/JavaScriptCore/dfg/DFGUseKind.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGUseKind.h (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGUseKind.h (working copy) >@@ -51,6 +51,7 @@ enum UseKind { > KnownBooleanUse, > CellUse, > KnownCellUse, >+ CellOrOtherUse, > ObjectUse, > FunctionUse, > FinalObjectUse, >@@ -108,6 +109,8 @@ inline SpeculatedType typeFilterFor(UseK > case CellUse: > case KnownCellUse: > return SpecCell; >+ case CellOrOtherUse: >+ return SpecCell | SpecOther; > case ObjectUse: > return SpecObject; > case FunctionUse: >Index: Source/JavaScriptCore/dfg/DFGValidate.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGValidate.cpp (revision 190059) >+++ Source/JavaScriptCore/dfg/DFGValidate.cpp (working copy) >@@ -263,6 +263,9 @@ public: > VALIDATE((node), !!node->child1()); > VALIDATE((node), !!node->child2()); > break; >+ case CheckStructure: >+ VALIDATE((node), !!node->child1()); >+ break; > case PutStructure: > VALIDATE((node), !node->transition()->previous->dfgShouldWatch()); > break; >Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp (revision 190059) >+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp (working copy) >@@ -418,6 +418,7 @@ CapabilityLevel canCompile(Graph& graph) > case KnownBooleanUse: > case CellUse: > case KnownCellUse: >+ case CellOrOtherUse: > case ObjectUse: > case FunctionUse: > case ObjectOrOtherUse: >Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (revision 190059) >+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (working copy) >@@ -2026,21 +2026,57 @@ private: > > void compileCheckStructure() > { >- LValue cell = lowCell(m_node->child1()); >- > ExitKind exitKind; > if (m_node->child1()->hasConstant()) > exitKind = BadConstantCache; > else > exitKind = BadCache; >- >- LValue structureID = m_out.load32(cell, m_heaps.JSCell_structureID); >- >- checkStructure( >- structureID, jsValueValue(cell), exitKind, m_node->structureSet(), >- [this] (Structure* structure) { >- return weakStructureID(structure); >- }); >+ >+ switch (m_node->child1().useKind()) { >+ case CellUse: >+ case KnownCellUse: { >+ LValue cell = lowCell(m_node->child1()); >+ >+ checkStructure( >+ m_out.load32(cell, m_heaps.JSCell_structureID), jsValueValue(cell), >+ exitKind, m_node->structureSet(), >+ [&] (Structure* structure) { >+ return weakStructureID(structure); >+ }); >+ return; >+ } >+ >+ case CellOrOtherUse: { >+ LValue value = lowJSValue(m_node->child1(), ManualOperandSpeculation); >+ >+ LBasicBlock cellCase = FTL_NEW_BLOCK(m_out, ("CheckStructure CellOrOtherUse cell case")); >+ LBasicBlock notCellCase = FTL_NEW_BLOCK(m_out, ("CheckStructure CellOrOtherUse not cell case")); >+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("CheckStructure CellOrOtherUse continuation")); >+ >+ m_out.branch( >+ isCell(value, provenType(m_node->child1())), unsure(cellCase), unsure(notCellCase)); >+ >+ LBasicBlock lastNext = m_out.appendTo(cellCase, notCellCase); >+ checkStructure( >+ m_out.load32(value, m_heaps.JSCell_structureID), jsValueValue(value), >+ exitKind, m_node->structureSet(), >+ [&] (Structure* structure) { >+ return weakStructureID(structure); >+ }); >+ m_out.jump(continuation); >+ >+ m_out.appendTo(notCellCase, continuation); >+ FTL_TYPE_CHECK(jsValueValue(value), m_node->child1(), SpecCell | SpecOther, isNotOther(value)); >+ m_out.jump(continuation); >+ >+ m_out.appendTo(continuation, lastNext); >+ return; >+ } >+ >+ default: >+ DFG_CRASH(m_graph, m_node, "Bad use kind"); >+ return; >+ } > } > > void compileCheckCell() >@@ -4022,6 +4058,8 @@ private: > m_out.appendTo(blocks[i], i + 1 < data.variants.size() ? blocks[i + 1] : exit); > > PutByIdVariant variant = data.variants[i]; >+ >+ checkInferredType(m_node->child2(), value, variant.requiredType()); > > LValue storage; > if (variant.kind() == PutByIdVariant::Replace) { >@@ -5674,6 +5712,135 @@ private: > m_out.appendTo(continuation, lastNext); > return m_out.phi(m_out.int32, results); > } >+ >+ void checkInferredType(Edge edge, LValue value, const InferredType::Descriptor& type) >+ { >+ // This cannot use FTL_TYPE_CHECK or typeCheck() because it is called partially, as in a node like: >+ // >+ // MultiPutByOffset(...) >+ // >+ // may be lowered to: >+ // >+ // switch (object->structure) { >+ // case 42: >+ // checkInferredType(..., type1); >+ // ... >+ // break; >+ // case 43: >+ // checkInferredType(..., type2); >+ // ... >+ // break; >+ // } >+ // >+ // where type1 and type2 are different. Using typeCheck() would mean that the edge would be >+ // filtered by type1 & type2, instead of type1 | type2. >+ >+ switch (type.kind()) { >+ case InferredType::Bottom: >+ speculate(BadType, jsValueValue(value), edge.node(), m_out.booleanTrue); >+ return; >+ >+ case InferredType::Boolean: >+ speculate(BadType, jsValueValue(value), edge.node(), isNotBoolean(value, provenType(edge))); >+ return; >+ >+ case InferredType::Other: >+ speculate(BadType, jsValueValue(value), edge.node(), isNotOther(value, provenType(edge))); >+ return; >+ >+ case InferredType::Int32: >+ speculate(BadType, jsValueValue(value), edge.node(), isNotInt32(value, provenType(edge))); >+ return; >+ >+ case InferredType::Number: >+ speculate(BadType, jsValueValue(value), edge.node(), isNotNumber(value, provenType(edge))); >+ return; >+ >+ case InferredType::String: >+ speculate(BadType, jsValueValue(value), edge.node(), isNotCell(value, provenType(edge))); >+ speculate(BadType, jsValueValue(value), edge.node(), isNotString(value, provenType(edge))); >+ return; >+ >+ case InferredType::ObjectWithStructure: >+ speculate(BadType, jsValueValue(value), edge.node(), isNotCell(value, provenType(edge))); >+ if (!abstractValue(edge).m_structure.isSubsetOf(StructureSet(type.structure()))) { >+ speculate( >+ BadType, jsValueValue(value), edge.node(), >+ m_out.notEqual( >+ m_out.load32(value, m_heaps.JSCell_structureID), >+ weakStructureID(type.structure()))); >+ } >+ return; >+ >+ case InferredType::ObjectWithStructureOrOther: { >+ LBasicBlock cellCase = FTL_NEW_BLOCK(m_out, ("checkInferredType ObjectWithStructureOrOther cell case")); >+ LBasicBlock notCellCase = FTL_NEW_BLOCK(m_out, ("checkInferredType ObjectWithStructureOrOther not cell case")); >+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("checkInferredType ObjectWithStructureOrOther continuation")); >+ >+ m_out.branch(isCell(value, provenType(edge)), unsure(cellCase), unsure(notCellCase)); >+ >+ LBasicBlock lastNext = m_out.appendTo(cellCase, notCellCase); >+ >+ if (!abstractValue(edge).m_structure.isSubsetOf(StructureSet(type.structure()))) { >+ speculate( >+ BadType, jsValueValue(value), edge.node(), >+ m_out.notEqual( >+ m_out.load32(value, m_heaps.JSCell_structureID), >+ weakStructureID(type.structure()))); >+ } >+ >+ m_out.jump(continuation); >+ >+ m_out.appendTo(notCellCase, continuation); >+ >+ speculate( >+ BadType, jsValueValue(value), edge.node(), >+ isNotOther(value, provenType(edge) & ~SpecCell)); >+ >+ m_out.jump(continuation); >+ >+ m_out.appendTo(continuation, lastNext); >+ return; >+ } >+ >+ case InferredType::Object: >+ speculate(BadType, jsValueValue(value), edge.node(), isNotCell(value, provenType(edge))); >+ speculate(BadType, jsValueValue(value), edge.node(), isNotObject(value, provenType(edge))); >+ return; >+ >+ case InferredType::ObjectOrOther: { >+ LBasicBlock cellCase = FTL_NEW_BLOCK(m_out, ("checkInferredType ObjectOrOther cell case")); >+ LBasicBlock notCellCase = FTL_NEW_BLOCK(m_out, ("checkInferredType ObjectOrOther not cell case")); >+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("checkInferredType ObjectOrOther continuation")); >+ >+ m_out.branch(isCell(value, provenType(edge)), unsure(cellCase), unsure(notCellCase)); >+ >+ LBasicBlock lastNext = m_out.appendTo(cellCase, notCellCase); >+ >+ speculate( >+ BadType, jsValueValue(value), edge.node(), >+ isNotObject(value, provenType(edge) & SpecCell)); >+ >+ m_out.jump(continuation); >+ >+ m_out.appendTo(notCellCase, continuation); >+ >+ speculate( >+ BadType, jsValueValue(value), edge.node(), >+ isNotOther(value, provenType(edge) & ~SpecCell)); >+ >+ m_out.jump(continuation); >+ >+ m_out.appendTo(continuation, lastNext); >+ return; >+ } >+ >+ case InferredType::Top: >+ return; >+ } >+ >+ DFG_CRASH(m_graph, m_node, "Bad inferred type"); >+ } > > LValue loadProperty(LValue storage, unsigned identifierNumber, PropertyOffset offset) > { >@@ -7514,6 +7681,9 @@ private: > case CellUse: > speculateCell(edge); > break; >+ case CellOrOtherUse: >+ speculateCellOrOther(edge); >+ break; > case KnownCellUse: > ASSERT(!m_interpreter.needsTypeCheck(edge)); > break; >@@ -7594,6 +7764,22 @@ private: > lowCell(edge); > } > >+ void speculateCellOrOther(Edge edge) >+ { >+ LValue value = lowJSValue(edge, ManualOperandSpeculation); >+ >+ LBasicBlock isNotCell = FTL_NEW_BLOCK(m_out, ("Speculate CellOrOther not cell")); >+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("Speculate CellOrOther continuation")); >+ >+ m_out.branch(isCell(value, provenType(edge)), unsure(continuation), unsure(isNotCell)); >+ >+ LBasicBlock lastNext = m_out.appendTo(isNotCell, continuation); >+ FTL_TYPE_CHECK(jsValueValue(value), edge, SpecCell | SpecOther, isNotOther(value)); >+ m_out.jump(continuation); >+ >+ m_out.appendTo(continuation, lastNext); >+ } >+ > void speculateMachineInt(Edge edge) > { > if (!m_interpreter.needsTypeCheck(edge)) >@@ -8127,6 +8313,9 @@ private: > } > } > >+ if (failCondition == m_out.booleanFalse) >+ return; >+ > ASSERT(m_ftlState.jitCode->osrExit.size() == m_ftlState.finalizer->osrExit.size()); > > m_ftlState.jitCode->osrExit.append(OSRExit( >@@ -8135,9 +8324,14 @@ private: > availabilityMap().m_locals.numberOfArguments(), > availabilityMap().m_locals.numberOfLocals())); > m_ftlState.finalizer->osrExit.append(OSRExitCompilationInfo()); >- >+ > OSRExit& exit = m_ftlState.jitCode->osrExit.last(); > >+ if (failCondition == m_out.booleanTrue) { >+ emitOSRExitCall(exit, lowValue); >+ return; >+ } >+ > LBasicBlock lastNext = nullptr; > LBasicBlock continuation = nullptr; > >Index: Source/JavaScriptCore/jit/AssemblyHelpers.cpp >=================================================================== >--- Source/JavaScriptCore/jit/AssemblyHelpers.cpp (revision 190059) >+++ Source/JavaScriptCore/jit/AssemblyHelpers.cpp (working copy) >@@ -55,6 +55,78 @@ Vector<BytecodeAndMachineOffset>& Assemb > return result.iterator->value; > } > >+AssemblyHelpers::JumpList AssemblyHelpers::branchIfNotType( >+ JSValueRegs regs, GPRReg tempGPR, const InferredType::Descriptor& descriptor, TagRegistersMode mode) >+{ >+ AssemblyHelpers::JumpList result; >+ >+ switch (descriptor.kind()) { >+ case InferredType::Bottom: >+ result.append(jump()); >+ break; >+ >+ case InferredType::Boolean: >+ result.append(branchIfNotBoolean(regs, tempGPR)); >+ break; >+ >+ case InferredType::Other: >+ result.append(branchIfNotOther(regs, tempGPR)); >+ break; >+ >+ case InferredType::Int32: >+ result.append(branchIfNotInt32(regs, mode)); >+ break; >+ >+ case InferredType::Number: >+ result.append(branchIfNotNumber(regs, tempGPR, mode)); >+ break; >+ >+ case InferredType::String: >+ result.append(branchIfNotCell(regs, mode)); >+ result.append(branchIfNotString(regs.payloadGPR())); >+ break; >+ >+ case InferredType::ObjectWithStructure: >+ result.append(branchIfNotCell(regs, mode)); >+ result.append( >+ branchStructure( >+ NotEqual, >+ Address(regs.payloadGPR(), JSCell::structureIDOffset()), >+ descriptor.structure())); >+ break; >+ >+ case InferredType::ObjectWithStructureOrOther: { >+ Jump ok = branchIfOther(regs, tempGPR); >+ result.append(branchIfNotCell(regs, mode)); >+ result.append( >+ branchStructure( >+ NotEqual, >+ Address(regs.payloadGPR(), JSCell::structureIDOffset()), >+ descriptor.structure())); >+ ok.link(this); >+ break; >+ } >+ >+ case InferredType::Object: >+ result.append(branchIfNotCell(regs, mode)); >+ result.append(branchIfNotObject(regs.payloadGPR())); >+ break; >+ >+ case InferredType::ObjectOrOther: { >+ Jump ok = branchIfOther(regs, tempGPR); >+ result.append(branchIfNotCell(regs, mode)); >+ result.append(branchIfNotObject(regs.payloadGPR())); >+ ok.link(this); >+ break; >+ } >+ >+ case InferredType::Top: >+ break; >+ } >+ >+ return result; >+} >+ > void AssemblyHelpers::purifyNaN(FPRReg fpr) > { > MacroAssembler::Jump notNaN = branchDouble(DoubleEqual, fpr, fpr); >Index: Source/JavaScriptCore/jit/AssemblyHelpers.h >=================================================================== >--- Source/JavaScriptCore/jit/AssemblyHelpers.h (revision 190059) >+++ Source/JavaScriptCore/jit/AssemblyHelpers.h (working copy) >@@ -555,37 +555,48 @@ public: > #endif > } > >- Jump branchIfNotCell(GPRReg reg) >+ enum TagRegistersMode { >+ DoNotHaveTagRegisters, >+ HaveTagRegisters >+ }; >+ >+ Jump branchIfNotCell(GPRReg reg, TagRegistersMode mode = HaveTagRegisters) > { > #if USE(JSVALUE64) >- return branchTest64(MacroAssembler::NonZero, reg, GPRInfo::tagMaskRegister); >+ if (mode == HaveTagRegisters) >+ return branchTest64(NonZero, reg, GPRInfo::tagMaskRegister); >+ return branchTest64(NonZero, reg, TrustedImm64(TagMask)); > #else >+ UNUSED_PARAM(mode); > return branch32(MacroAssembler::NotEqual, reg, TrustedImm32(JSValue::CellTag)); > #endif > } >- Jump branchIfNotCell(JSValueRegs regs) >+ Jump branchIfNotCell(JSValueRegs regs, TagRegistersMode mode = HaveTagRegisters) > { > #if USE(JSVALUE64) >- return branchIfNotCell(regs.gpr()); >+ return branchIfNotCell(regs.gpr(), mode); > #else >- return branchIfNotCell(regs.tagGPR()); >+ return branchIfNotCell(regs.tagGPR(), mode); > #endif > } > >- Jump branchIfCell(GPRReg reg) >+ Jump branchIfCell(GPRReg reg, TagRegistersMode mode = HaveTagRegisters) > { > #if USE(JSVALUE64) >- return branchTest64(MacroAssembler::Zero, reg, GPRInfo::tagMaskRegister); >+ if (mode == HaveTagRegisters) >+ return branchTest64(Zero, reg, GPRInfo::tagMaskRegister); >+ return branchTest64(Zero, reg, TrustedImm64(TagMask)); > #else >+ UNUSED_PARAM(mode); > return branch32(MacroAssembler::Equal, reg, TrustedImm32(JSValue::CellTag)); > #endif > } >- Jump branchIfCell(JSValueRegs regs) >+ Jump branchIfCell(JSValueRegs regs, TagRegistersMode mode = HaveTagRegisters) > { > #if USE(JSVALUE64) >- return branchIfCell(regs.gpr()); >+ return branchIfCell(regs.gpr(), mode); > #else >- return branchIfCell(regs.tagGPR()); >+ return branchIfCell(regs.tagGPR(), mode); > #endif > } > >@@ -613,43 +624,55 @@ public: > #endif > } > >- Jump branchIfInt32(JSValueRegs regs) >+ Jump branchIfInt32(JSValueRegs regs, TagRegistersMode mode = HaveTagRegisters) > { > #if USE(JSVALUE64) >- return branch64(AboveOrEqual, regs.gpr(), GPRInfo::tagTypeNumberRegister); >+ if (mode == HaveTagRegisters) >+ return branch64(AboveOrEqual, regs.gpr(), GPRInfo::tagTypeNumberRegister); >+ return branch64(AboveOrEqual, regs.gpr(), TrustedImm64(TagTypeNumber)); > #else >+ UNUSED_PARAM(mode); > return branch32(Equal, regs.tagGPR(), TrustedImm32(JSValue::Int32Tag)); > #endif > } > >- Jump branchIfNotInt32(JSValueRegs regs) >+ Jump branchIfNotInt32(JSValueRegs regs, TagRegistersMode mode = HaveTagRegisters) > { > #if USE(JSVALUE64) >- return branch64(Below, regs.gpr(), GPRInfo::tagTypeNumberRegister); >+ if (mode == HaveTagRegisters) >+ return branch64(Below, regs.gpr(), GPRInfo::tagTypeNumberRegister); >+ return branch64(Below, regs.gpr(), TrustedImm64(TagTypeNumber)); > #else >+ UNUSED_PARAM(mode); > return branch32(NotEqual, regs.tagGPR(), TrustedImm32(JSValue::Int32Tag)); > #endif > } > > // Note that the tempGPR is not used in 64-bit mode. >- Jump branchIfNumber(JSValueRegs regs, GPRReg tempGPR) >+ Jump branchIfNumber(JSValueRegs regs, GPRReg tempGPR, TagRegistersMode mode = HaveTagRegisters) > { > #if USE(JSVALUE64) > UNUSED_PARAM(tempGPR); >- return branchTest64(NonZero, regs.gpr(), GPRInfo::tagTypeNumberRegister); >+ if (mode == HaveTagRegisters) >+ return branchTest64(NonZero, regs.gpr(), GPRInfo::tagTypeNumberRegister); >+ return branchTest64(NonZero, regs.gpr(), TrustedImm64(TagTypeNumber)); > #else >+ UNUSED_PARAM(mode); > add32(TrustedImm32(1), regs.tagGPR(), tempGPR); > return branch32(Below, tempGPR, TrustedImm32(JSValue::LowestTag + 1)); > #endif > } > > // Note that the tempGPR is not used in 64-bit mode. >- Jump branchIfNotNumber(JSValueRegs regs, GPRReg tempGPR) >+ Jump branchIfNotNumber(JSValueRegs regs, GPRReg tempGPR, TagRegistersMode mode = HaveTagRegisters) > { > #if USE(JSVALUE64) > UNUSED_PARAM(tempGPR); >- return branchTest64(Zero, regs.gpr(), GPRInfo::tagTypeNumberRegister); >+ if (mode == HaveTagRegisters) >+ return branchTest64(Zero, regs.gpr(), GPRInfo::tagTypeNumberRegister); >+ return branchTest64(Zero, regs.gpr(), TrustedImm64(TagTypeNumber)); > #else >+ UNUSED_PARAM(mode); > add32(TrustedImm32(1), regs.tagGPR(), tempGPR); > return branch32(AboveOrEqual, tempGPR, TrustedImm32(JSValue::LowestTag + 1)); > #endif >@@ -719,6 +742,9 @@ public: > #endif > } > >+ JumpList branchIfNotType( >+ JSValueRegs, GPRReg tempGPR, const InferredType::Descriptor&, TagRegistersMode); >+ > template<typename T> > Jump branchStructure(RelationalCondition condition, T leftHandSide, Structure* structure) > { >Index: Source/JavaScriptCore/jit/Repatch.cpp >=================================================================== >--- Source/JavaScriptCore/jit/Repatch.cpp (revision 190059) >+++ Source/JavaScriptCore/jit/Repatch.cpp (working copy) >@@ -378,7 +378,8 @@ static InlineCacheAction tryCachePutByID > ptrdiff_t offsetToPatchedStorage = offsetRelativeToPatchedStorage(slot.cachedOffset()); > if (stubInfo.cacheType == CacheType::Unset > && MacroAssembler::isPtrAlignedAddressOffset(offsetToPatchedStorage) >- && !structure->needImpurePropertyWatchpoint()) { >+ && !structure->needImpurePropertyWatchpoint() >+ && !structure->inferredTypeFor(ident.impl())) { > > repatchByIdSelfAccess( > codeBlock, stubInfo, structure, slot.cachedOffset(), >Index: Source/JavaScriptCore/llint/LLIntSlowPaths.cpp >=================================================================== >--- Source/JavaScriptCore/llint/LLIntSlowPaths.cpp (revision 190059) >+++ Source/JavaScriptCore/llint/LLIntSlowPaths.cpp (working copy) >@@ -631,6 +631,8 @@ LLINT_SLOW_PATH_DECL(slow_path_put_by_id > pc[5].u.pointer = nullptr; // offset > pc[6].u.pointer = nullptr; // new structure > pc[7].u.pointer = nullptr; // structure chain >+ pc[8].u.putByIdFlags = >+ static_cast<PutByIdFlags>(pc[8].u.putByIdFlags & PutByIdPersistentFlagsMask); > > JSCell* baseCell = baseValue.asCell(); > Structure* structure = baseCell->structure(); >@@ -658,12 +660,18 @@ LLINT_SLOW_PATH_DECL(slow_path_put_by_id > pc[7].u.structureChain.set( > vm, codeBlock->ownerExecutable(), chain); > } >+ pc[8].u.putByIdFlags = static_cast<PutByIdFlags>( >+ pc[8].u.putByIdFlags | >+ structure->inferredTypeDescriptorFor(ident.impl()).putByIdFlags()); > } > } > } else { > structure->didCachePropertyReplacement(vm, slot.cachedOffset()); > pc[4].u.structureID = structure->id(); > pc[5].u.operand = slot.cachedOffset(); >+ pc[8].u.putByIdFlags = static_cast<PutByIdFlags>( >+ pc[8].u.putByIdFlags | >+ structure->inferredTypeDescriptorFor(ident.impl()).putByIdFlags()); > } > } > } >Index: Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >=================================================================== >--- Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm (revision 190059) >+++ Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm (working copy) >@@ -1391,17 +1391,91 @@ _llint_op_put_by_id: > # At this point, we have: > # t2 -> currentStructureID > # t0 -> object base >+ # We will lose currentStructureID in the shenanigans below. > >+ loadi 12[PC], t1 >+ loadConstantOrVariable(t1, t2, t3) >+ loadi 32[PC], t1 >+ >+ # At this point, we have: >+ # t0 -> object base >+ # t1 -> put by id flags >+ # t2 -> value tag >+ # t3 -> value payload >+ >+ btinz t1, PutByIdPrimaryTypeMask, .opPutByIdTypeCheckObjectWithStructureOrOther >+ >+ # We have one of the non-structure type checks. Find out which one. >+ andi PutByIdSecondaryTypeMask, t1 >+ bilt t1, PutByIdSecondaryTypeString, .opPutByIdTypeCheckLessThanString >+ >+ # We are one of the following: String, Object, ObjectOrOther, Top >+ bilt t1, PutByIdSecondaryTypeObjectOrOther, .opPutByIdTypeCheckLessThanObjectOrOther >+ >+ # We are either ObjectOrOther or Top. >+ bieq t1, PutByIdSecondaryTypeTop, .opPutByIdDoneCheckingTypes >+ >+ # Check if we are ObjectOrOther. >+ bieq t2, CellTag, .opPutByIdTypeCheckObject >+.opPutByIdTypeCheckOther: >+ bieq t2, NullTag, .opPutByIdDoneCheckingTypes >+ bieq t2, UndefinedTag, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckLessThanObjectOrOther: >+ # We are either String or Object. >+ bineq t2, CellTag, .opPutByIdSlow >+ bieq t1, PutByIdSecondaryTypeObject, .opPutByIdTypeCheckObject >+ bbeq JSCell::m_type[t3], StringType, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+.opPutByIdTypeCheckObject: >+ bbaeq JSCell::m_type[t3], ObjectType, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckLessThanString: >+ # We are one of the following: Bottom, Boolean, Other, Int32, Number. >+ bilt t1, PutByIdSecondaryTypeInt32, .opPutByIdTypeCheckLessThanInt32 >+ >+ # We are either Int32 or Number. >+ bieq t1, PutByIdSecondaryTypeNumber, .opPutByIdTypeCheckNumber >+ >+ bieq t2, Int32Tag, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckNumber: >+ bib t2, LowestTag + 1, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckLessThanInt32: >+ # We are one of the following: Bottom, Boolean, Other >+ bineq t1, PutByIdSecondaryTypeBoolean, .opPutByIdTypeCheckBottomOrOther >+ bieq t2, BooleanTag, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckBottomOrOther: >+ bieq t1, PutByIdSecondaryTypeOther, .opPutByIdTypeCheckOther >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckObjectWithStructureOrOther: >+ bieq t2, CellTag, .opPutByIdTypeCheckObjectWithStructure >+ btinz t1, PutByIdPrimaryTypeObjectWithStructureOrOther, .opPutByIdTypeCheckOther >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckObjectWithStructure: >+ andi PutByIdSecondaryTypeMask, t1 >+ bineq t1, JSCell::m_structureID[t3], .opPutByIdSlow >+ >+.opPutByIdDoneCheckingTypes: > loadi 24[PC], t1 > > btiz t1, .opPutByIdNotTransition > >- # This is the transition case. t1 holds the new Structure*. t2 holds the old Structure*. >- # If we have a chain, we need to check it. t0 is the base. We may clobber t1 to use it as >- # scratch. >+ # This is the transition case. t1 holds the new Structure*. If we have a chain, we need to >+ # check it. t0 is the base. We may clobber t1 to use it as scratch. > loadp 28[PC], t3 > btpz t3, .opPutByIdTransitionDirect > >+ loadi 16[PC], t2 # Need old structure again. > loadp StructureChain::m_vector[t3], t3 > assert(macro (ok) btpnz t3, ok end) > >Index: Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >=================================================================== >--- Source/JavaScriptCore/llint/LowLevelInterpreter64.asm (revision 190059) >+++ Source/JavaScriptCore/llint/LowLevelInterpreter64.asm (working copy) >@@ -1266,14 +1266,88 @@ _llint_op_put_by_id: > writeBarrierOnOperands(1, 3) > loadisFromInstruction(1, t3) > loadConstantOrVariableCell(t3, t0, .opPutByIdSlow) >- loadi JSCell::m_structureID[t0], t2 >- loadisFromInstruction(4, t1) >- bineq t2, t1, .opPutByIdSlow >+ loadisFromInstruction(4, t2) >+ bineq t2, JSCell::m_structureID[t0], .opPutByIdSlow > > # At this point, we have: >- # t1, t2 -> current structure ID >+ # t2 -> current structure ID > # t0 -> object base > >+ loadisFromInstruction(3, t1) >+ loadConstantOrVariable(t1, t3) >+ >+ loadpFromInstruction(8, t1) >+ >+ # At this point, we have: >+ # t0 -> object base >+ # t1 -> put by id flags >+ # t2 -> current structure ID >+ # t3 -> value to put >+ >+ btpnz t1, PutByIdPrimaryTypeMask, .opPutByIdTypeCheckObjectWithStructureOrOther >+ >+ # We have one of the non-structure type checks. Find out which one. >+ andp PutByIdSecondaryTypeMask, t1 >+ bplt t1, PutByIdSecondaryTypeString, .opPutByIdTypeCheckLessThanString >+ >+ # We are one of the following: String, Object, ObjectOrOther, Top >+ bplt t1, PutByIdSecondaryTypeObjectOrOther, .opPutByIdTypeCheckLessThanObjectOrOther >+ >+ # We are either ObjectOrOther or Top. >+ bpeq t1, PutByIdSecondaryTypeTop, .opPutByIdDoneCheckingTypes >+ >+ # Check if we are ObjectOrOther. >+ btqz t3, tagMask, .opPutByIdTypeCheckObject >+.opPutByIdTypeCheckOther: >+ andq ~TagBitUndefined, t3 >+ bqeq t3, ValueNull, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckLessThanObjectOrOther: >+ # We are either String or Object. >+ btqnz t3, tagMask, .opPutByIdSlow >+ bpeq t1, PutByIdSecondaryTypeObject, .opPutByIdTypeCheckObject >+ bbeq JSCell::m_type[t3], StringType, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+.opPutByIdTypeCheckObject: >+ bbaeq JSCell::m_type[t3], ObjectType, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckLessThanString: >+ # We are one of the following: Bottom, Boolean, Other, Int32, Number >+ bplt t1, PutByIdSecondaryTypeInt32, .opPutByIdTypeCheckLessThanInt32 >+ >+ # We are either Int32 or Number. >+ bpeq t1, PutByIdSecondaryTypeNumber, .opPutByIdTypeCheckNumber >+ >+ bqaeq t3, tagTypeNumber, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckNumber: >+ btqnz t3, tagTypeNumber, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckLessThanInt32: >+ # We are one of the following: Bottom, Boolean, Other. >+ bpneq t1, PutByIdSecondaryTypeBoolean, .opPutByIdTypeCheckBottomOrOther >+ xorq ValueFalse, t3 >+ btqz t3, ~1, .opPutByIdDoneCheckingTypes >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckBottomOrOther: >+ bpeq t1, PutByIdSecondaryTypeOther, .opPutByIdTypeCheckOther >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckObjectWithStructureOrOther: >+ btqz t3, tagMask, .opPutByIdTypeCheckObjectWithStructure >+ btpnz t1, PutByIdPrimaryTypeObjectWithStructureOrOther, .opPutByIdTypeCheckOther >+ jmp .opPutByIdSlow >+ >+.opPutByIdTypeCheckObjectWithStructure: >+ urshiftp 3, t1 >+ bineq t1, JSCell::m_structureID[t3], .opPutByIdSlow >+ >+.opPutByIdDoneCheckingTypes: > loadisFromInstruction(6, t1) > > btiz t1, .opPutByIdNotTransition >Index: Source/JavaScriptCore/llint/LowLevelInterpreter.asm >=================================================================== >--- Source/JavaScriptCore/llint/LowLevelInterpreter.asm (revision 190059) >+++ Source/JavaScriptCore/llint/LowLevelInterpreter.asm (working copy) >@@ -204,6 +204,22 @@ else > const LowestTag = DeletedValueTag > end > >+# NOTE: The values below must be in sync with what is in PutByIdFlags.h. >+const PutByIdPrimaryTypeMask = 0x6 >+const PutByIdPrimaryTypeSecondary = 0x0 >+const PutByIdPrimaryTypeObjectWithStructure = 0x2 >+const PutByIdPrimaryTypeObjectWithStructureOrOther = 0x4 >+const PutByIdSecondaryTypeMask = -0x8 >+const PutByIdSecondaryTypeBottom = 0x0 >+const PutByIdSecondaryTypeBoolean = 0x8 >+const PutByIdSecondaryTypeOther = 0x10 >+const PutByIdSecondaryTypeInt32 = 0x18 >+const PutByIdSecondaryTypeNumber = 0x20 >+const PutByIdSecondaryTypeString = 0x28 >+const PutByIdSecondaryTypeObject = 0x30 >+const PutByIdSecondaryTypeObjectOrOther = 0x38 >+const PutByIdSecondaryTypeTop = 0x40 >+ > const CallOpCodeSize = 9 > > if X86_64 or ARM64 or C_LOOP >Index: Source/JavaScriptCore/runtime/Executable.h >=================================================================== >--- Source/JavaScriptCore/runtime/Executable.h (revision 190059) >+++ Source/JavaScriptCore/runtime/Executable.h (working copy) >@@ -31,7 +31,6 @@ > #include "CodeBlockHash.h" > #include "CodeSpecializationKind.h" > #include "CompilationResult.h" >-#include "DFGPlan.h" > #include "ExecutableInfo.h" > #include "HandlerInfo.h" > #include "InferredValue.h" >Index: Source/JavaScriptCore/runtime/InferredType.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/InferredType.cpp (revision 0) >+++ Source/JavaScriptCore/runtime/InferredType.cpp (working copy) >@@ -0,0 +1,565 @@ >+/* >+ * Copyright (C) 2015 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 "InferredType.h" >+ >+#include "JSCInlines.h" >+ >+namespace JSC { >+ >+namespace { >+ >+class InferredTypeFireDetail : public FireDetail { >+public: >+ InferredTypeFireDetail( >+ InferredType* inferredType, UniquedStringImpl* uid, >+ const InferredType::Descriptor& oldDescriptor, const InferredType::Descriptor& newDescriptor, >+ JSValue offendingValue) >+ : m_inferredType(inferredType) >+ , m_uid(uid) >+ , m_oldDescriptor(oldDescriptor) >+ , m_newDescriptor(newDescriptor) >+ , m_offendingValue(offendingValue) >+ { >+ } >+ >+protected: >+ void dump(PrintStream& out) const override >+ { >+ out.print( >+ "Inferred type changed on ", RawPointer(m_inferredType), " for property ", m_uid, ": " >+ "old type was ", m_oldDescriptor, " while desired type is ", m_newDescriptor); >+ if (m_offendingValue) >+ out.print(" due to ", m_offendingValue); >+ } >+ >+private: >+ InferredType* m_inferredType; >+ RefPtr<UniquedStringImpl> m_uid; >+ InferredType::Descriptor m_oldDescriptor; >+ InferredType::Descriptor m_newDescriptor; >+ JSValue m_offendingValue; >+}; >+ >+} // anonymous namespace >+ >+const ClassInfo InferredType::s_info = { "InferredType", 0, 0, CREATE_METHOD_TABLE(InferredType) }; >+ >+InferredType* InferredType::create(VM& vm) >+{ >+ InferredType* result = new (NotNull, allocateCell<InferredType>(vm.heap)) InferredType(vm); >+ result->finishCreation(vm); >+ return result; >+} >+ >+void InferredType::destroy(JSCell* cell) >+{ >+ InferredType* inferredType = static_cast<InferredType*>(cell); >+ inferredType->InferredType::~InferredType(); >+} >+ >+Structure* InferredType::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) >+{ >+ return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); >+} >+ >+void InferredType::visitChildren(JSCell* cell, SlotVisitor& visitor) >+{ >+ InferredType* inferredType = jsCast<InferredType*>(cell); >+ >+ if (inferredType->m_structure) >+ visitor.addUnconditionalFinalizer(&inferredType->m_structure->m_finalizer); >+} >+ >+InferredType::Kind InferredType::kindForFlags(PutByIdFlags flags) >+{ >+ switch (flags & PutByIdPrimaryTypeMask) { >+ case PutByIdPrimaryTypeSecondary: >+ switch (flags & PutByIdSecondaryTypeMask) { >+ case PutByIdSecondaryTypeBottom: >+ return Bottom; >+ case PutByIdSecondaryTypeBoolean: >+ return Boolean; >+ case PutByIdSecondaryTypeOther: >+ return Other; >+ case PutByIdSecondaryTypeInt32: >+ return Int32; >+ case PutByIdSecondaryTypeNumber: >+ return Number; >+ case PutByIdSecondaryTypeString: >+ return String; >+ case PutByIdSecondaryTypeObject: >+ return Object; >+ case PutByIdSecondaryTypeObjectOrOther: >+ return ObjectOrOther; >+ case PutByIdSecondaryTypeTop: >+ return Top; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ return Bottom; >+ } >+ case PutByIdPrimaryTypeObjectWithStructure: >+ return ObjectWithStructure; >+ case PutByIdPrimaryTypeObjectWithStructureOrOther: >+ return ObjectWithStructureOrOther; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ return Bottom; >+ } >+} >+ >+InferredType::Descriptor InferredType::Descriptor::forValue(JSValue value) >+{ >+ if (value.isBoolean()) >+ return Boolean; >+ if (value.isUndefinedOrNull()) >+ return Other; >+ if (value.isInt32()) >+ return Int32; >+ if (value.isNumber()) >+ return Number; >+ if (value.isCell()) { >+ JSCell* cell = value.asCell(); >+ if (cell->isString()) >+ return String; >+ if (cell->isObject()) { >+ if (cell->structure()->transitionWatchpointSetIsStillValid()) >+ return Descriptor(ObjectWithStructure, cell->structure()); >+ return Object; >+ } >+ } >+ return Top; >+} >+ >+InferredType::Descriptor InferredType::Descriptor::forFlags(VM& vm, PutByIdFlags flags) >+{ >+ Kind kind = kindForFlags(flags); >+ Structure* structure; >+ if (hasStructure(kind)) >+ structure = vm.heap.structureIDTable().get(decodeStructureID(flags)); >+ else >+ structure = nullptr; >+ return Descriptor(kind, structure); >+} >+ >+PutByIdFlags InferredType::Descriptor::putByIdFlags() const >+{ >+ switch (m_kind) { >+ case Bottom: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeBottom); >+ case Boolean: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeBoolean); >+ case Other: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeOther); >+ case Int32: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeInt32); >+ case Number: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeNumber); >+ case String: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeString); >+ case Object: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeObject); >+ case ObjectOrOther: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeObjectOrOther); >+ case Top: >+ return static_cast<PutByIdFlags>(PutByIdPrimaryTypeSecondary | PutByIdSecondaryTypeTop); >+ case ObjectWithStructure: >+ return static_cast<PutByIdFlags>( >+ PutByIdPrimaryTypeObjectWithStructure | encodeStructureID(m_structure->id())); >+ case ObjectWithStructureOrOther: >+ return static_cast<PutByIdFlags>( >+ PutByIdPrimaryTypeObjectWithStructureOrOther | encodeStructureID(m_structure->id())); >+ } >+ RELEASE_ASSERT_NOT_REACHED(); >+ return PutByIdNone; >+} >+ >+void InferredType::Descriptor::merge(const Descriptor& other) >+{ >+ // Filter out common things to simplify the switch statement below. >+ if (*this == other) >+ return; >+ if (other.m_kind == Bottom) >+ return; >+ >+ switch (m_kind) { >+ case Bottom: >+ *this = other; >+ return; >+ case Boolean: >+ case String: >+ *this = Top; >+ return; >+ case Other: >+ switch (other.m_kind) { >+ case ObjectWithStructure: >+ case ObjectWithStructureOrOther: >+ *this = Descriptor(ObjectWithStructureOrOther, other.structure()); >+ return; >+ case Object: >+ case ObjectOrOther: >+ *this = ObjectOrOther; >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case Int32: >+ switch (other.m_kind) { >+ case Number: >+ *this = Number; >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case Number: >+ switch (other.m_kind) { >+ case Int32: >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case ObjectWithStructure: >+ switch (other.m_kind) { >+ case ObjectWithStructure: // If we see this here, then we know that the structures didn't match. >+ case Object: >+ *this = Object; >+ return; >+ case ObjectWithStructureOrOther: >+ if (m_structure == other.m_structure) { >+ *this = other; >+ return; >+ } >+ *this = ObjectOrOther; >+ return; >+ case ObjectOrOther: >+ *this = ObjectOrOther; >+ return; >+ case Other: >+ m_kind = ObjectWithStructureOrOther; >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case ObjectWithStructureOrOther: >+ switch (other.m_kind) { >+ case ObjectWithStructure: >+ if (m_structure == other.m_structure) >+ return; >+ *this = ObjectOrOther; >+ return; >+ case Object: >+ case ObjectWithStructureOrOther: // If we see this here, then we know that the structures didn't match. >+ case ObjectOrOther: >+ *this = ObjectOrOther; >+ return; >+ case Other: >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case Object: >+ switch (other.m_kind) { >+ case ObjectWithStructure: >+ return; >+ case ObjectWithStructureOrOther: >+ case ObjectOrOther: >+ case Other: >+ *this = ObjectOrOther; >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case ObjectOrOther: >+ switch (other.m_kind) { >+ case ObjectWithStructure: >+ case ObjectWithStructureOrOther: >+ case Object: >+ case Other: >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case Top: >+ return; >+ } >+ >+ RELEASE_ASSERT_NOT_REACHED(); >+} >+ >+void InferredType::Descriptor::removeStructure() >+{ >+ switch (m_kind) { >+ case ObjectWithStructure: >+ *this = Object; >+ return; >+ case ObjectWithStructureOrOther: >+ *this = ObjectOrOther; >+ return; >+ default: >+ return; >+ } >+} >+ >+bool InferredType::Descriptor::subsumes(const Descriptor& other) const >+{ >+ Descriptor merged = *this; >+ merged.merge(other); >+ return *this == merged; >+} >+ >+void InferredType::Descriptor::dumpInContext(PrintStream& out, DumpContext* context) const >+{ >+ out.print(m_kind); >+ if (m_structure) >+ out.print(":", inContext(*m_structure, context)); >+} >+ >+void InferredType::Descriptor::dump(PrintStream& out) const >+{ >+ dumpInContext(out, nullptr); >+} >+ >+InferredType::InferredType(VM& vm) >+ : Base(vm, vm.inferredTypeStructure.get()) >+ , m_watchpointSet(ClearWatchpoint) >+{ >+} >+ >+InferredType::~InferredType() >+{ >+} >+ >+bool InferredType::canWatch(const ConcurrentJITLocker& locker, const Descriptor& expected) >+{ >+ if (expected.kind() == Top) >+ return false; >+ >+ return descriptor(locker) == expected; >+} >+ >+bool InferredType::canWatch(const Descriptor& expected) >+{ >+ ConcurrentJITLocker locker(m_lock); >+ return canWatch(locker, expected); >+} >+ >+void InferredType::addWatchpoint(const ConcurrentJITLocker& locker, Watchpoint* watchpoint) >+{ >+ RELEASE_ASSERT(descriptor(locker).kind() != Top); >+ >+ m_watchpointSet.add(watchpoint); >+} >+ >+void InferredType::addWatchpoint(Watchpoint* watchpoint) >+{ >+ ConcurrentJITLocker locker(m_lock); >+ addWatchpoint(locker, watchpoint); >+} >+ >+void InferredType::dump(PrintStream& out) const >+{ >+ out.print(RawPointer(this), ":", descriptor()); >+} >+ >+bool InferredType::willStoreValueSlow(VM& vm, PropertyName propertyName, JSValue value) >+{ >+ ConcurrentJITLocker locker(m_lock); >+ Descriptor myType = descriptor(locker); >+ Descriptor otherType = Descriptor::forValue(value); >+ >+ myType.merge(otherType); >+ >+ ASSERT(descriptor(locker) != myType); // The type must have changed if we're on the slow path. >+ >+ set(locker, vm, propertyName, value, myType); >+ >+ return kind(locker) != Top; >+} >+ >+void InferredType::makeTopSlow(VM& vm, PropertyName propertyName) >+{ >+ ConcurrentJITLocker locker(m_lock); >+ set(locker, vm, propertyName, JSValue(), Top); >+} >+ >+void InferredType::set( >+ const ConcurrentJITLocker& locker, VM& vm, PropertyName propertyName, JSValue offendingValue, >+ Descriptor newDescriptor) >+{ >+ // We will trigger write barriers while holding our lock. Currently, write barriers don't GC, but that >+ // could change. If it does, we don't want to deadlock. Note that we could have used >+ // GCSafeConcurrentJITLocker in the caller, but the caller is on a fast path so maybe that wouldn't be >+ // a good idea. >+ DeferGCForAWhile deferGC(vm.heap); >+ >+ // Be defensive: if we're not really changing the type, then we don't have to do anything. >+ if (descriptor(locker) == newDescriptor) >+ return; >+ >+ // The new descriptor must be more general than the previous one. >+ ASSERT(newDescriptor.subsumes(descriptor(locker))); >+ >+ // If the new descriptors have different structures, then it can only be because one is null. >+ if (descriptor(locker).structure() != newDescriptor.structure()) >+ ASSERT(!descriptor(locker).structure() || !newDescriptor.structure()); >+ >+ // We are changing the type, so make sure that if anyone was watching, they find out about it now. If >+ // anyone is watching, we make sure to go to Top so that we don't do this sort of thing again. >+ if (m_watchpointSet.state() != ClearWatchpoint) { >+ // We cannot have been invalidated, since if we were, then we'd already be at Top. >+ ASSERT(m_watchpointSet.state() != IsInvalidated); >+ >+ InferredTypeFireDetail detail( >+ this, propertyName.uid(), descriptor(locker), newDescriptor, offendingValue); >+ >+ // We're about to do expensive things because some compiler thread decided to watch this type and >+ // then the type changed. Assume that this property is crazy, and don't ever do any more things for >+ // it. >+ newDescriptor = Top; >+ >+ m_watchpointSet.fireAll(detail); >+ } >+ >+ // Remove the old InferredStructure object if we no longer need it. >+ if (!newDescriptor.structure()) >+ m_structure = nullptr; >+ >+ // Add a new InferredStructure object if we need one now. >+ if (newDescriptor.structure()) { >+ if (m_structure) { >+ // We should agree on the structures if we get here. >+ ASSERT(newDescriptor.structure() == m_structure->structure()); >+ } else { >+ m_structure = std::make_unique<InferredStructure>(vm, this, newDescriptor.structure()); >+ newDescriptor.structure()->addTransitionWatchpoint(&m_structure->m_watchpoint); >+ } >+ } >+ >+ // Finally, set the descriptor kind. >+ m_kind = newDescriptor.kind(); >+ >+ // Assert that we did things. >+ ASSERT(descriptor(locker) == newDescriptor); >+} >+ >+void InferredType::removeStructure() >+{ >+ // FIXME: Find an elegant and cheap way to thread information about why we got here into the fire >+ // detail in set(). >+ >+ VM& vm = *Heap::heap(this)->vm(); >+ >+ ConcurrentJITLocker locker(m_lock); >+ >+ Descriptor newDescriptor = descriptor(locker); >+ newDescriptor.removeStructure(); >+ >+ set(locker, vm, PropertyName(nullptr), JSValue(), newDescriptor); >+} >+ >+void InferredType::InferredStructureWatchpoint::fireInternal(const FireDetail&) >+{ >+ InferredStructure* inferredStructure = >+ bitwise_cast<InferredStructure*>( >+ bitwise_cast<char*>(this) - OBJECT_OFFSETOF(InferredStructure, m_watchpoint)); >+ >+ inferredStructure->m_parent->removeStructure(); >+} >+ >+void InferredType::InferredStructureFinalizer::finalizeUnconditionally() >+{ >+ InferredStructure* inferredStructure = >+ bitwise_cast<InferredStructure*>( >+ bitwise_cast<char*>(this) - OBJECT_OFFSETOF(InferredStructure, m_finalizer)); >+ >+ ASSERT(Heap::isMarked(inferredStructure->m_parent)); >+ >+ if (!Heap::isMarked(inferredStructure->m_structure.get())) >+ inferredStructure->m_parent->removeStructure(); >+} >+ >+InferredType::InferredStructure::InferredStructure(VM& vm, InferredType* parent, Structure* structure) >+ : m_parent(parent) >+ , m_structure(vm, parent, structure) >+{ >+} >+ >+} // namespace JSC >+ >+namespace WTF { >+ >+using namespace JSC; >+ >+void printInternal(PrintStream& out, InferredType::Kind kind) >+{ >+ switch (kind) { >+ case InferredType::Bottom: >+ out.print("Bottom"); >+ return; >+ case InferredType::Boolean: >+ out.print("Boolean"); >+ return; >+ case InferredType::Other: >+ out.print("Other"); >+ return; >+ case InferredType::Int32: >+ out.print("Int32"); >+ return; >+ case InferredType::Number: >+ out.print("Number"); >+ return; >+ case InferredType::String: >+ out.print("String"); >+ return; >+ case InferredType::ObjectWithStructure: >+ out.print("ObjectWithStructure"); >+ return; >+ case InferredType::ObjectWithStructureOrOther: >+ out.print("ObjectWithStructureOrOther"); >+ return; >+ case InferredType::Object: >+ out.print("Object"); >+ return; >+ case InferredType::ObjectOrOther: >+ out.print("ObjectOrOther"); >+ return; >+ case InferredType::Top: >+ out.print("Top"); >+ return; >+ } >+ >+ RELEASE_ASSERT_NOT_REACHED(); >+} >+ >+} // namespace WTF >+ >Index: Source/JavaScriptCore/runtime/InferredType.h >=================================================================== >--- Source/JavaScriptCore/runtime/InferredType.h (revision 0) >+++ Source/JavaScriptCore/runtime/InferredType.h (working copy) >@@ -0,0 +1,286 @@ >+/* >+ * Copyright (C) 2015 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 InferredType_h >+#define InferredType_h >+ >+#include "ConcurrentJITLock.h" >+#include "JSCell.h" >+#include "PropertyName.h" >+#include "PutByIdFlags.h" >+#include "Watchpoint.h" >+ >+namespace JSC { >+ >+// This is an object used for the inference of the types of object properties. >+ >+class InferredType final : public JSCell { >+public: >+ typedef JSCell Base; >+ >+ static InferredType* create(VM&); >+ >+ static const bool needsDestruction = true; >+ static void destroy(JSCell*); >+ >+ static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags; >+ >+ static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); >+ >+ static void visitChildren(JSCell*, SlotVisitor&); >+ >+ DECLARE_INFO; >+ >+ enum Kind : uint8_t { >+ Bottom, >+ Boolean, >+ Other, >+ Int32, >+ Number, >+ String, >+ ObjectWithStructure, >+ ObjectWithStructureOrOther, >+ Object, >+ ObjectOrOther, >+ Top >+ }; >+ >+ static Kind kindForFlags(PutByIdFlags); >+ >+ static bool hasStructure(Kind kind) >+ { >+ switch (kind) { >+ case ObjectWithStructure: >+ case ObjectWithStructureOrOther: >+ return true; >+ default: >+ return false; >+ } >+ } >+ >+ class Descriptor { >+ public: >+ Descriptor() >+ : m_kind(Bottom) >+ , m_structure(nullptr) >+ { >+ } >+ >+ Descriptor(Kind kind, Structure* structure = nullptr) >+ : m_kind(kind) >+ , m_structure(structure) >+ { >+ if (hasStructure(kind)) >+ ASSERT(structure); >+ } >+ >+ static Descriptor forValue(JSValue); >+ >+ static Descriptor forFlags(VM&, PutByIdFlags); >+ >+ explicit operator bool() const >+ { >+ return m_kind != Bottom || m_structure; >+ } >+ >+ Kind kind() const { return m_kind; } >+ Structure* structure() const { return m_structure; } >+ >+ PutByIdFlags putByIdFlags() const; >+ >+ ALWAYS_INLINE bool includesValue(JSValue value) >+ { >+ switch (m_kind) { >+ case Bottom: >+ return false; >+ case Boolean: >+ return value.isBoolean(); >+ case Other: >+ return value.isUndefinedOrNull(); >+ case Int32: >+ return value.isInt32(); >+ case Number: >+ return value.isNumber(); >+ case String: >+ return value.isString(); >+ case ObjectWithStructure: >+ return value.isCell() && value.asCell()->structure() == m_structure; >+ case ObjectWithStructureOrOther: >+ return value.isUndefinedOrNull() >+ || (value.isCell() && value.asCell()->structure() == m_structure); >+ case Object: >+ return value.isObject(); >+ case ObjectOrOther: >+ return value.isUndefinedOrNull() || value.isObject(); >+ case Top: >+ return true; >+ } >+ >+ RELEASE_ASSERT_NOT_REACHED(); >+ } >+ >+ bool operator==(const Descriptor& other) const >+ { >+ return m_kind == other.m_kind >+ && m_structure == other.m_structure; >+ } >+ >+ bool operator!=(const Descriptor& other) const >+ { >+ return !(*this == other); >+ } >+ >+ unsigned hash() const >+ { >+ return WTF::PtrHash<Structure*>::hash(m_structure) ^ static_cast<unsigned>(m_kind); >+ } >+ >+ void merge(const Descriptor&); >+ void removeStructure(); >+ >+ // Returns true if this descriptor is more general than the other one. >+ bool subsumes(const Descriptor&) const; >+ >+ void dumpInContext(PrintStream&, DumpContext*) const; >+ void dump(PrintStream&) const; >+ >+ private: >+ Kind m_kind; >+ Structure* m_structure; >+ }; >+ >+ ConcurrentJITLock& lock() const { return m_lock; } >+ >+ Descriptor descriptorMainThread() const >+ { >+ return Descriptor(m_kind, m_structure ? m_structure->structure() : nullptr); >+ } >+ >+ Descriptor descriptor(const ConcurrentJITLocker&) const >+ { >+ return descriptorMainThread(); >+ } >+ Descriptor descriptor() const >+ { >+ ConcurrentJITLocker locker(m_lock); >+ return descriptor(locker); >+ } >+ >+ Kind kind(const ConcurrentJITLocker& locker) const { return descriptor(locker).kind(); } >+ >+ bool isTop() const { return m_kind == Top; } >+ bool isRelevant() const { return m_kind != Top; } >+ >+ // Returns true if the InferredType is still relevant after the store. It's not relevant if it's Top. >+ ALWAYS_INLINE bool willStoreValue(VM& vm, PropertyName propertyName, JSValue value) >+ { >+ Descriptor currentDescriptor = descriptorMainThread(); >+ if (currentDescriptor.includesValue(value)) >+ return currentDescriptor.kind() != Top; >+ return willStoreValueSlow(vm, propertyName, value); >+ } >+ >+ // Immediately makes this type irrelevant. >+ void makeTop(VM& vm, PropertyName propertyName) >+ { >+ if (isTop()) >+ return; >+ makeTopSlow(vm, propertyName); >+ } >+ >+ // Returns true if it currently makes sense to watch this InferredType for this descriptor. Note that >+ // this will always return false for Top. >+ bool canWatch(const ConcurrentJITLocker& locker, const Descriptor&); >+ bool canWatch(const Descriptor&); >+ >+ void addWatchpoint(const ConcurrentJITLocker& locker, Watchpoint*); >+ void addWatchpoint(Watchpoint*); >+ >+ void dump(PrintStream&) const; >+ >+private: >+ InferredType(VM&); >+ ~InferredType(); >+ >+ bool willStoreValueSlow(VM&, PropertyName, JSValue); >+ void makeTopSlow(VM&, PropertyName); >+ void set(const ConcurrentJITLocker&, VM&, PropertyName, JSValue, Descriptor); >+ void removeStructure(); >+ >+ mutable ConcurrentJITLock m_lock; >+ >+ Kind m_kind { Bottom }; >+ >+ class InferredStructureWatchpoint : public Watchpoint { >+ public: >+ InferredStructureWatchpoint() { } >+ protected: >+ void fireInternal(const FireDetail&) override; >+ }; >+ >+ class InferredStructureFinalizer : public UnconditionalFinalizer { >+ public: >+ InferredStructureFinalizer() { } >+ protected: >+ void finalizeUnconditionally() override; >+ }; >+ >+ class InferredStructure { >+ WTF_MAKE_FAST_ALLOCATED; >+ public: >+ InferredStructure(VM&, InferredType* parent, Structure*); >+ >+ Structure* structure() const { return m_structure.get(); }; >+ >+ private: >+ friend class InferredType; >+ friend class InferredStructureWatchpoint; >+ friend class InferredStructureFinalizer; >+ >+ InferredType* m_parent; >+ WriteBarrier<Structure> m_structure; >+ >+ InferredStructureWatchpoint m_watchpoint; >+ InferredStructureFinalizer m_finalizer; >+ }; >+ >+ std::unique_ptr<InferredStructure> m_structure; >+ >+ // NOTE: If this is being watched, we transform to Top because that implies that it wouldn't be >+ // profitable to watch it again. Also, this set is initialized clear, and is never exposed to the DFG >+ // thread. The DFG will use the InferredType as the thing that it watches. >+ InlineWatchpointSet m_watchpointSet; >+}; >+ >+} // namespace JSC >+ >+namespace WTF { >+ >+void printInternal(PrintStream&, JSC::InferredType::Kind); >+ >+} // namespace WTF >+ >+#endif // InferredType_h >+ >Index: Source/JavaScriptCore/runtime/InferredTypeTable.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/InferredTypeTable.cpp (revision 0) >+++ Source/JavaScriptCore/runtime/InferredTypeTable.cpp (working copy) >@@ -0,0 +1,166 @@ >+/* >+ * Copyright (C) 2015 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 "InferredTypeTable.h" >+ >+#include "JSCInlines.h" >+ >+namespace JSC { >+ >+const ClassInfo InferredTypeTable::s_info = { "InferredTypeTable", 0, 0, CREATE_METHOD_TABLE(InferredTypeTable) }; >+ >+InferredTypeTable* InferredTypeTable::create(VM& vm) >+{ >+ InferredTypeTable* result = new (NotNull, allocateCell<InferredTypeTable>(vm.heap)) InferredTypeTable(vm); >+ result->finishCreation(vm); >+ return result; >+} >+ >+void InferredTypeTable::destroy(JSCell* cell) >+{ >+ InferredTypeTable* inferredTypeTable = static_cast<InferredTypeTable*>(cell); >+ inferredTypeTable->InferredTypeTable::~InferredTypeTable(); >+} >+ >+Structure* InferredTypeTable::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) >+{ >+ return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); >+} >+ >+void InferredTypeTable::visitChildren(JSCell* cell, SlotVisitor& visitor) >+{ >+ InferredTypeTable* inferredTypeTable = jsCast<InferredTypeTable*>(cell); >+ >+ ConcurrentJITLocker locker(inferredTypeTable->m_lock); >+ >+ for (auto& entry : inferredTypeTable->m_table) { >+ if (!entry.value) >+ continue; >+ if (entry.value->isRelevant()) >+ visitor.append(&entry.value); >+ else >+ entry.value.clear(); >+ } >+} >+ >+InferredType* InferredTypeTable::get(const ConcurrentJITLocker&, UniquedStringImpl* uid) >+{ >+ auto iter = m_table.find(uid); >+ if (iter == m_table.end() || !iter->value) >+ return nullptr; >+ >+ // Take this opportunity to prune invalidated types. >+ if (!iter->value->isRelevant()) { >+ iter->value.clear(); >+ return nullptr; >+ } >+ >+ return iter->value.get(); >+} >+ >+InferredType* InferredTypeTable::get(UniquedStringImpl* uid) >+{ >+ ConcurrentJITLocker locker(m_lock); >+ return get(locker, uid); >+} >+ >+InferredType* InferredTypeTable::get(PropertyName propertyName) >+{ >+ return get(propertyName.uid()); >+} >+ >+bool InferredTypeTable::willStoreValue( >+ VM& vm, PropertyName propertyName, JSValue value, StoredPropertyAge age) >+{ >+ // The algorithm here relies on the fact that only one thread modifies the hash map. >+ >+ if (age == OldProperty) { >+ TableType::iterator iter = m_table.find(propertyName.uid()); >+ if (iter == m_table.end() || !iter->value) >+ return false; // Absence on replace => top. >+ >+ if (iter->value->willStoreValue(vm, propertyName, value)) >+ return true; >+ >+ iter->value.clear(); >+ return false; >+ } >+ >+ TableType::AddResult result; >+ { >+ ConcurrentJITLocker locker(m_lock); >+ result = m_table.add(propertyName.uid(), WriteBarrier<InferredType>()); >+ } >+ if (result.isNewEntry) { >+ InferredType* inferredType = InferredType::create(vm); >+ WTF::storeStoreFence(); >+ result.iterator->value.set(vm, this, inferredType); >+ } else if (!result.iterator->value) >+ return false; >+ >+ if (result.iterator->value->willStoreValue(vm, propertyName, value)) >+ return true; >+ >+ result.iterator->value.clear(); >+ return false; >+} >+ >+void InferredTypeTable::makeTop(VM& vm, PropertyName propertyName, StoredPropertyAge age) >+{ >+ // The algorithm here relies on the fact that only one thread modifies the hash map. >+ if (age == OldProperty) { >+ TableType::iterator iter = m_table.find(propertyName.uid()); >+ if (iter == m_table.end() || !iter->value) >+ return; // Absence on replace => top. >+ >+ iter->value->makeTop(vm, propertyName); >+ iter->value.clear(); >+ return; >+ } >+ >+ TableType::AddResult result; >+ { >+ ConcurrentJITLocker locker(m_lock); >+ result = m_table.add(propertyName.uid(), WriteBarrier<InferredType>()); >+ } >+ if (!result.iterator->value) >+ return; >+ >+ result.iterator->value->makeTop(vm, propertyName); >+ result.iterator->value.clear(); >+} >+ >+InferredTypeTable::InferredTypeTable(VM& vm) >+ : Base(vm, vm.inferredTypeTableStructure.get()) >+{ >+} >+ >+InferredTypeTable::~InferredTypeTable() >+{ >+} >+ >+} // namespace JSC >+ >Index: Source/JavaScriptCore/runtime/InferredTypeTable.h >=================================================================== >--- Source/JavaScriptCore/runtime/InferredTypeTable.h (revision 0) >+++ Source/JavaScriptCore/runtime/InferredTypeTable.h (working copy) >@@ -0,0 +1,114 @@ >+/* >+ * Copyright (C) 2015 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 InferredTypeTable_h >+#define InferredTypeTable_h >+ >+#include "Identifier.h" >+#include "InferredType.h" >+#include "JSCell.h" >+ >+namespace JSC { >+ >+// A table of inferred types for some structure. This is a JSCell because that simplifies the Structure >+// destructor and makes lifetime easier to manage. For example, since it's a cell, we know that this thing >+// cannot be deleted while the DFG is running. This is separate from PropertyTable because most properties >+// will not have an InferredType, since most properties are in dictionaries (if you think of "a property" >+// as being "a property in some Structure"). Also, this will happily conflate the types of properties from >+// different structures even if the structures represent disjoint sets of objects. >+ >+class InferredTypeTable final : public JSCell { >+public: >+ typedef JSCell Base; >+ >+ static InferredTypeTable* create(VM&); >+ >+ static const bool needsDestruction = true; >+ static void destroy(JSCell*); >+ >+ static const unsigned StructureFlags = StructureIsImmortal | Base::StructureFlags; >+ >+ static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); >+ >+ static void visitChildren(JSCell*, SlotVisitor&); >+ >+ DECLARE_INFO; >+ >+ ConcurrentJITLock& lock() { return m_lock; } >+ >+ bool isEmpty() const { return m_table.isEmpty(); } >+ >+ // Get the current inferred type. Returns nullptr for both Top and Bottom. Null means Bottom if the >+ // owning Structure doesn't know about the property. >+ InferredType* get(const ConcurrentJITLocker&, UniquedStringImpl*); >+ InferredType* get(UniquedStringImpl*); >+ InferredType* get(PropertyName); >+ >+ enum StoredPropertyAge { >+ NewProperty, >+ OldProperty >+ }; >+ >+ // Returns true if the InferredType for this property is still relevant after the store. It's not >+ // relevant if it's Top. Note that this table will internally prune Top entries. >+ bool willStoreValue(VM&, PropertyName, JSValue, StoredPropertyAge); >+ >+ // Invalidates the type for the property. Useful if we detect a store in a reflective context. >+ void makeTop(VM&, PropertyName, StoredPropertyAge); >+ >+private: >+ InferredTypeTable(VM&); >+ ~InferredTypeTable(); >+ >+ // Bottom: absence from table. >+ // Top: null value in table, !value->isRelevant(), or absence from table. >+ // >+ // We know how to determine if absence from the table is bottom or top depending on whether the >+ // property is present in the owning structure. Hence, depending on the structure, absence may have >+ // different meanings. The way that this class determines if the owning structure knows of the property >+ // is that we differentiate between actions: replace or transition. If we're adding a new property >+ // (transition), then absence means bottom. If we're storing to an existing property (replace), then >+ // absence means top. To make this work, we have to watch out for the case where two structures, S1 and >+ // S2, share the same InferredTypeTable and neither of them initially know about property P. S1 may >+ // want to add P without a type, while S2 may want to add P with a type. If S1 added P and used absence >+ // to indicate Top, then S2 would use that absence to mean Bottom and would end up creating a non-Top >+ // entry in the table. Then S1 would forget that it wanted Top, and would use S2's type. Clearly, >+ // that's bad. We avoid such confusion by ensuring that a transition always adds an entry. Hence, >+ // absence-means-bottom only comes into play for properties added before the InferredTypeTable was >+ // created. >+ typedef HashMap<RefPtr<UniquedStringImpl>, WriteBarrier<InferredType>, IdentifierRepHash, HashTraits<UniquedStringImpl*>> TableType; >+ >+ TableType m_table; >+ >+ // We only grab this lock when we're doing modifications on the main thread, or reads on the compiler >+ // thread. The compiler thread is not allowed to do modifications. >+ ConcurrentJITLock m_lock; >+}; >+ >+} // namespace JSC >+ >+#endif // InferredTypeTable_h >+ >+ >Index: Source/JavaScriptCore/runtime/JSObject.h >=================================================================== >--- Source/JavaScriptCore/runtime/JSObject.h (revision 190059) >+++ Source/JavaScriptCore/runtime/JSObject.h (working copy) >@@ -1189,6 +1189,8 @@ inline bool JSObject::putDirectInternal( > > Structure* structure = this->structure(vm); > if (structure->isDictionary()) { >+ ASSERT(!structure->hasInferredTypes()); >+ > unsigned currentAttributes; > PropertyOffset offset = structure->get(vm, propertyName, currentAttributes); > if (offset != invalidOffset) { >@@ -1227,29 +1229,40 @@ inline bool JSObject::putDirectInternal( > > PropertyOffset offset; > size_t currentCapacity = this->structure()->outOfLineCapacity(); >- if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, offset)) { >+ Structure* newStructure = Structure::addPropertyTransitionToExistingStructure( >+ structure, propertyName, attributes, offset); >+ if (newStructure) { >+ newStructure->willStoreValueForTransition( >+ vm, propertyName, value, slot.context() == PutPropertySlot::PutById); >+ > DeferGC deferGC(vm.heap); > Butterfly* newButterfly = butterfly(); >- if (currentCapacity != structure->outOfLineCapacity()) { >- ASSERT(structure != this->structure()); >- newButterfly = growOutOfLineStorage(vm, currentCapacity, structure->outOfLineCapacity()); >+ if (currentCapacity != newStructure->outOfLineCapacity()) { >+ ASSERT(newStructure != this->structure()); >+ newButterfly = growOutOfLineStorage(vm, currentCapacity, newStructure->outOfLineCapacity()); > } > > validateOffset(offset); >- ASSERT(structure->isValidOffset(offset)); >- setStructureAndButterfly(vm, structure, newButterfly); >+ ASSERT(newStructure->isValidOffset(offset)); >+ setStructureAndButterfly(vm, newStructure, newButterfly); > putDirect(vm, offset, value); > slot.setNewProperty(this, offset); > return true; > } > > unsigned currentAttributes; >- offset = structure->get(vm, propertyName, currentAttributes); >+ bool hasInferredType; >+ offset = structure->get(vm, propertyName, currentAttributes, hasInferredType); > if (offset != invalidOffset) { > if ((mode == PutModePut) && currentAttributes & ReadOnly) > return false; > > structure->didReplaceProperty(offset); >+ if (UNLIKELY(hasInferredType)) { >+ structure->willStoreValueForReplace( >+ vm, propertyName, value, slot.context() == PutPropertySlot::PutById); >+ } >+ > slot.setExistingProperty(this, offset); > putDirect(vm, offset, value); > >@@ -1268,16 +1281,19 @@ inline bool JSObject::putDirectInternal( > // we want. > DeferredStructureTransitionWatchpointFire deferredWatchpointFire; > >- structure = Structure::addPropertyTransition(vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire); >+ newStructure = Structure::addPropertyTransition( >+ vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire); >+ newStructure->willStoreValueForTransition( >+ vm, propertyName, value, slot.context() == PutPropertySlot::PutById); > > validateOffset(offset); >- ASSERT(structure->isValidOffset(offset)); >- setStructureAndReallocateStorageIfNecessary(vm, structure); >+ ASSERT(newStructure->isValidOffset(offset)); >+ setStructureAndReallocateStorageIfNecessary(vm, newStructure); > > putDirect(vm, offset, value); > slot.setNewProperty(this, offset); > if (attributes & ReadOnly) >- structure->setContainsReadOnlyProperties(); >+ newStructure->setContainsReadOnlyProperties(); > return true; > } > >@@ -1335,8 +1351,11 @@ inline void JSObject::putDirectWithoutTr > Butterfly* newButterfly = m_butterfly.get(); > if (structure()->putWillGrowOutOfLineStorage()) > newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); >- PropertyOffset offset = structure()->addPropertyWithoutTransition(vm, propertyName, attributes); >- setStructureAndButterfly(vm, structure(), newButterfly); >+ Structure* structure = this->structure(); >+ PropertyOffset offset = structure->addPropertyWithoutTransition(vm, propertyName, attributes); >+ bool shouldOptimize = false; >+ structure->willStoreValueForTransition(vm, propertyName, value, shouldOptimize); >+ setStructureAndButterfly(vm, structure, newButterfly); > putDirect(vm, offset, value); > } > >Index: Source/JavaScriptCore/runtime/Structure.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/Structure.cpp (revision 190059) >+++ Source/JavaScriptCore/runtime/Structure.cpp (working copy) >@@ -350,11 +350,15 @@ void Structure::materializePropertyMap(V > else > propertyTable().set(vm, this, table); > >+ InferredTypeTable* typeTable = m_inferredTypeTable.get(); >+ > for (size_t i = structures.size(); i--;) { > structure = structures[i]; > if (!structure->m_nameInPrevious) > continue; > PropertyMapEntry entry(structure->m_nameInPrevious.get(), structure->m_offset, structure->attributesInPrevious()); >+ if (typeTable && typeTable->get(structure->m_nameInPrevious.get())) >+ entry.hasInferredType = true; > propertyTable()->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange); > } > >@@ -464,6 +468,7 @@ Structure* Structure::addPropertyTransit > transition->setAttributesInPrevious(attributes); > transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); > transition->m_offset = structure->m_offset; >+ transition->m_inferredTypeTable.setMayBeNull(vm, transition, structure->m_inferredTypeTable.get()); > > offset = transition->add(vm, propertyName, attributes); > >@@ -479,6 +484,24 @@ Structure* Structure::addPropertyTransit > > Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset) > { >+ // NOTE: There are some good reasons why this goes directly to uncacheable dictionary rather than >+ // caching the removal. We can fix all of these things, but we must remember to do so, if we ever try >+ // to optimize this case. >+ // >+ // - Cached transitions usually steal the property table, and assume that this is possible because they >+ // can just rebuild the table by looking at past transitions. That code assumes that the table only >+ // grew and never shrank. To support removals, we'd have to change the property table materialization >+ // code to handle deletions. Also, we have logic to get the list of properties on a structure that >+ // lacks a property table by just looking back through the set of transitions since the last >+ // structure that had a pinned table. That logic would also have to be changed to handle cached >+ // removals. >+ // >+ // - InferredTypeTable assumes that removal has never happened. This is important since if we could >+ // remove a property and then re-add it later, then the "absence means top" optimization wouldn't >+ // work anymore, unless removal also either poisoned type inference (by doing something equivalent to >+ // hasBeenDictionary) or by strongly marking the entry as Top by ensuring that it is not absent, but >+ // instead, has a null entry. >+ > ASSERT(!structure->isUncacheableDictionary()); > > Structure* transition = toUncacheableDictionaryTransition(vm, structure); >@@ -841,6 +864,43 @@ void Structure::startWatchingInternalPro > setDidWatchInternalProperties(true); > } > >+void Structure::willStoreValueSlow( >+ VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize, >+ InferredTypeTable::StoredPropertyAge age) >+{ >+ ASSERT(!isCompilationThread()); >+ ASSERT(structure()->classInfo() == info()); >+ ASSERT(!hasBeenDictionary()); >+ >+ // Create the inferred type table before doing anything else, so that we don't GC after we have already >+ // grabbed a pointer into the property map. >+ InferredTypeTable* table = m_inferredTypeTable.get(); >+ if (!table) { >+ table = InferredTypeTable::create(vm); >+ WTF::storeStoreFence(); >+ m_inferredTypeTable.set(vm, this, table); >+ } >+ >+ // This only works if we've got a property table. >+ PropertyTable* propertyTable; >+ materializePropertyMapIfNecessary(vm, propertyTable); >+ >+ // We must be calling this after having created the given property or confirmed that it was present >+ // already, so we must have a property table now. >+ ASSERT(propertyTable); >+ >+ // ... and the property must be present. >+ PropertyMapEntry* entry = propertyTable->get(propertyName.uid()); >+ ASSERT(entry); >+ >+ if (shouldOptimize) >+ entry->hasInferredType = table->willStoreValue(vm, propertyName, value, age); >+ else { >+ table->makeTop(vm, propertyName, age); >+ entry->hasInferredType = false; >+ } >+} >+ > #if DUMP_PROPERTYMAP_STATS > > PropertyMapHashTableStats* propertyMapHashTableStats = 0; >@@ -1058,6 +1118,8 @@ void Structure::visitChildren(JSCell* ce > visitor.append(&thisObject->m_propertyTableUnsafe); > } else if (thisObject->m_propertyTableUnsafe) > thisObject->m_propertyTableUnsafe.clear(); >+ >+ visitor.append(&thisObject->m_inferredTypeTable); > } > > bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName) >Index: Source/JavaScriptCore/runtime/Structure.h >=================================================================== >--- Source/JavaScriptCore/runtime/Structure.h (revision 190059) >+++ Source/JavaScriptCore/runtime/Structure.h (working copy) >@@ -29,6 +29,7 @@ > #include "ClassInfo.h" > #include "ConcurrentJITLock.h" > #include "IndexingType.h" >+#include "InferredTypeTable.h" > #include "JSCJSValue.h" > #include "JSCell.h" > #include "JSType.h" >@@ -81,11 +82,13 @@ struct PropertyMapEntry { > UniquedStringImpl* key; > PropertyOffset offset; > uint8_t attributes; >+ bool hasInferredType; // This caches whether or not a property has an inferred type in the inferred type table, and is used for a fast check in JSObject::putDirectInternal(). > > PropertyMapEntry() > : key(nullptr) > , offset(invalidOffset) > , attributes(0) >+ , hasInferredType(false) > { > } > >@@ -93,6 +96,7 @@ struct PropertyMapEntry { > : key(key) > , offset(offset) > , attributes(attributes) >+ , hasInferredType(false) > { > ASSERT(this->attributes == attributes); > } >@@ -323,6 +327,7 @@ public: > > PropertyOffset get(VM&, PropertyName); > PropertyOffset get(VM&, PropertyName, unsigned& attributes); >+ PropertyOffset get(VM&, PropertyName, unsigned& attributes, bool& hasInferredType); > > // This is a somewhat internalish method. It will call your functor while possibly holding the > // Structure's lock. There is no guarantee whether the lock is held or not in any particular >@@ -481,6 +486,40 @@ public: > structure->startWatchingInternalPropertiesIfNecessary(vm); > } > >+ bool hasInferredTypes() const >+ { >+ return !!m_inferredTypeTable; >+ } >+ >+ InferredType* inferredTypeFor(UniquedStringImpl* uid) >+ { >+ if (InferredTypeTable* table = m_inferredTypeTable.get()) >+ return table->get(uid); >+ return nullptr; >+ } >+ >+ InferredType::Descriptor inferredTypeDescriptorFor(UniquedStringImpl* uid) >+ { >+ if (InferredType* result = inferredTypeFor(uid)) >+ return result->descriptor(); >+ return InferredType::Top; >+ } >+ >+ ALWAYS_INLINE void willStoreValueForTransition( >+ VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize) >+ { >+ if (hasBeenDictionary() || (!shouldOptimize && !m_inferredTypeTable)) >+ return; >+ willStoreValueSlow(vm, propertyName, value, shouldOptimize, InferredTypeTable::NewProperty); >+ } >+ ALWAYS_INLINE void willStoreValueForReplace( >+ VM& vm, PropertyName propertyName, JSValue value, bool shouldOptimize) >+ { >+ if (hasBeenDictionary()) >+ return; >+ willStoreValueSlow(vm, propertyName, value, shouldOptimize, InferredTypeTable::OldProperty); >+ } >+ > PassRefPtr<StructureShape> toStructureShape(JSValue); > > // Determines if the two structures match enough that this one could be used for allocations >@@ -632,6 +671,9 @@ private: > > void startWatchingInternalProperties(VM&); > >+ JS_EXPORT_PRIVATE void willStoreValueSlow( >+ VM&, PropertyName, JSValue, bool, InferredTypeTable::StoredPropertyAge); >+ > static const int s_maxTransitionLength = 64; > static const int s_maxTransitionLengthForNonEvalPutById = 512; > >@@ -655,6 +697,8 @@ private: > // Should be accessed through propertyTable(). During GC, it may be set to 0 by another thread. > WriteBarrier<PropertyTable> m_propertyTableUnsafe; > >+ WriteBarrier<InferredTypeTable> m_inferredTypeTable; >+ > mutable InlineWatchpointSet m_transitionWatchpointSet; > > COMPILE_ASSERT(firstOutOfLineOffset < 256, firstOutOfLineOffset_fits); >Index: Source/JavaScriptCore/runtime/StructureInlines.h >=================================================================== >--- Source/JavaScriptCore/runtime/StructureInlines.h (revision 190059) >+++ Source/JavaScriptCore/runtime/StructureInlines.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. >+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions >@@ -75,19 +75,19 @@ inline Structure* Structure::storedProto > > ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName) > { >- ASSERT(!isCompilationThread()); >- ASSERT(structure()->classInfo() == info()); >- PropertyTable* propertyTable; >- materializePropertyMapIfNecessary(vm, propertyTable); >- if (!propertyTable) >- return invalidOffset; >- >- PropertyMapEntry* entry = propertyTable->get(propertyName.uid()); >- return entry ? entry->offset : invalidOffset; >+ unsigned attributes; >+ bool hasInferredType; >+ return get(vm, propertyName, attributes, hasInferredType); > } > > ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes) > { >+ bool hasInferredType; >+ return get(vm, propertyName, attributes, hasInferredType); >+} >+ >+ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes, bool& hasInferredType) >+{ > ASSERT(!isCompilationThread()); > ASSERT(structure()->classInfo() == info()); > >@@ -101,6 +101,7 @@ ALWAYS_INLINE PropertyOffset Structure:: > return invalidOffset; > > attributes = entry->attributes; >+ hasInferredType = entry->hasInferredType; > return entry->offset; > } > >Index: Source/JavaScriptCore/runtime/VM.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/VM.cpp (revision 190059) >+++ Source/JavaScriptCore/runtime/VM.cpp (working copy) >@@ -51,6 +51,7 @@ > #include "HostCallReturnValue.h" > #include "Identifier.h" > #include "IncrementalSweeper.h" >+#include "InferredTypeTable.h" > #include "Interpreter.h" > #include "JITCode.h" > #include "JSAPIValueWrapper.h" >@@ -241,6 +242,8 @@ VM::VM(VMType vmType, HeapType heapType) > propertyTableStructure.set(*this, PropertyTable::createStructure(*this, 0, jsNull())); > weakMapDataStructure.set(*this, WeakMapData::createStructure(*this, 0, jsNull())); > inferredValueStructure.set(*this, InferredValue::createStructure(*this, 0, jsNull())); >+ inferredTypeStructure.set(*this, InferredType::createStructure(*this, 0, jsNull())); >+ inferredTypeTableStructure.set(*this, InferredTypeTable::createStructure(*this, 0, jsNull())); > functionRareDataStructure.set(*this, FunctionRareData::createStructure(*this, 0, jsNull())); > exceptionStructure.set(*this, Exception::createStructure(*this, 0, jsNull())); > promiseDeferredStructure.set(*this, JSPromiseDeferred::createStructure(*this, 0, jsNull())); >Index: Source/JavaScriptCore/runtime/VM.h >=================================================================== >--- Source/JavaScriptCore/runtime/VM.h (revision 190059) >+++ Source/JavaScriptCore/runtime/VM.h (working copy) >@@ -302,6 +302,8 @@ public: > Strong<Structure> propertyTableStructure; > Strong<Structure> weakMapDataStructure; > Strong<Structure> inferredValueStructure; >+ Strong<Structure> inferredTypeStructure; >+ Strong<Structure> inferredTypeTableStructure; > Strong<Structure> functionRareDataStructure; > Strong<Structure> exceptionStructure; > Strong<Structure> promiseDeferredStructure; >Index: Source/JavaScriptCore/tests/stress/prop-type-boolean-then-string.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-boolean-then-string.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-boolean-then-string.js (working copy) >@@ -0,0 +1,29 @@ >+function foo(o) { >+ return !!o.f; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, true); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== true) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, "hello"); >+var result = foo(o); >+if (result !== true) >+ throw "Error: bad result at end (true): " + result; >+bar(o, ""); >+result = foo(o); >+if (result !== false) >+ throw "Error: bad result at end (false): " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-int32-then-string.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-int32-then-string.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-int32-then-string.js (working copy) >@@ -0,0 +1,25 @@ >+function foo(o) { >+ return o.f + 1; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, 42); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== 43) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, "hello"); >+var result = foo(o); >+if (result !== "hello1") >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-number-then-string.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-number-then-string.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-number-then-string.js (working copy) >@@ -0,0 +1,25 @@ >+function foo(o) { >+ return o.f + 1; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, 4.2); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== 5.2) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, "hello"); >+var result = foo(o); >+if (result !== "hello1") >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-object-or-other-then-string.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-object-or-other-then-string.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-object-or-other-then-string.js (working copy) >@@ -0,0 +1,33 @@ >+String.prototype.g = 44; >+ >+function foo(o) { >+ return o.f == {toString:function() { return "hello"; }}; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+var p = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, null); >+for (var i = 0; i < 5; ++i) >+ bar(p, {g:43}); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== false) >+ throw "Error: bad result for o: " + result; >+ result = foo(p); >+ if (result !== false) >+ throw "Error: bad result for p: " + result; >+} >+ >+bar(o, "hello"); >+var result = foo(o); >+if (result !== true) >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-object-then-string.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-object-then-string.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-object-then-string.js (working copy) >@@ -0,0 +1,25 @@ >+function foo(o) { >+ return o.f == {toString:function() { return "hello"; }}; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, {}); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== false) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, "hello"); >+var result = foo(o); >+if (result !== true) >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-other-then-string.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-other-then-string.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-other-then-string.js (working copy) >@@ -0,0 +1,25 @@ >+function foo(o) { >+ return o.f == "hello"; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, null); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== false) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, "hello"); >+var result = foo(o); >+if (result !== true) >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-string-then-object.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-string-then-object.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-string-then-object.js (working copy) >@@ -0,0 +1,25 @@ >+function foo(o) { >+ return o.f + " world"; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, "hello"); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result != "hello world") >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, {toString: function() { return "hello"; }}); >+var result = foo(o); >+if (result != "hello world") >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-struct-or-other-then-string.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-struct-or-other-then-string.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-struct-or-other-then-string.js (working copy) >@@ -0,0 +1,36 @@ >+String.prototype.g = 44; >+ >+function foo(o) { >+ var tmp = o.f; >+ if (tmp) >+ return tmp.g; >+ return 42; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+var p = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, null); >+for (var i = 0; i < 5; ++i) >+ bar(p, {g:43}); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== 42) >+ throw "Error: bad result for o: " + result; >+ result = foo(p); >+ if (result !== 43) >+ throw "Error: bad result for p: " + result; >+} >+ >+bar(o, "hello"); >+var result = foo(o); >+if (result !== 44) >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt-fold.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt-fold.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt-fold.js (working copy) >@@ -0,0 +1,34 @@ >+// This is like prop-type-struct-then-object.js, but it checks that the optimizing JITs emit the right type >+// check above a hot put_by_id that starts polymorphic but is folded by AI. >+ >+function foo(o) { >+ return o.f.g; >+} >+ >+function bar(o, p, v) { >+ if (isFinalTier() || o == p) { >+ var tmp = p.f; >+ o = p; >+ } >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {f:{g:42}}; >+for (var i = 0; i < 10000; ++i) { >+ bar(o, o, {g:42}); >+ bar({a:1, b:2}, o, {g:42}); >+} >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== 42) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, o, Object.create({g:43})); >+var result = foo(o); >+if (result !== 43) >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt-multi.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt-multi.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt-multi.js (working copy) >@@ -0,0 +1,30 @@ >+// This is like prop-type-struct-then-object.js, but it checks that the optimizing JITs emit the right type >+// check above a hot polymorphic put_by_id that ends up being compiled as a MultiPutByOffset. >+ >+function foo(o) { >+ return o.f.g; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {f:{g:42}}; >+for (var i = 0; i < 10000; ++i) { >+ bar(o, {g:42}); >+ bar({a:1, b:2}, 42); >+} >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== 42) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, Object.create({g:43})); >+var result = foo(o); >+if (result !== 43) >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-struct-then-object-opt.js (working copy) >@@ -0,0 +1,28 @@ >+// This is like prop-type-struct-then-object.js, but it checks that the optimizing JITs emit the right type >+// check above a hot put_by_id. >+ >+function foo(o) { >+ return o.f.g; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {f:{g:42}}; >+for (var i = 0; i < 10000; ++i) >+ bar(o, {g:42}); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== 42) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, Object.create({g:43})); >+var result = foo(o); >+if (result !== 43) >+ throw "Error: bad result at end: " + result; >Index: Source/JavaScriptCore/tests/stress/prop-type-struct-then-object.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/prop-type-struct-then-object.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/prop-type-struct-then-object.js (working copy) >@@ -0,0 +1,25 @@ >+function foo(o) { >+ return o.f.g; >+} >+ >+function bar(o, v) { >+ o.f = v; >+} >+ >+noInline(foo); >+noInline(bar); >+ >+var o = {}; >+for (var i = 0; i < 5; ++i) >+ bar(o, {g:42}); >+ >+for (var i = 0; i < 10000; ++i) { >+ var result = foo(o); >+ if (result !== 42) >+ throw "Error: bad result: " + result; >+} >+ >+bar(o, Object.create({g:43})); >+var result = foo(o); >+if (result !== 43) >+ throw "Error: bad result at end: " + result; >Index: Source/WTF/ChangeLog >=================================================================== >--- Source/WTF/ChangeLog (revision 190059) >+++ Source/WTF/ChangeLog (working copy) >@@ -1,3 +1,15 @@ >+2015-09-17 Filip Pizlo <fpizlo@apple.com> >+ >+ JSC should infer property types >+ https://bugs.webkit.org/show_bug.cgi?id=148610 >+ >+ Reviewed by Geoffrey Garen. >+ >+ * wtf/HashTable.h: >+ (WTF::HashTableAddResult::HashTableAddResult): Make it possible to say "HashMap::AddResult result" without assigning anything to it yet. >+ * wtf/PrintStream.h: >+ (WTF::printInternal): Beef up printing of some common WTF types, in particular RefPtr<UniquedStringImpl>. >+ > 2015-09-20 Youenn Fablet <youenn.fablet@crf.canon.fr> > > Remove XHR_TIMEOUT compilation guard >Index: Source/WTF/wtf/HashTable.h >=================================================================== >--- Source/WTF/wtf/HashTable.h (revision 190059) >+++ Source/WTF/wtf/HashTable.h (working copy) >@@ -286,6 +286,7 @@ namespace WTF { > }; > > template<typename IteratorType> struct HashTableAddResult { >+ HashTableAddResult() : isNewEntry(false) { } > HashTableAddResult(IteratorType iter, bool isNewEntry) : iterator(iter), isNewEntry(isNewEntry) { } > IteratorType iterator; > bool isNewEntry; >Index: Source/WTF/wtf/PrintStream.h >=================================================================== >--- Source/WTF/wtf/PrintStream.h (revision 190059) >+++ Source/WTF/wtf/PrintStream.h (working copy) >@@ -31,6 +31,7 @@ > #include <wtf/FastMalloc.h> > #include <wtf/Noncopyable.h> > #include <wtf/RawPointer.h> >+#include <wtf/RefPtr.h> > #include <wtf/StdLibExtras.h> > > namespace WTF { >@@ -74,12 +75,14 @@ WTF_EXPORT_PRIVATE void printInternal(Pr > WTF_EXPORT_PRIVATE void printInternal(PrintStream&, const StringImpl*); > inline void printInternal(PrintStream& out, const AtomicStringImpl* value) { printInternal(out, bitwise_cast<const StringImpl*>(value)); } > inline void printInternal(PrintStream& out, const UniquedStringImpl* value) { printInternal(out, bitwise_cast<const StringImpl*>(value)); } >+inline void printInternal(PrintStream& out, const UniquedStringImpl& value) { printInternal(out, &value); } > inline void printInternal(PrintStream& out, char* value) { printInternal(out, static_cast<const char*>(value)); } > inline void printInternal(PrintStream& out, CString& value) { printInternal(out, static_cast<const CString&>(value)); } > inline void printInternal(PrintStream& out, String& value) { printInternal(out, static_cast<const String&>(value)); } > inline void printInternal(PrintStream& out, StringImpl* value) { printInternal(out, static_cast<const StringImpl*>(value)); } > inline void printInternal(PrintStream& out, AtomicStringImpl* value) { printInternal(out, static_cast<const AtomicStringImpl*>(value)); } > inline void printInternal(PrintStream& out, UniquedStringImpl* value) { printInternal(out, static_cast<const UniquedStringImpl*>(value)); } >+inline void printInternal(PrintStream& out, UniquedStringImpl& value) { printInternal(out, &value); } > WTF_EXPORT_PRIVATE void printInternal(PrintStream&, bool); > WTF_EXPORT_PRIVATE void printInternal(PrintStream&, signed char); // NOTE: this prints as a number, not as a character; use CharacterDump if you want the character > WTF_EXPORT_PRIVATE void printInternal(PrintStream&, unsigned char); // NOTE: see above. >@@ -169,6 +172,12 @@ void printInternal(PrintStream& out, con > out.print(pointerDump(value.get())); > } > >+template<typename T> >+void printInternal(PrintStream& out, const RefPtr<T>& value) >+{ >+ out.print(pointerDump(value.get())); >+} >+ > template<typename T, typename U> > class ValueInContext { > public: >Index: Tools/Scripts/run-jsc-stress-tests >=================================================================== >--- Tools/Scripts/run-jsc-stress-tests (revision 190059) >+++ Tools/Scripts/run-jsc-stress-tests (working copy) >@@ -699,6 +699,10 @@ def runWithRAMSize(size) > run("ram-size-#{size}", "--forceRAMSize=#{size}") > end > >+def runNoJIT >+ run("no-jit", "--useJIT=false") >+end >+ > def runNoLLInt > if $jitTests > run("no-llint", "--useLLInt=false")
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
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 148610
:
260294
|
260305
|
260312
|
260323
|
260376
|
260936
|
260955
|
260958
|
261049
|
261074
|
261136
|
261159
|
261216
|
261223
|
261240
|
261243
|
261256
|
261275
|
261284
|
261290
|
261295
|
261334
|
261417
|
261429
|
261483
|
261488
| 261675