JSC should infer property types
https://bugs.webkit.org/show_bug.cgi?id=148610

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

This change brings recursive type inference to JavaScript object properties in JSC. We check that a
value being stored into a property obeys a property's type before we do the store. If it doesn't,
we broaden the property's type to include the new value. If optimized code was relying on the old
type, we deoptimize that code.

The type system that this supports includes important primitive types like Int32 and Boolean. But
it goes further and also includes a type kind called ObjectWithStructure, which means that we
expect the property to always point to objects with a particular structure. This only works for
leaf structures (i.e. structures that have a valid transition watchpoint set). Invalidation of the
transition set causes the property type to become Object (meaning an object with any structure).
This capability gives us recursive type inference. It's possible for an expression like "o.f.g.h"
to execute without any type checks if .f and .g are both ObjectWithStructure.

The type inference of a property is tracked by an InferredType instance, which is a JSCell. This
means that it manages its own memory. That's convenient. For example, when the DFG is interested in
one of these, it can just list the InferredType as a weak reference in addition to setting a
watchpoint. This ensures that even if the InferredType is dropped by the owning structure, the DFG
won't read a dangling pointer. A mapping from property name to InferredType is implemented by
InferredTypeTable, which is also a JSCell. Each Structure may point to some InferredTypeTable.

This feature causes programs to be happier (run faster without otherwise doing bad things like
using lots of memory) when four conditions hold:

1) A property converges to one of the types that we support.
2) The property is loaded from more frequently than it is stored to.
3) The stores are all cached, so that we statically emit a type check.
4) We don't allocate a lot of meta-data for the property's type.

We maximize the likelihood of (1) by having a rich type system. But having a rich type system means
that a reflective put to a property has to have a large switch over the inferred type to decide how
to do the type check. That's why we need (3). We ensure (3) by having every reflective property
store (i.e. putDirectInternal in any context that isn't PutById) force the inferred type to become
Top. We don't really worry about ensuring (2); this is statistically true for most programs
already.

Probably the most subtle trickery goes into (4). Logically we'd like to say that each
(Structure, Property) maps to its own InferredType. If structure S1 has a transition edge to S2,
then we could ensure that the InferredType I1 where (S1, Property)->I1 has a data flow constraint
to I2 where (S2, Property)->I2. That would work, but it would involve a lot of memory. And when I1
gets invalidated in some way, it would have to tell I2 about it, and then I2 might tell other
InferredType objects downstream. That's madness. So, the first major compromise that we make here
is to say that if some property has some InferredType at some Structure, then anytime we
transition from that Structure, the new Structure shares the same InferredType for that property.
This unifies the type of the property over the entire transition tree starting at the Structure at
which the property was added. But this would still mean that each Structure would have its own
InferredTypeTable. We don't want that because experience with PropertyTable shows that this can be
a major memory hog. So, we don't create an InferredTypeTable until someone adds a property that is
subject to type inference (i.e. it was added non-reflectively), and we share that InferredTypeTable
with the entire structure transition tree rooted at the Structure that had the first inferred
property. We also drop the InferredTypeTable anytime that we do a dictionary transition, and we
don't allow further property type inference if a structure had ever been a dictionary.

This is a 3% speed-up on Octane and a 12% speed-up on Kraken on my setup. It's not a significant
slow-down on any benchmark I ran.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::branchTest64):
* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::branchTest64):
(JSC::MacroAssemblerX86_64::test64):
* bytecode/PolymorphicAccess.cpp:
(JSC::AccessCase::generate):
* bytecode/PutByIdFlags.cpp:
(WTF::printInternal):
* bytecode/PutByIdFlags.h:
(JSC::encodeStructureID):
(JSC::decodeStructureID):
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeFromLLInt):
(JSC::PutByIdStatus::computeFor):
(JSC::PutByIdStatus::computeForStubInfo):
* bytecode/PutByIdVariant.cpp:
(JSC::PutByIdVariant::operator=):
(JSC::PutByIdVariant::replace):
(JSC::PutByIdVariant::transition):
(JSC::PutByIdVariant::setter):
(JSC::PutByIdVariant::attemptToMerge):
(JSC::PutByIdVariant::dumpInContext):
* bytecode/PutByIdVariant.h:
(JSC::PutByIdVariant::newStructure):
(JSC::PutByIdVariant::requiredType):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedInstruction::UnlinkedInstruction):
* bytecode/Watchpoint.h:
(JSC::InlineWatchpointSet::touch):
(JSC::InlineWatchpointSet::isBeingWatched):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::addConstantValue):
(JSC::BytecodeGenerator::emitPutById):
(JSC::BytecodeGenerator::emitDirectPutById):
* dfg/DFGAbstractInterpreter.h:
(JSC::DFG::AbstractInterpreter::filter):
(JSC::DFG::AbstractInterpreter::filterByValue):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
(JSC::DFG::AbstractInterpreter<AbstractStateType>::filter):
* dfg/DFGAbstractValue.cpp:
(JSC::DFG::AbstractValue::setType):
(JSC::DFG::AbstractValue::set):
(JSC::DFG::AbstractValue::fixTypeForRepresentation):
(JSC::DFG::AbstractValue::mergeOSREntryValue):
(JSC::DFG::AbstractValue::isType):
(JSC::DFG::AbstractValue::filter):
(JSC::DFG::AbstractValue::filterValueByType):
* dfg/DFGAbstractValue.h:
(JSC::DFG::AbstractValue::setType):
(JSC::DFG::AbstractValue::isType):
(JSC::DFG::AbstractValue::validate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
(JSC::DFG::ByteCodeParser::handleGetByOffset):
(JSC::DFG::ByteCodeParser::handlePutByOffset):
(JSC::DFG::ByteCodeParser::load):
(JSC::DFG::ByteCodeParser::store):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::handlePutById):
* dfg/DFGClobbersExitState.cpp:
(JSC::DFG::clobbersExitState):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::emitGetByOffset):
(JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
(JSC::DFG::ConstantFoldingPhase::addBaseCheck):
* dfg/DFGDesiredInferredType.h: Added.
(JSC::DFG::DesiredInferredType::DesiredInferredType):
(JSC::DFG::DesiredInferredType::operator bool):
(JSC::DFG::DesiredInferredType::object):
(JSC::DFG::DesiredInferredType::expected):
(JSC::DFG::DesiredInferredType::isStillValid):
(JSC::DFG::DesiredInferredType::add):
(JSC::DFG::DesiredInferredType::operator==):
(JSC::DFG::DesiredInferredType::operator!=):
(JSC::DFG::DesiredInferredType::isHashTableDeletedValue):
(JSC::DFG::DesiredInferredType::hash):
(JSC::DFG::DesiredInferredType::dumpInContext):
(JSC::DFG::DesiredInferredType::dump):
(JSC::DFG::DesiredInferredTypeHash::hash):
(JSC::DFG::DesiredInferredTypeHash::equal):
* dfg/DFGDesiredWatchpoints.cpp:
(JSC::DFG::AdaptiveStructureWatchpointAdaptor::add):
(JSC::DFG::InferredTypeAdaptor::add):
(JSC::DFG::DesiredWatchpoints::DesiredWatchpoints):
(JSC::DFG::DesiredWatchpoints::~DesiredWatchpoints):
(JSC::DFG::DesiredWatchpoints::addLazily):
(JSC::DFG::DesiredWatchpoints::consider):
(JSC::DFG::DesiredWatchpoints::reallyAdd):
(JSC::DFG::DesiredWatchpoints::areStillValid):
(JSC::DFG::DesiredWatchpoints::dumpInContext):
* dfg/DFGDesiredWatchpoints.h:
(JSC::DFG::AdaptiveStructureWatchpointAdaptor::dumpInContext):
(JSC::DFG::InferredTypeAdaptor::hasBeenInvalidated):
(JSC::DFG::InferredTypeAdaptor::dumpInContext):
(JSC::DFG::DesiredWatchpoints::isWatched):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
(JSC::DFG::Graph::isSafeToLoad):
(JSC::DFG::Graph::inferredTypeFor):
(JSC::DFG::Graph::livenessFor):
(JSC::DFG::Graph::tryGetConstantProperty):
(JSC::DFG::Graph::inferredValueForProperty):
(JSC::DFG::Graph::tryGetConstantClosureVar):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::registerInferredType):
(JSC::DFG::Graph::inferredTypeForProperty):
* dfg/DFGInferredTypeCheck.cpp: Added.
(JSC::DFG::insertInferredTypeCheck):
* dfg/DFGInferredTypeCheck.h: Added.
* dfg/DFGNode.h:
* dfg/DFGObjectAllocationSinkingPhase.cpp:
* dfg/DFGPropertyTypeKey.h: Added.
(JSC::DFG::PropertyTypeKey::PropertyTypeKey):
(JSC::DFG::PropertyTypeKey::operator bool):
(JSC::DFG::PropertyTypeKey::structure):
(JSC::DFG::PropertyTypeKey::uid):
(JSC::DFG::PropertyTypeKey::operator==):
(JSC::DFG::PropertyTypeKey::operator!=):
(JSC::DFG::PropertyTypeKey::hash):
(JSC::DFG::PropertyTypeKey::isHashTableDeletedValue):
(JSC::DFG::PropertyTypeKey::dumpInContext):
(JSC::DFG::PropertyTypeKey::dump):
(JSC::DFG::PropertyTypeKey::deletedUID):
(JSC::DFG::PropertyTypeKeyHash::hash):
(JSC::DFG::PropertyTypeKeyHash::equal):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::SafeToExecuteEdge::operator()):
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileTypeOf):
(JSC::DFG::SpeculativeJIT::compileCheckStructure):
(JSC::DFG::SpeculativeJIT::compileAllocatePropertyStorage):
(JSC::DFG::SpeculativeJIT::speculateCell):
(JSC::DFG::SpeculativeJIT::speculateCellOrOther):
(JSC::DFG::SpeculativeJIT::speculateObject):
(JSC::DFG::SpeculativeJIT::speculate):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStoreBarrierInsertionPhase.cpp:
* dfg/DFGStructureAbstractValue.h:
(JSC::DFG::StructureAbstractValue::at):
(JSC::DFG::StructureAbstractValue::operator[]):
(JSC::DFG::StructureAbstractValue::onlyStructure):
(JSC::DFG::StructureAbstractValue::forEach):
* dfg/DFGUseKind.cpp:
(WTF::printInternal):
* dfg/DFGUseKind.h:
(JSC::DFG::typeFilterFor):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validate):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileCheckStructure):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCheckCell):
(JSC::FTL::DFG::LowerDFGToLLVM::compileMultiPutByOffset):
(JSC::FTL::DFG::LowerDFGToLLVM::numberOrNotCellToInt32):
(JSC::FTL::DFG::LowerDFGToLLVM::checkInferredType):
(JSC::FTL::DFG::LowerDFGToLLVM::loadProperty):
(JSC::FTL::DFG::LowerDFGToLLVM::speculate):
(JSC::FTL::DFG::LowerDFGToLLVM::speculateCell):
(JSC::FTL::DFG::LowerDFGToLLVM::speculateCellOrOther):
(JSC::FTL::DFG::LowerDFGToLLVM::speculateMachineInt):
(JSC::FTL::DFG::LowerDFGToLLVM::appendOSRExit):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::decodedCodeMapFor):
(JSC::AssemblyHelpers::branchIfNotType):
(JSC::AssemblyHelpers::purifyNaN):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::branchIfEqual):
(JSC::AssemblyHelpers::branchIfNotCell):
(JSC::AssemblyHelpers::branchIfCell):
(JSC::AssemblyHelpers::branchIfNotOther):
(JSC::AssemblyHelpers::branchIfInt32):
(JSC::AssemblyHelpers::branchIfNotInt32):
(JSC::AssemblyHelpers::branchIfNumber):
(JSC::AssemblyHelpers::branchIfNotNumber):
(JSC::AssemblyHelpers::branchIfEmpty):
(JSC::AssemblyHelpers::branchStructure):
* jit/Repatch.cpp:
(JSC::tryCachePutByID):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/InferredType.cpp: Added.
(JSC::InferredType::create):
(JSC::InferredType::destroy):
(JSC::InferredType::createStructure):
(JSC::InferredType::visitChildren):
(JSC::InferredType::kindForFlags):
(JSC::InferredType::Descriptor::forValue):
(JSC::InferredType::Descriptor::forFlags):
(JSC::InferredType::Descriptor::putByIdFlags):
(JSC::InferredType::Descriptor::merge):
(JSC::InferredType::Descriptor::removeStructure):
(JSC::InferredType::Descriptor::subsumes):
(JSC::InferredType::Descriptor::dumpInContext):
(JSC::InferredType::Descriptor::dump):
(JSC::InferredType::InferredType):
(JSC::InferredType::~InferredType):
(JSC::InferredType::canWatch):
(JSC::InferredType::addWatchpoint):
(JSC::InferredType::dump):
(JSC::InferredType::willStoreValueSlow):
(JSC::InferredType::makeTopSlow):
(JSC::InferredType::set):
(JSC::InferredType::removeStructure):
(JSC::InferredType::InferredStructureWatchpoint::fireInternal):
(JSC::InferredType::InferredStructureFinalizer::finalizeUnconditionally):
(JSC::InferredType::InferredStructure::InferredStructure):
(WTF::printInternal):
* runtime/InferredType.h: Added.
* runtime/InferredTypeTable.cpp: Added.
(JSC::InferredTypeTable::create):
(JSC::InferredTypeTable::destroy):
(JSC::InferredTypeTable::createStructure):
(JSC::InferredTypeTable::visitChildren):
(JSC::InferredTypeTable::get):
(JSC::InferredTypeTable::willStoreValue):
(JSC::InferredTypeTable::makeTop):
(JSC::InferredTypeTable::InferredTypeTable):
(JSC::InferredTypeTable::~InferredTypeTable):
* runtime/InferredTypeTable.h: Added.
* runtime/JSObject.h:
(JSC::JSObject::putDirectInternal):
(JSC::JSObject::putDirectWithoutTransition):
* runtime/Structure.cpp:
(JSC::Structure::materializePropertyMap):
(JSC::Structure::addPropertyTransition):
(JSC::Structure::removePropertyTransition):
(JSC::Structure::startWatchingInternalProperties):
(JSC::Structure::willStoreValueSlow):
(JSC::Structure::visitChildren):
(JSC::Structure::prototypeChainMayInterceptStoreTo):
* runtime/Structure.h:
(JSC::PropertyMapEntry::PropertyMapEntry):
* runtime/StructureInlines.h:
(JSC::Structure::get):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
* tests/stress/prop-type-boolean-then-string.js: Added.
* tests/stress/prop-type-int32-then-string.js: Added.
* tests/stress/prop-type-number-then-string.js: Added.
* tests/stress/prop-type-object-or-other-then-string.js: Added.
* tests/stress/prop-type-object-then-string.js: Added.
* tests/stress/prop-type-other-then-string.js: Added.
* tests/stress/prop-type-string-then-object.js: Added.
* tests/stress/prop-type-struct-or-other-then-string.js: Added.
* tests/stress/prop-type-struct-then-object.js: Added.
* tests/stress/prop-type-struct-then-object-opt.js: Added.
* tests/stress/prop-type-struct-then-object-opt-fold.js: Added.
* tests/stress/prop-type-struct-then-object-opt-multi.js: Added.

Source/WTF:

* wtf/HashTable.h:
(WTF::HashTableAddResult::HashTableAddResult): Make it possible to say "HashMap::AddResult result" without assigning anything to it yet.
* wtf/PrintStream.h:
(WTF::printInternal): Beef up printing of some common WTF types, in particular RefPtr<UniquedStringImpl>.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@190076 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h
index b3ebd68..e196917 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h
@@ -116,9 +116,9 @@
     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 @@
         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);
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index f6e43c0..4fa0b21 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -1829,10 +1829,10 @@
         }
         
         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 @@
                     // This thing won't give us a variant that involves prototypes. If it did, we'd
                     // have more work to do here.
                     DFG_ASSERT(m_graph, node, status[i].conditionSet().isEmpty());
-                    
-                    JSValue constantResult =
-                        m_graph.tryGetConstantProperty(value, status[i].offset());
-                    if (!constantResult) {
-                        result.makeHeapTop();
-                        break;
-                    }
-                    
-                    AbstractValue thisResult;
-                    thisResult.set(
-                        m_graph, *m_graph.freeze(constantResult),
-                        m_state.structureClobberState());
-                    result.merge(thisResult);
+
+                    result.merge(
+                        m_graph.inferredValueForProperty(
+                            value, uid, status[i].offset(), m_state.structureClobberState()));
                 }
-                if (status.numVariants() == 1 || isFTL(m_graph.m_plan.mode))
-                    m_state.setFoundConstants(true);
+                m_state.setFoundConstants(true);
                 forNode(node) = result;
                 break;
             }
@@ -1880,7 +1870,6 @@
         
     case CheckStructure: {
         AbstractValue& value = forNode(node->child1());
-        ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this.
 
         StructureSet& set = node->structureSet();
         
@@ -1888,12 +1877,24 @@
         // that includes the set we're testing. In that case we could make the structure check
         // more efficient. We currently don't.
         
-        if (value.m_structure.isSubsetOf(set)) {
+        if (value.m_structure.isSubsetOf(set))
             m_state.setFoundConstants(true);
+
+        SpeculatedType admittedTypes = SpecNone;
+        switch (node->child1().useKind()) {
+        case CellUse:
+        case KnownCellUse:
+            admittedTypes = SpecNone;
+            break;
+        case CellOrOtherUse:
+            admittedTypes = SpecOther;
+            break;
+        default:
+            DFG_CRASH(m_graph, node, "Bad use kind");
             break;
         }
-
-        filter(value, set);
+        
+        filter(value, set, admittedTypes);
         break;
     }
         
@@ -2084,13 +2085,29 @@
         
     case GetByOffset: {
         StorageAccessData& data = node->storageAccessData();
-        JSValue result = m_graph.tryGetConstantProperty(forNode(node->child2()), data.offset);
-        if (result) {
-            setConstant(node, *m_graph.freeze(result));
-            break;
-        }
-        
-        forNode(node).makeHeapTop();
+        UniquedStringImpl* uid = m_graph.identifiers()[data.identifierNumber];
+
+        // FIXME: The part of this that handles inferred property types relies on AI knowing the structure
+        // right now. That's probably not optimal. In some cases, we may perform an optimization (usually
+        // by something other than AI, maybe by CSE for example) that obscures AI's view of the structure
+        // at the point where GetByOffset runs. Currently, when that happens, we'll have to rely entirely
+        // on the type that ByteCodeParser was able to prove.
+        AbstractValue value = m_graph.inferredValueForProperty(
+            forNode(node->child2()), uid, data.offset, m_state.structureClobberState());
+
+        // It's possible that the type that ByteCodeParser came up with is better.
+        AbstractValue typeFromParsing;
+        typeFromParsing.set(m_graph, data.inferredType, m_state.structureClobberState());
+        value.filter(typeFromParsing);
+
+        // If we decide that there does not exist any value that this can return, then it's probably
+        // because the compilation was already invalidated.
+        if (value.isClear())
+            m_state.setIsValid(false);
+
+        forNode(node) = value;
+        if (value.m_value)
+            m_state.setFoundConstants(true);
         break;
     }
         
@@ -2120,6 +2137,8 @@
         // because of the effect on compile times, but this node is FTL-only.
         m_state.setFoundConstants(true);
         
+        UniquedStringImpl* uid = m_graph.identifiers()[node->multiGetByOffsetData().identifierNumber];
+
         AbstractValue base = forNode(node->child1());
         StructureSet baseSet;
         AbstractValue result;
@@ -2129,18 +2148,29 @@
             if (set.isEmpty())
                 continue;
             baseSet.merge(set);
-            
-            if (getCase.method().kind() != GetByOffsetMethod::Constant) {
-                result.makeHeapTop();
-                continue;
+
+            switch (getCase.method().kind()) {
+            case GetByOffsetMethod::Constant: {
+                AbstractValue thisResult;
+                thisResult.set(
+                    m_graph,
+                    *getCase.method().constant(),
+                    m_state.structureClobberState());
+                result.merge(thisResult);
+                break;
             }
-            
-            AbstractValue thisResult;
-            thisResult.set(
-                m_graph,
-                *getCase.method().constant(),
-                m_state.structureClobberState());
-            result.merge(thisResult);
+
+            case GetByOffsetMethod::Load: {
+                result.merge(
+                    m_graph.inferredValueForProperty(
+                        set, uid, m_state.structureClobberState()));
+                break;
+            }
+
+            default: {
+                result.makeHeapTop();
+                break;
+            } }
         }
         
         if (forNode(node->child1()).changeStructure(m_graph, baseSet) == Contradiction)
@@ -2163,6 +2193,8 @@
         m_state.setFoundConstants(true);
         
         AbstractValue base = forNode(node->child1());
+        AbstractValue originalValue = forNode(node->child2());
+        AbstractValue resultingValue;
         
         for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) {
             const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i];
@@ -2170,6 +2202,11 @@
             thisSet.filter(base);
             if (thisSet.isEmpty())
                 continue;
+
+            AbstractValue thisValue = originalValue;
+            thisValue.filter(m_graph, variant.requiredType());
+            resultingValue.merge(thisValue);
+            
             if (variant.kind() == PutByIdVariant::Transition) {
                 if (thisSet.onlyStructure() != variant.newStructure()) {
                     transitions.append(
@@ -2185,6 +2222,9 @@
         observeTransitions(clobberLimit, transitions);
         if (forNode(node->child1()).changeStructure(m_graph, newSet) == Contradiction)
             m_state.setIsValid(false);
+        forNode(node->child2()) = resultingValue;
+        if (!!originalValue && !resultingValue)
+            m_state.setIsValid(false);
         break;
     }
         
@@ -2264,13 +2304,13 @@
     case PutByIdFlush:
     case PutByIdDirect: {
         AbstractValue& value = forNode(node->child1());
-        if (!value.m_structure.isTop() && !value.m_structure.isClobbered()) {
+        if (value.m_structure.isFinite()) {
             PutByIdStatus status = PutByIdStatus::computeFor(
                 m_graph.globalObjectFor(node->origin.semantic),
                 value.m_structure.set(),
                 m_graph.identifiers()[node->identifierNumber()],
                 node->op() == PutByIdDirect);
-            
+
             if (status.isSimple()) {
                 StructureSet newSet;
                 TransitionVector transitions;
@@ -2615,9 +2655,9 @@
 
 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;
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp b/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
index 08466aa..dc2c22a 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
@@ -117,6 +117,56 @@
     checkConsistency();
 }
 
+void AbstractValue::set(Graph& graph, const InferredType::Descriptor& descriptor)
+{
+    switch (descriptor.kind()) {
+    case InferredType::Bottom:
+        clear();
+        return;
+    case InferredType::Boolean:
+        setType(SpecBoolean);
+        return;
+    case InferredType::Other:
+        setType(SpecOther);
+        return;
+    case InferredType::Int32:
+        setType(SpecInt32);
+        return;
+    case InferredType::Number:
+        setType(SpecBytecodeNumber);
+        return;
+    case InferredType::String:
+        set(graph, graph.m_vm.stringStructure.get());
+        return;
+    case InferredType::ObjectWithStructure:
+        set(graph, descriptor.structure());
+        return;
+    case InferredType::ObjectWithStructureOrOther:
+        set(graph, descriptor.structure());
+        merge(SpecOther);
+        return;
+    case InferredType::Object:
+        setType(graph, SpecObject);
+        return;
+    case InferredType::ObjectOrOther:
+        setType(graph, SpecObject | SpecOther);
+        return;
+    case InferredType::Top:
+        makeHeapTop();
+        return;
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+void AbstractValue::set(
+    Graph& graph, const InferredType::Descriptor& descriptor, StructureClobberState clobberState)
+{
+    set(graph, descriptor);
+    if (clobberState == StructuresAreClobbered)
+        clobberStructures();
+}
+
 void AbstractValue::fixTypeForRepresentation(Graph& graph, NodeFlags representation, Node* node)
 {
     if (representation == NodeResultDouble) {
@@ -189,8 +239,22 @@
     return oldMe != *this;
 }
 
-FiltrationResult AbstractValue::filter(Graph& graph, const StructureSet& other)
+bool AbstractValue::isType(Graph& graph, const InferredType::Descriptor& inferredType) const
 {
+    AbstractValue typeValue;
+    typeValue.set(graph, inferredType);
+
+    AbstractValue mergedValue = *this;
+    mergedValue.merge(typeValue);
+
+    return mergedValue == typeValue;
+}
+
+FiltrationResult AbstractValue::filter(
+    Graph& graph, const StructureSet& other, SpeculatedType admittedTypes)
+{
+    ASSERT(!(admittedTypes & SpecCell));
+    
     if (isClear())
         return FiltrationOK;
     
@@ -198,7 +262,7 @@
     // having structures, array modes, or a specific value.
     // https://bugs.webkit.org/show_bug.cgi?id=109663
     
-    m_type &= other.speculationFromStructures();
+    m_type &= other.speculationFromStructures() | admittedTypes;
     m_arrayModes &= other.arrayModesFromStructures();
     m_structure.filter(other);
     
@@ -315,6 +379,13 @@
     return Contradiction;
 }
 
+FiltrationResult AbstractValue::filter(Graph& graph, const InferredType::Descriptor& descriptor)
+{
+    AbstractValue filterValue;
+    filterValue.set(graph, descriptor);
+    return filter(filterValue);
+}
+
 void AbstractValue::filterValueByType()
 {
     // We could go further, and ensure that if the futurePossibleStructure contravenes
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
index 7318d0d..c3d3223 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
@@ -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 @@
         m_value = JSValue();
         checkConsistency();
     }
-    
+
+    void set(Graph&, const InferredType::Descriptor&);
+    void set(Graph&, const InferredType::Descriptor&, StructureClobberState);
+
     void fixTypeForRepresentation(Graph&, NodeFlags representation, Node* = nullptr);
     void fixTypeForRepresentation(Graph&, Node*);
     
@@ -280,17 +284,27 @@
     {
         return !(m_type & ~desiredType);
     }
+
+    bool isType(Graph&, const InferredType::Descriptor&) const;
+
+    // Filters the value using the given structure set. If the admittedTypes argument is not passed, this
+    // implicitly filters by the types implied by the structure set, which are usually a subset of
+    // SpecCell. Hence, after this call, the value will no longer have any non-cell members. But, you can
+    // use admittedTypes to preserve some non-cell types. Note that it's wrong for admittedTypes to overlap
+    // with SpecCell.
+    FiltrationResult filter(Graph&, const StructureSet&, SpeculatedType admittedTypes = SpecNone);
     
-    FiltrationResult filter(Graph&, const StructureSet&);
     FiltrationResult filterArrayModes(ArrayModes);
     FiltrationResult filter(SpeculatedType);
     FiltrationResult filterByValue(const FrozenValue& value);
     FiltrationResult filter(const AbstractValue&);
+
+    FiltrationResult filter(Graph&, const InferredType::Descriptor&);
     
     FiltrationResult changeStructure(Graph&, const StructureSet&);
     
     bool contains(Structure*) const;
-    
+
     bool validate(JSValue value) const
     {
         if (isHeapTop())
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index aa04361..f9fae4e 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -206,9 +206,8 @@
     bool handleTypedArrayConstructor(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, TypedArrayType, const ChecksFunctor& insertChecks);
     template<typename ChecksFunctor>
     bool handleConstantInternalFunction(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, CodeSpecializationKind, const ChecksFunctor& insertChecks);
-    Node* handlePutByOffset(Node* base, unsigned identifier, PropertyOffset, Node* value);
-    Node* handleGetByOffset(SpeculatedType, Node* base, unsigned identifierNumber, PropertyOffset, NodeType = GetByOffset);
-    Node* handleGetByOffset(SpeculatedType, Node* base, UniquedStringImpl*, PropertyOffset, NodeType = GetByOffset);
+    Node* handlePutByOffset(Node* base, unsigned identifier, PropertyOffset, const InferredType::Descriptor&, Node* value);
+    Node* handleGetByOffset(SpeculatedType, Node* base, unsigned identifierNumber, PropertyOffset, const InferredType::Descriptor&, NodeType = GetByOffset);
 
     // Create a presence ObjectPropertyCondition based on some known offset and structure set. Does not
     // check the validity of the condition, but it may return a null one if it encounters a contradiction.
@@ -2306,7 +2305,9 @@
     return false;
 }
 
-Node* ByteCodeParser::handleGetByOffset(SpeculatedType prediction, Node* base, unsigned identifierNumber, PropertyOffset offset, NodeType op)
+Node* ByteCodeParser::handleGetByOffset(
+    SpeculatedType prediction, Node* base, unsigned identifierNumber, PropertyOffset offset,
+    const InferredType::Descriptor& inferredType, NodeType op)
 {
     Node* propertyStorage;
     if (isInlineOffset(offset))
@@ -2317,13 +2318,17 @@
     StorageAccessData* data = m_graph.m_storageAccessData.add();
     data->offset = offset;
     data->identifierNumber = identifierNumber;
+    data->inferredType = inferredType;
+    m_graph.registerInferredType(inferredType);
     
     Node* getByOffset = addToGraph(op, OpInfo(data), OpInfo(prediction), propertyStorage, base);
 
     return getByOffset;
 }
 
-Node* ByteCodeParser::handlePutByOffset(Node* base, unsigned identifier, PropertyOffset offset, Node* value)
+Node* ByteCodeParser::handlePutByOffset(
+    Node* base, unsigned identifier, PropertyOffset offset, const InferredType::Descriptor& inferredType,
+    Node* value)
 {
     Node* propertyStorage;
     if (isInlineOffset(offset))
@@ -2334,6 +2339,8 @@
     StorageAccessData* data = m_graph.m_storageAccessData.add();
     data->offset = offset;
     data->identifierNumber = identifier;
+    data->inferredType = inferredType;
+    m_graph.registerInferredType(inferredType);
     
     Node* result = addToGraph(PutByOffset, OpInfo(data), propertyStorage, base, value);
     
@@ -2448,7 +2455,8 @@
         return addToGraph(JSConstant, OpInfo(method.constant()));
     case GetByOffsetMethod::LoadFromPrototype: {
         Node* baseNode = addToGraph(JSConstant, OpInfo(method.prototype()));
-        return handleGetByOffset(prediction, baseNode, identifierNumber, method.offset(), op);
+        return handleGetByOffset(
+            prediction, baseNode, identifierNumber, method.offset(), InferredType::Top, op);
     }
     case GetByOffsetMethod::Load:
         // Will never see this from planLoad().
@@ -2554,13 +2562,13 @@
     
     bool needStructureCheck = true;
     
+    UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
+    
     if (JSObject* knownBase = base->dynamicCastConstant<JSObject*>()) {
         // Try to optimize away the structure check. Note that it's not worth doing anything about this
         // if the base's structure is watched.
         Structure* structure = base->constant()->structure();
         if (!structure->dfgShouldWatch()) {
-            UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
-            
             if (!variant.conditionSet().isEmpty()) {
                 // This means that we're loading from a prototype. We expect the base not to have the
                 // property. We can only use ObjectPropertyCondition if all of the structures in the
@@ -2625,9 +2633,18 @@
             if (constant)
                 return weakJSConstant(constant);
         }
+
+        InferredType::Descriptor inferredType;
+        if (needStructureCheck) {
+            for (Structure* structure : variant.structureSet()) {
+                InferredType::Descriptor thisType = m_graph.inferredTypeForProperty(structure, uid);
+                inferredType.merge(thisType);
+            }
+        } else
+            inferredType = InferredType::Top;
         
         loadedValue = handleGetByOffset(
-            loadPrediction, base, identifierNumber, variant.offset(), loadOp);
+            loadPrediction, base, identifierNumber, variant.offset(), inferredType, loadOp);
     }
 
     return loadedValue;
@@ -2638,7 +2655,7 @@
     RELEASE_ASSERT(variant.kind() == PutByIdVariant::Replace);
 
     checkPresenceLike(base, m_graph.identifiers()[identifier], variant.offset(), variant.structure());
-    return handlePutByOffset(base, identifier, variant.offset(), value);
+    return handlePutByOffset(base, identifier, variant.offset(), variant.requiredType(), value);
 }
 
 void ByteCodeParser::handleGetById(
@@ -2797,6 +2814,9 @@
         
         if (m_graph.compilation())
             m_graph.compilation()->noticeInlinedPutById();
+
+        for (const PutByIdVariant& variant : putByIdStatus.variants())
+            m_graph.registerInferredType(variant.requiredType());
         
         MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
         data->variants = putByIdStatus.variants();
@@ -2853,6 +2873,8 @@
         StorageAccessData* data = m_graph.m_storageAccessData.add();
         data->offset = variant.offset();
         data->identifierNumber = identifierNumber;
+        data->inferredType = variant.requiredType();
+        m_graph.registerInferredType(data->inferredType);
         
         addToGraph(
             PutByOffset,
diff --git a/Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp b/Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp
index 89e4858..54dde1f 100644
--- a/Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGClobbersExitState.cpp
@@ -64,6 +64,8 @@
     case PhantomCreateActivation:
     case MaterializeCreateActivation:
     case CountExecution:
+    case AllocatePropertyStorage:
+    case ReallocatePropertyStorage:
         // These do clobber memory, but nothing that is observable. It may be nice to separate the
         // heaps into those that are observable and those that aren't, but we don't do that right now.
         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=148440
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index ba504e3..ed5df28 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -33,6 +33,7 @@
 #include "DFGBasicBlock.h"
 #include "DFGGraph.h"
 #include "DFGInPlaceAbstractState.h"
+#include "DFGInferredTypeCheck.h"
 #include "DFGInsertionSet.h"
 #include "DFGPhase.h"
 #include "GetByIdStatus.h"
@@ -373,7 +374,8 @@
                         && variant.oldStructure().onlyStructure() == variant.newStructure()) {
                         variant = PutByIdVariant::replace(
                             variant.oldStructure(),
-                            variant.offset());
+                            variant.offset(),
+                            variant.requiredType());
                         changed = true;
                     }
                 }
@@ -448,6 +450,7 @@
                 ASSERT(childEdge.useKind() == CellUse);
                 
                 AbstractValue baseValue = m_state.forNode(child);
+                AbstractValue valueValue = m_state.forNode(node->child2());
 
                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
@@ -463,7 +466,7 @@
                 
                 if (!status.isSimple())
                     break;
-                
+
                 ASSERT(status.numVariants());
                 
                 if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode))
@@ -640,7 +643,9 @@
         emitGetByOffset(indexInBlock, node, childEdge, identifierNumber, variant.offset());
     }
     
-    void emitGetByOffset(unsigned indexInBlock, Node* node, Edge childEdge, unsigned identifierNumber, PropertyOffset offset)
+    void emitGetByOffset(
+        unsigned indexInBlock, Node* node, Edge childEdge, unsigned identifierNumber,
+        PropertyOffset offset, const InferredType::Descriptor& inferredType = InferredType::Top)
     {
         childEdge.setUseKind(KnownCellUse);
         
@@ -656,6 +661,7 @@
         StorageAccessData& data = *m_graph.m_storageAccessData.add();
         data.offset = offset;
         data.identifierNumber = identifierNumber;
+        data.inferredType = inferredType;
         
         node->convertToGetByOffset(data, propertyStorage);
     }
@@ -664,8 +670,10 @@
     {
         NodeOrigin origin = node->origin;
         Edge childEdge = node->child1();
-        
+
         addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure());
+        insertInferredTypeCheck(
+            m_insertionSet, indexInBlock, origin, node->child2().node(), variant.requiredType());
 
         node->child1().setUseKind(KnownCellUse);
         childEdge.setUseKind(KnownCellUse);
@@ -730,6 +738,7 @@
             // Arises when we prune MultiGetByOffset. We could have a
             // MultiGetByOffset with a single variant that checks for structure S,
             // and the input has structures S and T, for example.
+            ASSERT(node->child1());
             m_insertionSet.insertNode(
                 indexInBlock, SpecNone, CheckStructure, node->origin,
                 OpInfo(m_graph.addStructureSet(set)), node->child1());
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredInferredType.h b/Source/JavaScriptCore/dfg/DFGDesiredInferredType.h
new file mode 100644
index 0000000..d021288
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGDesiredInferredType.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef DFGDesiredInferredType_h
+#define DFGDesiredInferredType_h
+
+#if ENABLE(DFG_JIT)
+
+#include "InferredType.h"
+#include <wtf/HashMap.h>
+
+namespace JSC { namespace DFG {
+
+// This documents the DFG's expectation for an InferredType: specifically, that we want it to have a live
+// InferredType object and that this object has the type that we originally saw.
+
+class DesiredInferredType {
+public:
+    DesiredInferredType()
+        : m_object(nullptr)
+    {
+    }
+    
+    DesiredInferredType(InferredType* object, const InferredType::Descriptor& expected)
+        : m_object(object)
+        , m_expected(expected)
+    {
+    }
+
+    DesiredInferredType(WTF::HashTableDeletedValueType)
+        : m_object(nullptr)
+        , m_expected(InferredType::Top)
+    {
+    }
+
+    explicit operator bool() const { return m_object && m_expected; }
+
+    InferredType* object() const { return m_object; }
+    InferredType::Descriptor expected() const { return m_expected; }
+
+    bool isStillValid() const
+    {
+        return m_object->canWatch(m_expected);
+    }
+
+    void add(Watchpoint* watchpoint) const
+    {
+        m_object->addWatchpoint(watchpoint);
+    }
+
+    bool operator==(const DesiredInferredType& other) const
+    {
+        return m_object == other.m_object
+            && m_expected == other.m_expected;
+    }
+
+    bool operator!=(const DesiredInferredType& other) const
+    {
+        return !(*this == other);
+    }
+
+    bool isHashTableDeletedValue() const
+    {
+        return !m_object && m_expected == InferredType::Top;
+    }
+
+    unsigned hash() const
+    {
+        return WTF::PtrHash<InferredType*>::hash(m_object) + m_expected.hash() * 7;
+    }
+
+    void dumpInContext(PrintStream& out, DumpContext* context) const
+    {
+        out.print(inContext(m_expected, context), " for ", RawPointer(m_object));
+    }
+
+    void dump(PrintStream& out) const
+    {
+        dumpInContext(out, nullptr);
+    }
+
+private:
+    InferredType* m_object;
+    InferredType::Descriptor m_expected;
+};
+
+struct DesiredInferredTypeHash {
+    static unsigned hash(const DesiredInferredType& key) { return key.hash(); }
+    static bool equal(const DesiredInferredType& a, const DesiredInferredType& b) { return a == b; }
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+} } // namespace JSC::DFG
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+template<> struct DefaultHash<JSC::DFG::DesiredInferredType> {
+    typedef JSC::DFG::DesiredInferredTypeHash Hash;
+};
+
+template<typename T> struct HashTraits;
+template<> struct HashTraits<JSC::DFG::DesiredInferredType> : SimpleClassHashTraits<JSC::DFG::DesiredInferredType> { };
+
+} // namespace WTF
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGDesiredInferredType_h
+
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp b/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp
index 6bea704..8761d9b 100644
--- a/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp
@@ -65,6 +65,11 @@
     }
 }
 
+void InferredTypeAdaptor::add(CodeBlock* codeBlock, const DesiredInferredType& key, CommonData& common)
+{
+    key.add(common.watchpoints.add(codeBlock));
+}
+
 DesiredWatchpoints::DesiredWatchpoints() { }
 DesiredWatchpoints::~DesiredWatchpoints() { }
 
@@ -93,6 +98,11 @@
     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 @@
     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 @@
         && 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 @@
     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
diff --git a/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h b/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
index bc86f43..09ec8aa 100644
--- a/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
+++ b/Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
@@ -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 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 @@
     // 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 @@
     {
         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 @@
     GenericDesiredWatchpoints<InferredValue*, InferredValueAdaptor> m_inferredValues;
     GenericDesiredWatchpoints<JSArrayBufferView*, ArrayBufferViewWatchpointAdaptor> m_bufferViews;
     GenericDesiredWatchpoints<ObjectPropertyCondition, AdaptiveStructureWatchpointAdaptor> m_adaptiveStructureSets;
+    GenericDesiredWatchpoints<DesiredInferredType, InferredTypeAdaptor> m_inferredTypes;
 };
 
 } } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 9581037..652d557 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -30,6 +30,7 @@
 
 #include "ArrayPrototype.h"
 #include "DFGGraph.h"
+#include "DFGInferredTypeCheck.h"
 #include "DFGInsertionSet.h"
 #include "DFGPhase.h"
 #include "DFGPredictionPropagationPhase.h"
@@ -1092,6 +1093,9 @@
             if (!node->child1()->hasStorageResult())
                 fixEdge<KnownCellUse>(node->child1());
             fixEdge<KnownCellUse>(node->child2());
+            insertInferredTypeCheck(
+                m_insertionSet, m_indexInBlock, node->origin, node->child3().node(),
+                node->storageAccessData().inferredType);
             speculateForBarrier(node->child3());
             break;
         }
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp
index d272d14..dddd491 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp
+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp
@@ -261,12 +261,16 @@
         StorageAccessData& storageAccessData = node->storageAccessData();
         out.print(comma, "id", storageAccessData.identifierNumber, "{", identifiers()[storageAccessData.identifierNumber], "}");
         out.print(", ", static_cast<ptrdiff_t>(storageAccessData.offset));
+        out.print(", inferredType = ", inContext(storageAccessData.inferredType, context));
     }
     if (node->hasMultiGetByOffsetData()) {
         MultiGetByOffsetData& data = node->multiGetByOffsetData();
         out.print(comma, "id", data.identifierNumber, "{", identifiers()[data.identifierNumber], "}");
-        for (unsigned i = 0; i < data.cases.size(); ++i)
+        for (unsigned i = 0; i < data.cases.size(); ++i) {
             out.print(comma, inContext(data.cases[i], context));
+            if (data.cases[i].method().kind() == GetByOffsetMethod::Load)
+                out.print(" (inferred value = ", inContext(inferredValueForProperty(data.cases[i].set(), identifiers()[data.identifierNumber]), context), ")");
+        }
     }
     if (node->hasMultiPutByOffsetData()) {
         MultiPutByOffsetData& data = node->multiPutByOffsetData();
@@ -887,6 +891,38 @@
     return m_safeToLoad.contains(std::make_pair(base, offset));
 }
 
+InferredType::Descriptor Graph::inferredTypeFor(const PropertyTypeKey& key)
+{
+    assertIsRegistered(key.structure());
+    
+    auto iter = m_inferredTypes.find(key);
+    if (iter != m_inferredTypes.end())
+        return iter->value;
+
+    InferredType* typeObject = key.structure()->inferredTypeFor(key.uid());
+    if (!typeObject) {
+        m_inferredTypes.add(key, InferredType::Top);
+        return InferredType::Top;
+    }
+
+    InferredType::Descriptor typeDescriptor = typeObject->descriptor();
+    if (typeDescriptor.kind() == InferredType::Top) {
+        m_inferredTypes.add(key, InferredType::Top);
+        return InferredType::Top;
+    }
+    
+    m_inferredTypes.add(key, typeDescriptor);
+
+    m_plan.weakReferences.addLazily(typeObject);
+    registerInferredType(typeDescriptor);
+
+    // Note that we may already be watching this desired inferred type, because multiple structures may
+    // point to the same InferredType instance.
+    m_plan.watchpoints.addLazily(DesiredInferredType(typeObject, typeDescriptor));
+
+    return typeDescriptor;
+}
+
 FullBytecodeLiveness& Graph::livenessFor(CodeBlock* codeBlock)
 {
     HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>>::iterator iter = m_bytecodeLiveness.find(codeBlock);
@@ -1061,7 +1097,7 @@
 JSValue Graph::tryGetConstantProperty(
     JSValue base, const StructureAbstractValue& structure, PropertyOffset offset)
 {
-    if (structure.isTop() || structure.isClobbered()) {
+    if (structure.isInfinite()) {
         // FIXME: If we just converted the offset to a uid, we could do ObjectPropertyCondition
         // watching to constant-fold the property.
         // https://bugs.webkit.org/show_bug.cgi?id=147271
@@ -1076,6 +1112,37 @@
     return tryGetConstantProperty(base.m_value, base.m_structure, offset);
 }
 
+AbstractValue Graph::inferredValueForProperty(
+    const StructureSet& base, UniquedStringImpl* uid, StructureClobberState clobberState)
+{
+    AbstractValue result;
+    base.forEach(
+        [&] (Structure* structure) {
+            AbstractValue value;
+            value.set(*this, inferredTypeForProperty(structure, uid));
+            result.merge(value);
+        });
+    if (clobberState == StructuresAreClobbered)
+        result.clobberStructures();
+    return result;
+}
+
+AbstractValue Graph::inferredValueForProperty(
+    const AbstractValue& base, UniquedStringImpl* uid, PropertyOffset offset,
+    StructureClobberState clobberState)
+{
+    if (JSValue value = tryGetConstantProperty(base, offset)) {
+        AbstractValue result;
+        result.set(*this, *freeze(value), clobberState);
+        return result;
+    }
+
+    if (base.m_structure.isFinite())
+        return inferredValueForProperty(base.m_structure.set(), uid, clobberState);
+
+    return AbstractValue::heapTop();
+}
+
 JSValue Graph::tryGetConstantClosureVar(JSValue base, ScopeOffset offset)
 {
     // This has an awesome concurrency story. See comment for GetGlobalVar in ByteCodeParser.
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index 6977831..ba070f2c 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -41,6 +41,7 @@
 #include "DFGNodeAllocator.h"
 #include "DFGPlan.h"
 #include "DFGPrePostNumbering.h"
+#include "DFGPropertyTypeKey.h"
 #include "DFGScannable.h"
 #include "FullBytecodeLiveness.h"
 #include "JSStack.h"
@@ -675,6 +676,27 @@
     // Checks if it's known that loading from the given object at the given offset is fine. This is
     // computed by tracking which conditions we track with watchCondition().
     bool isSafeToLoad(JSObject* base, PropertyOffset);
+
+    void registerInferredType(const InferredType::Descriptor& type)
+    {
+        if (type.structure())
+            registerStructure(type.structure());
+    }
+
+    // Tells us what inferred type we are able to prove the property to have now and in the future.
+    InferredType::Descriptor inferredTypeFor(const PropertyTypeKey&);
+    InferredType::Descriptor inferredTypeForProperty(Structure* structure, UniquedStringImpl* uid)
+    {
+        return inferredTypeFor(PropertyTypeKey(structure, uid));
+    }
+
+    AbstractValue inferredValueForProperty(
+        const StructureSet& base, UniquedStringImpl* uid, StructureClobberState = StructuresAreWatched);
+
+    // This uses either constant property inference or property type inference to derive a good abstract
+    // value for some property accessed with the given abstract value base.
+    AbstractValue inferredValueForProperty(
+        const AbstractValue& base, UniquedStringImpl* uid, PropertyOffset, StructureClobberState);
     
     FullBytecodeLiveness& livenessFor(CodeBlock*);
     FullBytecodeLiveness& livenessFor(InlineCallFrame*);
@@ -856,6 +878,7 @@
     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;
diff --git a/Source/JavaScriptCore/dfg/DFGInferredTypeCheck.cpp b/Source/JavaScriptCore/dfg/DFGInferredTypeCheck.cpp
new file mode 100644
index 0000000..c76e2e8
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGInferredTypeCheck.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DFGInferredTypeCheck.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "JSCInlines.h"
+
+namespace JSC { namespace DFG {
+
+void insertInferredTypeCheck(
+    InsertionSet& insertionSet, unsigned nodeIndex, NodeOrigin origin, Node* baseNode,
+    const InferredType::Descriptor& type)
+{
+    insertionSet.graph().registerInferredType(type);
+    
+    switch (type.kind()) {
+    case InferredType::Bottom:
+        insertionSet.insertNode(nodeIndex, SpecNone, ForceOSRExit, origin);
+        return;
+
+    case InferredType::Boolean:
+        insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, BooleanUse));
+        return;
+
+    case InferredType::Other:
+        insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, OtherUse));
+        return;
+
+    case InferredType::Int32:
+        insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, Int32Use));
+        return;
+
+    case InferredType::Number:
+        insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, NumberUse));
+        return;
+
+    case InferredType::String:
+        insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, StringUse));
+        return;
+
+    case InferredType::ObjectWithStructure:
+        insertionSet.insertNode(
+            nodeIndex, SpecNone, CheckStructure, origin,
+            OpInfo(insertionSet.graph().addStructureSet(type.structure())),
+            Edge(baseNode, CellUse));
+        return;
+
+    case InferredType::ObjectWithStructureOrOther:
+        insertionSet.insertNode(
+            nodeIndex, SpecNone, CheckStructure, origin,
+            OpInfo(insertionSet.graph().addStructureSet(type.structure())),
+            Edge(baseNode, CellOrOtherUse));
+        return;
+
+    case InferredType::Object:
+        insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, ObjectUse));
+        return;
+
+    case InferredType::ObjectOrOther:
+        insertionSet.insertNode(nodeIndex, SpecNone, Check, origin, Edge(baseNode, ObjectOrOtherUse));
+        return;
+
+    case InferredType::Top:
+        return;
+    }
+
+    DFG_CRASH(insertionSet.graph(), baseNode, "Bad inferred type");
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
diff --git a/Source/JavaScriptCore/dfg/DFGInferredTypeCheck.h b/Source/JavaScriptCore/dfg/DFGInferredTypeCheck.h
new file mode 100644
index 0000000..b6b69c7
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGInferredTypeCheck.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DFGInferredTypeCheck_h
+#define DFGInferredTypeCheck_h
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGInsertionSet.h"
+#include "InferredType.h"
+
+namespace JSC { namespace DFG {
+
+// Inserts a type check that ensures that baseNode has the given inferred type.
+void insertInferredTypeCheck(
+    InsertionSet&, unsigned nodeIndex, NodeOrigin, Node* baseNode, const InferredType::Descriptor&);
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGInferredTypeCheck_h
+
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 80ccaf5..3d330e2 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -64,6 +64,11 @@
 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 {
diff --git a/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp b/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
index 79118db..fe2da99 100644
--- a/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
@@ -2135,14 +2135,20 @@
                 for (Structure* structure : structures) {
                     PropertyOffset offset = structure->getConcurrently(uid);
                     if (offset != currentOffset) {
+                        // Because our analysis treats MultiPutByOffset like an escape, we only have to
+                        // deal with storing results that would have been previously stored by PutByOffset
+                        // nodes. Those nodes were guarded by the appropriate type checks. This means that
+                        // at this point, we can simply trust that the incoming value has the right type
+                        // for whatever structure we are using.
                         data->variants.append(
-                            PutByIdVariant::replace(currentSet, currentOffset));
+                            PutByIdVariant::replace(currentSet, currentOffset, InferredType::Top));
                         currentOffset = offset;
                         currentSet.clear();
                     }
                     currentSet.add(structure);
                 }
-                data->variants.append(PutByIdVariant::replace(currentSet, currentOffset));
+                data->variants.append(
+                    PutByIdVariant::replace(currentSet, currentOffset, InferredType::Top));
             }
 
             return m_graph.addNode(
diff --git a/Source/JavaScriptCore/dfg/DFGPropertyTypeKey.h b/Source/JavaScriptCore/dfg/DFGPropertyTypeKey.h
new file mode 100644
index 0000000..65732b7
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGPropertyTypeKey.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef DFGPropertyTypeKey_h
+#define DFGPropertyTypeKey_h
+
+#if ENABLE(DFG_JIT)
+
+#include "Structure.h"
+#include <wtf/HashMap.h>
+
+namespace JSC { namespace DFG {
+
+// This object is a key for finding a property's type. It's a tuple of Structure* and UniquedStringImpl*.
+
+class PropertyTypeKey {
+public:
+    PropertyTypeKey()
+        : m_structure(nullptr)
+        , m_uid(nullptr)
+    {
+    }
+
+    PropertyTypeKey(Structure* structure, UniquedStringImpl* uid)
+        : m_structure(structure)
+        , m_uid(uid)
+    {
+    }
+
+    PropertyTypeKey(WTF::HashTableDeletedValueType)
+        : m_structure(nullptr)
+        , m_uid(deletedUID())
+    {
+    }
+
+    explicit operator bool() const { return m_structure && m_uid; }
+
+    Structure* structure() const { return m_structure; }
+    UniquedStringImpl* uid() const { return m_uid; }
+
+    bool operator==(const PropertyTypeKey& other) const
+    {
+        return m_structure == other.m_structure
+            && m_uid == other.m_uid;
+    }
+
+    bool operator!=(const PropertyTypeKey& other) const
+    {
+        return !(*this == other);
+    }
+
+    unsigned hash() const
+    {
+        return WTF::PtrHash<Structure*>::hash(m_structure) + WTF::PtrHash<UniquedStringImpl*>::hash(m_uid);
+    }
+
+    bool isHashTableDeletedValue() const
+    {
+        return !m_structure && m_uid == deletedUID();
+    }
+
+    void dumpInContext(PrintStream& out, DumpContext* context) const
+    {
+        out.print(pointerDumpInContext(m_structure, context), "+", m_uid);
+    }
+
+    void dump(PrintStream& out) const
+    {
+        dumpInContext(out, nullptr);
+    }
+
+private:
+    static UniquedStringImpl* deletedUID()
+    {
+        return bitwise_cast<UniquedStringImpl*>(static_cast<intptr_t>(1));
+    }
+    
+    Structure* m_structure;
+    UniquedStringImpl* m_uid;
+};
+
+struct PropertyTypeKeyHash {
+    static unsigned hash(const PropertyTypeKey& key) { return key.hash(); }
+    static bool equal(const PropertyTypeKey& a, const PropertyTypeKey& b) { return a == b; }
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+} } // namespace JSC::DFG
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+template<> struct DefaultHash<JSC::DFG::PropertyTypeKey> {
+    typedef JSC::DFG::PropertyTypeKeyHash Hash;
+};
+
+template<typename T> struct HashTraits;
+template<> struct HashTraits<JSC::DFG::PropertyTypeKey> : SimpleClassHashTraits<JSC::DFG::PropertyTypeKey> {
+    static const bool emptyValueIsZero = false;
+};
+
+} // namespace WTF
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGPropertyTypeKey_h
+
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index 9e781f5..61a375f 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -53,6 +53,7 @@
         case RealNumberUse:
         case BooleanUse:
         case CellUse:
+        case CellOrOtherUse:
         case ObjectUse:
         case FunctionUse:
         case FinalObjectUse:
@@ -127,6 +128,9 @@
     if (!safeToExecuteEdge.result())
         return false;
 
+    // NOTE: This tends to lie when it comes to effectful nodes, because it knows that they aren't going to
+    // get hoisted anyway.
+
     switch (node->op()) {
     case JSConstant:
     case DoubleConstant:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 8fa6c94..61b4066 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -5254,6 +5254,81 @@
     cellResult(resultGPR, node);
 }
 
+void SpeculativeJIT::compileCheckStructure(Node* node, GPRReg cellGPR, GPRReg tempGPR)
+{
+    ASSERT(node->structureSet().size());
+    
+    if (node->structureSet().size() == 1) {
+        speculationCheck(
+            BadCache, JSValueSource::unboxedCell(cellGPR), 0,
+            m_jit.branchWeakStructure(
+                JITCompiler::NotEqual,
+                JITCompiler::Address(cellGPR, JSCell::structureIDOffset()),
+                node->structureSet()[0]));
+    } else {
+        std::unique_ptr<GPRTemporary> structure;
+        GPRReg structureGPR;
+        
+        if (tempGPR == InvalidGPRReg) {
+            structure = std::make_unique<GPRTemporary>(this);
+            structureGPR = structure->gpr();
+        } else
+            structureGPR = tempGPR;
+        
+        m_jit.load32(JITCompiler::Address(cellGPR, JSCell::structureIDOffset()), structureGPR);
+        
+        JITCompiler::JumpList done;
+        
+        for (size_t i = 0; i < node->structureSet().size() - 1; ++i) {
+            done.append(
+                m_jit.branchWeakStructure(JITCompiler::Equal, structureGPR, node->structureSet()[i]));
+        }
+        
+        speculationCheck(
+            BadCache, JSValueSource::unboxedCell(cellGPR), 0,
+            m_jit.branchWeakStructure(
+                JITCompiler::NotEqual, structureGPR, node->structureSet().last()));
+        
+        done.link(&m_jit);
+    }
+}
+
+void SpeculativeJIT::compileCheckStructure(Node* node)
+{
+    switch (node->child1().useKind()) {
+    case CellUse:
+    case KnownCellUse: {
+        SpeculateCellOperand cell(this, node->child1());
+        compileCheckStructure(node, cell.gpr(), InvalidGPRReg);
+        noResult(node);
+        return;
+    }
+
+    case CellOrOtherUse: {
+        JSValueOperand value(this, node->child1(), ManualOperandSpeculation);
+        GPRTemporary temp(this);
+
+        JSValueRegs valueRegs = value.jsValueRegs();
+        GPRReg tempGPR = temp.gpr();
+
+        JITCompiler::Jump cell = m_jit.branchIfCell(valueRegs);
+        DFG_TYPE_CHECK(
+            valueRegs, node->child1(), SpecCell | SpecOther,
+            m_jit.branchIfNotOther(valueRegs, tempGPR));
+        JITCompiler::Jump done = m_jit.jump();
+        cell.link(&m_jit);
+        compileCheckStructure(node, valueRegs.payloadGPR(), tempGPR);
+        done.link(&m_jit);
+        noResult(node);
+        return;
+    }
+
+    default:
+        DFG_CRASH(m_jit.graph(), node, "Bad use kind");
+        return;
+    }
+}
+
 void SpeculativeJIT::compileAllocatePropertyStorage(Node* node)
 {
     if (node->transition()->previous->couldHaveIndexingHeader()) {
@@ -5658,6 +5733,22 @@
     (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))
@@ -5938,6 +6029,9 @@
     case CellUse:
         speculateCell(edge);
         break;
+    case CellOrOtherUse:
+        speculateCellOrOther(edge);
+        break;
     case ObjectUse:
         speculateObject(edge);
         break;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 5e272ee8..d139ba2 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -2215,6 +2215,8 @@
     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 @@
     void speculateDoubleRepReal(Edge);
     void speculateBoolean(Edge);
     void speculateCell(Edge);
+    void speculateCellOrOther(Edge);
     void speculateObject(Edge);
     void speculateFunction(Edge);
     void speculateFinalObject(Edge);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 50070f6..f205531 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -3901,36 +3901,7 @@
     }
         
     case CheckStructure: {
-        SpeculateCellOperand base(this, node->child1());
-        
-        ASSERT(node->structureSet().size());
-        
-        if (node->structureSet().size() == 1) {
-            speculationCheck(
-                BadCache, JSValueSource::unboxedCell(base.gpr()), 0,
-                m_jit.branchWeakPtr(
-                    JITCompiler::NotEqual,
-                    JITCompiler::Address(base.gpr(), JSCell::structureIDOffset()),
-                    node->structureSet()[0]));
-        } else {
-            GPRTemporary structure(this);
-            
-            m_jit.loadPtr(JITCompiler::Address(base.gpr(), JSCell::structureIDOffset()), structure.gpr());
-            
-            JITCompiler::JumpList done;
-            
-            for (size_t i = 0; i < node->structureSet().size() - 1; ++i)
-                done.append(m_jit.branchWeakPtr(JITCompiler::Equal, structure.gpr(), node->structureSet()[i]));
-            
-            speculationCheck(
-                BadCache, JSValueSource::unboxedCell(base.gpr()), 0,
-                m_jit.branchWeakPtr(
-                    JITCompiler::NotEqual, structure.gpr(), node->structureSet().last()));
-            
-            done.link(&m_jit);
-        }
-        
-        noResult(node);
+        compileCheckStructure(node);
         break;
     }
         
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 3c27297..69dcb81 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -3916,38 +3916,7 @@
     }
         
     case CheckStructure: {
-        SpeculateCellOperand base(this, node->child1());
-        
-        ASSERT(node->structureSet().size());
-        
-        ExitKind exitKind;
-        if (node->child1()->hasConstant())
-            exitKind = BadConstantCache;
-        else
-            exitKind = BadCache;
-        
-        if (node->structureSet().size() == 1) {
-            speculationCheck(
-                exitKind, JSValueSource::unboxedCell(base.gpr()), 0,
-                m_jit.branchWeakStructure(
-                    JITCompiler::NotEqual,
-                    JITCompiler::Address(base.gpr(), JSCell::structureIDOffset()),
-                    node->structureSet()[0]));
-        } else {
-            JITCompiler::JumpList done;
-            
-            for (size_t i = 0; i < node->structureSet().size() - 1; ++i)
-                done.append(m_jit.branchWeakStructure(JITCompiler::Equal, MacroAssembler::Address(base.gpr(), JSCell::structureIDOffset()), node->structureSet()[i]));
-            
-            speculationCheck(
-                exitKind, JSValueSource::unboxedCell(base.gpr()), 0,
-                m_jit.branchWeakStructure(
-                    JITCompiler::NotEqual, MacroAssembler::Address(base.gpr(), JSCell::structureIDOffset()), node->structureSet().last()));
-            
-            done.link(&m_jit);
-        }
-        
-        noResult(node);
+        compileCheckStructure(node);
         break;
     }
         
diff --git a/Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp b/Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
index 649dbd3..d736efe 100644
--- a/Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGStoreBarrierInsertionPhase.cpp
@@ -314,7 +314,7 @@
             case AllocatePropertyStorage:
             case ReallocatePropertyStorage:
                 // These allocate but then run their own barrier.
-                insertBarrierWithInvalidExit(m_nodeIndex + 1, Edge(m_node->child1().node(), KnownCellUse));
+                insertBarrier(m_nodeIndex + 1, Edge(m_node->child1().node(), KnownCellUse));
                 m_node->setEpoch(Epoch());
                 break;
                 
@@ -481,11 +481,6 @@
         insertBarrier(m_nodeIndex, base);
     }
 
-    void insertBarrierWithInvalidExit(unsigned nodeIndex, Edge base)
-    {
-        insertBarrier(nodeIndex, base, false);
-    }
-
     void insertBarrier(unsigned nodeIndex, Edge base, bool exitOK = true)
     {
         // If we're in global mode, we should only insert the barriers once we have converged.
diff --git a/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h b/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h
index 16f59ab..a1a5f29 100644
--- a/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h
+++ b/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h
@@ -192,7 +192,7 @@
     }
     
     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 @@
             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;
diff --git a/Source/JavaScriptCore/dfg/DFGUseKind.cpp b/Source/JavaScriptCore/dfg/DFGUseKind.cpp
index ad4f26e..86a1bda 100644
--- a/Source/JavaScriptCore/dfg/DFGUseKind.cpp
+++ b/Source/JavaScriptCore/dfg/DFGUseKind.cpp
@@ -79,6 +79,9 @@
     case KnownCellUse:
         out.print("KnownCell");
         return;
+    case CellOrOtherUse:
+        out.print("CellOrOther");
+        return;
     case ObjectUse:
         out.print("Object");
         return;
diff --git a/Source/JavaScriptCore/dfg/DFGUseKind.h b/Source/JavaScriptCore/dfg/DFGUseKind.h
index d82fc1a..9b5143b 100644
--- a/Source/JavaScriptCore/dfg/DFGUseKind.h
+++ b/Source/JavaScriptCore/dfg/DFGUseKind.h
@@ -51,6 +51,7 @@
     KnownBooleanUse,
     CellUse,
     KnownCellUse,
+    CellOrOtherUse,
     ObjectUse,
     FunctionUse,
     FinalObjectUse,
@@ -108,6 +109,8 @@
     case CellUse:
     case KnownCellUse:
         return SpecCell;
+    case CellOrOtherUse:
+        return SpecCell | SpecOther;
     case ObjectUse:
         return SpecObject;
     case FunctionUse:
diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp
index 352475b..052599c 100644
--- a/Source/JavaScriptCore/dfg/DFGValidate.cpp
+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp
@@ -263,6 +263,9 @@
                     VALIDATE((node), !!node->child1());
                     VALIDATE((node), !!node->child2());
                     break;
+                case CheckStructure:
+                    VALIDATE((node), !!node->child1());
+                    break;
                 case PutStructure:
                     VALIDATE((node), !node->transition()->previous->dfgShouldWatch());
                     break;