/ bytecode / CallLinkInfo.cpp
CallLinkInfo.cpp
  1  /*
  2   * Copyright (C) 2012-2018 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 "CallLinkInfo.h"
 28  
 29  #include "CallFrameShuffleData.h"
 30  #include "FunctionCodeBlock.h"
 31  #include "JSCellInlines.h"
 32  #include "Opcode.h"
 33  #include "Repatch.h"
 34  #include <wtf/ListDump.h>
 35  
 36  #if ENABLE(JIT)
 37  namespace JSC {
 38  
 39  CallLinkInfo::CallType CallLinkInfo::callTypeFor(OpcodeID opcodeID)
 40  {
 41      switch (opcodeID) {
 42      case op_tail_call_varargs:
 43      case op_tail_call_forward_arguments:
 44          return TailCallVarargs;        
 45  
 46      case op_call:
 47      case op_call_eval:
 48      case op_iterator_open:
 49      case op_iterator_next:
 50          return Call;
 51  
 52      case op_call_varargs:
 53          return CallVarargs;
 54  
 55      case op_construct:
 56          return Construct;
 57  
 58      case op_construct_varargs:
 59          return ConstructVarargs;
 60  
 61      case op_tail_call:
 62          return TailCall;
 63  
 64      default:
 65          break;
 66      }
 67      RELEASE_ASSERT_NOT_REACHED();
 68      return Call;
 69  }
 70  
 71  CallLinkInfo::CallLinkInfo(CodeOrigin codeOrigin)
 72      : m_codeOrigin(codeOrigin)
 73      , m_hasSeenShouldRepatch(false)
 74      , m_hasSeenClosure(false)
 75      , m_clearedByGC(false)
 76      , m_clearedByVirtual(false)
 77      , m_allowStubs(true)
 78      , m_clearedByJettison(false)
 79      , m_callType(None)
 80  {
 81  }
 82  
 83  CallLinkInfo::~CallLinkInfo()
 84  {
 85      clearStub();
 86      
 87      if (isOnList())
 88          remove();
 89  }
 90  
 91  void CallLinkInfo::clearStub()
 92  {
 93      if (!stub())
 94          return;
 95  
 96      m_stub->clearCallNodesFor(this);
 97      m_stub = nullptr;
 98  }
 99  
100  void CallLinkInfo::unlink(VM& vm)
101  {
102      // We could be called even if we're not linked anymore because of how polymorphic calls
103      // work. Each callsite within the polymorphic call stub may separately ask us to unlink().
104      if (isLinked())
105          unlinkFor(vm, *this);
106  
107      // Either we were unlinked, in which case we should not have been on any list, or we unlinked
108      // ourselves so that we're not on any list anymore.
109      RELEASE_ASSERT(!isOnList());
110  }
111  
112  CodeLocationNearCall<JSInternalPtrTag> CallLinkInfo::callReturnLocation()
113  {
114      RELEASE_ASSERT(!isDirect());
115      return CodeLocationNearCall<JSInternalPtrTag>(m_callReturnLocationOrPatchableJump, NearCallMode::Regular);
116  }
117  
118  CodeLocationJump<JSInternalPtrTag> CallLinkInfo::patchableJump()
119  {
120      RELEASE_ASSERT(callType() == DirectTailCall);
121      return CodeLocationJump<JSInternalPtrTag>(m_callReturnLocationOrPatchableJump);
122  }
123  
124  CodeLocationDataLabelPtr<JSInternalPtrTag> CallLinkInfo::hotPathBegin()
125  {
126      RELEASE_ASSERT(!isDirect());
127      return CodeLocationDataLabelPtr<JSInternalPtrTag>(m_hotPathBeginOrSlowPathStart);
128  }
129  
130  CodeLocationLabel<JSInternalPtrTag> CallLinkInfo::slowPathStart()
131  {
132      RELEASE_ASSERT(isDirect());
133      return m_hotPathBeginOrSlowPathStart;
134  }
135  
136  void CallLinkInfo::setCallee(VM& vm, JSCell* owner, JSObject* callee)
137  {
138      RELEASE_ASSERT(!isDirect());
139      m_calleeOrCodeBlock.set(vm, owner, callee);
140  }
141  
142  void CallLinkInfo::clearCallee()
143  {
144      RELEASE_ASSERT(!isDirect());
145      m_calleeOrCodeBlock.clear();
146  }
147  
148  JSObject* CallLinkInfo::callee()
149  {
150      RELEASE_ASSERT(!isDirect());
151      return jsCast<JSObject*>(m_calleeOrCodeBlock.get());
152  }
153  
154  void CallLinkInfo::setCodeBlock(VM& vm, JSCell* owner, FunctionCodeBlock* codeBlock)
155  {
156      RELEASE_ASSERT(isDirect());
157      m_calleeOrCodeBlock.setMayBeNull(vm, owner, codeBlock);
158  }
159  
160  void CallLinkInfo::clearCodeBlock()
161  {
162      RELEASE_ASSERT(isDirect());
163      m_calleeOrCodeBlock.clear();
164  }
165  
166  FunctionCodeBlock* CallLinkInfo::codeBlock()
167  {
168      RELEASE_ASSERT(isDirect());
169      return jsCast<FunctionCodeBlock*>(m_calleeOrCodeBlock.get());
170  }
171  
172  void CallLinkInfo::setLastSeenCallee(VM& vm, const JSCell* owner, JSObject* callee)
173  {
174      RELEASE_ASSERT(!isDirect());
175      m_lastSeenCalleeOrExecutable.set(vm, owner, callee);
176  }
177  
178  void CallLinkInfo::clearLastSeenCallee()
179  {
180      RELEASE_ASSERT(!isDirect());
181      m_lastSeenCalleeOrExecutable.clear();
182  }
183  
184  JSObject* CallLinkInfo::lastSeenCallee() const
185  {
186      RELEASE_ASSERT(!isDirect());
187      return jsCast<JSObject*>(m_lastSeenCalleeOrExecutable.get());
188  }
189  
190  bool CallLinkInfo::haveLastSeenCallee() const
191  {
192      RELEASE_ASSERT(!isDirect());
193      return !!m_lastSeenCalleeOrExecutable;
194  }
195  
196  void CallLinkInfo::setExecutableDuringCompilation(ExecutableBase* executable)
197  {
198      RELEASE_ASSERT(isDirect());
199      m_lastSeenCalleeOrExecutable.setWithoutWriteBarrier(executable);
200  }
201  
202  ExecutableBase* CallLinkInfo::executable()
203  {
204      RELEASE_ASSERT(isDirect());
205      return jsCast<ExecutableBase*>(m_lastSeenCalleeOrExecutable.get());
206  }
207  
208  void CallLinkInfo::setMaxArgumentCountIncludingThis(unsigned value)
209  {
210      RELEASE_ASSERT(isDirect());
211      RELEASE_ASSERT(value);
212      m_maxArgumentCountIncludingThis = value;
213  }
214  
215  void CallLinkInfo::visitWeak(VM& vm)
216  {
217      auto handleSpecificCallee = [&] (JSFunction* callee) {
218          if (vm.heap.isMarked(callee->executable()))
219              m_hasSeenClosure = true;
220          else
221              m_clearedByGC = true;
222      };
223      
224      if (isLinked()) {
225          if (stub()) {
226              if (!stub()->visitWeak(vm)) {
227                  if (UNLIKELY(Options::verboseOSR())) {
228                      dataLog(
229                          "At ", m_codeOrigin, ", ", RawPointer(this), ": clearing call stub to ",
230                          listDump(stub()->variants()), ", stub routine ", RawPointer(stub()),
231                          ".\n");
232                  }
233                  unlink(vm);
234                  m_clearedByGC = true;
235              }
236          } else if (!vm.heap.isMarked(m_calleeOrCodeBlock.get())) {
237              if (isDirect()) {
238                  if (UNLIKELY(Options::verboseOSR())) {
239                      dataLog(
240                          "Clearing call to ", RawPointer(codeBlock()), " (",
241                          pointerDump(codeBlock()), ").\n");
242                  }
243              } else {
244                  if (callee()->type() == JSFunctionType) {
245                      if (UNLIKELY(Options::verboseOSR())) {
246                          dataLog(
247                              "Clearing call to ",
248                              RawPointer(callee()), " (",
249                              static_cast<JSFunction*>(callee())->executable()->hashFor(specializationKind()),
250                              ").\n");
251                      }
252                      handleSpecificCallee(static_cast<JSFunction*>(callee()));
253                  } else {
254                      if (UNLIKELY(Options::verboseOSR()))
255                          dataLog("Clearing call to ", RawPointer(callee()), ".\n");
256                      m_clearedByGC = true;
257                  }
258              }
259              unlink(vm);
260          } else if (isDirect() && !vm.heap.isMarked(m_lastSeenCalleeOrExecutable.get())) {
261              if (UNLIKELY(Options::verboseOSR())) {
262                  dataLog(
263                      "Clearing call to ", RawPointer(executable()),
264                      " because the executable is dead.\n");
265              }
266              unlink(vm);
267              // We should only get here once the owning CodeBlock is dying, since the executable must
268              // already be in the owner's weak references.
269              m_lastSeenCalleeOrExecutable.clear();
270          }
271      }
272      if (!isDirect() && haveLastSeenCallee() && !vm.heap.isMarked(lastSeenCallee())) {
273          if (lastSeenCallee()->type() == JSFunctionType)
274              handleSpecificCallee(jsCast<JSFunction*>(lastSeenCallee()));
275          else
276              m_clearedByGC = true;
277          clearLastSeenCallee();
278      }
279  }
280  
281  void CallLinkInfo::setFrameShuffleData(const CallFrameShuffleData& shuffleData)
282  {
283      m_frameShuffleData = makeUnique<CallFrameShuffleData>(shuffleData);
284  }
285  
286  } // namespace JSC
287  #endif // ENABLE(JIT)
288