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/asmjs/asm-js.h"
9#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
10#include "src/compiler.h"
11#include "src/deoptimizer.h"
12#include "src/frames-inl.h"
13#include "src/full-codegen/full-codegen.h"
14#include "src/isolate-inl.h"
15#include "src/messages.h"
16#include "src/v8threads.h"
17#include "src/vm-state-inl.h"
18
19namespace v8 {
20namespace internal {
21
22RUNTIME_FUNCTION(Runtime_CompileLazy) {
23  HandleScope scope(isolate);
24  DCHECK_EQ(1, args.length());
25  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
26
27#ifdef DEBUG
28  if (FLAG_trace_lazy && !function->shared()->is_compiled()) {
29    PrintF("[unoptimized: ");
30    function->PrintName();
31    PrintF("]\n");
32  }
33#endif
34
35  StackLimitCheck check(isolate);
36  if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
37  if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) {
38    return isolate->heap()->exception();
39  }
40  DCHECK(function->is_compiled());
41  return function->code();
42}
43
44RUNTIME_FUNCTION(Runtime_CompileBaseline) {
45  HandleScope scope(isolate);
46  DCHECK_EQ(1, args.length());
47  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
48  StackLimitCheck check(isolate);
49  if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
50  if (!Compiler::CompileBaseline(function)) {
51    return isolate->heap()->exception();
52  }
53  DCHECK(function->is_compiled());
54  return function->code();
55}
56
57RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent) {
58  HandleScope scope(isolate);
59  DCHECK_EQ(1, args.length());
60  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
61  StackLimitCheck check(isolate);
62  if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
63  if (!Compiler::CompileOptimized(function, Compiler::CONCURRENT)) {
64    return isolate->heap()->exception();
65  }
66  DCHECK(function->is_compiled());
67  return function->code();
68}
69
70
71RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent) {
72  HandleScope scope(isolate);
73  DCHECK_EQ(1, args.length());
74  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
75  StackLimitCheck check(isolate);
76  if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow();
77  if (!Compiler::CompileOptimized(function, Compiler::NOT_CONCURRENT)) {
78    return isolate->heap()->exception();
79  }
80  DCHECK(function->is_compiled());
81  return function->code();
82}
83
84RUNTIME_FUNCTION(Runtime_InstantiateAsmJs) {
85  HandleScope scope(isolate);
86  DCHECK_EQ(args.length(), 4);
87  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
88
89  Handle<JSReceiver> stdlib;
90  if (args[1]->IsJSReceiver()) {
91    stdlib = args.at<JSReceiver>(1);
92  }
93  Handle<JSObject> foreign;
94  if (args[2]->IsJSObject()) {
95    foreign = args.at<JSObject>(2);
96  }
97  Handle<JSArrayBuffer> memory;
98  if (args[3]->IsJSArrayBuffer()) {
99    memory = args.at<JSArrayBuffer>(3);
100  }
101  if (function->shared()->HasAsmWasmData() &&
102      AsmJs::IsStdlibValid(isolate, handle(function->shared()->asm_wasm_data()),
103                           stdlib)) {
104    MaybeHandle<Object> result;
105    result = AsmJs::InstantiateAsmWasm(
106        isolate, handle(function->shared()->asm_wasm_data()), memory, foreign);
107    if (!result.is_null()) {
108      return *result.ToHandleChecked();
109    }
110  }
111  // Remove wasm data, mark as broken for asm->wasm,
112  // replace code with CompileLazy, and return a smi 0 to indicate failure.
113  if (function->shared()->HasAsmWasmData()) {
114    function->shared()->ClearAsmWasmData();
115  }
116  function->shared()->set_is_asm_wasm_broken(true);
117  DCHECK(function->code() ==
118         isolate->builtins()->builtin(Builtins::kInstantiateAsmJs));
119  function->ReplaceCode(isolate->builtins()->builtin(Builtins::kCompileLazy));
120  if (function->shared()->code() ==
121      isolate->builtins()->builtin(Builtins::kInstantiateAsmJs)) {
122    function->shared()->ReplaceCode(
123        isolate->builtins()->builtin(Builtins::kCompileLazy));
124  }
125  return Smi::kZero;
126}
127
128RUNTIME_FUNCTION(Runtime_NotifyStubFailure) {
129  HandleScope scope(isolate);
130  DCHECK_EQ(0, args.length());
131  Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
132  DCHECK(AllowHeapAllocation::IsAllowed());
133  delete deoptimizer;
134  return isolate->heap()->undefined_value();
135}
136
137class ActivationsFinder : public ThreadVisitor {
138 public:
139  Code* code_;
140  bool has_code_activations_;
141
142  explicit ActivationsFinder(Code* code)
143      : code_(code), has_code_activations_(false) {}
144
145  void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
146    JavaScriptFrameIterator it(isolate, top);
147    VisitFrames(&it);
148  }
149
150  void VisitFrames(JavaScriptFrameIterator* it) {
151    for (; !it->done(); it->Advance()) {
152      JavaScriptFrame* frame = it->frame();
153      if (code_->contains(frame->pc())) has_code_activations_ = true;
154    }
155  }
156};
157
158
159RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
160  HandleScope scope(isolate);
161  DCHECK_EQ(1, args.length());
162  CONVERT_SMI_ARG_CHECKED(type_arg, 0);
163  Deoptimizer::BailoutType type =
164      static_cast<Deoptimizer::BailoutType>(type_arg);
165  Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
166  DCHECK(AllowHeapAllocation::IsAllowed());
167  TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
168  TRACE_EVENT0("v8", "V8.DeoptimizeCode");
169
170  Handle<JSFunction> function = deoptimizer->function();
171  Handle<Code> optimized_code = deoptimizer->compiled_code();
172
173  DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION);
174  DCHECK(type == deoptimizer->bailout_type());
175  DCHECK_NULL(isolate->context());
176
177  // TODO(turbofan): For Crankshaft we restore the context before objects are
178  // being materialized, because it never de-materializes the context but it
179  // requires a context to materialize arguments objects. This is specific to
180  // Crankshaft and can be removed once only TurboFan goes through here.
181  if (!optimized_code->is_turbofanned()) {
182    JavaScriptFrameIterator top_it(isolate);
183    JavaScriptFrame* top_frame = top_it.frame();
184    isolate->set_context(Context::cast(top_frame->context()));
185  } else {
186    // TODO(turbofan): We currently need the native context to materialize
187    // the arguments object, but only to get to its map.
188    isolate->set_context(function->native_context());
189  }
190
191  // Make sure to materialize objects before causing any allocation.
192  JavaScriptFrameIterator it(isolate);
193  deoptimizer->MaterializeHeapObjects(&it);
194  delete deoptimizer;
195
196  // Ensure the context register is updated for materialized objects.
197  if (optimized_code->is_turbofanned()) {
198    JavaScriptFrameIterator top_it(isolate);
199    JavaScriptFrame* top_frame = top_it.frame();
200    isolate->set_context(Context::cast(top_frame->context()));
201  }
202
203  if (type == Deoptimizer::LAZY) {
204    return isolate->heap()->undefined_value();
205  }
206
207  // Search for other activations of the same optimized code.
208  // At this point {it} is at the topmost frame of all the frames materialized
209  // by the deoptimizer. Note that this frame does not necessarily represent
210  // an activation of {function} because of potential inlined tail-calls.
211  ActivationsFinder activations_finder(*optimized_code);
212  activations_finder.VisitFrames(&it);
213  isolate->thread_manager()->IterateArchivedThreads(&activations_finder);
214
215  if (!activations_finder.has_code_activations_) {
216    if (function->code() == *optimized_code) {
217      if (FLAG_trace_deopt) {
218        PrintF("[removing optimized code for: ");
219        function->PrintName();
220        PrintF("]\n");
221      }
222      function->ReplaceCode(function->shared()->code());
223    }
224    // Evict optimized code for this function from the cache so that it
225    // doesn't get used for new closures.
226    function->shared()->EvictFromOptimizedCodeMap(*optimized_code,
227                                                  "notify deoptimized");
228  } else {
229    // TODO(titzer): we should probably do DeoptimizeCodeList(code)
230    // unconditionally if the code is not already marked for deoptimization.
231    // If there is an index by shared function info, all the better.
232    Deoptimizer::DeoptimizeFunction(*function);
233  }
234
235  return isolate->heap()->undefined_value();
236}
237
238
239static bool IsSuitableForOnStackReplacement(Isolate* isolate,
240                                            Handle<JSFunction> function) {
241  // Keep track of whether we've succeeded in optimizing.
242  if (function->shared()->optimization_disabled()) return false;
243  // If we are trying to do OSR when there are already optimized
244  // activations of the function, it means (a) the function is directly or
245  // indirectly recursive and (b) an optimized invocation has been
246  // deoptimized so that we are currently in an unoptimized activation.
247  // Check for optimized activations of this function.
248  for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
249    JavaScriptFrame* frame = it.frame();
250    if (frame->is_optimized() && frame->function() == *function) return false;
251  }
252
253  return true;
254}
255
256namespace {
257
258BailoutId DetermineEntryAndDisarmOSRForBaseline(JavaScriptFrame* frame) {
259  Handle<Code> caller_code(frame->function()->shared()->code());
260
261  // Passing the PC in the JavaScript frame from the caller directly is
262  // not GC safe, so we walk the stack to get it.
263  if (!caller_code->contains(frame->pc())) {
264    // Code on the stack may not be the code object referenced by the shared
265    // function info.  It may have been replaced to include deoptimization data.
266    caller_code = Handle<Code>(frame->LookupCode());
267  }
268
269  DCHECK_EQ(frame->LookupCode(), *caller_code);
270  DCHECK_EQ(Code::FUNCTION, caller_code->kind());
271  DCHECK(caller_code->contains(frame->pc()));
272
273  // Revert the patched back edge table, regardless of whether OSR succeeds.
274  BackEdgeTable::Revert(frame->isolate(), *caller_code);
275
276  // Return a BailoutId representing an AST id of the {IterationStatement}.
277  uint32_t pc_offset =
278      static_cast<uint32_t>(frame->pc() - caller_code->instruction_start());
279  return caller_code->TranslatePcOffsetToAstId(pc_offset);
280}
281
282BailoutId DetermineEntryAndDisarmOSRForInterpreter(JavaScriptFrame* frame) {
283  InterpretedFrame* iframe = reinterpret_cast<InterpretedFrame*>(frame);
284
285  // Note that the bytecode array active on the stack might be different from
286  // the one installed on the function (e.g. patched by debugger). This however
287  // is fine because we guarantee the layout to be in sync, hence any BailoutId
288  // representing the entry point will be valid for any copy of the bytecode.
289  Handle<BytecodeArray> bytecode(iframe->GetBytecodeArray());
290
291  DCHECK(frame->LookupCode()->is_interpreter_trampoline_builtin());
292  DCHECK(frame->function()->shared()->HasBytecodeArray());
293  DCHECK(frame->is_interpreted());
294  DCHECK(FLAG_ignition_osr);
295
296  // Reset the OSR loop nesting depth to disarm back edges.
297  bytecode->set_osr_loop_nesting_level(0);
298
299  // Return a BailoutId representing the bytecode offset of the back branch.
300  return BailoutId(iframe->GetBytecodeOffset());
301}
302
303}  // namespace
304
305RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
306  HandleScope scope(isolate);
307  DCHECK_EQ(1, args.length());
308  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
309
310  // We're not prepared to handle a function with arguments object.
311  DCHECK(!function->shared()->uses_arguments());
312
313  // Only reachable when OST is enabled.
314  CHECK(FLAG_use_osr);
315
316  // Determine frame triggering OSR request.
317  JavaScriptFrameIterator it(isolate);
318  JavaScriptFrame* frame = it.frame();
319  DCHECK_EQ(frame->function(), *function);
320
321  // Determine the entry point for which this OSR request has been fired and
322  // also disarm all back edges in the calling code to stop new requests.
323  BailoutId ast_id = frame->is_interpreted()
324                         ? DetermineEntryAndDisarmOSRForInterpreter(frame)
325                         : DetermineEntryAndDisarmOSRForBaseline(frame);
326  DCHECK(!ast_id.IsNone());
327
328  MaybeHandle<Code> maybe_result;
329  if (IsSuitableForOnStackReplacement(isolate, function)) {
330    if (FLAG_trace_osr) {
331      PrintF("[OSR - Compiling: ");
332      function->PrintName();
333      PrintF(" at AST id %d]\n", ast_id.ToInt());
334    }
335    maybe_result = Compiler::GetOptimizedCodeForOSR(function, ast_id, frame);
336  }
337
338  // Check whether we ended up with usable optimized code.
339  Handle<Code> result;
340  if (maybe_result.ToHandle(&result) &&
341      result->kind() == Code::OPTIMIZED_FUNCTION) {
342    DeoptimizationInputData* data =
343        DeoptimizationInputData::cast(result->deoptimization_data());
344
345    if (data->OsrPcOffset()->value() >= 0) {
346      DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id);
347      if (FLAG_trace_osr) {
348        PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
349               ast_id.ToInt(), data->OsrPcOffset()->value());
350      }
351      // TODO(titzer): this is a massive hack to make the deopt counts
352      // match. Fix heuristics for reenabling optimizations!
353      function->shared()->increment_deopt_count();
354
355      if (result->is_turbofanned()) {
356        // When we're waiting for concurrent optimization, set to compile on
357        // the next call - otherwise we'd run unoptimized once more
358        // and potentially compile for OSR another time as well.
359        if (function->IsMarkedForConcurrentOptimization()) {
360          if (FLAG_trace_osr) {
361            PrintF("[OSR - Re-marking ");
362            function->PrintName();
363            PrintF(" for non-concurrent optimization]\n");
364          }
365          function->ReplaceCode(
366              isolate->builtins()->builtin(Builtins::kCompileOptimized));
367        }
368      } else {
369        // Crankshafted OSR code can be installed into the function.
370        function->ReplaceCode(*result);
371      }
372      return *result;
373    }
374  }
375
376  // Failed.
377  if (FLAG_trace_osr) {
378    PrintF("[OSR - Failed: ");
379    function->PrintName();
380    PrintF(" at AST id %d]\n", ast_id.ToInt());
381  }
382
383  if (!function->IsOptimized()) {
384    function->ReplaceCode(function->shared()->code());
385  }
386  return NULL;
387}
388
389
390RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) {
391  HandleScope scope(isolate);
392  DCHECK_EQ(1, args.length());
393  CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
394
395  // First check if this is a real stack overflow.
396  StackLimitCheck check(isolate);
397  if (check.JsHasOverflowed()) {
398    SealHandleScope shs(isolate);
399    return isolate->StackOverflow();
400  }
401
402  isolate->optimizing_compile_dispatcher()->InstallOptimizedFunctions();
403  return (function->IsOptimized()) ? function->code()
404                                   : function->shared()->code();
405}
406
407
408bool CodeGenerationFromStringsAllowed(Isolate* isolate,
409                                      Handle<Context> context) {
410  DCHECK(context->allow_code_gen_from_strings()->IsFalse(isolate));
411  // Check with callback if set.
412  AllowCodeGenerationFromStringsCallback callback =
413      isolate->allow_code_gen_callback();
414  if (callback == NULL) {
415    // No callback set and code generation disallowed.
416    return false;
417  } else {
418    // Callback set. Let it decide if code generation is allowed.
419    VMState<EXTERNAL> state(isolate);
420    return callback(v8::Utils::ToLocal(context));
421  }
422}
423
424static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
425                                 Handle<SharedFunctionInfo> outer_info,
426                                 LanguageMode language_mode,
427                                 int eval_scope_position, int eval_position) {
428  Handle<Context> context = Handle<Context>(isolate->context());
429  Handle<Context> native_context = Handle<Context>(context->native_context());
430
431  // Check if native context allows code generation from
432  // strings. Throw an exception if it doesn't.
433  if (native_context->allow_code_gen_from_strings()->IsFalse(isolate) &&
434      !CodeGenerationFromStringsAllowed(isolate, native_context)) {
435    Handle<Object> error_message =
436        native_context->ErrorMessageForCodeGenerationFromStrings();
437    Handle<Object> error;
438    MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
439        MessageTemplate::kCodeGenFromStrings, error_message);
440    if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
441    return isolate->heap()->exception();
442  }
443
444  // Deal with a normal eval call with a string argument. Compile it
445  // and return the compiled function bound in the local context.
446  static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
447  Handle<JSFunction> compiled;
448  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
449      isolate, compiled,
450      Compiler::GetFunctionFromEval(source, outer_info, context, language_mode,
451                                    restriction, kNoSourcePosition,
452                                    eval_scope_position, eval_position),
453      isolate->heap()->exception());
454  return *compiled;
455}
456
457
458RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) {
459  HandleScope scope(isolate);
460  DCHECK_EQ(6, args.length());
461
462  Handle<Object> callee = args.at(0);
463
464  // If "eval" didn't refer to the original GlobalEval, it's not a
465  // direct call to eval.
466  // (And even if it is, but the first argument isn't a string, just let
467  // execution default to an indirect call to eval, which will also return
468  // the first argument without doing anything).
469  if (*callee != isolate->native_context()->global_eval_fun() ||
470      !args[1]->IsString()) {
471    return *callee;
472  }
473
474  DCHECK(args[3]->IsSmi());
475  DCHECK(is_valid_language_mode(args.smi_at(3)));
476  LanguageMode language_mode = static_cast<LanguageMode>(args.smi_at(3));
477  DCHECK(args[4]->IsSmi());
478  Handle<SharedFunctionInfo> outer_info(args.at<JSFunction>(2)->shared(),
479                                        isolate);
480  return CompileGlobalEval(isolate, args.at<String>(1), outer_info,
481                           language_mode, args.smi_at(4), args.smi_at(5));
482}
483}  // namespace internal
484}  // namespace v8
485