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/arguments-inl.h"
6 #include "src/asmjs/asm-js.h"
7 #include "src/compiler-dispatcher/optimizing-compile-dispatcher.h"
8 #include "src/compiler.h"
9 #include "src/deoptimizer.h"
10 #include "src/frames-inl.h"
11 #include "src/isolate-inl.h"
12 #include "src/messages.h"
13 #include "src/objects/js-array-buffer-inl.h"
14 #include "src/objects/js-array-inl.h"
15 #include "src/runtime/runtime-utils.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(kStackSpaceRequiredForCompilation * KB)) {
37     return isolate->StackOverflow();
38   }
39   if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) {
40     return ReadOnlyRoots(isolate).exception();
41   }
42   DCHECK(function->is_compiled());
43   return function->code();
44 }
45 
RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent)46 RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent) {
47   HandleScope scope(isolate);
48   DCHECK_EQ(1, args.length());
49   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
50   StackLimitCheck check(isolate);
51   if (check.JsHasOverflowed(kStackSpaceRequiredForCompilation * KB)) {
52     return isolate->StackOverflow();
53   }
54   if (!Compiler::CompileOptimized(function, ConcurrencyMode::kConcurrent)) {
55     return ReadOnlyRoots(isolate).exception();
56   }
57   DCHECK(function->is_compiled());
58   return function->code();
59 }
60 
RUNTIME_FUNCTION(Runtime_FunctionFirstExecution)61 RUNTIME_FUNCTION(Runtime_FunctionFirstExecution) {
62   HandleScope scope(isolate);
63   StackLimitCheck check(isolate);
64   DCHECK_EQ(1, args.length());
65 
66   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
67   DCHECK_EQ(function->feedback_vector()->optimization_marker(),
68             OptimizationMarker::kLogFirstExecution);
69   DCHECK(FLAG_log_function_events);
70   Handle<SharedFunctionInfo> sfi(function->shared(), isolate);
71   LOG(isolate, FunctionEvent(
72                    "first-execution", Script::cast(sfi->script())->id(), 0,
73                    sfi->StartPosition(), sfi->EndPosition(), sfi->DebugName()));
74   function->feedback_vector()->ClearOptimizationMarker();
75   // Return the code to continue execution, we don't care at this point whether
76   // this is for lazy compilation or has been eagerly complied.
77   return function->code();
78 }
79 
RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent)80 RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent) {
81   HandleScope scope(isolate);
82   DCHECK_EQ(1, args.length());
83   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
84   StackLimitCheck check(isolate);
85   if (check.JsHasOverflowed(kStackSpaceRequiredForCompilation * KB)) {
86     return isolate->StackOverflow();
87   }
88   if (!Compiler::CompileOptimized(function, ConcurrencyMode::kNotConcurrent)) {
89     return ReadOnlyRoots(isolate).exception();
90   }
91   DCHECK(function->is_compiled());
92   return function->code();
93 }
94 
RUNTIME_FUNCTION(Runtime_EvictOptimizedCodeSlot)95 RUNTIME_FUNCTION(Runtime_EvictOptimizedCodeSlot) {
96   SealHandleScope scope(isolate);
97   DCHECK_EQ(1, args.length());
98   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
99 
100   DCHECK(function->shared()->is_compiled());
101 
102   function->feedback_vector()->EvictOptimizedCodeMarkedForDeoptimization(
103       function->shared(), "Runtime_EvictOptimizedCodeSlot");
104   return function->code();
105 }
106 
RUNTIME_FUNCTION(Runtime_InstantiateAsmJs)107 RUNTIME_FUNCTION(Runtime_InstantiateAsmJs) {
108   HandleScope scope(isolate);
109   DCHECK_EQ(args.length(), 4);
110   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
111 
112   Handle<JSReceiver> stdlib;
113   if (args[1]->IsJSReceiver()) {
114     stdlib = args.at<JSReceiver>(1);
115   }
116   Handle<JSReceiver> foreign;
117   if (args[2]->IsJSReceiver()) {
118     foreign = args.at<JSReceiver>(2);
119   }
120   Handle<JSArrayBuffer> memory;
121   if (args[3]->IsJSArrayBuffer()) {
122     memory = args.at<JSArrayBuffer>(3);
123   }
124   if (function->shared()->HasAsmWasmData()) {
125     Handle<SharedFunctionInfo> shared(function->shared(), isolate);
126     Handle<FixedArray> data(shared->asm_wasm_data(), isolate);
127     MaybeHandle<Object> result = AsmJs::InstantiateAsmWasm(
128         isolate, shared, data, stdlib, foreign, memory);
129     if (!result.is_null()) {
130       return *result.ToHandleChecked();
131     }
132   }
133   // Remove wasm data, mark as broken for asm->wasm, replace function code with
134   // UncompiledData, and return a smi 0 to indicate failure.
135   if (function->shared()->HasAsmWasmData()) {
136     SharedFunctionInfo::DiscardCompiled(isolate,
137                                         handle(function->shared(), isolate));
138   }
139   function->shared()->set_is_asm_wasm_broken(true);
140   DCHECK(function->code() ==
141          isolate->builtins()->builtin(Builtins::kInstantiateAsmJs));
142   function->set_code(isolate->builtins()->builtin(Builtins::kCompileLazy));
143   return Smi::kZero;
144 }
145 
RUNTIME_FUNCTION(Runtime_NotifyDeoptimized)146 RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
147   HandleScope scope(isolate);
148   DCHECK_EQ(0, args.length());
149   Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate);
150   DCHECK(deoptimizer->compiled_code()->kind() == Code::OPTIMIZED_FUNCTION);
151   DCHECK(deoptimizer->compiled_code()->is_turbofanned());
152   DCHECK(AllowHeapAllocation::IsAllowed());
153   DCHECK_NULL(isolate->context());
154 
155   TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
156   TRACE_EVENT0("v8", "V8.DeoptimizeCode");
157   Handle<JSFunction> function = deoptimizer->function();
158   DeoptimizeKind type = deoptimizer->deopt_kind();
159 
160   // TODO(turbofan): We currently need the native context to materialize
161   // the arguments object, but only to get to its map.
162   isolate->set_context(deoptimizer->function()->native_context());
163 
164   // Make sure to materialize objects before causing any allocation.
165   deoptimizer->MaterializeHeapObjects();
166   delete deoptimizer;
167 
168   // Ensure the context register is updated for materialized objects.
169   JavaScriptFrameIterator top_it(isolate);
170   JavaScriptFrame* top_frame = top_it.frame();
171   isolate->set_context(Context::cast(top_frame->context()));
172 
173   // Invalidate the underlying optimized code on non-lazy deopts.
174   if (type != DeoptimizeKind::kLazy) {
175     Deoptimizer::DeoptimizeFunction(*function);
176   }
177 
178   return ReadOnlyRoots(isolate).undefined_value();
179 }
180 
181 
IsSuitableForOnStackReplacement(Isolate * isolate,Handle<JSFunction> function)182 static bool IsSuitableForOnStackReplacement(Isolate* isolate,
183                                             Handle<JSFunction> function) {
184   // Keep track of whether we've succeeded in optimizing.
185   if (function->shared()->optimization_disabled()) return false;
186   // If we are trying to do OSR when there are already optimized
187   // activations of the function, it means (a) the function is directly or
188   // indirectly recursive and (b) an optimized invocation has been
189   // deoptimized so that we are currently in an unoptimized activation.
190   // Check for optimized activations of this function.
191   for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
192     JavaScriptFrame* frame = it.frame();
193     if (frame->is_optimized() && frame->function() == *function) return false;
194   }
195 
196   return true;
197 }
198 
199 namespace {
200 
DetermineEntryAndDisarmOSRForInterpreter(JavaScriptFrame * frame)201 BailoutId DetermineEntryAndDisarmOSRForInterpreter(JavaScriptFrame* frame) {
202   InterpretedFrame* iframe = reinterpret_cast<InterpretedFrame*>(frame);
203 
204   // Note that the bytecode array active on the stack might be different from
205   // the one installed on the function (e.g. patched by debugger). This however
206   // is fine because we guarantee the layout to be in sync, hence any BailoutId
207   // representing the entry point will be valid for any copy of the bytecode.
208   Handle<BytecodeArray> bytecode(iframe->GetBytecodeArray(), iframe->isolate());
209 
210   DCHECK(frame->LookupCode()->is_interpreter_trampoline_builtin());
211   DCHECK(frame->function()->shared()->HasBytecodeArray());
212   DCHECK(frame->is_interpreted());
213 
214   // Reset the OSR loop nesting depth to disarm back edges.
215   bytecode->set_osr_loop_nesting_level(0);
216 
217   // Return a BailoutId representing the bytecode offset of the back branch.
218   return BailoutId(iframe->GetBytecodeOffset());
219 }
220 
221 }  // namespace
222 
RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement)223 RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) {
224   HandleScope scope(isolate);
225   DCHECK_EQ(1, args.length());
226   CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
227 
228   // Only reachable when OST is enabled.
229   CHECK(FLAG_use_osr);
230 
231   // Determine frame triggering OSR request.
232   JavaScriptFrameIterator it(isolate);
233   JavaScriptFrame* frame = it.frame();
234   DCHECK_EQ(frame->function(), *function);
235   DCHECK(frame->is_interpreted());
236 
237   // Determine the entry point for which this OSR request has been fired and
238   // also disarm all back edges in the calling code to stop new requests.
239   BailoutId ast_id = DetermineEntryAndDisarmOSRForInterpreter(frame);
240   DCHECK(!ast_id.IsNone());
241 
242   MaybeHandle<Code> maybe_result;
243   if (IsSuitableForOnStackReplacement(isolate, function)) {
244     if (FLAG_trace_osr) {
245       PrintF("[OSR - Compiling: ");
246       function->PrintName();
247       PrintF(" at AST id %d]\n", ast_id.ToInt());
248     }
249     maybe_result = Compiler::GetOptimizedCodeForOSR(function, ast_id, frame);
250   }
251 
252   // Check whether we ended up with usable optimized code.
253   Handle<Code> result;
254   if (maybe_result.ToHandle(&result) &&
255       result->kind() == Code::OPTIMIZED_FUNCTION) {
256     DeoptimizationData* data =
257         DeoptimizationData::cast(result->deoptimization_data());
258 
259     if (data->OsrPcOffset()->value() >= 0) {
260       DCHECK(BailoutId(data->OsrBytecodeOffset()->value()) == ast_id);
261       if (FLAG_trace_osr) {
262         PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n",
263                ast_id.ToInt(), data->OsrPcOffset()->value());
264       }
265 
266       DCHECK(result->is_turbofanned());
267       if (!function->HasOptimizedCode()) {
268         // If we're not already optimized, set to optimize non-concurrently on
269         // the next call, otherwise we'd run unoptimized once more and
270         // potentially compile for OSR again.
271         if (FLAG_trace_osr) {
272           PrintF("[OSR - Re-marking ");
273           function->PrintName();
274           PrintF(" for non-concurrent optimization]\n");
275         }
276         function->SetOptimizationMarker(OptimizationMarker::kCompileOptimized);
277       }
278       return *result;
279     }
280   }
281 
282   // Failed.
283   if (FLAG_trace_osr) {
284     PrintF("[OSR - Failed: ");
285     function->PrintName();
286     PrintF(" at AST id %d]\n", ast_id.ToInt());
287   }
288 
289   if (!function->IsOptimized()) {
290     function->set_code(function->shared()->GetCode());
291   }
292   return nullptr;
293 }
294 
CompileGlobalEval(Isolate * isolate,Handle<String> source,Handle<SharedFunctionInfo> outer_info,LanguageMode language_mode,int eval_scope_position,int eval_position)295 static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
296                                  Handle<SharedFunctionInfo> outer_info,
297                                  LanguageMode language_mode,
298                                  int eval_scope_position, int eval_position) {
299   Handle<Context> context(isolate->context(), isolate);
300   Handle<Context> native_context(context->native_context(), isolate);
301 
302   // Check if native context allows code generation from
303   // strings. Throw an exception if it doesn't.
304   if (native_context->allow_code_gen_from_strings()->IsFalse(isolate) &&
305       !Compiler::CodeGenerationFromStringsAllowed(isolate, native_context,
306                                                   source)) {
307     Handle<Object> error_message =
308         native_context->ErrorMessageForCodeGenerationFromStrings();
309     Handle<Object> error;
310     MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError(
311         MessageTemplate::kCodeGenFromStrings, error_message);
312     if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
313     return ReadOnlyRoots(isolate).exception();
314   }
315 
316   // Deal with a normal eval call with a string argument. Compile it
317   // and return the compiled function bound in the local context.
318   static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
319   Handle<JSFunction> compiled;
320   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
321       isolate, compiled,
322       Compiler::GetFunctionFromEval(source, outer_info, context, language_mode,
323                                     restriction, kNoSourcePosition,
324                                     eval_scope_position, eval_position),
325       ReadOnlyRoots(isolate).exception());
326   return *compiled;
327 }
328 
329 
RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval)330 RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) {
331   HandleScope scope(isolate);
332   DCHECK_EQ(6, args.length());
333 
334   Handle<Object> callee = args.at(0);
335 
336   // If "eval" didn't refer to the original GlobalEval, it's not a
337   // direct call to eval.
338   // (And even if it is, but the first argument isn't a string, just let
339   // execution default to an indirect call to eval, which will also return
340   // the first argument without doing anything).
341   if (*callee != isolate->native_context()->global_eval_fun() ||
342       !args[1]->IsString()) {
343     return *callee;
344   }
345 
346   DCHECK(args[3]->IsSmi());
347   DCHECK(is_valid_language_mode(args.smi_at(3)));
348   LanguageMode language_mode = static_cast<LanguageMode>(args.smi_at(3));
349   DCHECK(args[4]->IsSmi());
350   Handle<SharedFunctionInfo> outer_info(args.at<JSFunction>(2)->shared(),
351                                         isolate);
352   return CompileGlobalEval(isolate, args.at<String>(1), outer_info,
353                            language_mode, args.smi_at(4), args.smi_at(5));
354 }
355 }  // namespace internal
356 }  // namespace v8
357