WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
rebased and using a slightly more space-optimal InferredTypeTable
blah.patch (text/plain), 111.68 KB, created by
Filip Pizlo
on 2015-09-10 14:11:17 PDT
(
hide
)
Description:
rebased and using a slightly more space-optimal InferredTypeTable
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2015-09-10 14:11:17 PDT
Size:
111.68 KB
patch
obsolete
>Index: Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (revision 189571) >+++ 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, ); }; }; >@@ -752,6 +754,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 */; }; >+ 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, ); }; }; >@@ -1920,6 +1926,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>"; }; >@@ -2583,6 +2591,10 @@ > 0FF922CF14F46B130041A24E /* JSCLLIntOffsetsExtractor */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = JSCLLIntOffsetsExtractor; sourceTree = BUILT_PRODUCTS_DIR; }; > 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>"; }; >@@ -4657,8 +4669,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 */, >@@ -4692,6 +4704,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 */, >@@ -4742,10 +4758,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 */, >@@ -5049,9 +5065,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 */, >@@ -5203,6 +5219,7 @@ > 0F2FC77116E12F6F0038D976 /* DFGDCEPhase.h */, > 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */, > 0F8F2B98172F04FD007DBDA5 /* DFGDesiredIdentifiers.h */, >+ 0FFC92131B94E83E0071DD66 /* DFGDesiredInferredType.h */, > C2C0F7CB17BBFC5B00464FE4 /* DFGDesiredTransitions.cpp */, > C2C0F7CC17BBFC5B00464FE4 /* DFGDesiredTransitions.h */, > 0FE853491723CDA500B618F5 /* DFGDesiredWatchpoints.cpp */, >@@ -5350,6 +5367,7 @@ > 0F3E01A919D353A500F61B7F /* DFGPrePostNumbering.h */, > 0F2B9CE019D0BA7D00B1D1B5 /* DFGPromotedHeapLocation.cpp */, > 0F2B9CE119D0BA7D00B1D1B5 /* DFGPromotedHeapLocation.h */, >+ 0FFC92151B94FB3E0071DD66 /* DFGPropertyTypeKey.h */, > 0FB1765E196B8F9E0091052A /* DFGPureValue.cpp */, > 0FB1765F196B8F9E0091052A /* DFGPureValue.h */, > 0F3A1BF71A9ECB7D000DE01A /* DFGPutStackSinkingPhase.cpp */, >@@ -5940,6 +5958,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 */, >@@ -6053,6 +6072,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 */, >@@ -6338,6 +6358,7 @@ > 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 */, >@@ -6504,6 +6525,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 */, >@@ -7448,6 +7470,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 */, >@@ -7863,6 +7886,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/bytecode/Watchpoint.h >=================================================================== >--- Source/JavaScriptCore/bytecode/Watchpoint.h (revision 189571) >+++ 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/dfg/DFGAbstractInterpreter.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (revision 189571) >+++ 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 189571) >+++ 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_graph.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,23 @@ 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: >+ admittedTypes = SpecNone; >+ break; >+ case CellOrOther: >+ admittedTypes = SpecOther; >+ break; >+ default: >+ DFG_CRASH(m_graph, node, "Bad use kind"); > break; > } >- >- filter(value, set); >+ >+ filter(value, set, admittedTypes); > break; > } > >@@ -2084,13 +2084,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 +2136,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 +2147,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 GetByOffetMethod::Load: { >+ result.merge( >+ m_graph.inferredValueForProperty( >+ set, uid, getCase.method().offset(), m_graph.structureClobberState())); >+ break; >+ } >+ >+ default: { >+ result.makeHeapTop(); >+ break; >+ } } > } > > if (forNode(node->child1()).changeStructure(m_graph, baseSet) == Contradiction) >@@ -2615,9 +2644,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 189571) >+++ Source/JavaScriptCore/dfg/DFGAbstractValue.cpp (working copy) >@@ -117,6 +117,59 @@ 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::Double: >+ setType(SpecBytecodeDouble); >+ 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 +242,11 @@ bool AbstractValue::mergeOSREntryValue(G > return oldMe != *this; > } > >-FiltrationResult AbstractValue::filter(Graph& graph, const StructureSet& other) >+FiltrationResult AbstractValue::filter( >+ Graph& graph, const StructureSet& other, SpeculatedType admittedTypes) > { >+ ASSERT(!(admittedTypes & SpecCell)); >+ > if (isClear()) > return FiltrationOK; > >@@ -198,7 +254,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); > >Index: Source/JavaScriptCore/dfg/DFGAbstractValue.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractValue.h (revision 189571) >+++ 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,8 +284,14 @@ struct AbstractValue { > { > return !(m_type & ~desiredType); > } >+ >+ // 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); >Index: Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (revision 189571) >+++ Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp (working copy) >@@ -205,9 +205,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. >@@ -2303,7 +2302,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)) >@@ -2314,13 +2315,16 @@ Node* ByteCodeParser::handleGetByOffset( > StorageAccessData* data = m_graph.m_storageAccessData.add(); > data->offset = offset; > data->identifierNumber = identifierNumber; >+ data->inferredType = 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)) >@@ -2331,6 +2335,7 @@ Node* ByteCodeParser::handlePutByOffset( > StorageAccessData* data = m_graph.m_storageAccessData.add(); > data->offset = offset; > data->identifierNumber = identifier; >+ data->inferredType = inferredType; > > Node* result = addToGraph(PutByOffset, OpInfo(data), propertyStorage, base, value); > >@@ -2445,7 +2450,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(). >@@ -2551,13 +2557,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 >@@ -2622,9 +2628,16 @@ Node* ByteCodeParser::load( > if (constant) > return weakJSConstant(constant); > } >+ >+ InferredType::Descriptor inferredType; >+ if (needStructureCheck) { >+ for (Structure* structure : variant.structureSet()) >+ inferredType.merge(m_graph.inferredTypeForProperty(structure, uid)); >+ } else >+ inferredType = InferredType::Top; > > loadedValue = handleGetByOffset( >- loadPrediction, base, identifierNumber, variant.offset(), loadOp); >+ loadPrediction, base, identifierNumber, variant.offset(), inferredType, loadOp); > } > > return loadedValue; >@@ -2635,7 +2648,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.inferredType(), value); > } > > void ByteCodeParser::handleGetById( >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(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 == 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, context); >+ } >+ >+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 189571) >+++ 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 189571) >+++ 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/DFGGraph.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGGraph.cpp (revision 189571) >+++ Source/JavaScriptCore/dfg/DFGGraph.cpp (working copy) >@@ -887,6 +887,50 @@ 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.get(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.isStillValid()) { >+ m_inferredTypes.add(key, InferredType::Top); >+ return InferredType::Top; >+ } >+ >+ m_inferredTypes.add(key, typeDescriptor); >+ >+ m_plan.weakReferences.addLazily(typeObject); >+ if (typeDescriptor.structure()) >+ registerStructure(typeDescriptor.structure()); >+ >+ // 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; >+} >+ >+bool Graph::watchDesiredType(const DesiredInferredType& key) >+{ >+ if (!key.isStillValid()) >+ return false; >+ >+ m_plan.weakReferences.addLazily(key.object()); >+ if (Structure* structure = key.expected().structure()) >+ registerStructure(structure); >+ m_plan.watchpoints.addLazily(key); >+} >+ > FullBytecodeLiveness& Graph::livenessFor(CodeBlock* codeBlock) > { > HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>>::iterator iter = m_bytecodeLiveness.find(codeBlock); >@@ -1061,7 +1105,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 +1120,35 @@ 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); >+ }); >+ 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 189571) >+++ Source/JavaScriptCore/dfg/DFGGraph.h (working copy) >@@ -675,6 +675,21 @@ 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); >+ >+ // 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); >+ >+ // 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 +871,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/DFGNode.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNode.h (revision 189571) >+++ 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/DFGPropertyTypeKey.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGPropertyTypeKey.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGPropertyTypeKey.h (working copy) >@@ -0,0 +1,128 @@ >+/* >+ * 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 <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_object(nullptr) >+ , m_uid(deletedUID()) >+ { >+ } >+ >+ explicit operator bool() const { return m_structure && m_uid; } >+ >+ Structure* structure() const { return m_structure; } >+ PropertyOffset 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_object && 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, context); >+ } >+ >+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 189571) >+++ 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: >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (revision 189571) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (working copy) >@@ -5229,6 +5229,80 @@ 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 == InvalidGPR) { >+ 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.branchWeakStructur(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: { >+ 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(this); >+ return; >+ } >+ >+ default: >+ DFG_CRASH(m_jit.graph(), node, "Bad use kind"); >+ return; >+ } >+} >+ > void SpeculativeJIT::compileAllocatePropertyStorage(Node* node) > { > if (node->transition()->previous->couldHaveIndexingHeader()) { >@@ -5633,6 +5707,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)) >@@ -5913,6 +6003,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 189571) >+++ 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/DFGStructureAbstractValue.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h (revision 189571) >+++ 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 189571) >+++ 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 189571) >+++ 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/ftl/FTLCapabilities.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp (revision 189571) >+++ 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 189571) >+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (working copy) >@@ -2026,21 +2026,56 @@ 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: { >+ 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(cell, m_heaps.JSCell_structureID), jsValueValue(cell), >+ 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() >@@ -7509,6 +7544,9 @@ private: > case CellUse: > speculateCell(edge); > break; >+ case CellOrOtherUse: >+ speculateCellOrOther(edge); >+ break; > case KnownCellUse: > ASSERT(!m_interpreter.needsTypeCheck(edge)); > break; >@@ -7589,6 +7627,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)) >Index: Source/JavaScriptCore/jit/AssemblyHelpers.cpp >=================================================================== >--- Source/JavaScriptCore/jit/AssemblyHelpers.cpp (revision 189571) >+++ Source/JavaScriptCore/jit/AssemblyHelpers.cpp (working copy) >@@ -55,6 +55,75 @@ Vector<BytecodeAndMachineOffset>& Assemb > return result.iterator->value; > } > >+AssemblyHelpers::JumpList AssemblyHelpers::branchIfNotType( >+ JSValueRegs regs, GPRReg tempGPR, const InferredType::Descriptor& descriptor) >+{ >+ 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)); >+ break; >+ >+ case InferredType::Double: >+ result.append(branchIfInt32(regs)); >+ result.append(branchIfNotNumber(regs)); >+ break; >+ >+ case InferredType::Number: >+ result.append(branchIfNotNumber(regs)); >+ break; >+ >+ case InferredType::String: >+ result.append(branchIfNotCell(regs)); >+ result.append(branchIfNotString(regs.payloadGPR())); >+ break; >+ >+ case InferredType::ObjectWithStructure: >+ result.append(branchIfNotCell(regs)); >+ result.append(branchStructure(regs.payloadGPR(), descriptor.structure())); >+ break; >+ >+ case InferredType::ObjectWithStructureOrOther: { >+ Jump ok = branchIfOther(regs, tempGPR); >+ result.append(branchIfNotCell(regs)); >+ result.append(branchStructure(regs.payloadGPR(), descriptor.structure())); >+ ok.link(this); >+ break; >+ } >+ >+ case InferredType::Object: >+ result.append(branchIfNotCell(regs)); >+ result.append(branchIfNotObject(regs.payloadGPR())); >+ break; >+ >+ case InferredType::ObjectOrOther: { >+ Jump ok = branchIfOther(regs, tempGPR); >+ result.append(branchIfNotCell(regs)); >+ 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 189571) >+++ Source/JavaScriptCore/jit/AssemblyHelpers.h (working copy) >@@ -563,6 +563,18 @@ public: > return branch32(Equal, regs.tagGPR(), TrustedImm32(JSValue::EmptyValueTag)); > #endif > } >+ >+ JumpList branchIfNotType(JSValueRegs regs, GPRReg tempGPR, const InferredType::Descriptor&); >+ >+ template<typename T> >+ Jump branchStructure(RelationalCondition condition, T leftHandSide, Structure* structure) >+ { >+#if USE(JSVALUE64) >+ return branch32(condition, leftHandSide, TrustedImm32(structure->id())); >+#else >+ return branchPtr(condition, leftHandSide, TrustedImmPtr(structure)); >+#endif >+ } > > template<typename T> > Jump branchStructure(RelationalCondition condition, T leftHandSide, Structure* structure) >Index: Source/JavaScriptCore/runtime/InferredType.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/InferredType.cpp (revision 0) >+++ Source/JavaScriptCore/runtime/InferredType.cpp (working copy) >@@ -0,0 +1,443 @@ >+/* >+ * 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 { >+ >+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.get()); >+} >+ >+InferredType::Descriptor InferredType::Descriptor::forValue(JSValue value) >+{ >+ if (value.isBoolean()) >+ return Boolean; >+ if (value.isUndefinedOrNull()) >+ return Other; >+ if (value.isInt32()) >+ return Int32; >+ if (value.isDouble()) >+ return Double; >+ 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; >+} >+ >+void InferredType::Descriptor::merge(const Descriptor& other) >+{ >+ if (*this == other) >+ return; >+ >+ switch (m_kind) { >+ case Bottom: >+ *this = other; >+ return; >+ case Boolean: >+ case String: >+ *this = Top; >+ return; >+ case Other: >+ switch (other.m_kind) { >+ case ObjectWithStructure: >+ m_kind = ObjectWithStructureOrOther; >+ return; >+ case Object: >+ m_kind = ObjectOrOther; >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case Int32: >+ switch (other.m_kind) { >+ case Double: >+ case Number: >+ m_kind = Number; >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case Double: >+ switch (other.m_kind) { >+ case Int32: >+ case Number: >+ m_kind = Number; >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case Number: >+ switch (other.m_kind) { >+ case Int32: >+ case Double: >+ return; >+ default: >+ *this = Top; >+ return; >+ } >+ case ObjectWithStructure: >+ switch (other.m_kind) { >+ case ObjectWithStructure: >+ 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: >+ 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: >+ m_kind = 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::isStillValid(const ConcurrentJITLocker& locker, const Descriptor& expected) >+{ >+ if (expected.kind() == Top) >+ return false; >+ >+ return descriptor(locker) == expected; >+} >+ >+bool InferredType::isStillValid(const Descriptor& expected) >+{ >+ ConcurrentJITLocker locker(m_lock); >+ return isStillValid(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()); >+} >+ >+void InferredType::willStoreValueSlow(VM& vm, 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, myType); >+} >+ >+void InferredType::makeTopSlow(VM& vm) >+{ >+ ConcurrentJITLocker locker(m_lock); >+ set(locker, vm, Top); >+} >+ >+void InferredType::set(const ConcurrentJITLocker& locker, VM& vm, 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); >+ >+ // 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("Inferred type changed"); >+ } >+ >+ // 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>(this, newDescriptor.structure()); >+ newDescriptor.structure()->addTransitionWatchpoint(m_structure.get()); >+ } >+ } >+ >+ // Finally, set the descriptor kind. >+ m_kind = newDescriptor.kind(); >+ >+ // Assert that we did things. >+ ASSERT(descriptor(locker) == newDescriptor); >+} >+ >+void InferredType::removeStructure() >+{ >+ VM& vm = *Heap::heap(this)->vm(); >+ >+ ConcurrentJITLocker locker(m_parent->m_lock); >+ >+ Descriptor newDescriptor = descriptor(locker); >+ newDescriptor.removeStructure(); >+ >+ set(locker, vm, newDescriptor); >+} >+ >+void InferredType::InferredStructure::InferredStructure(InferredType* parent, Structure* structure) >+ : m_parent(parent) >+ , m_structure(vm, parent, structure) >+{ >+} >+ >+void InferredType::InferredStructure::fireInternal(const FireDetail&) >+{ >+ m_parent->removeStructure(); >+} >+ >+void InferredType::InferredStructure::finalizeUnconditionally() >+{ >+ VM& vm = *Heap::heap(m_parent)->vm(); >+ >+ ASSERT(Heap::isMarked(m_parent)); >+ >+ if (!Heap::isMarked(m_structure.get())) >+ m_parent->removeStructure(); >+} >+ >+} // 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::Double: >+ out.print("Double"); >+ 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,248 @@ >+/* >+ * 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. >+ */ >+ >+#infdef InferredType_h >+#define InferredType_h >+ >+#include "ConcurrentJITLock.h" >+#include "JSCell.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 Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype); >+ >+ static void visitChildren(JSCell*, SlotVisitor&); >+ >+ DECLARE_INFO; >+ >+ enum Kind : uint8_t { >+ Bottom, >+ Boolean, >+ Other, >+ Int32, >+ Double, >+ Number, >+ String, >+ ObjectWithStructure, >+ ObjectWithStructureOrOther, >+ Object, >+ ObjectOrOther, >+ Top >+ }; >+ >+ class Descriptor { >+ public: >+ Descriptor() >+ : m_kind(Bottom) >+ , m_structure(nullptr) >+ { >+ } >+ >+ Descriptor(Kind kind, Structure* structure = nullptr) >+ : m_kind(kind) >+ , m_structure(structure) >+ { >+ } >+ >+ static Descriptor forValue(JSValue); >+ >+ explicit operator bool() const >+ { >+ return m_kind == Bottom && !m_structure; >+ } >+ >+ Kind kind() const { return m_kind; } >+ Structure* structure() const { return m_structure; } >+ >+ 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 Double: >+ return value.isDouble(); >+ 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) >+ { >+ return m_kind == other.m_kind >+ && m_structure == other.m_structure; >+ } >+ >+ bool operator!=(const Descriptor& other) >+ { >+ 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().kind(); } >+ Structure* structure(const ConcurrentJITLocker& locker) const { return descriptor().structure(); } >+ >+ 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, JSValue value) >+ { >+ Descriptor currentDescriptor = descriptorMainThread(); >+ if (currentDescriptor.includesValue(value)) >+ return currentDescriptor.kind() != Top; >+ return willStoreValueSlow(vm, value); >+ } >+ >+ // Immediately makes this type irrelevant. >+ void makeTop(VM& vm) >+ { >+ if (isTop()) >+ return; >+ makeTopSlow(vm); >+ } >+ >+ // 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(); >+ >+ void willStoreValueSlow(VM&, JSValue); >+ void makeTopSlow(VM&); >+ void set(const ConcurrentJITLocker&, VM&, Descriptor); >+ void removeStructure(); >+ >+ mutable ConcurrentJITLock m_lock; >+ >+ Kind m_kind { Bottom }; >+ >+ class InferredStructure : public Watchpoint, public UnconditionalFinalizer { >+ public: >+ InferredStructure(InferredType* parent, Structure*); >+ >+ Structure* structure() const { return m_structure.get(); }; >+ >+ protected: >+ void fireInternal(const FireDetail&) override; >+ void finalizeUnconditionally() override; >+ >+ private: >+ InferredType* m_parent; >+ WriteBarrier<Structure> m_structure; >+ }; >+ >+ 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,156 @@ >+/* >+ * 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(m_lock); >+ >+ for (auto& entry : m_table) { >+ if (entry.value->isRelevant()) >+ visitor.append(&entry.value); >+ else >+ entry.value.clear(); >+ } >+} >+ >+InferredType* InferredTypeTable::get(const ConcurrentJITLocker& locker, 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) { >+ TypeTable::iterator iter = m_table.find(propertyName); >+ if (iter == m_table.end() || !iter->value) >+ return false; // Absence on replace => top. >+ >+ if (iter->value->willStoreValue(vm, value)) >+ return true; >+ >+ iter->value.clear(); >+ return false; >+ } >+ >+ TypeTable::AddResult result; >+ { >+ ConcurrentJITLocker locker(m_lock); >+ result = m_table.add(propertyName, 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, 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) { >+ TypeTable::iterator iter = m_table.find(propertyName); >+ if (iter == m_table.end() || !iter->value) >+ return; // Absence on replace => top. >+ >+ iter->value->makeTop(); >+ iter->value.clear(); >+ return; >+ } >+ >+ ConcurrentJITLocker locker(m_lock); >+ TypeTable::AddResult result = m_table.add(propertyName, WriteBarrier<InferredType>()); >+ if (!result.iterator->value) >+ return; >+ >+ result.iterator->value->makeTop(); >+ result.iterator->value.clear(); >+} >+ >+void InferredTypeTable::makeTopForTransition(VM& vm, PropertyName propertyName) >+{ >+} >+ >+} // 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,111 @@ >+/* >+ * 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 "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 lifetype 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 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 189571) >+++ 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) { >@@ -1228,6 +1230,8 @@ inline bool JSObject::putDirectInternal( > PropertyOffset offset; > size_t currentCapacity = this->structure()->outOfLineCapacity(); > if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, offset)) { >+ structure->willStoreValueForTransition(vm, propertyName, value, slot.context() == PutById); >+ > DeferGC deferGC(vm.heap); > Butterfly* newButterfly = butterfly(); > if (currentCapacity != structure->outOfLineCapacity()) { >@@ -1244,12 +1248,16 @@ inline bool JSObject::putDirectInternal( > } > > 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() == PutById); >+ > slot.setExistingProperty(this, offset); > putDirect(vm, offset, value); > >@@ -1269,6 +1277,7 @@ inline bool JSObject::putDirectInternal( > DeferredStructureTransitionWatchpointFire deferredWatchpointFire; > > structure = Structure::addPropertyTransition(vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire); >+ structure->willStoreValueForTransition(vm, propertyName, value, slot.context() == PutById); > > validateOffset(offset); > ASSERT(structure->isValidOffset(offset)); >Index: Source/JavaScriptCore/runtime/Structure.cpp >=================================================================== >--- Source/JavaScriptCore/runtime/Structure.cpp (revision 189571) >+++ Source/JavaScriptCore/runtime/Structure.cpp (working copy) >@@ -347,11 +347,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); > } > >@@ -461,6 +465,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); > >@@ -476,6 +481,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); >@@ -838,6 +861,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(!hadBeenDictionary()); >+ >+ // 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::storeStoreBarrier(); >+ 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; >@@ -1055,6 +1115,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 189571) >+++ 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 >@@ -477,6 +482,34 @@ 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; >+ } >+ >+ // Returns true if the type of the property is still valid. >+ 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; >+ willSloreValueSlow(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 >@@ -627,6 +660,9 @@ private: > > void startWatchingInternalProperties(VM&); > >+ void willStoreValueSlow( >+ VM&, PropertyOffset, PropertyName, JSValue, bool, InferredTypeTable::StoredPropertyAge); >+ > static const int s_maxTransitionLength = 64; > static const int s_maxTransitionLengthForNonEvalPutById = 512; > >@@ -650,6 +686,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 189571) >+++ Source/JavaScriptCore/runtime/StructureInlines.h (working copy) >@@ -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; > } >
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