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/compiler.h" 9#include "src/deoptimizer.h" 10#include "src/frames-inl.h" 11#include "src/full-codegen/full-codegen.h" 12#include "src/isolate-inl.h" 13#include "src/messages.h" 14#include "src/v8threads.h" 15#include "src/vm-state-inl.h" 16 17namespace v8 { 18namespace internal { 19 20RUNTIME_FUNCTION(Runtime_CompileLazy) { 21 HandleScope scope(isolate); 22 DCHECK_EQ(1, args.length()); 23 CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); 24 25#ifdef DEBUG 26 if (FLAG_trace_lazy && !function->shared()->is_compiled()) { 27 PrintF("[unoptimized: "); 28 function->PrintName(); 29 PrintF("]\n"); 30 } 31#endif 32 33 StackLimitCheck check(isolate); 34 if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow(); 35 if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) { 36 return isolate->heap()->exception(); 37 } 38 DCHECK(function->is_compiled()); 39 return function->code(); 40} 41 42RUNTIME_FUNCTION(Runtime_CompileBaseline) { 43 HandleScope scope(isolate); 44 DCHECK_EQ(1, args.length()); 45 CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); 46 StackLimitCheck check(isolate); 47 if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow(); 48 if (!Compiler::CompileBaseline(function)) { 49 return isolate->heap()->exception(); 50 } 51 DCHECK(function->is_compiled()); 52 return function->code(); 53} 54 55RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent) { 56 HandleScope scope(isolate); 57 DCHECK_EQ(1, args.length()); 58 CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); 59 StackLimitCheck check(isolate); 60 if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow(); 61 if (!Compiler::CompileOptimized(function, Compiler::CONCURRENT)) { 62 return isolate->heap()->exception(); 63 } 64 DCHECK(function->is_compiled()); 65 return function->code(); 66} 67 68 69RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent) { 70 HandleScope scope(isolate); 71 DCHECK_EQ(1, args.length()); 72 CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); 73 StackLimitCheck check(isolate); 74 if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow(); 75 if (!Compiler::CompileOptimized(function, Compiler::NOT_CONCURRENT)) { 76 return isolate->heap()->exception(); 77 } 78 DCHECK(function->is_compiled()); 79 return function->code(); 80} 81 82 83RUNTIME_FUNCTION(Runtime_NotifyStubFailure) { 84 HandleScope scope(isolate); 85 DCHECK(args.length() == 0); 86 Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate); 87 DCHECK(AllowHeapAllocation::IsAllowed()); 88 delete deoptimizer; 89 return isolate->heap()->undefined_value(); 90} 91 92 93class ActivationsFinder : public ThreadVisitor { 94 public: 95 Code* code_; 96 bool has_code_activations_; 97 98 explicit ActivationsFinder(Code* code) 99 : code_(code), has_code_activations_(false) {} 100 101 void VisitThread(Isolate* isolate, ThreadLocalTop* top) { 102 JavaScriptFrameIterator it(isolate, top); 103 VisitFrames(&it); 104 } 105 106 void VisitFrames(JavaScriptFrameIterator* it) { 107 for (; !it->done(); it->Advance()) { 108 JavaScriptFrame* frame = it->frame(); 109 if (code_->contains(frame->pc())) has_code_activations_ = true; 110 } 111 } 112}; 113 114 115RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) { 116 HandleScope scope(isolate); 117 DCHECK(args.length() == 1); 118 CONVERT_SMI_ARG_CHECKED(type_arg, 0); 119 Deoptimizer::BailoutType type = 120 static_cast<Deoptimizer::BailoutType>(type_arg); 121 Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate); 122 DCHECK(AllowHeapAllocation::IsAllowed()); 123 TimerEventScope<TimerEventDeoptimizeCode> timer(isolate); 124 TRACE_EVENT0("v8", "V8.DeoptimizeCode"); 125 126 Handle<JSFunction> function = deoptimizer->function(); 127 Handle<Code> optimized_code = deoptimizer->compiled_code(); 128 129 DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION); 130 DCHECK(type == deoptimizer->bailout_type()); 131 132 // Make sure to materialize objects before causing any allocation. 133 JavaScriptFrameIterator it(isolate); 134 deoptimizer->MaterializeHeapObjects(&it); 135 delete deoptimizer; 136 137 // Ensure the context register is updated for materialized objects. 138 JavaScriptFrameIterator top_it(isolate); 139 JavaScriptFrame* top_frame = top_it.frame(); 140 isolate->set_context(Context::cast(top_frame->context())); 141 142 if (type == Deoptimizer::LAZY) { 143 return isolate->heap()->undefined_value(); 144 } 145 146 // Search for other activations of the same optimized code. 147 // At this point {it} is at the topmost frame of all the frames materialized 148 // by the deoptimizer. Note that this frame does not necessarily represent 149 // an activation of {function} because of potential inlined tail-calls. 150 ActivationsFinder activations_finder(*optimized_code); 151 activations_finder.VisitFrames(&it); 152 isolate->thread_manager()->IterateArchivedThreads(&activations_finder); 153 154 if (!activations_finder.has_code_activations_) { 155 if (function->code() == *optimized_code) { 156 if (FLAG_trace_deopt) { 157 PrintF("[removing optimized code for: "); 158 function->PrintName(); 159 PrintF("]\n"); 160 } 161 function->ReplaceCode(function->shared()->code()); 162 } 163 // Evict optimized code for this function from the cache so that it 164 // doesn't get used for new closures. 165 function->shared()->EvictFromOptimizedCodeMap(*optimized_code, 166 "notify deoptimized"); 167 } else { 168 // TODO(titzer): we should probably do DeoptimizeCodeList(code) 169 // unconditionally if the code is not already marked for deoptimization. 170 // If there is an index by shared function info, all the better. 171 Deoptimizer::DeoptimizeFunction(*function); 172 } 173 174 return isolate->heap()->undefined_value(); 175} 176 177 178static bool IsSuitableForOnStackReplacement(Isolate* isolate, 179 Handle<JSFunction> function) { 180 // Keep track of whether we've succeeded in optimizing. 181 if (function->shared()->optimization_disabled()) return false; 182 // If we are trying to do OSR when there are already optimized 183 // activations of the function, it means (a) the function is directly or 184 // indirectly recursive and (b) an optimized invocation has been 185 // deoptimized so that we are currently in an unoptimized activation. 186 // Check for optimized activations of this function. 187 for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) { 188 JavaScriptFrame* frame = it.frame(); 189 if (frame->is_optimized() && frame->function() == *function) return false; 190 } 191 192 return true; 193} 194 195 196RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) { 197 HandleScope scope(isolate); 198 DCHECK(args.length() == 1); 199 CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); 200 Handle<Code> caller_code(function->shared()->code()); 201 202 // We're not prepared to handle a function with arguments object. 203 DCHECK(!function->shared()->uses_arguments()); 204 205 CHECK(FLAG_use_osr); 206 207 // Passing the PC in the javascript frame from the caller directly is 208 // not GC safe, so we walk the stack to get it. 209 JavaScriptFrameIterator it(isolate); 210 JavaScriptFrame* frame = it.frame(); 211 if (!caller_code->contains(frame->pc())) { 212 // Code on the stack may not be the code object referenced by the shared 213 // function info. It may have been replaced to include deoptimization data. 214 caller_code = Handle<Code>(frame->LookupCode()); 215 } 216 217 uint32_t pc_offset = 218 static_cast<uint32_t>(frame->pc() - caller_code->instruction_start()); 219 220#ifdef DEBUG 221 DCHECK_EQ(frame->function(), *function); 222 DCHECK_EQ(frame->LookupCode(), *caller_code); 223 DCHECK(caller_code->contains(frame->pc())); 224#endif // DEBUG 225 226 BailoutId ast_id = caller_code->TranslatePcOffsetToAstId(pc_offset); 227 DCHECK(!ast_id.IsNone()); 228 229 MaybeHandle<Code> maybe_result; 230 if (IsSuitableForOnStackReplacement(isolate, function)) { 231 if (FLAG_trace_osr) { 232 PrintF("[OSR - Compiling: "); 233 function->PrintName(); 234 PrintF(" at AST id %d]\n", ast_id.ToInt()); 235 } 236 maybe_result = Compiler::GetOptimizedCodeForOSR(function, ast_id, frame); 237 } 238 239 // Revert the patched back edge table, regardless of whether OSR succeeds. 240 BackEdgeTable::Revert(isolate, *caller_code); 241 242 // Check whether we ended up with usable optimized code. 243 Handle<Code> result; 244 if (maybe_result.ToHandle(&result) && 245 result->kind() == Code::OPTIMIZED_FUNCTION) { 246 DeoptimizationInputData* data = 247 DeoptimizationInputData::cast(result->deoptimization_data()); 248 249 if (data->OsrPcOffset()->value() >= 0) { 250 DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id); 251 if (FLAG_trace_osr) { 252 PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n", 253 ast_id.ToInt(), data->OsrPcOffset()->value()); 254 } 255 // TODO(titzer): this is a massive hack to make the deopt counts 256 // match. Fix heuristics for reenabling optimizations! 257 function->shared()->increment_deopt_count(); 258 259 if (result->is_turbofanned()) { 260 // TurboFanned OSR code cannot be installed into the function. 261 // But the function is obviously hot, so optimize it next time. 262 function->ReplaceCode( 263 isolate->builtins()->builtin(Builtins::kCompileOptimized)); 264 } else { 265 // Crankshafted OSR code can be installed into the function. 266 function->ReplaceCode(*result); 267 } 268 return *result; 269 } 270 } 271 272 // Failed. 273 if (FLAG_trace_osr) { 274 PrintF("[OSR - Failed: "); 275 function->PrintName(); 276 PrintF(" at AST id %d]\n", ast_id.ToInt()); 277 } 278 279 if (!function->IsOptimized()) { 280 function->ReplaceCode(function->shared()->code()); 281 } 282 return NULL; 283} 284 285 286RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) { 287 HandleScope scope(isolate); 288 DCHECK(args.length() == 1); 289 CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); 290 291 // First check if this is a real stack overflow. 292 StackLimitCheck check(isolate); 293 if (check.JsHasOverflowed()) { 294 SealHandleScope shs(isolate); 295 return isolate->StackOverflow(); 296 } 297 298 isolate->optimizing_compile_dispatcher()->InstallOptimizedFunctions(); 299 return (function->IsOptimized()) ? function->code() 300 : function->shared()->code(); 301} 302 303 304bool CodeGenerationFromStringsAllowed(Isolate* isolate, 305 Handle<Context> context) { 306 DCHECK(context->allow_code_gen_from_strings()->IsFalse(isolate)); 307 // Check with callback if set. 308 AllowCodeGenerationFromStringsCallback callback = 309 isolate->allow_code_gen_callback(); 310 if (callback == NULL) { 311 // No callback set and code generation disallowed. 312 return false; 313 } else { 314 // Callback set. Let it decide if code generation is allowed. 315 VMState<EXTERNAL> state(isolate); 316 return callback(v8::Utils::ToLocal(context)); 317 } 318} 319 320static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source, 321 Handle<SharedFunctionInfo> outer_info, 322 LanguageMode language_mode, 323 int eval_scope_position, int eval_position) { 324 Handle<Context> context = Handle<Context>(isolate->context()); 325 Handle<Context> native_context = Handle<Context>(context->native_context()); 326 327 // Check if native context allows code generation from 328 // strings. Throw an exception if it doesn't. 329 if (native_context->allow_code_gen_from_strings()->IsFalse(isolate) && 330 !CodeGenerationFromStringsAllowed(isolate, native_context)) { 331 Handle<Object> error_message = 332 native_context->ErrorMessageForCodeGenerationFromStrings(); 333 Handle<Object> error; 334 MaybeHandle<Object> maybe_error = isolate->factory()->NewEvalError( 335 MessageTemplate::kCodeGenFromStrings, error_message); 336 if (maybe_error.ToHandle(&error)) isolate->Throw(*error); 337 return isolate->heap()->exception(); 338 } 339 340 // Deal with a normal eval call with a string argument. Compile it 341 // and return the compiled function bound in the local context. 342 static const ParseRestriction restriction = NO_PARSE_RESTRICTION; 343 Handle<JSFunction> compiled; 344 ASSIGN_RETURN_ON_EXCEPTION_VALUE( 345 isolate, compiled, Compiler::GetFunctionFromEval( 346 source, outer_info, context, language_mode, 347 restriction, eval_scope_position, eval_position), 348 isolate->heap()->exception()); 349 return *compiled; 350} 351 352 353RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) { 354 HandleScope scope(isolate); 355 DCHECK(args.length() == 6); 356 357 Handle<Object> callee = args.at<Object>(0); 358 359 // If "eval" didn't refer to the original GlobalEval, it's not a 360 // direct call to eval. 361 // (And even if it is, but the first argument isn't a string, just let 362 // execution default to an indirect call to eval, which will also return 363 // the first argument without doing anything). 364 if (*callee != isolate->native_context()->global_eval_fun() || 365 !args[1]->IsString()) { 366 return *callee; 367 } 368 369 DCHECK(args[3]->IsSmi()); 370 DCHECK(is_valid_language_mode(args.smi_at(3))); 371 LanguageMode language_mode = static_cast<LanguageMode>(args.smi_at(3)); 372 DCHECK(args[4]->IsSmi()); 373 Handle<SharedFunctionInfo> outer_info(args.at<JSFunction>(2)->shared(), 374 isolate); 375 return CompileGlobalEval(isolate, args.at<String>(1), outer_info, 376 language_mode, args.smi_at(4), args.smi_at(5)); 377} 378} // namespace internal 379} // namespace v8 380