/ bytecode / DeleteByStatus.cpp
DeleteByStatus.cpp
  1  /*
  2   * Copyright (C) 2020 Apple Inc. All rights reserved.
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer.
  9   * 2. Redistributions in binary form must reproduce the above copyright
 10   *    notice, this list of conditions and the following disclaimer in the
 11   *    documentation and/or other materials provided with the distribution.
 12   *
 13   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 14   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 15   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 16   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 17   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 18   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 19   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 20   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 21   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 23   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 24   */
 25  
 26  #include "config.h"
 27  #include "DeleteByStatus.h"
 28  
 29  #include "CacheableIdentifierInlines.h"
 30  #include "CodeBlock.h"
 31  #include "ICStatusUtils.h"
 32  #include "PolymorphicAccess.h"
 33  #include "StructureStubInfo.h"
 34  #include <wtf/ListDump.h>
 35  
 36  namespace JSC {
 37  
 38  bool DeleteByStatus::appendVariant(const DeleteByIdVariant& variant)
 39  {
 40      return appendICStatusVariant(m_variants, variant);
 41  }
 42  
 43  DeleteByStatus DeleteByStatus::computeForBaseline(CodeBlock* baselineBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, ExitFlag didExit)
 44  {
 45      ConcurrentJSLocker locker(baselineBlock->m_lock);
 46  
 47      DeleteByStatus result;
 48  
 49  #if ENABLE(DFG_JIT)
 50      result = computeForStubInfoWithoutExitSiteFeedback(
 51          locker, baselineBlock, map.get(CodeOrigin(bytecodeIndex)).stubInfo);
 52  
 53      if (didExit)
 54          return result.slowVersion();
 55  #else
 56      UNUSED_PARAM(map);
 57      UNUSED_PARAM(didExit);
 58      UNUSED_PARAM(bytecodeIndex);
 59  #endif
 60  
 61      return result;
 62  }
 63  
 64  #if ENABLE(JIT)
 65  DeleteByStatus::DeleteByStatus(StubInfoSummary summary, StructureStubInfo& stubInfo)
 66  {
 67      switch (summary) {
 68      case StubInfoSummary::NoInformation:
 69          m_state = NoInformation;
 70          return;
 71      case StubInfoSummary::Simple:
 72      case StubInfoSummary::MakesCalls:
 73      case StubInfoSummary::TakesSlowPathAndMakesCalls:
 74          RELEASE_ASSERT_NOT_REACHED();
 75          return;
 76      case StubInfoSummary::TakesSlowPath:
 77          m_state = stubInfo.tookSlowPath ? ObservedTakesSlowPath : LikelyTakesSlowPath;
 78          return;
 79      }
 80      RELEASE_ASSERT_NOT_REACHED();
 81  }
 82  
 83  DeleteByStatus DeleteByStatus::computeForStubInfoWithoutExitSiteFeedback(
 84      const ConcurrentJSLocker&, CodeBlock* block, StructureStubInfo* stubInfo)
 85  {
 86      StubInfoSummary summary = StructureStubInfo::summary(block->vm(), stubInfo);
 87      if (!isInlineable(summary))
 88          return DeleteByStatus(summary, *stubInfo);
 89  
 90      DeleteByStatus result;
 91      result.m_state = Simple;
 92      switch (stubInfo->cacheType()) {
 93      case CacheType::Unset:
 94          return DeleteByStatus(NoInformation);
 95  
 96      case CacheType::Stub: {
 97          PolymorphicAccess* list = stubInfo->u.stub;
 98  
 99          for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
100              const AccessCase& access = list->at(listIndex);
101              ASSERT(!access.viaProxy());
102  
103              Structure* structure = access.structure();
104              ASSERT(structure);
105  
106              switch (access.type()) {
107              case AccessCase::DeleteMiss:
108              case AccessCase::DeleteNonConfigurable: {
109                  DeleteByIdVariant variant(access.identifier(), access.type() == AccessCase::DeleteMiss ? true : false, structure, nullptr, invalidOffset);
110                  if (!result.appendVariant(variant))
111                      return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
112                  break;
113              }
114              case AccessCase::Delete: {
115                  PropertyOffset offset;
116                  Structure* newStructure = Structure::removePropertyTransitionFromExistingStructureConcurrently(structure, access.identifier().uid(), offset);
117                  if (!newStructure)
118                      return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
119                  ASSERT_UNUSED(offset, offset == access.offset());                
120                  DeleteByIdVariant variant(access.identifier(), true, structure, newStructure, access.offset());
121  
122                  if (!result.appendVariant(variant))
123                      return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
124                  break;
125              }
126              default:
127                  ASSERT_NOT_REACHED();
128                  return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
129              }
130          }
131  
132          return result;
133      }
134  
135      default:
136          return DeleteByStatus(JSC::slowVersion(summary), *stubInfo);
137      }
138  
139      RELEASE_ASSERT_NOT_REACHED();
140      return DeleteByStatus();
141  }
142  
143  DeleteByStatus DeleteByStatus::computeFor(
144      CodeBlock* baselineBlock, ICStatusMap& baselineMap,
145      ICStatusContextStack& contextStack, CodeOrigin codeOrigin)
146  {
147      BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex();
148      ExitFlag didExit = hasBadCacheExitSite(baselineBlock, bytecodeIndex);
149  
150      for (ICStatusContext* context : contextStack) {
151          ICStatus status = context->get(codeOrigin);
152  
153          auto bless = [&] (const DeleteByStatus& result) -> DeleteByStatus {
154              if (!context->isInlined(codeOrigin)) {
155                  DeleteByStatus baselineResult = computeForBaseline(
156                      baselineBlock, baselineMap, bytecodeIndex, didExit);
157                  baselineResult.merge(result);
158                  return baselineResult;
159              }
160              if (didExit.isSet(ExitFromInlined))
161                  return result.slowVersion();
162              return result;
163          };
164  
165          if (status.stubInfo) {
166              DeleteByStatus result;
167              {
168                  ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
169                  result = computeForStubInfoWithoutExitSiteFeedback(
170                      locker, context->optimizedCodeBlock, status.stubInfo);
171              }
172              if (result.isSet())
173                  return bless(result);
174          }
175  
176          if (status.deleteStatus)
177              return bless(*status.deleteStatus);
178      }
179  
180      return computeForBaseline(baselineBlock, baselineMap, bytecodeIndex, didExit);
181  }
182  
183  #endif // ENABLE(JIT)
184  
185  DeleteByStatus DeleteByStatus::slowVersion() const
186  {
187      if (observedSlowPath())
188          return DeleteByStatus(ObservedTakesSlowPath);
189      return DeleteByStatus(LikelyTakesSlowPath);
190  }
191  
192  void DeleteByStatus::merge(const DeleteByStatus& other)
193  {
194      if (other.m_state == NoInformation)
195          return;
196  
197      auto mergeSlow = [&] () {
198          if (observedSlowPath() || other.observedSlowPath())
199              *this = DeleteByStatus(ObservedTakesSlowPath);
200          else
201              *this = DeleteByStatus(LikelyTakesSlowPath);
202      };
203  
204      switch (m_state) {
205      case NoInformation:
206          *this = other;
207          return;
208  
209      case Simple:
210          if (m_state != other.m_state)
211              return mergeSlow();
212  
213          for (auto& otherVariant : other.m_variants) {
214              if (!appendVariant(otherVariant))
215                  return mergeSlow();
216          }
217          return;
218  
219      case LikelyTakesSlowPath:
220      case ObservedTakesSlowPath:
221          return mergeSlow();
222      }
223  
224      RELEASE_ASSERT_NOT_REACHED();
225  }
226  
227  void DeleteByStatus::filter(const StructureSet& set)
228  {
229      if (m_state != Simple)
230          return;
231      m_variants.removeAllMatching(
232          [&] (auto& variant) -> bool {
233              return !set.contains(variant.oldStructure());
234          });
235      if (m_variants.isEmpty())
236          m_state = NoInformation;
237  }
238  
239  CacheableIdentifier DeleteByStatus::singleIdentifier() const
240  {
241      if (m_variants.isEmpty())
242          return nullptr;
243  
244      CacheableIdentifier result = m_variants.first().identifier();
245      if (!result)
246          return nullptr;
247      for (size_t i = 1; i < m_variants.size(); ++i) {
248          CacheableIdentifier identifier = m_variants[i].identifier();
249          if (!identifier)
250              return nullptr;
251          if (identifier != result)
252              return nullptr;
253      }
254      return result;
255  }
256  
257  void DeleteByStatus::visitAggregate(SlotVisitor& visitor)
258  {
259      for (DeleteByIdVariant& variant : m_variants)
260          variant.visitAggregate(visitor);
261  }
262  
263  void DeleteByStatus::markIfCheap(SlotVisitor& visitor)
264  {
265      for (DeleteByIdVariant& variant : m_variants)
266          variant.markIfCheap(visitor);
267  }
268  
269  bool DeleteByStatus::finalize(VM& vm)
270  {
271      for (auto& variant : m_variants) {
272          if (!variant.finalize(vm))
273              return false;
274      }
275      return true;
276  }
277  
278  void DeleteByStatus::dump(PrintStream& out) const
279  {
280      out.print("(");
281      switch (m_state) {
282      case NoInformation:
283          out.print("NoInformation");
284          break;
285      case Simple:
286          out.print("Simple");
287          break;
288      case LikelyTakesSlowPath:
289          out.print("LikelyTakesSlowPath");
290          break;
291      case ObservedTakesSlowPath:
292          out.print("ObservedTakesSlowPath");
293          break;
294      }
295      out.print(", ", listDump(m_variants), ")");
296  }
297  
298  } // namespace JSC