bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other) { if (m_offset != other.m_offset) return false; switch (m_kind) { case Replace: switch (other.m_kind) { case Replace: { ASSERT(m_constantChecks.isEmpty()); ASSERT(other.m_constantChecks.isEmpty()); m_oldStructure.merge(other.m_oldStructure); return true; } case Transition: { PutByIdVariant 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); default: return false; } default: return false; } }
bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& replace) { ASSERT(m_kind == Transition); ASSERT(replace.m_kind == Replace); ASSERT(m_offset == replace.m_offset); ASSERT(!replace.writesStructures()); ASSERT(!replace.reallocatesStorage()); // 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; }
bool PutByIdStatus::appendVariant(const PutByIdVariant& variant) { for (unsigned i = 0; i < m_variants.size(); ++i) { if (m_variants[i].attemptToMerge(variant)) return true; } for (unsigned i = 0; i < m_variants.size(); ++i) { if (m_variants[i].oldStructure().overlaps(variant.oldStructure())) return false; } m_variants.append(variant); return true; }
void emitPutByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const PutByIdVariant& variant, unsigned identifierNumber) { NodeOrigin origin = node->origin; Edge childEdge = node->child1(); addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure()); childEdge.setUseKind(KnownCellUse); Transition* transition = 0; if (variant.kind() == PutByIdVariant::Transition) { transition = m_graph.m_transitions.add( variant.oldStructureForTransition(), variant.newStructure()); } Edge propertyStorage; if (isInlineOffset(variant.offset())) propertyStorage = childEdge; else if (!variant.reallocatesStorage()) { propertyStorage = Edge(m_insertionSet.insertNode( indexInBlock, SpecNone, GetButterfly, origin, childEdge)); } else if (!variant.oldStructureForTransition()->outOfLineCapacity()) { ASSERT(variant.newStructure()->outOfLineCapacity()); ASSERT(!isInlineOffset(variant.offset())); Node* allocatePropertyStorage = m_insertionSet.insertNode( indexInBlock, SpecNone, AllocatePropertyStorage, origin, OpInfo(transition), childEdge); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); propertyStorage = Edge(allocatePropertyStorage); } else { ASSERT(variant.oldStructureForTransition()->outOfLineCapacity()); ASSERT(variant.newStructure()->outOfLineCapacity() > variant.oldStructureForTransition()->outOfLineCapacity()); ASSERT(!isInlineOffset(variant.offset())); Node* reallocatePropertyStorage = m_insertionSet.insertNode( indexInBlock, SpecNone, ReallocatePropertyStorage, origin, OpInfo(transition), childEdge, Edge(m_insertionSet.insertNode( indexInBlock, SpecNone, GetButterfly, origin, childEdge))); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); propertyStorage = Edge(reallocatePropertyStorage); } if (variant.kind() == PutByIdVariant::Transition) { Node* putStructure = m_graph.addNode(SpecNone, PutStructure, origin, OpInfo(transition), childEdge); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); m_insertionSet.insert(indexInBlock, putStructure); } node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage); m_insertionSet.insertNode( indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child2().node(), KnownCellUse)); StorageAccessData storageAccessData; storageAccessData.offset = variant.offset(); storageAccessData.identifierNumber = identifierNumber; m_graph.m_storageAccessData.append(storageAccessData); }
void emitPutByOffset(unsigned indexInBlock, Node* node, Structure* structure, const PutByIdVariant& variant, unsigned identifierNumber) { NodeOrigin origin = node->origin; Edge childEdge = node->child1(); Node* child = childEdge.node(); ASSERT(variant.oldStructure() == structure); bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell; // Now before we do anything else, push the CFA forward over the PutById // and make sure we signal to the loop that it should continue and not // do any eliminations. m_interpreter.execute(indexInBlock); if (needsWatchpoint) { m_insertionSet.insertNode( indexInBlock, SpecNone, StructureTransitionWatchpoint, origin, OpInfo(structure), childEdge); } else if (needsCellCheck) { m_insertionSet.insertNode( indexInBlock, SpecNone, Phantom, origin, childEdge); } childEdge.setUseKind(KnownCellUse); StructureTransitionData* transitionData = 0; if (variant.kind() == PutByIdVariant::Transition) { transitionData = m_graph.addStructureTransitionData( StructureTransitionData(structure, variant.newStructure())); if (node->op() == PutById) { if (!structure->storedPrototype().isNull()) { addStructureTransitionCheck( origin, indexInBlock, structure->storedPrototype().asCell()); } m_graph.chains().addLazily(variant.structureChain()); for (unsigned i = 0; i < variant.structureChain()->size(); ++i) { JSValue prototype = variant.structureChain()->at(i)->storedPrototype(); if (prototype.isNull()) continue; ASSERT(prototype.isCell()); addStructureTransitionCheck( origin, indexInBlock, prototype.asCell()); } } } Edge propertyStorage; if (isInlineOffset(variant.offset())) propertyStorage = childEdge; else if ( variant.kind() == PutByIdVariant::Replace || structure->outOfLineCapacity() == variant.newStructure()->outOfLineCapacity()) { propertyStorage = Edge(m_insertionSet.insertNode( indexInBlock, SpecNone, GetButterfly, origin, childEdge)); } else if (!structure->outOfLineCapacity()) { ASSERT(variant.newStructure()->outOfLineCapacity()); ASSERT(!isInlineOffset(variant.offset())); Node* allocatePropertyStorage = m_insertionSet.insertNode( indexInBlock, SpecNone, AllocatePropertyStorage, origin, OpInfo(transitionData), childEdge); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); propertyStorage = Edge(allocatePropertyStorage); } else { ASSERT(structure->outOfLineCapacity()); ASSERT(variant.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity()); ASSERT(!isInlineOffset(variant.offset())); Node* reallocatePropertyStorage = m_insertionSet.insertNode( indexInBlock, SpecNone, ReallocatePropertyStorage, origin, OpInfo(transitionData), childEdge, Edge(m_insertionSet.insertNode( indexInBlock, SpecNone, GetButterfly, origin, childEdge))); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); propertyStorage = Edge(reallocatePropertyStorage); } if (variant.kind() == PutByIdVariant::Transition) { Node* putStructure = m_graph.addNode(SpecNone, PutStructure, origin, OpInfo(transitionData), childEdge); m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child1().node(), KnownCellUse)); m_insertionSet.insert(indexInBlock, putStructure); } node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage); m_insertionSet.insertNode( indexInBlock, SpecNone, StoreBarrier, origin, Edge(node->child2().node(), KnownCellUse)); StorageAccessData storageAccessData; storageAccessData.offset = variant.offset(); storageAccessData.identifierNumber = identifierNumber; m_graph.m_storageAccessData.append(storageAccessData); }