/ bytecode / InByIdStatus.cpp
InByIdStatus.cpp
  1  /*
  2   * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>.
  3   * Copyright (C) 2018 Apple Inc. All rights reserved.
  4   *
  5   * Redistribution and use in source and binary forms, with or without
  6   * modification, are permitted provided that the following conditions
  7   * are met:
  8   * 1. Redistributions of source code must retain the above copyright
  9   *    notice, this list of conditions and the following disclaimer.
 10   * 2. Redistributions in binary form must reproduce the above copyright
 11   *    notice, this list of conditions and the following disclaimer in the
 12   *    documentation and/or other materials provided with the distribution.
 13   *
 14   * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 15   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 16   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 17   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 18   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 19   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 20   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 21   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 22   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 23   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 24   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 25   */
 26  
 27  #include "config.h"
 28  #include "InByIdStatus.h"
 29  
 30  #include "CodeBlock.h"
 31  #include "ComplexGetStatus.h"
 32  #include "ICStatusUtils.h"
 33  #include "PolymorphicAccess.h"
 34  #include "StructureStubInfo.h"
 35  #include <wtf/ListDump.h>
 36  
 37  #ifdef DARLING
 38  #include "JSCellInlines.h"
 39  #endif
 40  
 41  namespace JSC {
 42  
 43  bool InByIdStatus::appendVariant(const InByIdVariant& variant)
 44  {
 45      return appendICStatusVariant(m_variants, variant);
 46  }
 47  
 48  #if ENABLE(JIT)
 49  InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, UniquedStringImpl* uid, ExitFlag didExit)
 50  {
 51      ConcurrentJSLocker locker(profiledBlock->m_lock);
 52  
 53      InByIdStatus result;
 54  
 55  #if ENABLE(DFG_JIT)
 56      result = computeForStubInfoWithoutExitSiteFeedback(locker, profiledBlock->vm(), map.get(CodeOrigin(bytecodeIndex)).stubInfo, uid);
 57  
 58      if (!result.takesSlowPath() && didExit)
 59          return InByIdStatus(TakesSlowPath);
 60  #else
 61      UNUSED_PARAM(map);
 62      UNUSED_PARAM(bytecodeIndex);
 63      UNUSED_PARAM(uid);
 64      UNUSED_PARAM(didExit);
 65  #endif
 66  
 67      return result;
 68  }
 69  
 70  InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, UniquedStringImpl* uid)
 71  {
 72      return computeFor(profiledBlock, map, bytecodeIndex, uid, hasBadCacheExitSite(profiledBlock, bytecodeIndex));
 73  }
 74  
 75  InByIdStatus InByIdStatus::computeFor(
 76      CodeBlock* profiledBlock, ICStatusMap& baselineMap,
 77      ICStatusContextStack& contextStack, CodeOrigin codeOrigin, UniquedStringImpl* uid)
 78  {
 79      BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex();
 80      ExitFlag didExit = hasBadCacheExitSite(profiledBlock, bytecodeIndex);
 81      
 82      for (ICStatusContext* context : contextStack) {
 83          ICStatus status = context->get(codeOrigin);
 84          
 85          auto bless = [&] (const InByIdStatus& result) -> InByIdStatus {
 86              if (!context->isInlined(codeOrigin)) {
 87                  InByIdStatus baselineResult = computeFor(
 88                      profiledBlock, baselineMap, bytecodeIndex, uid, didExit);
 89                  baselineResult.merge(result);
 90                  return baselineResult;
 91              }
 92              if (didExit.isSet(ExitFromInlined))
 93                  return InByIdStatus(TakesSlowPath);
 94              return result;
 95          };
 96          
 97  #if ENABLE(DFG_JIT)
 98          if (status.stubInfo) {
 99              InByIdStatus result;
100              {
101                  ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock);
102                  result = computeForStubInfoWithoutExitSiteFeedback(locker, profiledBlock->vm(), status.stubInfo, uid);
103              }
104              if (result.isSet())
105                  return bless(result);
106          }
107  #endif
108          
109          if (status.inStatus)
110              return bless(*status.inStatus);
111      }
112      
113      return computeFor(profiledBlock, baselineMap, bytecodeIndex, uid, didExit);
114  }
115  #endif // ENABLE(JIT)
116  
117  #if ENABLE(DFG_JIT)
118  InByIdStatus InByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid)
119  {
120      InByIdStatus result = InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(locker, profiledBlock->vm(), stubInfo, uid);
121  
122      if (!result.takesSlowPath() && hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex()))
123          return InByIdStatus(TakesSlowPath);
124      return result;
125  }
126  
127  InByIdStatus InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(const ConcurrentJSLocker&, VM& vm, StructureStubInfo* stubInfo, UniquedStringImpl* uid)
128  {
129      StubInfoSummary summary = StructureStubInfo::summary(vm, stubInfo);
130      if (!isInlineable(summary))
131          return InByIdStatus(summary);
132      
133      // Finally figure out if we can derive an access strategy.
134      InByIdStatus result;
135      result.m_state = Simple;
136      switch (stubInfo->cacheType()) {
137      case CacheType::Unset:
138          return InByIdStatus(NoInformation);
139  
140      case CacheType::InByIdSelf: {
141          Structure* structure = stubInfo->u.byIdSelf.baseObjectStructure.get();
142          if (structure->takesSlowPathInDFGForImpureProperty())
143              return InByIdStatus(TakesSlowPath);
144          unsigned attributes;
145          InByIdVariant variant;
146          variant.m_offset = structure->getConcurrently(uid, attributes);
147          if (!isValidOffset(variant.m_offset))
148              return InByIdStatus(TakesSlowPath);
149          if (attributes & PropertyAttribute::CustomAccessorOrValue)
150              return InByIdStatus(TakesSlowPath);
151  
152          variant.m_structureSet.add(structure);
153          bool didAppend = result.appendVariant(variant);
154          ASSERT_UNUSED(didAppend, didAppend);
155          return result;
156      }
157  
158      case CacheType::Stub: {
159          PolymorphicAccess* list = stubInfo->u.stub;
160          for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
161              const AccessCase& access = list->at(listIndex);
162              if (access.viaProxy())
163                  return InByIdStatus(TakesSlowPath);
164  
165              if (access.usesPolyProto())
166                  return InByIdStatus(TakesSlowPath);
167  
168              Structure* structure = access.structure();
169              if (!structure) {
170                  // The null structure cases arise due to array.length. We have no way of creating a
171                  // InByIdVariant for those, and we don't really have to since the DFG handles those
172                  // cases in FixupPhase using value profiling. That's a bit awkward - we shouldn't
173                  // have to use value profiling to discover something that the AccessCase could have
174                  // told us. But, it works well enough. So, our only concern here is to not
175                  // crash on null structure.
176                  return InByIdStatus(TakesSlowPath);
177              }
178  
179              ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(structure, access.conditionSet(), uid);
180              switch (complexGetStatus.kind()) {
181              case ComplexGetStatus::ShouldSkip:
182                  continue;
183  
184              case ComplexGetStatus::TakesSlowPath:
185                  return InByIdStatus(TakesSlowPath);
186  
187              case ComplexGetStatus::Inlineable: {
188                  switch (access.type()) {
189                  case AccessCase::InHit:
190                  case AccessCase::InMiss:
191                      break;
192                  default:
193                      return InByIdStatus(TakesSlowPath);
194                  }
195  
196                  InByIdVariant variant(
197                      StructureSet(structure), complexGetStatus.offset(),
198                      complexGetStatus.conditionSet());
199  
200                  if (!result.appendVariant(variant))
201                      return InByIdStatus(TakesSlowPath);
202                  break;
203              }
204              }
205          }
206  
207          return result;
208      }
209  
210      default:
211          return InByIdStatus(TakesSlowPath);
212      }
213  
214      RELEASE_ASSERT_NOT_REACHED();
215      return InByIdStatus();
216  }
217  #endif
218  
219  void InByIdStatus::merge(const InByIdStatus& other)
220  {
221      if (other.m_state == NoInformation)
222          return;
223      
224      switch (m_state) {
225      case NoInformation:
226          *this = other;
227          return;
228          
229      case Simple:
230          if (other.m_state != Simple) {
231              *this = InByIdStatus(TakesSlowPath);
232              return;
233          }
234          for (const InByIdVariant& otherVariant : other.m_variants) {
235              if (!appendVariant(otherVariant)) {
236                  *this = InByIdStatus(TakesSlowPath);
237                  return;
238              }
239          }
240          return;
241          
242      case TakesSlowPath:
243          return;
244      }
245      
246      RELEASE_ASSERT_NOT_REACHED();
247  }
248  
249  void InByIdStatus::filter(const StructureSet& structureSet)
250  {
251      if (m_state != Simple)
252          return;
253      filterICStatusVariants(m_variants, structureSet);
254      if (m_variants.isEmpty())
255          m_state = NoInformation;
256  }
257  
258  void InByIdStatus::markIfCheap(SlotVisitor& visitor)
259  {
260      for (InByIdVariant& variant : m_variants)
261          variant.markIfCheap(visitor);
262  }
263  
264  bool InByIdStatus::finalize(VM& vm)
265  {
266      for (InByIdVariant& variant : m_variants) {
267          if (!variant.finalize(vm))
268              return false;
269      }
270      return true;
271  }
272  
273  void InByIdStatus::dump(PrintStream& out) const
274  {
275      out.print("(");
276      switch (m_state) {
277      case NoInformation:
278          out.print("NoInformation");
279          break;
280      case Simple:
281          out.print("Simple");
282          break;
283      case TakesSlowPath:
284          out.print("TakesSlowPath");
285          break;
286      }
287      out.print(", ", listDump(m_variants), ")");
288  }
289  
290  } // namespace JSC
291