• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 namespace v8 {
20 namespace internal {
21 
RUNTIME_FUNCTION(Runtime_CompileLazy)22 RUNTIME_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 
RUNTIME_FUNCTION(Runtime_CompileBaseline)44 RUNTIME_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 
RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent)57 RUNTIME_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 
RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent)71 RUNTIME_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 
RUNTIME_FUNCTION(Runtime_InstantiateAsmJs)84 RUNTIME_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 
RUNTIME_FUNCTION(Runtime_NotifyStubFailure)128 RUNTIME_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 
137 class ActivationsFinder : public ThreadVisitor {
138  public:
139   Code* code_;
140   bool has_code_activations_;
141 
ActivationsFinder(Code * code)142   explicit ActivationsFinder(Code* code)
143       : code_(code), has_code_activations_(false) {}
144 
VisitThread(Isolate * isolate,ThreadLocalTop * top)145   void VisitThread(Isolate* isolate, ThreadLocalTop* top) {
146     JavaScriptFrameIterator it(isolate, top);
147     VisitFrames(&it);
148   }
149 
VisitFrames(JavaScriptFrameIterator * it)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 
RUNTIME_FUNCTION(Runtime_NotifyDeoptimized)159 RUNTIME_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 
IsSuitableForOnStackReplacement(Isolate * isolate,Handle<JSFunction> function)239 static 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 
256 namespace {
257 
DetermineEntryAndDisarmOSRForBaseline(JavaScriptFrame * frame)258 BailoutId 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 
DetermineEntryAndDisarmOSRForInterpreter(JavaScriptFrame * frame)282 BailoutId 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 
RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement)305 RUNTIME_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 
RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode)390 RUNTIME_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 
CodeGenerationFromStringsAllowed(Isolate * isolate,Handle<Context> context)408 bool 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 
CompileGlobalEval(Isolate * isolate,Handle<String> source,Handle<SharedFunctionInfo> outer_info,LanguageMode language_mode,int eval_scope_position,int eval_position)424 static 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 
RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval)458 RUNTIME_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