FTL should do polymorphic PutById inlining
https://bugs.webkit.org/show_bug.cgi?id=129210

Source/JavaScriptCore: 

Reviewed by Mark Hahnenberg and Oliver Hunt.
        
This makes PutByIdStatus inform us about polymorphic cases by returning an array of
PutByIdVariants. The DFG now has a node called MultiPutByOffset that indicates a
selection of multiple inlined PutByIdVariants.
        
MultiPutByOffset is almost identical to MultiGetByOffset, which we added in
http://trac.webkit.org/changeset/164207.
        
This also does some FTL refactoring to make MultiPutByOffset share code with some nodes
that generate similar code.
        
1% speed-up on V8v7 due to splay improving by 6.8%. Splay does the thing where it
sometimes swaps field insertion order, creating fake polymorphism.

* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeFromLLInt):
(JSC::PutByIdStatus::computeFor):
(JSC::PutByIdStatus::computeForStubInfo):
(JSC::PutByIdStatus::dump):
* bytecode/PutByIdStatus.h:
(JSC::PutByIdStatus::PutByIdStatus):
(JSC::PutByIdStatus::isSimple):
(JSC::PutByIdStatus::numVariants):
(JSC::PutByIdStatus::variants):
(JSC::PutByIdStatus::at):
(JSC::PutByIdStatus::operator[]):
* bytecode/PutByIdVariant.cpp: Added.
(JSC::PutByIdVariant::dump):
(JSC::PutByIdVariant::dumpInContext):
* bytecode/PutByIdVariant.h: Added.
(JSC::PutByIdVariant::PutByIdVariant):
(JSC::PutByIdVariant::replace):
(JSC::PutByIdVariant::transition):
(JSC::PutByIdVariant::kind):
(JSC::PutByIdVariant::isSet):
(JSC::PutByIdVariant::operator!):
(JSC::PutByIdVariant::structure):
(JSC::PutByIdVariant::oldStructure):
(JSC::PutByIdVariant::newStructure):
(JSC::PutByIdVariant::structureChain):
(JSC::PutByIdVariant::offset):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::emitPrototypeChecks):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::emitPutById):
(JSC::DFG::ByteCodeParser::handlePutById):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::checkStructureElimination):
(JSC::DFG::CSEPhase::structureTransitionWatchpointElimination):
(JSC::DFG::CSEPhase::putStructureStoreElimination):
(JSC::DFG::CSEPhase::getByOffsetLoadElimination):
(JSC::DFG::CSEPhase::putByOffsetStoreElimination):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGGraph.h:
* dfg/DFGNode.cpp:
(JSC::DFG::MultiPutByOffsetData::writesStructures):
(JSC::DFG::MultiPutByOffsetData::reallocatesStorage):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToPutByOffset):
(JSC::DFG::Node::hasMultiPutByOffsetData):
(JSC::DFG::Node::multiPutByOffsetData):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compilePutStructure):
(JSC::FTL::LowerDFGToLLVM::compileAllocatePropertyStorage):
(JSC::FTL::LowerDFGToLLVM::compileReallocatePropertyStorage):
(JSC::FTL::LowerDFGToLLVM::compileGetByOffset):
(JSC::FTL::LowerDFGToLLVM::compileMultiGetByOffset):
(JSC::FTL::LowerDFGToLLVM::compilePutByOffset):
(JSC::FTL::LowerDFGToLLVM::compileMultiPutByOffset):
(JSC::FTL::LowerDFGToLLVM::loadProperty):
(JSC::FTL::LowerDFGToLLVM::storeProperty):
(JSC::FTL::LowerDFGToLLVM::addressOfProperty):
(JSC::FTL::LowerDFGToLLVM::storageForTransition):
(JSC::FTL::LowerDFGToLLVM::allocatePropertyStorage):
(JSC::FTL::LowerDFGToLLVM::reallocatePropertyStorage):
(JSC::FTL::LowerDFGToLLVM::emitStoreBarrier):
* tests/stress/fold-multi-put-by-offset-to-put-by-offset.js: Added.
* tests/stress/multi-put-by-offset-reallocation-butterfly-cse.js: Added.
* tests/stress/multi-put-by-offset-reallocation-cases.js: Added.

LayoutTests: 

Reviewed by Mark Hahnenberg and Oliver Hunt.
        
Add a microbenchmark for polymorphic PutById.

* js/regress/polymorphic-put-by-id-expected.txt: Added.
* js/regress/polymorphic-put-by-id.html: Added.
* js/regress/script-tests/polymorphic-put-by-id.js: Added.
(foo):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@164620 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
index b1e1538..a06fb1e 100644
--- a/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
+++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
@@ -30,8 +30,10 @@
 #include "LLIntData.h"
 #include "LowLevelInterpreter.h"
 #include "JSCInlines.h"
+#include "PolymorphicPutByIdList.h"
 #include "Structure.h"
 #include "StructureChain.h"
+#include <wtf/ListDump.h>
 
 namespace JSC {
 
@@ -56,15 +58,15 @@
 
     Structure* structure = instruction[4].u.structure.get();
     if (!structure)
-        return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+        return PutByIdStatus(NoInformation);
     
     if (instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id)
         || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_out_of_line)) {
         PropertyOffset offset = structure->getConcurrently(*profiledBlock->vm(), uid);
         if (!isValidOffset(offset))
-            return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+            return PutByIdStatus(NoInformation);
         
-        return PutByIdStatus(SimpleReplace, structure, 0, 0, offset);
+        return PutByIdVariant::replace(structure, offset);
     }
     
     ASSERT(structure->transitionWatchpointSetHasBeenInvalidated());
@@ -81,14 +83,14 @@
     
     PropertyOffset offset = newStructure->getConcurrently(*profiledBlock->vm(), uid);
     if (!isValidOffset(offset))
-        return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+        return PutByIdStatus(NoInformation);
     
-    return PutByIdStatus(
-        SimpleTransition, structure, newStructure,
+    return PutByIdVariant::transition(
+        structure, newStructure,
         chain ? adoptRef(new IntendedStructureChain(profiledBlock, structure, chain)) : 0,
         offset);
 #else
-    return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+    return PutByIdStatus(NoInformation);
 #endif
 }
 
@@ -102,7 +104,7 @@
 #if ENABLE(DFG_JIT)
     if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex)
         || hasExitSite(locker, profiledBlock, bytecodeIndex))
-        return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+        return PutByIdStatus(TakesSlowPath);
     
     StructureStubInfo* stubInfo = map.get(CodeOrigin(bytecodeIndex));
     PutByIdStatus result = computeForStubInfo(locker, profiledBlock, stubInfo, uid);
@@ -112,7 +114,7 @@
     return result;
 #else // ENABLE(JIT)
     UNUSED_PARAM(map);
-    return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset);
+    return PutByIdStatus(NoInformation);
 #endif // ENABLE(JIT)
 }
 
@@ -123,25 +125,22 @@
         return PutByIdStatus();
     
     if (stubInfo->resetByGC)
-        return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+        return PutByIdStatus(TakesSlowPath);
 
     switch (stubInfo->accessType) {
     case access_unset:
         // If the JIT saw it but didn't optimize it, then assume that this takes slow path.
-        return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+        return PutByIdStatus(TakesSlowPath);
         
     case access_put_by_id_replace: {
         PropertyOffset offset =
             stubInfo->u.putByIdReplace.baseObjectStructure->getConcurrently(
                 *profiledBlock->vm(), uid);
         if (isValidOffset(offset)) {
-            return PutByIdStatus(
-                SimpleReplace,
-                stubInfo->u.putByIdReplace.baseObjectStructure.get(),
-                0, 0,
-                offset);
+            return PutByIdVariant::replace(
+                stubInfo->u.putByIdReplace.baseObjectStructure.get(), offset);
         }
-        return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+        return PutByIdStatus(TakesSlowPath);
     }
         
     case access_put_by_id_transition_normal:
@@ -151,8 +150,7 @@
             stubInfo->u.putByIdTransition.structure->getConcurrently(
                 *profiledBlock->vm(), uid);
         if (isValidOffset(offset)) {
-            return PutByIdStatus(
-                SimpleTransition,
+            return PutByIdVariant::transition(
                 stubInfo->u.putByIdTransition.previousStructure.get(),
                 stubInfo->u.putByIdTransition.structure.get(),
                 stubInfo->u.putByIdTransition.chain ? adoptRef(new IntendedStructureChain(
@@ -160,13 +158,51 @@
                     stubInfo->u.putByIdTransition.chain.get())) : 0,
                 offset);
         }
-        return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+        return PutByIdStatus(TakesSlowPath);
+    }
+        
+    case access_put_by_id_list: {
+        PolymorphicPutByIdList* list = stubInfo->u.putByIdList.list;
+        
+        PutByIdStatus result;
+        result.m_state = Simple;
+        
+        for (unsigned i = 0; i < list->size(); ++i) {
+            const PutByIdAccess& access = list->at(i);
+            
+            switch (access.type()) {
+            case PutByIdAccess::Replace: {
+                Structure* structure = access.structure();
+                PropertyOffset offset = structure->getConcurrently(*profiledBlock->vm(), uid);
+                if (!isValidOffset(offset))
+                    return PutByIdStatus(TakesSlowPath);
+                result.m_variants.append(PutByIdVariant::replace(structure, offset));
+                break;
+            }
+                
+            case PutByIdAccess::Transition: {
+                PropertyOffset offset =
+                    access.newStructure()->getConcurrently(*profiledBlock->vm(), uid);
+                if (!isValidOffset(offset))
+                    return PutByIdStatus(TakesSlowPath);
+                result.m_variants.append(PutByIdVariant::transition(
+                    access.oldStructure(), access.newStructure(),
+                    access.chain() ? adoptRef(new IntendedStructureChain(
+                        profiledBlock, access.oldStructure(), access.chain())) : 0,
+                    offset));
+                break;
+            }
+
+            default:
+                return PutByIdStatus(TakesSlowPath);
+            }
+        }
+        
+        return result;
     }
         
     default:
-        // FIXME: We should handle polymorphic PutById. We probably have some interesting things
-        // we could do about it.
-        return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset);
+        return PutByIdStatus(TakesSlowPath);
     }
 }
 #endif
@@ -223,7 +259,7 @@
             // the specialized slot.
             return PutByIdStatus(TakesSlowPath);
         }
-        return PutByIdStatus(SimpleReplace, structure, 0, 0, offset);
+        return PutByIdVariant::replace(structure, offset);
     }
     
     // Our hypothesis is that we're doing a transition. Before we prove that this is really
@@ -276,7 +312,26 @@
     ASSERT(!transition->transitionDidInvolveSpecificValue());
     ASSERT(isValidOffset(offset));
     
-    return PutByIdStatus(SimpleTransition, structure, transition, chain.release(), offset);
+    return PutByIdVariant::transition(structure, transition, chain.release(), offset);
+}
+
+void PutByIdStatus::dump(PrintStream& out) const
+{
+    switch (m_state) {
+    case NoInformation:
+        out.print("(NoInformation)");
+        return;
+        
+    case Simple:
+        out.print("(", listDump(m_variants), ")");
+        return;
+        
+    case TakesSlowPath:
+        out.print("(TakesSlowPath)");
+        return;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
 }
 
 } // namespace JSC
diff --git a/Source/JavaScriptCore/bytecode/PutByIdStatus.h b/Source/JavaScriptCore/bytecode/PutByIdStatus.h
index 1684a74..4bb8b98 100644
--- a/Source/JavaScriptCore/bytecode/PutByIdStatus.h
+++ b/Source/JavaScriptCore/bytecode/PutByIdStatus.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,8 +27,7 @@
 #define PutByIdStatus_h
 
 #include "ExitingJITType.h"
-#include "IntendedStructureChain.h"
-#include "PropertyOffset.h"
+#include "PutByIdVariant.h"
 #include "StructureStubInfo.h"
 #include <wtf/text/StringImpl.h>
 
@@ -45,51 +44,27 @@
     enum State {
         // It's uncached so we have no information.
         NoInformation,
-        // It's cached as a direct store into an object property for cases where the object
-        // already has the property.
-        SimpleReplace,
-        // It's cached as a transition from one structure that lacks the property to one that
-        // includes the property, and a direct store to this new property.
-        SimpleTransition,
+        // It's cached as a simple store of some kind.
+        Simple,
         // It's known to often take slow path.
         TakesSlowPath
     };
     
     PutByIdStatus()
         : m_state(NoInformation)
-        , m_oldStructure(0)
-        , m_newStructure(0)
-        , m_structureChain(0)
-        , m_offset(invalidOffset)
     {
     }
     
     explicit PutByIdStatus(State state)
         : m_state(state)
-        , m_oldStructure(0)
-        , m_newStructure(0)
-        , m_structureChain(0)
-        , m_offset(invalidOffset)
     {
         ASSERT(m_state == NoInformation || m_state == TakesSlowPath);
     }
     
-    PutByIdStatus(
-        State state,
-        Structure* oldStructure,
-        Structure* newStructure,
-        PassRefPtr<IntendedStructureChain> structureChain,
-        PropertyOffset offset)
-        : m_state(state)
-        , m_oldStructure(oldStructure)
-        , m_newStructure(newStructure)
-        , m_structureChain(structureChain)
-        , m_offset(offset)
+    PutByIdStatus(const PutByIdVariant& variant)
+        : m_state(Simple)
     {
-        ASSERT((m_state == NoInformation || m_state == TakesSlowPath) == !m_oldStructure);
-        ASSERT((m_state != SimpleTransition) == !m_newStructure);
-        ASSERT(!((m_state != SimpleTransition) && m_structureChain));
-        ASSERT((m_state == NoInformation || m_state == TakesSlowPath) == (m_offset == invalidOffset));
+        m_variants.append(variant);
     }
     
     static PutByIdStatus computeFor(CodeBlock*, StubInfoMap&, unsigned bytecodeIndex, StringImpl* uid);
@@ -101,14 +76,15 @@
     
     bool isSet() const { return m_state != NoInformation; }
     bool operator!() const { return m_state == NoInformation; }
-    bool isSimpleReplace() const { return m_state == SimpleReplace; }
-    bool isSimpleTransition() const { return m_state == SimpleTransition; }
+    bool isSimple() const { return m_state == Simple; }
     bool takesSlowPath() const { return m_state == TakesSlowPath; }
     
-    Structure* oldStructure() const { return m_oldStructure; }
-    Structure* newStructure() const { return m_newStructure; }
-    IntendedStructureChain* structureChain() const { return m_structureChain.get(); }
-    PropertyOffset offset() const { return m_offset; }
+    size_t numVariants() const { return m_variants.size(); }
+    const Vector<PutByIdVariant, 1>& variants() const { return m_variants; }
+    const PutByIdVariant& at(size_t index) const { return m_variants[index]; }
+    const PutByIdVariant& operator[](size_t index) const { return at(index); }
+    
+    void dump(PrintStream&) const;
     
 private:
 #if ENABLE(DFG_JIT)
@@ -120,10 +96,7 @@
     static PutByIdStatus computeFromLLInt(CodeBlock*, unsigned bytecodeIndex, StringImpl* uid);
     
     State m_state;
-    Structure* m_oldStructure;
-    Structure* m_newStructure;
-    RefPtr<IntendedStructureChain> m_structureChain;
-    PropertyOffset m_offset;
+    Vector<PutByIdVariant, 1> m_variants;
 };
 
 } // namespace JSC
diff --git a/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp b/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp
new file mode 100644
index 0000000..f83c102
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/PutByIdVariant.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "PutByIdVariant.h"
+
+namespace JSC {
+
+void PutByIdVariant::dump(PrintStream& out) const
+{
+    dumpInContext(out, 0);
+}
+
+void PutByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    switch (kind()) {
+    case NotSet:
+        out.print("<empty>");
+        return;
+        
+    case Replace:
+        out.print(
+            "<Replace: ", pointerDumpInContext(structure(), context), ", ", offset(), ">");
+        return;
+        
+    case Transition:
+        out.print(
+            "<Transition: ", pointerDumpInContext(oldStructure(), context), " -> ",
+            pointerDumpInContext(newStructure(), context), ", ",
+            pointerDumpInContext(structureChain(), context), ", ", offset(), ">");
+        return;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/PutByIdVariant.h b/Source/JavaScriptCore/bytecode/PutByIdVariant.h
new file mode 100644
index 0000000..eba95e8
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/PutByIdVariant.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef PutByIdVariant_h
+#define PutByIdVariant_h
+
+#include "IntendedStructureChain.h"
+#include "PropertyOffset.h"
+
+namespace JSC {
+
+class PutByIdVariant {
+public:
+    enum Kind {
+        NotSet,
+        Replace,
+        Transition
+    };
+    
+    PutByIdVariant()
+        : m_kind(NotSet)
+        , m_oldStructure(0)
+        , m_newStructure(0)
+        , m_offset(invalidOffset)
+    {
+    }
+    
+    static PutByIdVariant replace(Structure* structure, PropertyOffset offset)
+    {
+        PutByIdVariant result;
+        result.m_kind = Replace;
+        result.m_oldStructure = structure;
+        result.m_offset = offset;
+        return result;
+    }
+    
+    static PutByIdVariant transition(
+        Structure* oldStructure, Structure* newStructure,
+        PassRefPtr<IntendedStructureChain> structureChain, PropertyOffset offset)
+    {
+        PutByIdVariant result;
+        result.m_kind = Transition;
+        result.m_oldStructure = oldStructure;
+        result.m_newStructure = newStructure;
+        result.m_structureChain = structureChain;
+        result.m_offset = offset;
+        return result;
+    }
+    
+    Kind kind() const { return m_kind; }
+    
+    bool isSet() const { return kind() != NotSet; }
+    bool operator!() const { return !isSet(); }
+    
+    Structure* structure() const
+    {
+        ASSERT(kind() == Replace);
+        return m_oldStructure;
+    }
+    
+    Structure* oldStructure() const
+    {
+        ASSERT(kind() == Transition || kind() == Replace);
+        return m_oldStructure;
+    }
+    
+    Structure* newStructure() const
+    {
+        ASSERT(kind() == Transition);
+        return m_newStructure;
+    }
+    
+    IntendedStructureChain* structureChain() const
+    {
+        ASSERT(kind() == Transition);
+        return m_structureChain.get();
+    }
+    
+    PropertyOffset offset() const
+    {
+        ASSERT(isSet());
+        return m_offset;
+    }
+    
+    void dump(PrintStream&) const;
+    void dumpInContext(PrintStream&, DumpContext*) const;
+
+private:
+    Kind m_kind;
+    Structure* m_oldStructure;
+    Structure* m_newStructure;
+    RefPtr<IntendedStructureChain> m_structureChain;
+    PropertyOffset m_offset;
+};
+
+} // namespace JSC
+
+#endif // PutByIdVariant_h
+