[JSC] Polymorphic PutByVal
https://bugs.webkit.org/show_bug.cgi?id=229229

Reviewed by Saam Barati.

This patch changes PutByVal IC to modern style. This polymorphic PutByVal can handle multiple array types and multiple identifiers.
Also, this removes adhoc IC code in Baseline so that it paves the way to unlinked Baseline JIT by cleaning up IC.

Several interesting points of the design.

1. We need to pass ArrayProfile* via GPRReg to IC since we need to profile mayStoreToHole, which is still important to avoid the slow path.
2. Transition / Replace IC need to record propertyRegs if it exists not to clobber these registers. This is important in DFG / FTL since
   IC should not clobber these registers unless it is flushed. It also makes Baseline code smaller since we do not reload them in the slow path call.
3. Added a path folding String/Symbol when emitting PutByVal in DFG / FTL. This edge-case is found via a microbenchmark. Let's consider the case: one
   put_by_val site has one identifier "foo", but it has so many different Structures. Previously, we emit JITPutByIdGenerator adhocly, and still we
   cache this "foo" identifier in cachedId. In DFG / FTL, while we cannot make it PutByOffset, we can emit PutById since we know that identifier is
   always "foo". But after this patch's change, such a site becomes slow-path. And then this identifier information is missed, and we were emitting
   PutByVal for that. For now, we attempt to fold to one identifier in DFGByteCodeParser so that we can still attempt to make it PutById, which
   can be PutByOffset in constant folding phase. We would like to handle this one identifier slow-path case in PutByStatus / GetByStatus in the future
   patch.
4. Now, DFG OSR exit does not query to ByValInfo for setter calls since JITPutByValGenerator use StructureStubInfo in Baseline.

Results of Microbenchmarks look good.

                                                     ToT                     Patched

put-by-val-direct-large-index                  94.6265+-0.9076           93.4550+-0.7121          might be 1.0125x faster
inlined-put-by-val-with-string-transition
                                               23.7131+-0.3282     ^     22.7679+-0.1137        ^ definitely 1.0415x faster
put-by-val-with-string-slightly-polymorphic
                                                1.9852+-0.0284            1.9580+-0.0224          might be 1.0139x faster
get-and-put-by-val-double-index-dont-fall-off-a-cliff
                                              185.4762+-0.5737     ?    185.6325+-0.5819        ?
polymorphic-put-by-val-with-string             30.9903+-0.1207           30.8097+-0.1285
put-by-val-machine-int                          1.8803+-0.0384            1.8707+-0.0440
fold-put-by-val-with-symbol-to-multi-put-by-offset
                                                4.8463+-0.1148            4.7839+-0.0547          might be 1.0130x faster
put-by-val-with-string-replace-and-transition
                                                8.8730+-1.5934     ^      6.2276+-0.0585        ^ definitely 1.4248x faster
fold-put-by-val-with-string-to-multi-put-by-offset
                                                4.8183+-0.0841     ?      4.8233+-0.0892        ?
put-by-val-direct                               0.2845+-0.0091     ?      0.2901+-0.0088        ? might be 1.0196x slower
put-by-val-with-symbol-replace-and-transition
                                                6.3527+-0.0686     ?      6.3933+-0.0961        ?
put-by-val-with-symbol                          9.3556+-3.1421            7.1509+-0.1019          might be 1.3083x faster
put-by-val-with-symbol-slightly-polymorphic
                                                2.0052+-0.0309            1.9781+-0.0397          might be 1.0137x faster
put-by-val-negative-array-index                14.9572+-0.1221     ^     14.5636+-0.1044        ^ definitely 1.0270x faster
put-by-val-with-string                         11.6345+-4.3048     ^      7.0919+-0.0918        ^ definitely 1.6405x faster
put-by-val-large-index-blank-indexing-type
                                                3.1425+-0.1165            3.1236+-0.0378
inlined-put-by-val-with-symbol-transition
                                               23.4932+-0.3186     ^     22.8469+-0.0873        ^ definitely 1.0283x faster
polymorphic-put-by-val-with-symbol             36.6046+-1.6519     ^     30.8597+-0.1474        ^ definitely 1.1862x faster

Speedometer2 showed roughly 0.2-0.3% progression.

----------------------------------------------------------------------------------------------------------------------------------
|               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
----------------------------------------------------------------------------------------------------------------------------------
| Elm-TodoMVC                          |121.916667   |121.958333   |1.000342  | 0.876802                                         |
| VueJS-TodoMVC                        |26.263333    |26.006667    |0.990227  | 0.263868                                         |
| EmberJS-TodoMVC                      |127.080000   |127.866667   |1.006190  | 0.011497 (significant)                           |
| BackboneJS-TodoMVC                   |48.920000    |49.318333    |1.008143  | 0.003395 (significant)                           |
| Preact-TodoMVC                       |19.828333    |19.828333    |1.000000  | 1.000000                                         |
| AngularJS-TodoMVC                    |134.011667   |132.080000   |0.985586  | 0.000000 (significant)                           |
| Vanilla-ES2015-TodoMVC               |63.726667    |63.838333    |1.001752  | 0.408404                                         |
| Inferno-TodoMVC                      |65.153333    |63.753333    |0.978512  | 0.000000 (significant)                           |
| Flight-TodoMVC                       |78.133333    |78.780000    |1.008276  | 0.097794                                         |
| Angular2-TypeScript-TodoMVC          |40.415000    |40.100000    |0.992206  | 0.287630                                         |
| VanillaJS-TodoMVC                    |51.931667    |52.500000    |1.010944  | 0.004149 (significant)                           |
| jQuery-TodoMVC                       |226.056667   |225.073333   |0.995650  | 0.007796 (significant)                           |
| EmberJS-Debug-TodoMVC                |341.210000   |340.978333   |0.999321  | 0.623386                                         |
| React-TodoMVC                        |87.198333    |86.893333    |0.996502  | 0.042189                                         |
| React-Redux-TodoMVC                  |146.506667   |145.958333   |0.996257  | 0.018801 (significant)                           |
| Vanilla-ES2015-Babel-Webpack-TodoMVC |61.450000    |61.870000    |1.006835  | 0.000049 (significant)                           |
----------------------------------------------------------------------------------------------------------------------------------
a mean = 254.85111
b mean = 255.25735
pValue = 0.1856561656
(Bigger means are better.)
1.002 times better
Results ARE NOT significant

* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* bytecode/AccessCase.cpp:
(JSC::AccessCase::create):
(JSC::AccessCase::guardedByStructureCheckSkippingConstantIdentifierCheck const):
(JSC::AccessCase::requiresIdentifierNameMatch const):
(JSC::AccessCase::requiresInt32PropertyCheck const):
(JSC::AccessCase::needsScratchFPR const):
(JSC::AccessCase::forEachDependentCell const):
(JSC::AccessCase::doesCalls const):
(JSC::AccessCase::canReplace const):
(JSC::AccessCase::generateWithGuard):
(JSC::AccessCase::generateImpl):
(JSC::AccessCase::toTypedArrayType):
(JSC::AccessCase::canBeShared):
* bytecode/AccessCase.h:
(JSC::SharedJITStubSet::Hash::Key::Key):
(JSC::SharedJITStubSet::Hash::Key::operator==):
(JSC::SharedJITStubSet::Searcher::Translator::equal):
* bytecode/ArrayProfile.h:
(JSC::ArrayProfile::offsetOfMayStoreToHole):
(JSC::ArrayProfile::offsetOfLastSeenStructureID):
* bytecode/GetterSetterAccessCase.cpp:
(JSC::GetterSetterAccessCase::emitDOMJITGetter):
* bytecode/ICStatusMap.h:
* bytecode/InlineAccess.cpp:
(JSC::getScratchRegister):
* bytecode/PolymorphicAccess.cpp:
(JSC::PolymorphicAccess::regenerate):
(WTF::printInternal):
* bytecode/PutByStatus.cpp: Renamed from Source/JavaScriptCore/bytecode/PutByIdStatus.cpp.
(JSC::PutByStatus::appendVariant):
(JSC::PutByStatus::shrinkToFit):
(JSC::PutByStatus::computeFromLLInt):
(JSC::PutByStatus::PutByStatus):
(JSC::PutByStatus::computeFor):
(JSC::PutByStatus::computeForStubInfo):
(JSC::PutByStatus::makesCalls const):
(JSC::PutByStatus::slowVersion const):
(JSC::PutByStatus::singleIdentifier const):
(JSC::PutByStatus::visitAggregateImpl):
(JSC::PutByStatus::markIfCheap):
(JSC::PutByStatus::finalize):
(JSC::PutByStatus::merge):
(JSC::PutByStatus::filter):
(JSC::PutByStatus::dump const):
* bytecode/PutByStatus.h: Renamed from Source/JavaScriptCore/bytecode/PutByIdStatus.h.
* bytecode/PutByVariant.cpp: Renamed from Source/JavaScriptCore/bytecode/PutByIdVariant.cpp.
(JSC::PutByVariant::PutByVariant):
(JSC::PutByVariant::operator=):
(JSC::PutByVariant::replace):
(JSC::PutByVariant::transition):
(JSC::PutByVariant::setter):
(JSC::PutByVariant::oldStructureForTransition const):
(JSC::PutByVariant::fixTransitionToReplaceIfNecessary):
(JSC::PutByVariant::writesStructures const):
(JSC::PutByVariant::reallocatesStorage const):
(JSC::PutByVariant::makesCalls const):
(JSC::PutByVariant::attemptToMerge):
(JSC::PutByVariant::attemptToMergeTransitionWithReplace):
(JSC::PutByVariant::visitAggregateImpl):
(JSC::PutByVariant::markIfCheap):
(JSC::PutByVariant::finalize):
(JSC::PutByVariant::dump const):
(JSC::PutByVariant::dumpInContext const):
* bytecode/PutByVariant.h: Renamed from Source/JavaScriptCore/bytecode/PutByIdVariant.h.
(JSC::PutByVariant::PutByVariant):
(JSC::PutByVariant::identifier const):
(JSC::PutByVariant::overlaps):
* bytecode/RecordedStatuses.cpp:
(JSC::RecordedStatuses::addPutByStatus):
(JSC::RecordedStatuses::visitAggregateImpl):
(JSC::RecordedStatuses::addPutByIdStatus): Deleted.
* bytecode/RecordedStatuses.h:
* bytecode/StructureStubInfo.cpp:
(JSC::StructureStubInfo::reset):
* bytecode/StructureStubInfo.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
(JSC::DFG::AbstractInterpreter<AbstractStateType>::filterICStatus):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::load):
(JSC::DFG::ByteCodeParser::store):
(JSC::DFG::ByteCodeParser::emitPutById):
(JSC::DFG::ByteCodeParser::handlePutById):
(JSC::DFG::ByteCodeParser::handlePutPrivateNameById):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::handlePutByVal):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGClobbersExitState.cpp:
(JSC::DFG::clobbersExitState):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::emitPutByOffset):
(JSC::DFG::ConstantFoldingPhase::tryFoldAsPutByOffset):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGGraph.h:
* dfg/DFGJITCompiler.cpp:
(JSC::DFG::JITCompiler::link):
* dfg/DFGJITCompiler.h:
(JSC::DFG::JITCompiler::addPutByVal):
* dfg/DFGMayExit.cpp:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasPutByStatus):
(JSC::DFG::Node::putByStatus):
(JSC::DFG::Node::hasPutByIdStatus): Deleted.
(JSC::DFG::Node::putByIdStatus): Deleted.
* dfg/DFGNodeType.h:
* dfg/DFGOSRExitCompilerCommon.cpp:
(JSC::DFG::callerReturnPC):
* dfg/DFGObjectAllocationSinkingPhase.cpp:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStoreBarrierInsertionPhase.cpp:
* dfg/DFGValidate.cpp:
* dfg/DFGVarargsForwardingPhase.cpp:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compilePutByVal):
(JSC::FTL::DFG::LowerDFGToB3::compileMultiPutByOffset):
* generator/DSL.rb:
* jit/ICStats.h:
* jit/JIT.cpp:
(JSC::JIT::privateCompileSlowCases):
(JSC::JIT::link):
* jit/JIT.h:
* jit/JITInlineCacheGenerator.cpp:
(JSC::JITPutByIdGenerator::JITPutByIdGenerator):
(JSC::JITPutByValGenerator::JITPutByValGenerator):
(JSC::JITPutByValGenerator::generateFastPath):
(JSC::JITPutByValGenerator::finalize):
* jit/JITInlineCacheGenerator.h:
* jit/JITInlines.h:
(JSC::JIT::emitArrayProfilingSiteWithCell):
(JSC::JIT::chooseArrayMode): Deleted.
* jit/JITOperations.cpp:
(JSC::JSC_DEFINE_JIT_OPERATION):
(JSC::putByVal):
(JSC::directPutByVal):
(JSC::putByValOptimize):
(JSC::directPutByValOptimize):
(JSC::tryPutByValOptimize): Deleted.
(JSC::tryDirectPutByValOptimize): Deleted.
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::emitSlow_op_put_by_val):
(JSC::JIT::slow_op_put_by_val_prepareCallGenerator):
(JSC::JIT::emitSlow_op_put_private_name):
(JSC::JIT::slow_op_put_private_name_prepareCallGenerator):
(JSC::JIT::emitGenericContiguousPutByVal): Deleted.
(JSC::JIT::emitArrayStoragePutByVal): Deleted.
(JSC::JIT::privateCompilePutByVal): Deleted.
(JSC::JIT::privateCompilePutByValWithCachedId): Deleted.
(JSC::JIT::emitIntTypedArrayPutByVal): Deleted.
(JSC::JIT::emitFloatTypedArrayPutByVal): Deleted.
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::emitSlow_op_put_by_val):
(JSC::JIT::emitGenericContiguousPutByVal): Deleted.
(JSC::JIT::emitArrayStoragePutByVal): Deleted.
* jit/Repatch.cpp:
(JSC::appropriateGenericPutByFunction):
(JSC::appropriateOptimizingPutByFunction):
(JSC::tryCachePutBy):
(JSC::repatchPutBy):
(JSC::tryCacheArrayPutByVal):
(JSC::repatchArrayPutByVal):
(JSC::tryCacheInBy):
(JSC::resetPutBy):
(JSC::appropriateGenericPutByIdFunction): Deleted.
(JSC::appropriateOptimizingPutByIdFunction): Deleted.
(JSC::tryCachePutByID): Deleted.
(JSC::repatchPutByID): Deleted.
(JSC::resetPutByID): Deleted.
* jit/Repatch.h:
* llint/LowLevelInterpreter.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@281615 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/bytecode/PutByVariant.cpp b/Source/JavaScriptCore/bytecode/PutByVariant.cpp
new file mode 100644
index 0000000..b07c535
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/PutByVariant.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2014-2021 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 "PutByVariant.h"
+
+#include "CacheableIdentifierInlines.h"
+#include "CallLinkStatus.h"
+#include "HeapInlines.h"
+
+namespace JSC {
+
+PutByVariant::PutByVariant(const PutByVariant& other)
+    : PutByVariant(other.m_identifier)
+{
+    *this = other;
+}
+
+PutByVariant& PutByVariant::operator=(const PutByVariant& other)
+{
+    m_kind = other.m_kind;
+    m_oldStructure = other.m_oldStructure;
+    m_newStructure = other.m_newStructure;
+    m_conditionSet = other.m_conditionSet;
+    m_offset = other.m_offset;
+    if (other.m_callLinkStatus)
+        m_callLinkStatus = makeUnique<CallLinkStatus>(*other.m_callLinkStatus);
+    else
+        m_callLinkStatus = nullptr;
+    m_identifier = other.m_identifier;
+    return *this;
+}
+
+PutByVariant PutByVariant::replace(CacheableIdentifier identifier, const StructureSet& structure, PropertyOffset offset)
+{
+    PutByVariant result(WTFMove(identifier));
+    result.m_kind = Replace;
+    result.m_oldStructure = structure;
+    result.m_offset = offset;
+    return result;
+}
+
+PutByVariant PutByVariant::transition(CacheableIdentifier identifier, const StructureSet& oldStructure, Structure* newStructure, const ObjectPropertyConditionSet& conditionSet, PropertyOffset offset)
+{
+    PutByVariant result(WTFMove(identifier));
+    result.m_kind = Transition;
+    result.m_oldStructure = oldStructure;
+    result.m_newStructure = newStructure;
+    result.m_conditionSet = conditionSet;
+    result.m_offset = offset;
+    return result;
+}
+
+PutByVariant PutByVariant::setter(CacheableIdentifier identifier, const StructureSet& structure, PropertyOffset offset, const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<CallLinkStatus> callLinkStatus)
+{
+    PutByVariant result(WTFMove(identifier));
+    result.m_kind = Setter;
+    result.m_oldStructure = structure;
+    result.m_conditionSet = conditionSet;
+    result.m_offset = offset;
+    result.m_callLinkStatus = WTFMove(callLinkStatus);
+    return result;
+}
+
+Structure* PutByVariant::oldStructureForTransition() const
+{
+    RELEASE_ASSERT(kind() == Transition);
+    RELEASE_ASSERT(m_oldStructure.size() <= 2);
+    for (unsigned i = m_oldStructure.size(); i--;) {
+        Structure* structure = m_oldStructure[i];
+        if (structure != m_newStructure)
+            return structure;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+
+    return nullptr;
+}
+
+void PutByVariant::fixTransitionToReplaceIfNecessary()
+{
+    if (kind() != Transition)
+        return;
+    
+    RELEASE_ASSERT(m_oldStructure.size() <= 2);
+    for (unsigned i = m_oldStructure.size(); i--;) {
+        Structure* structure = m_oldStructure[i];
+        if (structure != m_newStructure)
+            return;
+    }
+    
+    m_newStructure = nullptr;
+    m_kind = Replace;
+    m_conditionSet = ObjectPropertyConditionSet();
+    RELEASE_ASSERT(!m_callLinkStatus);
+}
+
+bool PutByVariant::writesStructures() const
+{
+    switch (kind()) {
+    case Transition:
+    case Setter:
+        return true;
+    default:
+        return false;
+    }
+}
+
+bool PutByVariant::reallocatesStorage() const
+{
+    switch (kind()) {
+    case Transition:
+        return oldStructureForTransition()->outOfLineCapacity() != newStructure()->outOfLineCapacity();
+    case Setter:
+        return true;
+    default:
+        return false;
+    }
+}
+
+bool PutByVariant::makesCalls() const
+{
+    return kind() == Setter;
+}
+
+bool PutByVariant::attemptToMerge(const PutByVariant& other)
+{
+    if (!!m_identifier != !!other.m_identifier)
+        return false;
+
+    if (m_identifier && (m_identifier != other.m_identifier))
+        return false;
+
+    if (m_offset != other.m_offset)
+        return false;
+
+    switch (m_kind) {
+    case NotSet:
+        RELEASE_ASSERT_NOT_REACHED();
+        return false;
+        
+    case Replace: {
+        switch (other.m_kind) {
+        case Replace: {
+            ASSERT(m_conditionSet.isEmpty());
+            ASSERT(other.m_conditionSet.isEmpty());
+            
+            m_oldStructure.merge(other.m_oldStructure);
+            return true;
+        }
+            
+        case Transition: {
+            PutByVariant newVariant = other;
+            if (newVariant.attemptToMergeTransitionWithReplace(*this)) {
+                *this = newVariant;
+                return true;
+            }
+            return false;
+        }
+            
+        default:
+            return false;
+        }
+    }
+        
+    case Transition:
+        switch (other.m_kind) {
+        case Replace:
+            return attemptToMergeTransitionWithReplace(other);
+            
+        case Transition: {
+            if (m_oldStructure != other.m_oldStructure)
+                return false;
+            
+            if (m_newStructure != other.m_newStructure)
+                return false;
+            
+            ObjectPropertyConditionSet mergedConditionSet;
+            if (!m_conditionSet.isEmpty()) {
+                mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
+                if (!mergedConditionSet.isValid())
+                    return false;
+            }
+            m_conditionSet = mergedConditionSet;
+            return true;
+        }
+            
+        default:
+            return false;
+        }
+        
+    case Setter: {
+        if (other.m_kind != Setter)
+            return false;
+        
+        if (m_callLinkStatus || other.m_callLinkStatus) {
+            if (!(m_callLinkStatus && other.m_callLinkStatus))
+                return false;
+        }
+        
+        if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty())
+            return false;
+        
+        ObjectPropertyConditionSet mergedConditionSet;
+        if (!m_conditionSet.isEmpty()) {
+            mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
+            if (!mergedConditionSet.isValid() || !mergedConditionSet.hasOneSlotBaseCondition())
+                return false;
+        }
+        m_conditionSet = mergedConditionSet;
+        
+        if (m_callLinkStatus)
+            m_callLinkStatus->merge(*other.m_callLinkStatus);
+        
+        m_oldStructure.merge(other.m_oldStructure);
+        return true;
+    } }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+    return false;
+}
+
+bool PutByVariant::attemptToMergeTransitionWithReplace(const PutByVariant& replace)
+{
+    ASSERT(m_kind == Transition);
+    ASSERT(replace.m_kind == Replace);
+    ASSERT(m_offset == replace.m_offset);
+    ASSERT(!replace.writesStructures());
+    ASSERT(!replace.reallocatesStorage());
+    ASSERT(replace.conditionSet().isEmpty());
+    
+    // This sort of merging only works when we have one path along which we add a new field which
+    // transitions to structure S while the other path was already on structure S. This doesn't
+    // work if we need to reallocate anything or if the replace path is polymorphic.
+    
+    if (reallocatesStorage())
+        return false;
+    
+    if (replace.m_oldStructure.onlyStructure() != m_newStructure)
+        return false;
+    
+    m_oldStructure.merge(m_newStructure);
+    return true;
+}
+
+template<typename Visitor>
+void PutByVariant::visitAggregateImpl(Visitor& visitor)
+{
+    m_identifier.visitAggregate(visitor);
+}
+
+DEFINE_VISIT_AGGREGATE(PutByVariant);
+
+template<typename Visitor>
+void PutByVariant::markIfCheap(Visitor& visitor)
+{
+    m_oldStructure.markIfCheap(visitor);
+    if (m_newStructure)
+        m_newStructure->markIfCheap(visitor);
+}
+
+template void PutByVariant::markIfCheap(AbstractSlotVisitor&);
+template void PutByVariant::markIfCheap(SlotVisitor&);
+
+bool PutByVariant::finalize(VM& vm)
+{
+    if (!m_oldStructure.isStillAlive(vm))
+        return false;
+    if (m_newStructure && !vm.heap.isMarked(m_newStructure))
+        return false;
+    if (!m_conditionSet.areStillLive(vm))
+        return false;
+    if (m_callLinkStatus && !m_callLinkStatus->finalize(vm))
+        return false;
+    return true;
+}
+
+void PutByVariant::dump(PrintStream& out) const
+{
+    dumpInContext(out, nullptr);
+}
+
+void PutByVariant::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    out.print("<");
+    out.print("id='", m_identifier, "', ");
+    switch (kind()) {
+    case NotSet:
+        out.print("empty>");
+        return;
+        
+    case Replace:
+        out.print(
+            "Replace: ", inContext(structure(), context), ", offset = ", offset(), ", ", ">");
+        return;
+        
+    case Transition:
+        out.print(
+            "Transition: ", inContext(oldStructure(), context), " to ",
+            pointerDumpInContext(newStructure(), context), ", [",
+            inContext(m_conditionSet, context), "], offset = ", offset(), ", ", ">");
+        return;
+        
+    case Setter:
+        out.print(
+            "Setter: ", inContext(structure(), context), ", [",
+            inContext(m_conditionSet, context), "]");
+        out.print(", offset = ", m_offset);
+        out.print(", call = ", *m_callLinkStatus);
+        out.print(">");
+        return;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace JSC
+