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