1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/runtime/runtime-utils.h"
6
7#include "src/arguments.h"
8#include "src/compiler.h"
9#include "src/deoptimizer.h"
10#include "src/frames-inl.h"
11#include "src/full-codegen/full-codegen.h"
12#include "src/isolate-inl.h"
13#include "src/messages.h"
14#include "src/v8threads.h"
15#include "src/vm-state-inl.h"
16
17namespace v8 {
18namespace internal {
19
20RUNTIME_FUNCTION(Runtime_CompileLazy) {
21  HandleScope scope(isolate);
22  DCHECK_EQ(1, args.length());
23  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
24
25#ifdef DEBUG
26  if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
27    PrintF("[unoptimized: ");
28    function->PrintName();
29    PrintF("]\n");
30  }
31#endif
32
33  StackLimitCheck check(isolate);
34  if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
35  if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) {
36    return isolate->heap()->exception();
37  }
38  DCHECK(function->is_compiled());
39  return function->code();
40}
41
42RUNTIME_FUNCTION(Runtime_CompileBaseline) {
43  HandleScope scope(isolate);
44  DCHECK_EQ(1, args.length());
45  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
46  StackLimitCheck check(isolate);
47  if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
48  if (!Compiler::CompileBaseline(function)) {
49    return isolate->heap()->exception();
50  }
51  DCHECK(function->is_compiled());
52  return function->code();
53}
54
55RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent) {
56  HandleScope scope(isolate);
57  DCHECK_EQ(1, args.length());
58  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
59  StackLimitCheck check(isolate);
60  if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
61  if (!Compiler::CompileOptimized(function, Compiler::CONCURRENT)) {
62    return isolate->heap()->exception();
63  }
64  DCHECK(function->is_compiled());
65  return function->code();
66}
67
68
69RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent) {
70  HandleScope scope(isolate);
71  DCHECK_EQ(1, args.length());
72  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
73  StackLimitCheck check(isolate);
74  if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
75  if (!Compiler::CompileOptimized(function, Compiler::NOT_CONCURRENT)) {
76    return isolate->heap()->exception();
77  }
78  DCHECK(function->is_compiled());
79  return function->code();
80}
81
82
83RUNTIME_FUNCTION(Runtime_NotifyStubFailure) {
84  HandleScope scope(isolate);
85  DCHECK(args.length() == 0);
86  Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
87  DCHECK(AllowHeapAllocation::IsAllowed());
88  delete deoptimizer;
89  return isolate->heap()->undefined_value();
90}
91
92
93class ActivationsFinder : public ThreadVisitor {
94 public:
95  Code* code_;
96  bool has_code_activations_;
97
98  explicit ActivationsFinder(Code* code)
99      : code_(code), has_code_activations_(false) {}
100
101  void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
102    JavaScriptFrameIterator it(isolate, top);
103    VisitFrames(&it);
104  }
105
106  void VisitFrames(JavaScriptFrameIterator* it) {
107    for (; !it->done(); it->Advance()) {
108      JavaScriptFrame* frame = it->frame();
109      if (code_->contains(frame->pc())) has_code_activations_ = true;
110    }
111  }
112};
113
114
115RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
116  HandleScope scope(isolate);
117  DCHECK(args.length() == 1);
118  CONVERT_SMI_ARG_CHECKED(type_arg, 0);
119  Deoptimizer::BailoutType type =
120      static_cast<Deoptimizer::BailoutType>(type_arg);
121  Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
122  DCHECK(AllowHeapAllocation::IsAllowed());
123  TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
124  TRACE_EVENT0("v8", "V8.DeoptimizeCode");
125
126  Handle<JSFunction> function = deoptimizer->function();
127  Handle<Code> optimized_code = deoptimizer->compiled_code();
128
129  DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION);
130  DCHECK(type == deoptimizer->bailout_type());
131
132  // Make sure to materialize objects before causing any allocation.
133  JavaScriptFrameIterator it(isolate);
134  deoptimizer->MaterializeHeapObjects(&it);
135  delete deoptimizer;
136
137  // Ensure the context register is updated for materialized objects.
138  JavaScriptFrameIterator top_it(isolate);
139  JavaScriptFrame* top_frame = top_it.frame();
140  isolate->set_context(Context::cast(top_frame->context()));
141
142  if (type == Deoptimizer::LAZY) {
143    return isolate->heap()->undefined_value();
144  }
145
146  // Search for other activations of the same optimized code.
147  // At this point {it} is at the topmost frame of all the frames materialized
148  // by the deoptimizer. Note that this frame does not necessarily represent
149  // an activation of {function} because of potential inlined tail-calls.
150  ActivationsFinder activations_finder(*optimized_code);
151  activations_finder.VisitFrames(&it);
152  isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
153
154  if (!activations_finder.has_code_activations_) {
155    if (function->code() == *optimized_code) {
156      if (FLAG_trace_deopt) {
157        PrintF("[removing optimized code for: ");
158        function->PrintName();
159        PrintF("]\n");
160      }
161      function->ReplaceCode(function->shared()->code());
162    }
163    // Evict optimized code for this function from the cache so that it
164    // doesn't get used for new closures.
165    function->shared()->EvictFromOptimizedCodeMap(*optimized_code,
166                                                  "notify deoptimized");
167  } else {
168    // TODO(titzer): we should probably do DeoptimizeCodeList(code)
169    // unconditionally if the code is not already marked for deoptimization.
170    // If there is an index by shared function info, all the better.
171    Deoptimizer::DeoptimizeFunction(*function);
172  }
173
174  return isolate->heap()->undefined_value();
175}
176
177
178static bool IsSuitableForOnStackReplacement(Isolate* isolate,
179                                            Handle<JSFunction> function) {
180  // Keep track of whether we've succeeded in optimizing.
181  if (function->shared()->optimization_disabled()) return false;
182  // If we are trying to do OSR when there are already optimized
183  // activations of the function, it means (a) the function is directly or
184  // indirectly recursive and (b) an optimized invocation has been
185  // deoptimized so that we are currently in an unoptimized activation.
186  // Check for optimized activations of this function.
187  for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
188    JavaScriptFrame* frame = it.frame();
189    if (frame->is_optimized() && frame->function() == *function) return false;
190  }
191
192  return true;
193}
194
195
196RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
197  HandleScope scope(isolate);
198  DCHECK(args.length() == 1);
199  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
200  Handle<Code> caller_code(function->shared()->code());
201
202  // We're not prepared to handle a function with arguments object.
203  DCHECK(!function->shared()->uses_arguments());
204
205  CHECK(FLAG_use_osr);
206
207  // Passing the PC in the javascript frame from the caller directly is
208  // not GC safe, so we walk the stack to get it.
209  JavaScriptFrameIterator it(isolate);
210  JavaScriptFrame* frame = it.frame();
211  if (!caller_code->contains(frame->pc())) {
212    // Code on the stack may not be the code object referenced by the shared
213    // function info.  It may have been replaced to include deoptimization data.
214    caller_code = Handle<Code>(frame->LookupCode());
215  }
216
217  uint32_t pc_offset =
218      static_cast<uint32_t>(frame->pc() - caller_code->instruction_start());
219
220#ifdef DEBUG
221  DCHECK_EQ(frame->function(), *function);
222  DCHECK_EQ(frame->LookupCode(), *caller_code);
223  DCHECK(caller_code->contains(frame->pc()));
224#endif  // DEBUG
225
226  BailoutId ast_id = caller_code->TranslatePcOffsetToAstId(pc_offset);
227  DCHECK(!ast_id.IsNone());
228
229  MaybeHandle<Code> maybe_result;
230  if (IsSuitableForOnStackReplacement(isolate, function)) {
231    if (FLAG_trace_osr) {
232      PrintF("[OSR - Compiling: ");
233      function->PrintName();
234      PrintF(" at AST id %d]\n", ast_id.ToInt());
235    }
236    maybe_result = Compiler::GetOptimizedCodeForOSR(function, ast_id, frame);
237  }
238
239  // Revert the patched back edge table, regardless of whether OSR succeeds.
240  BackEdgeTable::Revert(isolate, *caller_code);
241
242  // Check whether we ended up with usable optimized code.
243  Handle<Code> result;
244  if (maybe_result.ToHandle(&result) &&
245      result->kind() == Code::OPTIMIZED_FUNCTION) {
246    DeoptimizationInputData* data =
247        DeoptimizationInputData::cast(result->deoptimization_data());
248
249    if (data->OsrPcOffset()->value() >= 0) {
250      DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id);
251      if (FLAG_trace_osr) {
252        PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
253               ast_id.ToInt(), data->OsrPcOffset()->value());
254      }
255      // TODO(titzer): this is a massive hack to make the deopt counts
256      // match. Fix heuristics for reenabling optimizations!
257      function->shared()->increment_deopt_count();
258
259      if (result->is_turbofanned()) {
260        // TurboFanned OSR code cannot be installed into the function.
261        // But the function is obviously hot, so optimize it next time.
262        function->ReplaceCode(
263            isolate->builtins()->builtin(Builtins::kCompileOptimized));
264      } else {
265        // Crankshafted OSR code can be installed into the function.
266        function->ReplaceCode(*result);
267      }
268      return *result;
269    }
270  }
271
272  // Failed.
273  if (FLAG_trace_osr) {
274    PrintF("[OSR - Failed: ");
275    function->PrintName();
276    PrintF(" at AST id %d]\n", ast_id.ToInt());
277  }
278
279  if (!function->IsOptimized()) {
280    function->ReplaceCode(function->shared()->code());
281  }
282  return NULL;
283}
284
285
286RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) {
287  HandleScope scope(isolate);
288  DCHECK(args.length() == 1);
289  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
290
291  // First check if this is a real stack overflow.
292  StackLimitCheck check(isolate);
293  if (check.JsHasOverflowed()) {
294    SealHandleScope shs(isolate);
295    return isolate->StackOverflow();
296  }
297
298  isolate->optimizing_compile_dispatcher()->InstallOptimizedFunctions();
299  return (function->IsOptimized()) ? function->code()
300                                   : function->shared()->code();
301}
302
303
304bool CodeGenerationFromStringsAllowed(Isolate* isolate,
305                                      Handle<Context> context) {
306  DCHECK(context->allow_code_gen_from_strings()->IsFalse(isolate));
307  // Check with callback if set.
308  AllowCodeGenerationFromStringsCallback callback =
309      isolate->allow_code_gen_callback();
310  if (callback == NULL) {
311    // No callback set and code generation disallowed.
312    return false;
313  } else {
314    // Callback set. Let it decide if code generation is allowed.
315    VMState<EXTERNAL> state(isolate);
316    return callback(v8::Utils::ToLocal(context));
317  }
318}
319
320static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
321                                 Handle<SharedFunctionInfo> outer_info,
322                                 LanguageMode language_mode,
323                                 int eval_scope_position, int eval_position) {
324  Handle<Context> context = Handle<Context>(isolate->context());
325  Handle<Context> native_context = Handle<Context>(context->native_context());
326
327  // Check if native context allows code generation from
328  // strings. Throw an exception if it doesn't.
329  if (native_context->allow_code_gen_from_strings()->IsFalse(isolate) &&
330      !CodeGenerationFromStringsAllowed(isolate, native_context)) {
331    Handle<Object> error_message =
332        native_context->ErrorMessageForCodeGenerationFromStrings();
333    Handle<Object> error;
334    MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
335        MessageTemplate::kCodeGenFromStrings, error_message);
336    if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
337    return isolate->heap()->exception();
338  }
339
340  // Deal with a normal eval call with a string argument. Compile it
341  // and return the compiled function bound in the local context.
342  static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
343  Handle<JSFunction> compiled;
344  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
345      isolate, compiled, Compiler::GetFunctionFromEval(
346                             source, outer_info, context, language_mode,
347                             restriction, eval_scope_position, eval_position),
348      isolate->heap()->exception());
349  return *compiled;
350}
351
352
353RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) {
354  HandleScope scope(isolate);
355  DCHECK(args.length() == 6);
356
357  Handle<Object> callee = args.at<Object>(0);
358
359  // If "eval" didn't refer to the original GlobalEval, it's not a
360  // direct call to eval.
361  // (And even if it is, but the first argument isn't a string, just let
362  // execution default to an indirect call to eval, which will also return
363  // the first argument without doing anything).
364  if (*callee != isolate->native_context()->global_eval_fun() ||
365      !args[1]->IsString()) {
366    return *callee;
367  }
368
369  DCHECK(args[3]->IsSmi());
370  DCHECK(is_valid_language_mode(args.smi_at(3)));
371  LanguageMode language_mode = static_cast<LanguageMode>(args.smi_at(3));
372  DCHECK(args[4]->IsSmi());
373  Handle<SharedFunctionInfo> outer_info(args.at<JSFunction>(2)->shared(),
374                                        isolate);
375  return CompileGlobalEval(isolate, args.at<String>(1), outer_info,
376                           language_mode, args.smi_at(4), args.smi_at(5));
377}
378}  // namespace internal
379}  // namespace v8
380