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