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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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