[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
+