1 // Copyright 2013 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/deoptimizer/deoptimizer.h"
6
7 #include <memory>
8
9 #include "src/ast/prettyprinter.h"
10 #include "src/builtins/accessors.h"
11 #include "src/codegen/assembler-inl.h"
12 #include "src/codegen/callable.h"
13 #include "src/codegen/macro-assembler.h"
14 #include "src/codegen/register-configuration.h"
15 #include "src/common/assert-scope.h"
16 #include "src/diagnostics/disasm.h"
17 #include "src/execution/frames-inl.h"
18 #include "src/execution/pointer-authentication.h"
19 #include "src/execution/v8threads.h"
20 #include "src/handles/global-handles.h"
21 #include "src/heap/heap-inl.h"
22 #include "src/init/v8.h"
23 #include "src/interpreter/interpreter.h"
24 #include "src/logging/counters.h"
25 #include "src/logging/log.h"
26 #include "src/objects/arguments.h"
27 #include "src/objects/debug-objects-inl.h"
28 #include "src/objects/heap-number-inl.h"
29 #include "src/objects/smi.h"
30 #include "src/snapshot/embedded/embedded-data.h"
31 #include "src/tracing/trace-event.h"
32
33 // Has to be the last include (doesn't have include guards)
34 #include "src/objects/object-macros.h"
35
36 namespace v8 {
37 namespace internal {
38
39 // {FrameWriter} offers a stack writer abstraction for writing
40 // FrameDescriptions. The main service the class provides is managing
41 // {top_offset_}, i.e. the offset of the next slot to write to.
42 class FrameWriter {
43 public:
44 static const int NO_INPUT_INDEX = -1;
FrameWriter(Deoptimizer * deoptimizer,FrameDescription * frame,CodeTracer::Scope * trace_scope)45 FrameWriter(Deoptimizer* deoptimizer, FrameDescription* frame,
46 CodeTracer::Scope* trace_scope)
47 : deoptimizer_(deoptimizer),
48 frame_(frame),
49 trace_scope_(trace_scope),
50 top_offset_(frame->GetFrameSize()) {}
51
PushRawValue(intptr_t value,const char * debug_hint)52 void PushRawValue(intptr_t value, const char* debug_hint) {
53 PushValue(value);
54 if (trace_scope_ != nullptr) {
55 DebugPrintOutputValue(value, debug_hint);
56 }
57 }
58
PushRawObject(Object obj,const char * debug_hint)59 void PushRawObject(Object obj, const char* debug_hint) {
60 intptr_t value = obj.ptr();
61 PushValue(value);
62 if (trace_scope_ != nullptr) {
63 DebugPrintOutputObject(obj, top_offset_, debug_hint);
64 }
65 }
66
67 // There is no check against the allowed addresses for bottommost frames, as
68 // the caller's pc could be anything. The caller's pc pushed here should never
69 // be re-signed.
PushBottommostCallerPc(intptr_t pc)70 void PushBottommostCallerPc(intptr_t pc) {
71 top_offset_ -= kPCOnStackSize;
72 frame_->SetFrameSlot(top_offset_, pc);
73 DebugPrintOutputPc(pc, "bottommost caller's pc\n");
74 }
75
PushApprovedCallerPc(intptr_t pc)76 void PushApprovedCallerPc(intptr_t pc) {
77 top_offset_ -= kPCOnStackSize;
78 frame_->SetCallerPc(top_offset_, pc);
79 DebugPrintOutputPc(pc, "caller's pc\n");
80 }
81
PushCallerFp(intptr_t fp)82 void PushCallerFp(intptr_t fp) {
83 top_offset_ -= kFPOnStackSize;
84 frame_->SetCallerFp(top_offset_, fp);
85 DebugPrintOutputValue(fp, "caller's fp\n");
86 }
87
PushCallerConstantPool(intptr_t cp)88 void PushCallerConstantPool(intptr_t cp) {
89 top_offset_ -= kSystemPointerSize;
90 frame_->SetCallerConstantPool(top_offset_, cp);
91 DebugPrintOutputValue(cp, "caller's constant_pool\n");
92 }
93
PushTranslatedValue(const TranslatedFrame::iterator & iterator,const char * debug_hint="")94 void PushTranslatedValue(const TranslatedFrame::iterator& iterator,
95 const char* debug_hint = "") {
96 Object obj = iterator->GetRawValue();
97 PushRawObject(obj, debug_hint);
98 if (trace_scope_) {
99 PrintF(trace_scope_->file(), " (input #%d)\n", iterator.input_index());
100 }
101 deoptimizer_->QueueValueForMaterialization(output_address(top_offset_), obj,
102 iterator);
103 }
104
PushStackJSArguments(TranslatedFrame::iterator & iterator,int parameters_count)105 void PushStackJSArguments(TranslatedFrame::iterator& iterator,
106 int parameters_count) {
107 std::vector<TranslatedFrame::iterator> parameters;
108 parameters.reserve(parameters_count);
109 for (int i = 0; i < parameters_count; ++i, ++iterator) {
110 parameters.push_back(iterator);
111 }
112 for (auto& parameter : base::Reversed(parameters)) {
113 PushTranslatedValue(parameter, "stack parameter");
114 }
115 }
116
top_offset() const117 unsigned top_offset() const { return top_offset_; }
118
frame()119 FrameDescription* frame() { return frame_; }
120
121 private:
PushValue(intptr_t value)122 void PushValue(intptr_t value) {
123 CHECK_GE(top_offset_, 0);
124 top_offset_ -= kSystemPointerSize;
125 frame_->SetFrameSlot(top_offset_, value);
126 }
127
output_address(unsigned output_offset)128 Address output_address(unsigned output_offset) {
129 Address output_address =
130 static_cast<Address>(frame_->GetTop()) + output_offset;
131 return output_address;
132 }
133
DebugPrintOutputValue(intptr_t value,const char * debug_hint="")134 void DebugPrintOutputValue(intptr_t value, const char* debug_hint = "") {
135 if (trace_scope_ != nullptr) {
136 PrintF(trace_scope_->file(),
137 " " V8PRIxPTR_FMT ": [top + %3d] <- " V8PRIxPTR_FMT " ; %s",
138 output_address(top_offset_), top_offset_, value, debug_hint);
139 }
140 }
141
DebugPrintOutputPc(intptr_t value,const char * debug_hint="")142 void DebugPrintOutputPc(intptr_t value, const char* debug_hint = "") {
143 #ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
144 if (trace_scope_ != nullptr) {
145 PrintF(trace_scope_->file(),
146 " " V8PRIxPTR_FMT ": [top + %3d] <- " V8PRIxPTR_FMT
147 " (signed) " V8PRIxPTR_FMT " (unsigned) ; %s",
148 output_address(top_offset_), top_offset_, value,
149 PointerAuthentication::StripPAC(value), debug_hint);
150 }
151 #else
152 DebugPrintOutputValue(value, debug_hint);
153 #endif
154 }
155
DebugPrintOutputObject(Object obj,unsigned output_offset,const char * debug_hint="")156 void DebugPrintOutputObject(Object obj, unsigned output_offset,
157 const char* debug_hint = "") {
158 if (trace_scope_ != nullptr) {
159 PrintF(trace_scope_->file(), " " V8PRIxPTR_FMT ": [top + %3d] <- ",
160 output_address(output_offset), output_offset);
161 if (obj.IsSmi()) {
162 PrintF(trace_scope_->file(), V8PRIxPTR_FMT " <Smi %d>", obj.ptr(),
163 Smi::cast(obj).value());
164 } else {
165 obj.ShortPrint(trace_scope_->file());
166 }
167 PrintF(trace_scope_->file(), " ; %s", debug_hint);
168 }
169 }
170
171 Deoptimizer* deoptimizer_;
172 FrameDescription* frame_;
173 CodeTracer::Scope* const trace_scope_;
174 unsigned top_offset_;
175 };
176
FindDeoptimizingCode(Address addr)177 Code Deoptimizer::FindDeoptimizingCode(Address addr) {
178 if (function_.IsHeapObject()) {
179 // Search all deoptimizing code in the native context of the function.
180 Isolate* isolate = isolate_;
181 NativeContext native_context = function_.context().native_context();
182 Object element = native_context.DeoptimizedCodeListHead();
183 while (!element.IsUndefined(isolate)) {
184 Code code = Code::cast(element);
185 CHECK(CodeKindCanDeoptimize(code.kind()));
186 if (code.contains(addr)) return code;
187 element = code.next_code_link();
188 }
189 }
190 return Code();
191 }
192
193 // We rely on this function not causing a GC. It is called from generated code
194 // without having a real stack frame in place.
New(Address raw_function,DeoptimizeKind kind,unsigned bailout_id,Address from,int fp_to_sp_delta,Isolate * isolate)195 Deoptimizer* Deoptimizer::New(Address raw_function, DeoptimizeKind kind,
196 unsigned bailout_id, Address from,
197 int fp_to_sp_delta, Isolate* isolate) {
198 JSFunction function = JSFunction::cast(Object(raw_function));
199 Deoptimizer* deoptimizer = new Deoptimizer(isolate, function, kind,
200 bailout_id, from, fp_to_sp_delta);
201 isolate->set_current_deoptimizer(deoptimizer);
202 return deoptimizer;
203 }
204
Grab(Isolate * isolate)205 Deoptimizer* Deoptimizer::Grab(Isolate* isolate) {
206 Deoptimizer* result = isolate->GetAndClearCurrentDeoptimizer();
207 result->DeleteFrameDescriptions();
208 return result;
209 }
210
DebuggerInspectableFrame(JavaScriptFrame * frame,int jsframe_index,Isolate * isolate)211 DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame(
212 JavaScriptFrame* frame, int jsframe_index, Isolate* isolate) {
213 CHECK(frame->is_optimized());
214
215 TranslatedState translated_values(frame);
216 translated_values.Prepare(frame->fp());
217
218 TranslatedState::iterator frame_it = translated_values.end();
219 int counter = jsframe_index;
220 for (auto it = translated_values.begin(); it != translated_values.end();
221 it++) {
222 if (it->kind() == TranslatedFrame::kInterpretedFunction ||
223 it->kind() == TranslatedFrame::kJavaScriptBuiltinContinuation ||
224 it->kind() ==
225 TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch) {
226 if (counter == 0) {
227 frame_it = it;
228 break;
229 }
230 counter--;
231 }
232 }
233 CHECK(frame_it != translated_values.end());
234 // We only include kJavaScriptBuiltinContinuation frames above to get the
235 // counting right.
236 CHECK_EQ(frame_it->kind(), TranslatedFrame::kInterpretedFunction);
237
238 DeoptimizedFrameInfo* info =
239 new DeoptimizedFrameInfo(&translated_values, frame_it, isolate);
240
241 return info;
242 }
243
244 namespace {
245 class ActivationsFinder : public ThreadVisitor {
246 public:
ActivationsFinder(std::set<Code> * codes,Code topmost_optimized_code,bool safe_to_deopt_topmost_optimized_code)247 explicit ActivationsFinder(std::set<Code>* codes, Code topmost_optimized_code,
248 bool safe_to_deopt_topmost_optimized_code)
249 : codes_(codes) {
250 #ifdef DEBUG
251 topmost_ = topmost_optimized_code;
252 safe_to_deopt_ = safe_to_deopt_topmost_optimized_code;
253 #endif
254 }
255
256 // Find the frames with activations of codes marked for deoptimization, search
257 // for the trampoline to the deoptimizer call respective to each code, and use
258 // it to replace the current pc on the stack.
VisitThread(Isolate * isolate,ThreadLocalTop * top)259 void VisitThread(Isolate* isolate, ThreadLocalTop* top) override {
260 for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) {
261 if (it.frame()->type() == StackFrame::OPTIMIZED) {
262 Code code = it.frame()->LookupCode();
263 if (CodeKindCanDeoptimize(code.kind()) &&
264 code.marked_for_deoptimization()) {
265 codes_->erase(code);
266 // Obtain the trampoline to the deoptimizer call.
267 SafepointEntry safepoint = code.GetSafepointEntry(it.frame()->pc());
268 int trampoline_pc = safepoint.trampoline_pc();
269 DCHECK_IMPLIES(code == topmost_, safe_to_deopt_);
270 CHECK_GE(trampoline_pc, 0);
271 // Replace the current pc on the stack with the trampoline.
272 // TODO(v8:10026): avoid replacing a signed pointer.
273 Address* pc_addr = it.frame()->pc_address();
274 Address new_pc = code.raw_instruction_start() + trampoline_pc;
275 PointerAuthentication::ReplacePC(pc_addr, new_pc, kSystemPointerSize);
276 }
277 }
278 }
279 }
280
281 private:
282 std::set<Code>* codes_;
283
284 #ifdef DEBUG
285 Code topmost_;
286 bool safe_to_deopt_;
287 #endif
288 };
289 } // namespace
290
291 // Move marked code from the optimized code list to the deoptimized code list,
292 // and replace pc on the stack for codes marked for deoptimization.
293 // static
DeoptimizeMarkedCodeForContext(NativeContext native_context)294 void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) {
295 DisallowHeapAllocation no_allocation;
296
297 Isolate* isolate = native_context.GetIsolate();
298 Code topmost_optimized_code;
299 bool safe_to_deopt_topmost_optimized_code = false;
300 #ifdef DEBUG
301 // Make sure all activations of optimized code can deopt at their current PC.
302 // The topmost optimized code has special handling because it cannot be
303 // deoptimized due to weak object dependency.
304 for (StackFrameIterator it(isolate, isolate->thread_local_top()); !it.done();
305 it.Advance()) {
306 StackFrame::Type type = it.frame()->type();
307 if (type == StackFrame::OPTIMIZED) {
308 Code code = it.frame()->LookupCode();
309 JSFunction function =
310 static_cast<OptimizedFrame*>(it.frame())->function();
311 TraceFoundActivation(isolate, function);
312 SafepointEntry safepoint = code.GetSafepointEntry(it.frame()->pc());
313
314 // Turbofan deopt is checked when we are patching addresses on stack.
315 bool safe_if_deopt_triggered = safepoint.has_deoptimization_index();
316 bool is_builtin_code = code.kind() == CodeKind::BUILTIN;
317 DCHECK(topmost_optimized_code.is_null() || safe_if_deopt_triggered ||
318 is_builtin_code);
319 if (topmost_optimized_code.is_null()) {
320 topmost_optimized_code = code;
321 safe_to_deopt_topmost_optimized_code = safe_if_deopt_triggered;
322 }
323 }
324 }
325 #endif
326
327 // We will use this set to mark those Code objects that are marked for
328 // deoptimization and have not been found in stack frames.
329 std::set<Code> codes;
330
331 // Move marked code from the optimized code list to the deoptimized code list.
332 // Walk over all optimized code objects in this native context.
333 Code prev;
334 Object element = native_context.OptimizedCodeListHead();
335 while (!element.IsUndefined(isolate)) {
336 Code code = Code::cast(element);
337 CHECK(CodeKindCanDeoptimize(code.kind()));
338 Object next = code.next_code_link();
339
340 if (code.marked_for_deoptimization()) {
341 codes.insert(code);
342
343 if (!prev.is_null()) {
344 // Skip this code in the optimized code list.
345 prev.set_next_code_link(next);
346 } else {
347 // There was no previous node, the next node is the new head.
348 native_context.SetOptimizedCodeListHead(next);
349 }
350
351 // Move the code to the _deoptimized_ code list.
352 code.set_next_code_link(native_context.DeoptimizedCodeListHead());
353 native_context.SetDeoptimizedCodeListHead(code);
354 } else {
355 // Not marked; preserve this element.
356 prev = code;
357 }
358 element = next;
359 }
360
361 ActivationsFinder visitor(&codes, topmost_optimized_code,
362 safe_to_deopt_topmost_optimized_code);
363 // Iterate over the stack of this thread.
364 visitor.VisitThread(isolate, isolate->thread_local_top());
365 // In addition to iterate over the stack of this thread, we also
366 // need to consider all the other threads as they may also use
367 // the code currently beings deoptimized.
368 isolate->thread_manager()->IterateArchivedThreads(&visitor);
369
370 // If there's no activation of a code in any stack then we can remove its
371 // deoptimization data. We do this to ensure that code objects that are
372 // unlinked don't transitively keep objects alive unnecessarily.
373 for (Code code : codes) {
374 isolate->heap()->InvalidateCodeDeoptimizationData(code);
375 }
376
377 native_context.GetOSROptimizedCodeCache().EvictMarkedCode(
378 native_context.GetIsolate());
379 }
380
DeoptimizeAll(Isolate * isolate)381 void Deoptimizer::DeoptimizeAll(Isolate* isolate) {
382 RuntimeCallTimerScope runtimeTimer(isolate,
383 RuntimeCallCounterId::kDeoptimizeCode);
384 TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
385 TRACE_EVENT0("v8", "V8.DeoptimizeCode");
386 TraceDeoptAll(isolate);
387 isolate->AbortConcurrentOptimization(BlockingBehavior::kBlock);
388 DisallowHeapAllocation no_allocation;
389 // For all contexts, mark all code, then deoptimize.
390 Object context = isolate->heap()->native_contexts_list();
391 while (!context.IsUndefined(isolate)) {
392 NativeContext native_context = NativeContext::cast(context);
393 MarkAllCodeForContext(native_context);
394 OSROptimizedCodeCache::Clear(native_context);
395 DeoptimizeMarkedCodeForContext(native_context);
396 context = native_context.next_context_link();
397 }
398 }
399
DeoptimizeMarkedCode(Isolate * isolate)400 void Deoptimizer::DeoptimizeMarkedCode(Isolate* isolate) {
401 RuntimeCallTimerScope runtimeTimer(isolate,
402 RuntimeCallCounterId::kDeoptimizeCode);
403 TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
404 TRACE_EVENT0("v8", "V8.DeoptimizeCode");
405 TraceDeoptMarked(isolate);
406 DisallowHeapAllocation no_allocation;
407 // For all contexts, deoptimize code already marked.
408 Object context = isolate->heap()->native_contexts_list();
409 while (!context.IsUndefined(isolate)) {
410 NativeContext native_context = NativeContext::cast(context);
411 DeoptimizeMarkedCodeForContext(native_context);
412 context = native_context.next_context_link();
413 }
414 }
415
MarkAllCodeForContext(NativeContext native_context)416 void Deoptimizer::MarkAllCodeForContext(NativeContext native_context) {
417 Object element = native_context.OptimizedCodeListHead();
418 Isolate* isolate = native_context.GetIsolate();
419 while (!element.IsUndefined(isolate)) {
420 Code code = Code::cast(element);
421 CHECK(CodeKindCanDeoptimize(code.kind()));
422 code.set_marked_for_deoptimization(true);
423 element = code.next_code_link();
424 }
425 }
426
DeoptimizeFunction(JSFunction function,Code code)427 void Deoptimizer::DeoptimizeFunction(JSFunction function, Code code) {
428 Isolate* isolate = function.GetIsolate();
429 RuntimeCallTimerScope runtimeTimer(isolate,
430 RuntimeCallCounterId::kDeoptimizeCode);
431 TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
432 TRACE_EVENT0("v8", "V8.DeoptimizeCode");
433 function.ResetIfBytecodeFlushed();
434 if (code.is_null()) code = function.code();
435
436 if (CodeKindCanDeoptimize(code.kind())) {
437 // Mark the code for deoptimization and unlink any functions that also
438 // refer to that code. The code cannot be shared across native contexts,
439 // so we only need to search one.
440 code.set_marked_for_deoptimization(true);
441 // The code in the function's optimized code feedback vector slot might
442 // be different from the code on the function - evict it if necessary.
443 function.feedback_vector().EvictOptimizedCodeMarkedForDeoptimization(
444 function.shared(), "unlinking code marked for deopt");
445 if (!code.deopt_already_counted()) {
446 code.set_deopt_already_counted(true);
447 }
448 DeoptimizeMarkedCodeForContext(function.context().native_context());
449 // TODO(mythria): Ideally EvictMarkCode should compact the cache without
450 // having to explicitly call this. We don't do this currently because
451 // compacting causes GC and DeoptimizeMarkedCodeForContext uses raw
452 // pointers. Update DeoptimizeMarkedCodeForContext to use handles and remove
453 // this call from here.
454 OSROptimizedCodeCache::Compact(
455 Handle<NativeContext>(function.context().native_context(), isolate));
456 }
457 }
458
ComputeOutputFrames(Deoptimizer * deoptimizer)459 void Deoptimizer::ComputeOutputFrames(Deoptimizer* deoptimizer) {
460 deoptimizer->DoComputeOutputFrames();
461 }
462
MessageFor(DeoptimizeKind kind,bool reuse_code)463 const char* Deoptimizer::MessageFor(DeoptimizeKind kind, bool reuse_code) {
464 DCHECK_IMPLIES(reuse_code, kind == DeoptimizeKind::kSoft);
465 switch (kind) {
466 case DeoptimizeKind::kEager:
467 return "deopt-eager";
468 case DeoptimizeKind::kSoft:
469 return reuse_code ? "bailout-soft" : "deopt-soft";
470 case DeoptimizeKind::kLazy:
471 return "deopt-lazy";
472 case DeoptimizeKind::kBailout:
473 return "bailout";
474 }
475 }
476
477 namespace {
478
InternalFormalParameterCountWithReceiver(SharedFunctionInfo sfi)479 uint16_t InternalFormalParameterCountWithReceiver(SharedFunctionInfo sfi) {
480 static constexpr int kTheReceiver = 1;
481 return sfi.internal_formal_parameter_count() + kTheReceiver;
482 }
483
484 } // namespace
485
Deoptimizer(Isolate * isolate,JSFunction function,DeoptimizeKind kind,unsigned bailout_id,Address from,int fp_to_sp_delta)486 Deoptimizer::Deoptimizer(Isolate* isolate, JSFunction function,
487 DeoptimizeKind kind, unsigned bailout_id, Address from,
488 int fp_to_sp_delta)
489 : isolate_(isolate),
490 function_(function),
491 bailout_id_(bailout_id),
492 deopt_kind_(kind),
493 from_(from),
494 fp_to_sp_delta_(fp_to_sp_delta),
495 deoptimizing_throw_(false),
496 catch_handler_data_(-1),
497 catch_handler_pc_offset_(-1),
498 input_(nullptr),
499 output_count_(0),
500 jsframe_count_(0),
501 output_(nullptr),
502 caller_frame_top_(0),
503 caller_fp_(0),
504 caller_pc_(0),
505 caller_constant_pool_(0),
506 input_frame_context_(0),
507 actual_argument_count_(0),
508 stack_fp_(0),
509 trace_scope_(FLAG_trace_deopt
510 ? new CodeTracer::Scope(isolate->GetCodeTracer())
511 : nullptr) {
512 if (isolate->deoptimizer_lazy_throw()) {
513 isolate->set_deoptimizer_lazy_throw(false);
514 deoptimizing_throw_ = true;
515 }
516
517 DCHECK(bailout_id_ == kFixedExitSizeMarker ||
518 bailout_id_ < kMaxNumberOfEntries);
519
520 DCHECK_NE(from, kNullAddress);
521 compiled_code_ = FindOptimizedCode();
522 DCHECK(!compiled_code_.is_null());
523
524 DCHECK(function.IsJSFunction());
525 #ifdef DEBUG
526 DCHECK(AllowHeapAllocation::IsAllowed());
527 DCHECK(AllowGarbageCollection::IsAllowed());
528 disallow_garbage_collection_ = new DisallowGarbageCollection();
529 #endif // DEBUG
530 CHECK(CodeKindCanDeoptimize(compiled_code_.kind()));
531 if (!compiled_code_.deopt_already_counted() &&
532 deopt_kind_ == DeoptimizeKind::kSoft) {
533 isolate->counters()->soft_deopts_executed()->Increment();
534 }
535 compiled_code_.set_deopt_already_counted(true);
536 {
537 HandleScope scope(isolate_);
538 PROFILE(isolate_,
539 CodeDeoptEvent(handle(compiled_code_, isolate_), kind, from_,
540 fp_to_sp_delta_, should_reuse_code()));
541 }
542 unsigned size = ComputeInputFrameSize();
543 const int parameter_count =
544 InternalFormalParameterCountWithReceiver(function.shared());
545 input_ = new (size) FrameDescription(size, parameter_count);
546
547 if (kSupportsFixedDeoptExitSizes) {
548 DCHECK_EQ(bailout_id_, kFixedExitSizeMarker);
549 // Calculate bailout id from return address.
550 DCHECK_GT(kNonLazyDeoptExitSize, 0);
551 DCHECK_GT(kLazyDeoptExitSize, 0);
552 DeoptimizationData deopt_data =
553 DeoptimizationData::cast(compiled_code_.deoptimization_data());
554 Address deopt_start = compiled_code_.raw_instruction_start() +
555 deopt_data.DeoptExitStart().value();
556 int non_lazy_deopt_count = deopt_data.NonLazyDeoptCount().value();
557 Address lazy_deopt_start =
558 deopt_start + non_lazy_deopt_count * kNonLazyDeoptExitSize;
559 // The deoptimization exits are sorted so that lazy deopt exits appear last.
560 static_assert(DeoptimizeKind::kLazy == kLastDeoptimizeKind,
561 "lazy deopts are expected to be emitted last");
562 // from_ is the value of the link register after the call to the
563 // deoptimizer, so for the last lazy deopt, from_ points to the first
564 // non-lazy deopt, so we use <=.
565 if (from_ <= lazy_deopt_start) {
566 int offset =
567 static_cast<int>(from_ - kNonLazyDeoptExitSize - deopt_start);
568 DCHECK_EQ(0, offset % kNonLazyDeoptExitSize);
569 bailout_id_ = offset / kNonLazyDeoptExitSize;
570 } else {
571 int offset =
572 static_cast<int>(from_ - kLazyDeoptExitSize - lazy_deopt_start);
573 DCHECK_EQ(0, offset % kLazyDeoptExitSize);
574 bailout_id_ = non_lazy_deopt_count + (offset / kLazyDeoptExitSize);
575 }
576 }
577 }
578
FindOptimizedCode()579 Code Deoptimizer::FindOptimizedCode() {
580 Code compiled_code = FindDeoptimizingCode(from_);
581 return !compiled_code.is_null() ? compiled_code
582 : isolate_->FindCodeObject(from_);
583 }
584
function() const585 Handle<JSFunction> Deoptimizer::function() const {
586 return Handle<JSFunction>(function_, isolate());
587 }
compiled_code() const588 Handle<Code> Deoptimizer::compiled_code() const {
589 return Handle<Code>(compiled_code_, isolate());
590 }
591
should_reuse_code() const592 bool Deoptimizer::should_reuse_code() const {
593 int count = compiled_code_.deoptimization_count();
594 return deopt_kind_ == DeoptimizeKind::kSoft &&
595 count < FLAG_reuse_opt_code_count;
596 }
597
~Deoptimizer()598 Deoptimizer::~Deoptimizer() {
599 DCHECK(input_ == nullptr && output_ == nullptr);
600 DCHECK_NULL(disallow_garbage_collection_);
601 }
602
DeleteFrameDescriptions()603 void Deoptimizer::DeleteFrameDescriptions() {
604 delete input_;
605 for (int i = 0; i < output_count_; ++i) {
606 if (output_[i] != input_) delete output_[i];
607 }
608 delete[] output_;
609 input_ = nullptr;
610 output_ = nullptr;
611 #ifdef DEBUG
612 DCHECK(!AllowGarbageCollection::IsAllowed());
613 DCHECK_NOT_NULL(disallow_garbage_collection_);
614 delete disallow_garbage_collection_;
615 disallow_garbage_collection_ = nullptr;
616 #endif // DEBUG
617 }
618
GetDeoptimizationEntry(Isolate * isolate,DeoptimizeKind kind)619 Builtins::Name Deoptimizer::GetDeoptimizationEntry(Isolate* isolate,
620 DeoptimizeKind kind) {
621 switch (kind) {
622 case DeoptimizeKind::kEager:
623 return Builtins::kDeoptimizationEntry_Eager;
624 case DeoptimizeKind::kSoft:
625 return Builtins::kDeoptimizationEntry_Soft;
626 case DeoptimizeKind::kBailout:
627 return Builtins::kDeoptimizationEntry_Bailout;
628 case DeoptimizeKind::kLazy:
629 return Builtins::kDeoptimizationEntry_Lazy;
630 }
631 }
632
IsDeoptimizationEntry(Isolate * isolate,Address addr,DeoptimizeKind * type_out)633 bool Deoptimizer::IsDeoptimizationEntry(Isolate* isolate, Address addr,
634 DeoptimizeKind* type_out) {
635 Code maybe_code = InstructionStream::TryLookupCode(isolate, addr);
636 if (maybe_code.is_null()) return false;
637
638 Code code = maybe_code;
639 switch (code.builtin_index()) {
640 case Builtins::kDeoptimizationEntry_Eager:
641 *type_out = DeoptimizeKind::kEager;
642 return true;
643 case Builtins::kDeoptimizationEntry_Soft:
644 *type_out = DeoptimizeKind::kSoft;
645 return true;
646 case Builtins::kDeoptimizationEntry_Bailout:
647 *type_out = DeoptimizeKind::kBailout;
648 return true;
649 case Builtins::kDeoptimizationEntry_Lazy:
650 *type_out = DeoptimizeKind::kLazy;
651 return true;
652 default:
653 return false;
654 }
655
656 UNREACHABLE();
657 }
658
GetDeoptimizedCodeCount(Isolate * isolate)659 int Deoptimizer::GetDeoptimizedCodeCount(Isolate* isolate) {
660 int length = 0;
661 // Count all entries in the deoptimizing code list of every context.
662 Object context = isolate->heap()->native_contexts_list();
663 while (!context.IsUndefined(isolate)) {
664 NativeContext native_context = NativeContext::cast(context);
665 Object element = native_context.DeoptimizedCodeListHead();
666 while (!element.IsUndefined(isolate)) {
667 Code code = Code::cast(element);
668 DCHECK(CodeKindCanDeoptimize(code.kind()));
669 if (!code.marked_for_deoptimization()) {
670 length++;
671 }
672 element = code.next_code_link();
673 }
674 context = Context::cast(context).next_context_link();
675 }
676 return length;
677 }
678
679 namespace {
680
LookupCatchHandler(TranslatedFrame * translated_frame,int * data_out)681 int LookupCatchHandler(TranslatedFrame* translated_frame, int* data_out) {
682 switch (translated_frame->kind()) {
683 case TranslatedFrame::kInterpretedFunction: {
684 int bytecode_offset = translated_frame->node_id().ToInt();
685 HandlerTable table(
686 translated_frame->raw_shared_info().GetBytecodeArray());
687 return table.LookupRange(bytecode_offset, data_out, nullptr);
688 }
689 case TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch: {
690 return 0;
691 }
692 default:
693 break;
694 }
695 return -1;
696 }
697
698 } // namespace
699
TraceDeoptBegin(int optimization_id,int node_id)700 void Deoptimizer::TraceDeoptBegin(int optimization_id, int node_id) {
701 DCHECK(tracing_enabled());
702 FILE* file = trace_scope()->file();
703 Deoptimizer::DeoptInfo info =
704 Deoptimizer::GetDeoptInfo(compiled_code_, from_);
705 PrintF(file, "[bailout (kind: %s, reason: %s): begin. deoptimizing ",
706 MessageFor(deopt_kind_, should_reuse_code()),
707 DeoptimizeReasonToString(info.deopt_reason));
708 if (function_.IsJSFunction()) {
709 function_.ShortPrint(file);
710 } else {
711 PrintF(file, "%s", CodeKindToString(compiled_code_.kind()));
712 }
713 PrintF(file,
714 ", opt id %d, node id %d, bailout id %d, FP to SP delta %d, "
715 "caller SP " V8PRIxPTR_FMT ", pc " V8PRIxPTR_FMT "]\n",
716 optimization_id, node_id, bailout_id_, fp_to_sp_delta_,
717 caller_frame_top_, PointerAuthentication::StripPAC(from_));
718 if (verbose_tracing_enabled() && deopt_kind_ != DeoptimizeKind::kLazy) {
719 PrintF(file, " ;;; deoptimize at ");
720 OFStream outstr(file);
721 info.position.Print(outstr, compiled_code_);
722 PrintF(file, "\n");
723 }
724 }
725
TraceDeoptEnd(double deopt_duration)726 void Deoptimizer::TraceDeoptEnd(double deopt_duration) {
727 DCHECK(verbose_tracing_enabled());
728 PrintF(trace_scope()->file(), "[bailout end. took %0.3f ms]\n",
729 deopt_duration);
730 }
731
732 // static
TraceMarkForDeoptimization(Code code,const char * reason)733 void Deoptimizer::TraceMarkForDeoptimization(Code code, const char* reason) {
734 if (!FLAG_trace_deopt_verbose) return;
735
736 DisallowHeapAllocation no_gc;
737 Isolate* isolate = code.GetIsolate();
738 Object maybe_data = code.deoptimization_data();
739 if (maybe_data == ReadOnlyRoots(isolate).empty_fixed_array()) return;
740
741 DeoptimizationData deopt_data = DeoptimizationData::cast(maybe_data);
742 CodeTracer::Scope scope(isolate->GetCodeTracer());
743 PrintF(scope.file(), "[marking dependent code " V8PRIxPTR_FMT " (",
744 code.ptr());
745 deopt_data.SharedFunctionInfo().ShortPrint(scope.file());
746 PrintF(") (opt id %d) for deoptimization, reason: %s]\n",
747 deopt_data.OptimizationId().value(), reason);
748 {
749 AllowHeapAllocation yes_gc;
750 HandleScope scope(isolate);
751 PROFILE(
752 isolate,
753 CodeDependencyChangeEvent(
754 handle(code, isolate),
755 handle(SharedFunctionInfo::cast(deopt_data.SharedFunctionInfo()),
756 isolate),
757 reason));
758 }
759 }
760
761 // static
TraceEvictFromOptimizedCodeCache(SharedFunctionInfo sfi,const char * reason)762 void Deoptimizer::TraceEvictFromOptimizedCodeCache(SharedFunctionInfo sfi,
763 const char* reason) {
764 if (!FLAG_trace_deopt_verbose) return;
765
766 DisallowHeapAllocation no_gc;
767 CodeTracer::Scope scope(sfi.GetIsolate()->GetCodeTracer());
768 PrintF(scope.file(),
769 "[evicting optimized code marked for deoptimization (%s) for ",
770 reason);
771 sfi.ShortPrint(scope.file());
772 PrintF(scope.file(), "]\n");
773 }
774
775 #ifdef DEBUG
776 // static
TraceFoundActivation(Isolate * isolate,JSFunction function)777 void Deoptimizer::TraceFoundActivation(Isolate* isolate, JSFunction function) {
778 if (!FLAG_trace_deopt_verbose) return;
779 CodeTracer::Scope scope(isolate->GetCodeTracer());
780 PrintF(scope.file(), "[deoptimizer found activation of function: ");
781 function.PrintName(scope.file());
782 PrintF(scope.file(), " / %" V8PRIxPTR "]\n", function.ptr());
783 }
784 #endif // DEBUG
785
786 // static
TraceDeoptAll(Isolate * isolate)787 void Deoptimizer::TraceDeoptAll(Isolate* isolate) {
788 if (!FLAG_trace_deopt_verbose) return;
789 CodeTracer::Scope scope(isolate->GetCodeTracer());
790 PrintF(scope.file(), "[deoptimize all code in all contexts]\n");
791 }
792
793 // static
TraceDeoptMarked(Isolate * isolate)794 void Deoptimizer::TraceDeoptMarked(Isolate* isolate) {
795 if (!FLAG_trace_deopt_verbose) return;
796 CodeTracer::Scope scope(isolate->GetCodeTracer());
797 PrintF(scope.file(), "[deoptimize marked code in all contexts]\n");
798 }
799
800 // We rely on this function not causing a GC. It is called from generated code
801 // without having a real stack frame in place.
DoComputeOutputFrames()802 void Deoptimizer::DoComputeOutputFrames() {
803 // When we call this function, the return address of the previous frame has
804 // been removed from the stack by the DeoptimizationEntry builtin, so the
805 // stack is not iterable by the SafeStackFrameIterator.
806 #if V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK
807 DCHECK_EQ(0, isolate()->isolate_data()->stack_is_iterable());
808 #endif
809 base::ElapsedTimer timer;
810
811 // Determine basic deoptimization information. The optimized frame is
812 // described by the input data.
813 DeoptimizationData input_data =
814 DeoptimizationData::cast(compiled_code_.deoptimization_data());
815
816 {
817 // Read caller's PC, caller's FP and caller's constant pool values
818 // from input frame. Compute caller's frame top address.
819
820 Register fp_reg = JavaScriptFrame::fp_register();
821 stack_fp_ = input_->GetRegister(fp_reg.code());
822
823 caller_frame_top_ = stack_fp_ + ComputeInputFrameAboveFpFixedSize();
824
825 Address fp_address = input_->GetFramePointerAddress();
826 caller_fp_ = Memory<intptr_t>(fp_address);
827 caller_pc_ =
828 Memory<intptr_t>(fp_address + CommonFrameConstants::kCallerPCOffset);
829 input_frame_context_ = Memory<intptr_t>(
830 fp_address + CommonFrameConstants::kContextOrFrameTypeOffset);
831 actual_argument_count_ = static_cast<int>(
832 Memory<intptr_t>(fp_address + StandardFrameConstants::kArgCOffset));
833
834 if (FLAG_enable_embedded_constant_pool) {
835 caller_constant_pool_ = Memory<intptr_t>(
836 fp_address + CommonFrameConstants::kConstantPoolOffset);
837 }
838 }
839
840 StackGuard* const stack_guard = isolate()->stack_guard();
841 CHECK_GT(static_cast<uintptr_t>(caller_frame_top_),
842 stack_guard->real_jslimit());
843
844 BailoutId node_id = input_data.BytecodeOffset(bailout_id_);
845 ByteArray translations = input_data.TranslationByteArray();
846 unsigned translation_index = input_data.TranslationIndex(bailout_id_).value();
847
848 if (tracing_enabled()) {
849 timer.Start();
850 TraceDeoptBegin(input_data.OptimizationId().value(), node_id.ToInt());
851 }
852
853 FILE* trace_file =
854 verbose_tracing_enabled() ? trace_scope()->file() : nullptr;
855 TranslationIterator state_iterator(translations, translation_index);
856 translated_state_.Init(
857 isolate_, input_->GetFramePointerAddress(), stack_fp_, &state_iterator,
858 input_data.LiteralArray(), input_->GetRegisterValues(), trace_file,
859 function_.IsHeapObject()
860 ? function_.shared().internal_formal_parameter_count()
861 : 0,
862 actual_argument_count_);
863
864 // Do the input frame to output frame(s) translation.
865 size_t count = translated_state_.frames().size();
866 // If we are supposed to go to the catch handler, find the catching frame
867 // for the catch and make sure we only deoptimize up to that frame.
868 if (deoptimizing_throw_) {
869 size_t catch_handler_frame_index = count;
870 for (size_t i = count; i-- > 0;) {
871 catch_handler_pc_offset_ = LookupCatchHandler(
872 &(translated_state_.frames()[i]), &catch_handler_data_);
873 if (catch_handler_pc_offset_ >= 0) {
874 catch_handler_frame_index = i;
875 break;
876 }
877 }
878 CHECK_LT(catch_handler_frame_index, count);
879 count = catch_handler_frame_index + 1;
880 }
881
882 DCHECK_NULL(output_);
883 output_ = new FrameDescription*[count];
884 for (size_t i = 0; i < count; ++i) {
885 output_[i] = nullptr;
886 }
887 output_count_ = static_cast<int>(count);
888
889 // Translate each output frame.
890 int frame_index = 0; // output_frame_index
891 size_t total_output_frame_size = 0;
892 for (size_t i = 0; i < count; ++i, ++frame_index) {
893 // Read the ast node id, function, and frame height for this output frame.
894 TranslatedFrame* translated_frame = &(translated_state_.frames()[i]);
895 bool handle_exception = deoptimizing_throw_ && i == count - 1;
896 switch (translated_frame->kind()) {
897 case TranslatedFrame::kInterpretedFunction:
898 DoComputeInterpretedFrame(translated_frame, frame_index,
899 handle_exception);
900 jsframe_count_++;
901 break;
902 case TranslatedFrame::kArgumentsAdaptor:
903 DoComputeArgumentsAdaptorFrame(translated_frame, frame_index);
904 break;
905 case TranslatedFrame::kConstructStub:
906 DoComputeConstructStubFrame(translated_frame, frame_index);
907 break;
908 case TranslatedFrame::kBuiltinContinuation:
909 DoComputeBuiltinContinuation(translated_frame, frame_index,
910 BuiltinContinuationMode::STUB);
911 break;
912 case TranslatedFrame::kJavaScriptBuiltinContinuation:
913 DoComputeBuiltinContinuation(translated_frame, frame_index,
914 BuiltinContinuationMode::JAVASCRIPT);
915 break;
916 case TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch:
917 DoComputeBuiltinContinuation(
918 translated_frame, frame_index,
919 handle_exception
920 ? BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION
921 : BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH);
922 break;
923 case TranslatedFrame::kInvalid:
924 FATAL("invalid frame");
925 break;
926 }
927 total_output_frame_size += output_[frame_index]->GetFrameSize();
928 }
929
930 FrameDescription* topmost = output_[count - 1];
931 topmost->GetRegisterValues()->SetRegister(kRootRegister.code(),
932 isolate()->isolate_root());
933
934 // Print some helpful diagnostic information.
935 if (verbose_tracing_enabled()) {
936 TraceDeoptEnd(timer.Elapsed().InMillisecondsF());
937 }
938
939 // The following invariant is fairly tricky to guarantee, since the size of
940 // an optimized frame and its deoptimized counterparts usually differs. We
941 // thus need to consider the case in which deoptimized frames are larger than
942 // the optimized frame in stack checks in optimized code. We do this by
943 // applying an offset to stack checks (see kArchStackPointerGreaterThan in the
944 // code generator).
945 // Note that we explicitly allow deopts to exceed the limit by a certain
946 // number of slack bytes.
947 CHECK_GT(
948 static_cast<uintptr_t>(caller_frame_top_) - total_output_frame_size,
949 stack_guard->real_jslimit() - kStackLimitSlackForDeoptimizationInBytes);
950 }
951
DoComputeInterpretedFrame(TranslatedFrame * translated_frame,int frame_index,bool goto_catch_handler)952 void Deoptimizer::DoComputeInterpretedFrame(TranslatedFrame* translated_frame,
953 int frame_index,
954 bool goto_catch_handler) {
955 SharedFunctionInfo shared = translated_frame->raw_shared_info();
956
957 TranslatedFrame::iterator value_iterator = translated_frame->begin();
958 const bool is_bottommost = (0 == frame_index);
959 const bool is_topmost = (output_count_ - 1 == frame_index);
960
961 const int real_bytecode_offset = translated_frame->node_id().ToInt();
962 const int bytecode_offset =
963 goto_catch_handler ? catch_handler_pc_offset_ : real_bytecode_offset;
964
965 const int parameters_count = InternalFormalParameterCountWithReceiver(shared);
966
967 #ifdef V8_NO_ARGUMENTS_ADAPTOR
968 // If this is the bottom most frame or the previous frame was the arguments
969 // adaptor fake frame, then we already have extra arguments in the stack
970 // (including any extra padding). Therefore we should not try to add any
971 // padding.
972 bool should_pad_arguments =
973 !is_bottommost && (translated_state_.frames()[frame_index - 1]).kind() !=
974 TranslatedFrame::kArgumentsAdaptor;
975 #else
976 bool should_pad_arguments = true;
977 #endif
978
979 const int locals_count = translated_frame->height();
980 InterpretedFrameInfo frame_info = InterpretedFrameInfo::Precise(
981 parameters_count, locals_count, is_topmost, should_pad_arguments);
982 const uint32_t output_frame_size = frame_info.frame_size_in_bytes();
983
984 TranslatedFrame::iterator function_iterator = value_iterator++;
985 if (verbose_tracing_enabled()) {
986 PrintF(trace_scope()->file(), " translating interpreted frame ");
987 std::unique_ptr<char[]> name = shared.DebugName().ToCString();
988 PrintF(trace_scope()->file(), "%s", name.get());
989 PrintF(trace_scope()->file(),
990 " => bytecode_offset=%d, variable_frame_size=%d, frame_size=%d%s\n",
991 real_bytecode_offset, frame_info.frame_size_in_bytes_without_fixed(),
992 output_frame_size, goto_catch_handler ? " (throw)" : "");
993 }
994
995 // Allocate and store the output frame description.
996 FrameDescription* output_frame = new (output_frame_size)
997 FrameDescription(output_frame_size, parameters_count);
998 FrameWriter frame_writer(this, output_frame, verbose_trace_scope());
999
1000 CHECK(frame_index >= 0 && frame_index < output_count_);
1001 CHECK_NULL(output_[frame_index]);
1002 output_[frame_index] = output_frame;
1003
1004 // The top address of the frame is computed from the previous frame's top and
1005 // this frame's size.
1006 const intptr_t top_address =
1007 is_bottommost ? caller_frame_top_ - output_frame_size
1008 : output_[frame_index - 1]->GetTop() - output_frame_size;
1009 output_frame->SetTop(top_address);
1010
1011 // Compute the incoming parameter translation.
1012 ReadOnlyRoots roots(isolate());
1013 if (should_pad_arguments && ShouldPadArguments(parameters_count)) {
1014 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1015 }
1016
1017 // Note: parameters_count includes the receiver.
1018 if (verbose_tracing_enabled() && is_bottommost &&
1019 actual_argument_count_ > parameters_count - 1) {
1020 PrintF(trace_scope_->file(),
1021 " -- %d extra argument(s) already in the stack --\n",
1022 actual_argument_count_ - parameters_count + 1);
1023 }
1024 frame_writer.PushStackJSArguments(value_iterator, parameters_count);
1025
1026 DCHECK_EQ(output_frame->GetLastArgumentSlotOffset(should_pad_arguments),
1027 frame_writer.top_offset());
1028 if (verbose_tracing_enabled()) {
1029 PrintF(trace_scope()->file(), " -------------------------\n");
1030 }
1031
1032 // There are no translation commands for the caller's pc and fp, the
1033 // context, the function and the bytecode offset. Synthesize
1034 // their values and set them up
1035 // explicitly.
1036 //
1037 // The caller's pc for the bottommost output frame is the same as in the
1038 // input frame. For all subsequent output frames, it can be read from the
1039 // previous one. This frame's pc can be computed from the non-optimized
1040 // function code and AST id of the bailout.
1041 if (is_bottommost) {
1042 frame_writer.PushBottommostCallerPc(caller_pc_);
1043 } else {
1044 frame_writer.PushApprovedCallerPc(output_[frame_index - 1]->GetPc());
1045 }
1046
1047 // The caller's frame pointer for the bottommost output frame is the same
1048 // as in the input frame. For all subsequent output frames, it can be
1049 // read from the previous one. Also compute and set this frame's frame
1050 // pointer.
1051 const intptr_t caller_fp =
1052 is_bottommost ? caller_fp_ : output_[frame_index - 1]->GetFp();
1053 frame_writer.PushCallerFp(caller_fp);
1054
1055 const intptr_t fp_value = top_address + frame_writer.top_offset();
1056 output_frame->SetFp(fp_value);
1057 if (is_topmost) {
1058 Register fp_reg = InterpretedFrame::fp_register();
1059 output_frame->SetRegister(fp_reg.code(), fp_value);
1060 }
1061
1062 if (FLAG_enable_embedded_constant_pool) {
1063 // For the bottommost output frame the constant pool pointer can be gotten
1064 // from the input frame. For subsequent output frames, it can be read from
1065 // the previous frame.
1066 const intptr_t caller_cp =
1067 is_bottommost ? caller_constant_pool_
1068 : output_[frame_index - 1]->GetConstantPool();
1069 frame_writer.PushCallerConstantPool(caller_cp);
1070 }
1071
1072 // For the bottommost output frame the context can be gotten from the input
1073 // frame. For all subsequent output frames it can be gotten from the function
1074 // so long as we don't inline functions that need local contexts.
1075
1076 // When deoptimizing into a catch block, we need to take the context
1077 // from a register that was specified in the handler table.
1078 TranslatedFrame::iterator context_pos = value_iterator++;
1079 if (goto_catch_handler) {
1080 // Skip to the translated value of the register specified
1081 // in the handler table.
1082 for (int i = 0; i < catch_handler_data_ + 1; ++i) {
1083 context_pos++;
1084 }
1085 }
1086 // Read the context from the translations.
1087 Object context = context_pos->GetRawValue();
1088 output_frame->SetContext(static_cast<intptr_t>(context.ptr()));
1089 frame_writer.PushTranslatedValue(context_pos, "context");
1090
1091 // The function was mentioned explicitly in the BEGIN_FRAME.
1092 frame_writer.PushTranslatedValue(function_iterator, "function");
1093
1094 // Actual argument count.
1095 int argc;
1096 if (is_bottommost) {
1097 argc = actual_argument_count_;
1098 } else {
1099 TranslatedFrame::Kind previous_frame_kind =
1100 (translated_state_.frames()[frame_index - 1]).kind();
1101 argc = previous_frame_kind == TranslatedFrame::kArgumentsAdaptor
1102 ? output_[frame_index - 1]->parameter_count()
1103 : parameters_count - 1;
1104 }
1105 frame_writer.PushRawValue(argc, "actual argument count\n");
1106
1107 // Set the bytecode array pointer.
1108 Object bytecode_array = shared.HasBreakInfo()
1109 ? shared.GetDebugInfo().DebugBytecodeArray()
1110 : shared.GetBytecodeArray();
1111 frame_writer.PushRawObject(bytecode_array, "bytecode array\n");
1112
1113 // The bytecode offset was mentioned explicitly in the BEGIN_FRAME.
1114 const int raw_bytecode_offset =
1115 BytecodeArray::kHeaderSize - kHeapObjectTag + bytecode_offset;
1116 Smi smi_bytecode_offset = Smi::FromInt(raw_bytecode_offset);
1117 frame_writer.PushRawObject(smi_bytecode_offset, "bytecode offset\n");
1118
1119 if (verbose_tracing_enabled()) {
1120 PrintF(trace_scope()->file(), " -------------------------\n");
1121 }
1122
1123 // Translate the rest of the interpreter registers in the frame.
1124 // The return_value_offset is counted from the top. Here, we compute the
1125 // register index (counted from the start).
1126 const int return_value_first_reg =
1127 locals_count - translated_frame->return_value_offset();
1128 const int return_value_count = translated_frame->return_value_count();
1129 for (int i = 0; i < locals_count; ++i, ++value_iterator) {
1130 // Ensure we write the return value if we have one and we are returning
1131 // normally to a lazy deopt point.
1132 if (is_topmost && !goto_catch_handler &&
1133 deopt_kind_ == DeoptimizeKind::kLazy && i >= return_value_first_reg &&
1134 i < return_value_first_reg + return_value_count) {
1135 const int return_index = i - return_value_first_reg;
1136 if (return_index == 0) {
1137 frame_writer.PushRawValue(input_->GetRegister(kReturnRegister0.code()),
1138 "return value 0\n");
1139 // We do not handle the situation when one return value should go into
1140 // the accumulator and another one into an ordinary register. Since
1141 // the interpreter should never create such situation, just assert
1142 // this does not happen.
1143 CHECK_LE(return_value_first_reg + return_value_count, locals_count);
1144 } else {
1145 CHECK_EQ(return_index, 1);
1146 frame_writer.PushRawValue(input_->GetRegister(kReturnRegister1.code()),
1147 "return value 1\n");
1148 }
1149 } else {
1150 // This is not return value, just write the value from the translations.
1151 frame_writer.PushTranslatedValue(value_iterator, "stack parameter");
1152 }
1153 }
1154
1155 uint32_t register_slots_written = static_cast<uint32_t>(locals_count);
1156 DCHECK_LE(register_slots_written, frame_info.register_stack_slot_count());
1157 // Some architectures must pad the stack frame with extra stack slots
1158 // to ensure the stack frame is aligned. Do this now.
1159 while (register_slots_written < frame_info.register_stack_slot_count()) {
1160 register_slots_written++;
1161 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1162 }
1163
1164 // Translate the accumulator register (depending on frame position).
1165 if (is_topmost) {
1166 if (kPadArguments) {
1167 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1168 }
1169 // For topmost frame, put the accumulator on the stack. The
1170 // {NotifyDeoptimized} builtin pops it off the topmost frame (possibly
1171 // after materialization).
1172 if (goto_catch_handler) {
1173 // If we are lazy deopting to a catch handler, we set the accumulator to
1174 // the exception (which lives in the result register).
1175 intptr_t accumulator_value =
1176 input_->GetRegister(kInterpreterAccumulatorRegister.code());
1177 frame_writer.PushRawObject(Object(accumulator_value), "accumulator\n");
1178 } else {
1179 // If we are lazily deoptimizing make sure we store the deopt
1180 // return value into the appropriate slot.
1181 if (deopt_kind_ == DeoptimizeKind::kLazy &&
1182 translated_frame->return_value_offset() == 0 &&
1183 translated_frame->return_value_count() > 0) {
1184 CHECK_EQ(translated_frame->return_value_count(), 1);
1185 frame_writer.PushRawValue(input_->GetRegister(kReturnRegister0.code()),
1186 "return value 0\n");
1187 } else {
1188 frame_writer.PushTranslatedValue(value_iterator, "accumulator");
1189 }
1190 }
1191 ++value_iterator; // Move over the accumulator.
1192 } else {
1193 // For non-topmost frames, skip the accumulator translation. For those
1194 // frames, the return value from the callee will become the accumulator.
1195 ++value_iterator;
1196 }
1197 CHECK_EQ(translated_frame->end(), value_iterator);
1198 CHECK_EQ(0u, frame_writer.top_offset());
1199
1200 // Compute this frame's PC and state. The PC will be a special builtin that
1201 // continues the bytecode dispatch. Note that non-topmost and lazy-style
1202 // bailout handlers also advance the bytecode offset before dispatch, hence
1203 // simulating what normal handlers do upon completion of the operation.
1204 Builtins* builtins = isolate_->builtins();
1205 Code dispatch_builtin =
1206 (!is_topmost || (deopt_kind_ == DeoptimizeKind::kLazy)) &&
1207 !goto_catch_handler
1208 ? builtins->builtin(Builtins::kInterpreterEnterBytecodeAdvance)
1209 : builtins->builtin(Builtins::kInterpreterEnterBytecodeDispatch);
1210 if (is_topmost) {
1211 // Only the pc of the topmost frame needs to be signed since it is
1212 // authenticated at the end of the DeoptimizationEntry builtin.
1213 const intptr_t top_most_pc = PointerAuthentication::SignAndCheckPC(
1214 static_cast<intptr_t>(dispatch_builtin.InstructionStart()),
1215 frame_writer.frame()->GetTop());
1216 output_frame->SetPc(top_most_pc);
1217 } else {
1218 output_frame->SetPc(
1219 static_cast<intptr_t>(dispatch_builtin.InstructionStart()));
1220 }
1221
1222 // Update constant pool.
1223 if (FLAG_enable_embedded_constant_pool) {
1224 intptr_t constant_pool_value =
1225 static_cast<intptr_t>(dispatch_builtin.constant_pool());
1226 output_frame->SetConstantPool(constant_pool_value);
1227 if (is_topmost) {
1228 Register constant_pool_reg =
1229 InterpretedFrame::constant_pool_pointer_register();
1230 output_frame->SetRegister(constant_pool_reg.code(), constant_pool_value);
1231 }
1232 }
1233
1234 // Clear the context register. The context might be a de-materialized object
1235 // and will be materialized by {Runtime_NotifyDeoptimized}. For additional
1236 // safety we use Smi(0) instead of the potential {arguments_marker} here.
1237 if (is_topmost) {
1238 intptr_t context_value = static_cast<intptr_t>(Smi::zero().ptr());
1239 Register context_reg = JavaScriptFrame::context_register();
1240 output_frame->SetRegister(context_reg.code(), context_value);
1241 // Set the continuation for the topmost frame.
1242 Code continuation = builtins->builtin(Builtins::kNotifyDeoptimized);
1243 output_frame->SetContinuation(
1244 static_cast<intptr_t>(continuation.InstructionStart()));
1245 }
1246 }
1247
DoComputeArgumentsAdaptorFrame(TranslatedFrame * translated_frame,int frame_index)1248 void Deoptimizer::DoComputeArgumentsAdaptorFrame(
1249 TranslatedFrame* translated_frame, int frame_index) {
1250 // Arguments adaptor can not be top most, nor the bottom most frames.
1251 CHECK(frame_index < output_count_ - 1);
1252 CHECK_GT(frame_index, 0);
1253 CHECK_NULL(output_[frame_index]);
1254
1255 #ifdef V8_NO_ARGUMENTS_ADAPTOR
1256 // During execution, V8 does not understand arguments adaptor frames anymore,
1257 // so during deoptimization we only push the extra arguments (arguments with
1258 // index greater than the formal parameter count). Therefore we call this
1259 // TranslatedFrame the fake adaptor frame. For more info, see the design
1260 // document shorturl.at/fKT49.
1261
1262 TranslatedFrame::iterator value_iterator = translated_frame->begin();
1263 const int argument_count_without_receiver = translated_frame->height() - 1;
1264 const int formal_parameter_count =
1265 translated_frame->raw_shared_info().internal_formal_parameter_count();
1266 const int extra_argument_count =
1267 argument_count_without_receiver - formal_parameter_count;
1268 // The number of pushed arguments is the maximum of the actual argument count
1269 // and the formal parameter count + the receiver.
1270 const bool should_pad_args = ShouldPadArguments(
1271 std::max(argument_count_without_receiver, formal_parameter_count) + 1);
1272 const int output_frame_size =
1273 std::max(0, extra_argument_count * kSystemPointerSize) +
1274 (should_pad_args ? kSystemPointerSize : 0);
1275 if (verbose_tracing_enabled()) {
1276 PrintF(trace_scope_->file(),
1277 " translating arguments adaptor => variable_size=%d\n",
1278 output_frame_size);
1279 }
1280
1281 // Allocate and store the output frame description.
1282 FrameDescription* output_frame = new (output_frame_size)
1283 FrameDescription(output_frame_size, argument_count_without_receiver);
1284 // The top address of the frame is computed from the previous frame's top and
1285 // this frame's size.
1286 const intptr_t top_address =
1287 output_[frame_index - 1]->GetTop() - output_frame_size;
1288 output_frame->SetTop(top_address);
1289 // This is not a real frame, we take PC and FP values from the parent frame.
1290 output_frame->SetPc(output_[frame_index - 1]->GetPc());
1291 output_frame->SetFp(output_[frame_index - 1]->GetFp());
1292 output_[frame_index] = output_frame;
1293
1294 FrameWriter frame_writer(this, output_frame, verbose_trace_scope());
1295
1296 ReadOnlyRoots roots(isolate());
1297 if (should_pad_args) {
1298 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1299 }
1300
1301 if (extra_argument_count > 0) {
1302 // The receiver and arguments with index below the formal parameter
1303 // count are in the fake adaptor frame, because they are used to create the
1304 // arguments object. We should however not push them, since the interpreter
1305 // frame with do that.
1306 value_iterator++; // Skip function.
1307 value_iterator++; // Skip receiver.
1308 for (int i = 0; i < formal_parameter_count; i++) value_iterator++;
1309 frame_writer.PushStackJSArguments(value_iterator, extra_argument_count);
1310 }
1311 #else
1312 TranslatedFrame::iterator value_iterator = translated_frame->begin();
1313 const bool is_bottommost = (0 == frame_index);
1314
1315 const int parameters_count = translated_frame->height();
1316 ArgumentsAdaptorFrameInfo frame_info =
1317 ArgumentsAdaptorFrameInfo::Precise(parameters_count);
1318 const uint32_t output_frame_size = frame_info.frame_size_in_bytes();
1319
1320 TranslatedFrame::iterator function_iterator = value_iterator++;
1321 if (verbose_tracing_enabled()) {
1322 PrintF(trace_scope()->file(),
1323 " translating arguments adaptor => variable_frame_size=%d, "
1324 "frame_size=%d\n",
1325 frame_info.frame_size_in_bytes_without_fixed(), output_frame_size);
1326 }
1327
1328 // Allocate and store the output frame description.
1329 FrameDescription* output_frame = new (output_frame_size)
1330 FrameDescription(output_frame_size, parameters_count);
1331 FrameWriter frame_writer(this, output_frame, verbose_trace_scope());
1332
1333 // Arguments adaptor can not be topmost.
1334 CHECK(frame_index < output_count_ - 1);
1335 CHECK_NULL(output_[frame_index]);
1336 output_[frame_index] = output_frame;
1337
1338 // The top address of the frame is computed from the previous frame's top and
1339 // this frame's size.
1340 const intptr_t top_address =
1341 is_bottommost ? caller_frame_top_ - output_frame_size
1342 : output_[frame_index - 1]->GetTop() - output_frame_size;
1343 output_frame->SetTop(top_address);
1344
1345 ReadOnlyRoots roots(isolate());
1346 if (ShouldPadArguments(parameters_count)) {
1347 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1348 }
1349
1350 // Compute the incoming parameter translation.
1351 frame_writer.PushStackJSArguments(value_iterator, parameters_count);
1352
1353 DCHECK_EQ(output_frame->GetLastArgumentSlotOffset(),
1354 frame_writer.top_offset());
1355
1356 // Read caller's PC from the previous frame.
1357 if (is_bottommost) {
1358 frame_writer.PushBottommostCallerPc(caller_pc_);
1359 } else {
1360 frame_writer.PushApprovedCallerPc(output_[frame_index - 1]->GetPc());
1361 }
1362
1363 // Read caller's FP from the previous frame, and set this frame's FP.
1364 const intptr_t caller_fp =
1365 is_bottommost ? caller_fp_ : output_[frame_index - 1]->GetFp();
1366 frame_writer.PushCallerFp(caller_fp);
1367
1368 intptr_t fp_value = top_address + frame_writer.top_offset();
1369 output_frame->SetFp(fp_value);
1370
1371 if (FLAG_enable_embedded_constant_pool) {
1372 // Read the caller's constant pool from the previous frame.
1373 const intptr_t caller_cp =
1374 is_bottommost ? caller_constant_pool_
1375 : output_[frame_index - 1]->GetConstantPool();
1376 frame_writer.PushCallerConstantPool(caller_cp);
1377 }
1378
1379 // A marker value is used in place of the context.
1380 intptr_t marker = StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR);
1381 frame_writer.PushRawValue(marker, "context (adaptor sentinel)\n");
1382
1383 // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
1384 frame_writer.PushTranslatedValue(function_iterator, "function\n");
1385
1386 // Number of incoming arguments.
1387 const uint32_t parameters_count_without_receiver = parameters_count - 1;
1388 frame_writer.PushRawObject(Smi::FromInt(parameters_count_without_receiver),
1389 "argc\n");
1390
1391 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1392
1393 CHECK_EQ(translated_frame->end(), value_iterator);
1394 DCHECK_EQ(0, frame_writer.top_offset());
1395
1396 Builtins* builtins = isolate_->builtins();
1397 Code adaptor_trampoline =
1398 builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
1399 intptr_t pc_value = static_cast<intptr_t>(
1400 adaptor_trampoline.InstructionStart() +
1401 isolate_->heap()->arguments_adaptor_deopt_pc_offset().value());
1402 output_frame->SetPc(pc_value);
1403 if (FLAG_enable_embedded_constant_pool) {
1404 intptr_t constant_pool_value =
1405 static_cast<intptr_t>(adaptor_trampoline.constant_pool());
1406 output_frame->SetConstantPool(constant_pool_value);
1407 }
1408 #endif
1409 }
1410
DoComputeConstructStubFrame(TranslatedFrame * translated_frame,int frame_index)1411 void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
1412 int frame_index) {
1413 TranslatedFrame::iterator value_iterator = translated_frame->begin();
1414 const bool is_topmost = (output_count_ - 1 == frame_index);
1415 // The construct frame could become topmost only if we inlined a constructor
1416 // call which does a tail call (otherwise the tail callee's frame would be
1417 // the topmost one). So it could only be the DeoptimizeKind::kLazy case.
1418 CHECK(!is_topmost || deopt_kind_ == DeoptimizeKind::kLazy);
1419
1420 Builtins* builtins = isolate_->builtins();
1421 Code construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric);
1422 BailoutId bailout_id = translated_frame->node_id();
1423
1424 const int parameters_count = translated_frame->height();
1425 ConstructStubFrameInfo frame_info =
1426 ConstructStubFrameInfo::Precise(parameters_count, is_topmost);
1427 const uint32_t output_frame_size = frame_info.frame_size_in_bytes();
1428
1429 TranslatedFrame::iterator function_iterator = value_iterator++;
1430 if (verbose_tracing_enabled()) {
1431 PrintF(trace_scope()->file(),
1432 " translating construct stub => bailout_id=%d (%s), "
1433 "variable_frame_size=%d, frame_size=%d\n",
1434 bailout_id.ToInt(),
1435 bailout_id == BailoutId::ConstructStubCreate() ? "create" : "invoke",
1436 frame_info.frame_size_in_bytes_without_fixed(), output_frame_size);
1437 }
1438
1439 // Allocate and store the output frame description.
1440 FrameDescription* output_frame = new (output_frame_size)
1441 FrameDescription(output_frame_size, parameters_count);
1442 FrameWriter frame_writer(this, output_frame, verbose_trace_scope());
1443
1444 // Construct stub can not be topmost.
1445 DCHECK(frame_index > 0 && frame_index < output_count_);
1446 DCHECK_NULL(output_[frame_index]);
1447 output_[frame_index] = output_frame;
1448
1449 // The top address of the frame is computed from the previous frame's top and
1450 // this frame's size.
1451 const intptr_t top_address =
1452 output_[frame_index - 1]->GetTop() - output_frame_size;
1453 output_frame->SetTop(top_address);
1454
1455 ReadOnlyRoots roots(isolate());
1456 if (ShouldPadArguments(parameters_count)) {
1457 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1458 }
1459
1460 // The allocated receiver of a construct stub frame is passed as the
1461 // receiver parameter through the translation. It might be encoding
1462 // a captured object, so we need save it for later.
1463 TranslatedFrame::iterator receiver_iterator = value_iterator;
1464
1465 // Compute the incoming parameter translation.
1466 frame_writer.PushStackJSArguments(value_iterator, parameters_count);
1467
1468 DCHECK_EQ(output_frame->GetLastArgumentSlotOffset(),
1469 frame_writer.top_offset());
1470
1471 // Read caller's PC from the previous frame.
1472 const intptr_t caller_pc = output_[frame_index - 1]->GetPc();
1473 frame_writer.PushApprovedCallerPc(caller_pc);
1474
1475 // Read caller's FP from the previous frame, and set this frame's FP.
1476 const intptr_t caller_fp = output_[frame_index - 1]->GetFp();
1477 frame_writer.PushCallerFp(caller_fp);
1478
1479 const intptr_t fp_value = top_address + frame_writer.top_offset();
1480 output_frame->SetFp(fp_value);
1481 if (is_topmost) {
1482 Register fp_reg = JavaScriptFrame::fp_register();
1483 output_frame->SetRegister(fp_reg.code(), fp_value);
1484 }
1485
1486 if (FLAG_enable_embedded_constant_pool) {
1487 // Read the caller's constant pool from the previous frame.
1488 const intptr_t caller_cp = output_[frame_index - 1]->GetConstantPool();
1489 frame_writer.PushCallerConstantPool(caller_cp);
1490 }
1491
1492 // A marker value is used to mark the frame.
1493 intptr_t marker = StackFrame::TypeToMarker(StackFrame::CONSTRUCT);
1494 frame_writer.PushRawValue(marker, "context (construct stub sentinel)\n");
1495
1496 frame_writer.PushTranslatedValue(value_iterator++, "context");
1497
1498 // Number of incoming arguments.
1499 const uint32_t parameters_count_without_receiver = parameters_count - 1;
1500 frame_writer.PushRawObject(Smi::FromInt(parameters_count_without_receiver),
1501 "argc\n");
1502
1503 // The constructor function was mentioned explicitly in the
1504 // CONSTRUCT_STUB_FRAME.
1505 frame_writer.PushTranslatedValue(function_iterator, "constructor function\n");
1506
1507 // The deopt info contains the implicit receiver or the new target at the
1508 // position of the receiver. Copy it to the top of stack, with the hole value
1509 // as padding to maintain alignment.
1510
1511 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1512
1513 CHECK(bailout_id == BailoutId::ConstructStubCreate() ||
1514 bailout_id == BailoutId::ConstructStubInvoke());
1515 const char* debug_hint = bailout_id == BailoutId::ConstructStubCreate()
1516 ? "new target\n"
1517 : "allocated receiver\n";
1518 frame_writer.PushTranslatedValue(receiver_iterator, debug_hint);
1519
1520 if (is_topmost) {
1521 if (kPadArguments) {
1522 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1523 }
1524 // Ensure the result is restored back when we return to the stub.
1525 Register result_reg = kReturnRegister0;
1526 intptr_t result = input_->GetRegister(result_reg.code());
1527 frame_writer.PushRawValue(result, "subcall result\n");
1528 }
1529
1530 CHECK_EQ(translated_frame->end(), value_iterator);
1531 CHECK_EQ(0u, frame_writer.top_offset());
1532
1533 // Compute this frame's PC.
1534 DCHECK(bailout_id.IsValidForConstructStub());
1535 Address start = construct_stub.InstructionStart();
1536 const int pc_offset =
1537 bailout_id == BailoutId::ConstructStubCreate()
1538 ? isolate_->heap()->construct_stub_create_deopt_pc_offset().value()
1539 : isolate_->heap()->construct_stub_invoke_deopt_pc_offset().value();
1540 intptr_t pc_value = static_cast<intptr_t>(start + pc_offset);
1541 if (is_topmost) {
1542 // Only the pc of the topmost frame needs to be signed since it is
1543 // authenticated at the end of the DeoptimizationEntry builtin.
1544 output_frame->SetPc(PointerAuthentication::SignAndCheckPC(
1545 pc_value, frame_writer.frame()->GetTop()));
1546 } else {
1547 output_frame->SetPc(pc_value);
1548 }
1549
1550 // Update constant pool.
1551 if (FLAG_enable_embedded_constant_pool) {
1552 intptr_t constant_pool_value =
1553 static_cast<intptr_t>(construct_stub.constant_pool());
1554 output_frame->SetConstantPool(constant_pool_value);
1555 if (is_topmost) {
1556 Register constant_pool_reg =
1557 JavaScriptFrame::constant_pool_pointer_register();
1558 output_frame->SetRegister(constant_pool_reg.code(), constant_pool_value);
1559 }
1560 }
1561
1562 // Clear the context register. The context might be a de-materialized object
1563 // and will be materialized by {Runtime_NotifyDeoptimized}. For additional
1564 // safety we use Smi(0) instead of the potential {arguments_marker} here.
1565 if (is_topmost) {
1566 intptr_t context_value = static_cast<intptr_t>(Smi::zero().ptr());
1567 Register context_reg = JavaScriptFrame::context_register();
1568 output_frame->SetRegister(context_reg.code(), context_value);
1569 }
1570
1571 // Set the continuation for the topmost frame.
1572 if (is_topmost) {
1573 Builtins* builtins = isolate_->builtins();
1574 DCHECK_EQ(DeoptimizeKind::kLazy, deopt_kind_);
1575 Code continuation = builtins->builtin(Builtins::kNotifyDeoptimized);
1576 output_frame->SetContinuation(
1577 static_cast<intptr_t>(continuation.InstructionStart()));
1578 }
1579 }
1580
1581 namespace {
1582
BuiltinContinuationModeIsJavaScript(BuiltinContinuationMode mode)1583 bool BuiltinContinuationModeIsJavaScript(BuiltinContinuationMode mode) {
1584 switch (mode) {
1585 case BuiltinContinuationMode::STUB:
1586 return false;
1587 case BuiltinContinuationMode::JAVASCRIPT:
1588 case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
1589 case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
1590 return true;
1591 }
1592 UNREACHABLE();
1593 }
1594
BuiltinContinuationModeToFrameType(BuiltinContinuationMode mode)1595 StackFrame::Type BuiltinContinuationModeToFrameType(
1596 BuiltinContinuationMode mode) {
1597 switch (mode) {
1598 case BuiltinContinuationMode::STUB:
1599 return StackFrame::BUILTIN_CONTINUATION;
1600 case BuiltinContinuationMode::JAVASCRIPT:
1601 return StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION;
1602 case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
1603 return StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH;
1604 case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
1605 return StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH;
1606 }
1607 UNREACHABLE();
1608 }
1609
1610 } // namespace
1611
TrampolineForBuiltinContinuation(BuiltinContinuationMode mode,bool must_handle_result)1612 Builtins::Name Deoptimizer::TrampolineForBuiltinContinuation(
1613 BuiltinContinuationMode mode, bool must_handle_result) {
1614 switch (mode) {
1615 case BuiltinContinuationMode::STUB:
1616 return must_handle_result ? Builtins::kContinueToCodeStubBuiltinWithResult
1617 : Builtins::kContinueToCodeStubBuiltin;
1618 case BuiltinContinuationMode::JAVASCRIPT:
1619 case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
1620 case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
1621 return must_handle_result
1622 ? Builtins::kContinueToJavaScriptBuiltinWithResult
1623 : Builtins::kContinueToJavaScriptBuiltin;
1624 }
1625 UNREACHABLE();
1626 }
1627
1628 // BuiltinContinuationFrames capture the machine state that is expected as input
1629 // to a builtin, including both input register values and stack parameters. When
1630 // the frame is reactivated (i.e. the frame below it returns), a
1631 // ContinueToBuiltin stub restores the register state from the frame and tail
1632 // calls to the actual target builtin, making it appear that the stub had been
1633 // directly called by the frame above it. The input values to populate the frame
1634 // are taken from the deopt's FrameState.
1635 //
1636 // Frame translation happens in two modes, EAGER and LAZY. In EAGER mode, all of
1637 // the parameters to the Builtin are explicitly specified in the TurboFan
1638 // FrameState node. In LAZY mode, there is always one fewer parameters specified
1639 // in the FrameState than expected by the Builtin. In that case, construction of
1640 // BuiltinContinuationFrame adds the final missing parameter during
1641 // deoptimization, and that parameter is always on the stack and contains the
1642 // value returned from the callee of the call site triggering the LAZY deopt
1643 // (e.g. rax on x64). This requires that continuation Builtins for LAZY deopts
1644 // must have at least one stack parameter.
1645 //
1646 // TO
1647 // | .... |
1648 // +-------------------------+
1649 // | arg padding (arch dept) |<- at most 1*kSystemPointerSize
1650 // +-------------------------+
1651 // | builtin param 0 |<- FrameState input value n becomes
1652 // +-------------------------+
1653 // | ... |
1654 // +-------------------------+
1655 // | builtin param m |<- FrameState input value n+m-1, or in
1656 // +-----needs-alignment-----+ the LAZY case, return LAZY result value
1657 // | ContinueToBuiltin entry |
1658 // +-------------------------+
1659 // | | saved frame (FP) |
1660 // | +=====needs=alignment=====+<- fpreg
1661 // | |constant pool (if ool_cp)|
1662 // v +-------------------------+
1663 // |BUILTIN_CONTINUATION mark|
1664 // +-------------------------+
1665 // | JSFunction (or zero) |<- only if JavaScript builtin
1666 // +-------------------------+
1667 // | frame height above FP |
1668 // +-------------------------+
1669 // | context |<- this non-standard context slot contains
1670 // +-------------------------+ the context, even for non-JS builtins.
1671 // | builtin index |
1672 // +-------------------------+
1673 // | builtin input GPR reg0 |<- populated from deopt FrameState using
1674 // +-------------------------+ the builtin's CallInterfaceDescriptor
1675 // | ... | to map a FrameState's 0..n-1 inputs to
1676 // +-------------------------+ the builtin's n input register params.
1677 // | builtin input GPR regn |
1678 // +-------------------------+
1679 // | reg padding (arch dept) |
1680 // +-----needs--alignment----+
1681 // | res padding (arch dept) |<- only if {is_topmost}; result is pop'd by
1682 // +-------------------------+<- kNotifyDeopt ASM stub and moved to acc
1683 // | result value |<- reg, as ContinueToBuiltin stub expects.
1684 // +-----needs-alignment-----+<- spreg
1685 //
DoComputeBuiltinContinuation(TranslatedFrame * translated_frame,int frame_index,BuiltinContinuationMode mode)1686 void Deoptimizer::DoComputeBuiltinContinuation(
1687 TranslatedFrame* translated_frame, int frame_index,
1688 BuiltinContinuationMode mode) {
1689 TranslatedFrame::iterator value_iterator = translated_frame->begin();
1690
1691 const BailoutId bailout_id = translated_frame->node_id();
1692 Builtins::Name builtin_name = Builtins::GetBuiltinFromBailoutId(bailout_id);
1693 CallInterfaceDescriptor continuation_descriptor =
1694 Builtins::CallInterfaceDescriptorFor(builtin_name);
1695
1696 const RegisterConfiguration* config = RegisterConfiguration::Default();
1697
1698 const bool is_bottommost = (0 == frame_index);
1699 const bool is_topmost = (output_count_ - 1 == frame_index);
1700
1701 const int parameters_count = translated_frame->height();
1702 BuiltinContinuationFrameInfo frame_info =
1703 BuiltinContinuationFrameInfo::Precise(parameters_count,
1704 continuation_descriptor, config,
1705 is_topmost, deopt_kind_, mode);
1706
1707 const unsigned output_frame_size = frame_info.frame_size_in_bytes();
1708 const unsigned output_frame_size_above_fp =
1709 frame_info.frame_size_in_bytes_above_fp();
1710
1711 // Validate types of parameters. They must all be tagged except for argc for
1712 // JS builtins.
1713 bool has_argc = false;
1714 const int register_parameter_count =
1715 continuation_descriptor.GetRegisterParameterCount();
1716 for (int i = 0; i < register_parameter_count; ++i) {
1717 MachineType type = continuation_descriptor.GetParameterType(i);
1718 int code = continuation_descriptor.GetRegisterParameter(i).code();
1719 // Only tagged and int32 arguments are supported, and int32 only for the
1720 // arguments count on JavaScript builtins.
1721 if (type == MachineType::Int32()) {
1722 CHECK_EQ(code, kJavaScriptCallArgCountRegister.code());
1723 has_argc = true;
1724 } else {
1725 // Any other argument must be a tagged value.
1726 CHECK(IsAnyTagged(type.representation()));
1727 }
1728 }
1729 CHECK_EQ(BuiltinContinuationModeIsJavaScript(mode), has_argc);
1730
1731 if (verbose_tracing_enabled()) {
1732 PrintF(trace_scope()->file(),
1733 " translating BuiltinContinuation to %s,"
1734 " => register_param_count=%d,"
1735 " stack_param_count=%d, frame_size=%d\n",
1736 Builtins::name(builtin_name), register_parameter_count,
1737 frame_info.stack_parameter_count(), output_frame_size);
1738 }
1739
1740 FrameDescription* output_frame = new (output_frame_size)
1741 FrameDescription(output_frame_size, frame_info.stack_parameter_count());
1742 output_[frame_index] = output_frame;
1743 FrameWriter frame_writer(this, output_frame, verbose_trace_scope());
1744
1745 // The top address of the frame is computed from the previous frame's top and
1746 // this frame's size.
1747 const intptr_t top_address =
1748 is_bottommost ? caller_frame_top_ - output_frame_size
1749 : output_[frame_index - 1]->GetTop() - output_frame_size;
1750 output_frame->SetTop(top_address);
1751
1752 // Get the possible JSFunction for the case that this is a
1753 // JavaScriptBuiltinContinuationFrame, which needs the JSFunction pointer
1754 // like a normal JavaScriptFrame.
1755 const intptr_t maybe_function = value_iterator->GetRawValue().ptr();
1756 ++value_iterator;
1757
1758 ReadOnlyRoots roots(isolate());
1759 if (ShouldPadArguments(frame_info.stack_parameter_count())) {
1760 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1761 }
1762
1763 if (mode == BuiltinContinuationMode::STUB) {
1764 DCHECK_EQ(Builtins::CallInterfaceDescriptorFor(builtin_name)
1765 .GetStackArgumentOrder(),
1766 StackArgumentOrder::kDefault);
1767 for (uint32_t i = 0; i < frame_info.translated_stack_parameter_count();
1768 ++i, ++value_iterator) {
1769 frame_writer.PushTranslatedValue(value_iterator, "stack parameter");
1770 }
1771 if (frame_info.frame_has_result_stack_slot()) {
1772 frame_writer.PushRawObject(
1773 roots.the_hole_value(),
1774 "placeholder for return result on lazy deopt\n");
1775 }
1776 } else {
1777 // JavaScript builtin.
1778 if (frame_info.frame_has_result_stack_slot()) {
1779 frame_writer.PushRawObject(
1780 roots.the_hole_value(),
1781 "placeholder for return result on lazy deopt\n");
1782 }
1783 switch (mode) {
1784 case BuiltinContinuationMode::STUB:
1785 UNREACHABLE();
1786 case BuiltinContinuationMode::JAVASCRIPT:
1787 break;
1788 case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH: {
1789 frame_writer.PushRawObject(roots.the_hole_value(),
1790 "placeholder for exception on lazy deopt\n");
1791 } break;
1792 case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION: {
1793 intptr_t accumulator_value =
1794 input_->GetRegister(kInterpreterAccumulatorRegister.code());
1795 frame_writer.PushRawObject(Object(accumulator_value),
1796 "exception (from accumulator)\n");
1797 } break;
1798 }
1799 frame_writer.PushStackJSArguments(
1800 value_iterator, frame_info.translated_stack_parameter_count());
1801 }
1802
1803 DCHECK_EQ(output_frame->GetLastArgumentSlotOffset(),
1804 frame_writer.top_offset());
1805
1806 std::vector<TranslatedFrame::iterator> register_values;
1807 int total_registers = config->num_general_registers();
1808 register_values.resize(total_registers, {value_iterator});
1809
1810 for (int i = 0; i < register_parameter_count; ++i, ++value_iterator) {
1811 int code = continuation_descriptor.GetRegisterParameter(i).code();
1812 register_values[code] = value_iterator;
1813 }
1814
1815 // The context register is always implicit in the CallInterfaceDescriptor but
1816 // its register must be explicitly set when continuing to the builtin. Make
1817 // sure that it's harvested from the translation and copied into the register
1818 // set (it was automatically added at the end of the FrameState by the
1819 // instruction selector).
1820 Object context = value_iterator->GetRawValue();
1821 const intptr_t value = context.ptr();
1822 TranslatedFrame::iterator context_register_value = value_iterator++;
1823 register_values[kContextRegister.code()] = context_register_value;
1824 output_frame->SetContext(value);
1825 output_frame->SetRegister(kContextRegister.code(), value);
1826
1827 // Set caller's PC (JSFunction continuation).
1828 if (is_bottommost) {
1829 frame_writer.PushBottommostCallerPc(caller_pc_);
1830 } else {
1831 frame_writer.PushApprovedCallerPc(output_[frame_index - 1]->GetPc());
1832 }
1833
1834 // Read caller's FP from the previous frame, and set this frame's FP.
1835 const intptr_t caller_fp =
1836 is_bottommost ? caller_fp_ : output_[frame_index - 1]->GetFp();
1837 frame_writer.PushCallerFp(caller_fp);
1838
1839 const intptr_t fp_value = top_address + frame_writer.top_offset();
1840 output_frame->SetFp(fp_value);
1841
1842 DCHECK_EQ(output_frame_size_above_fp, frame_writer.top_offset());
1843
1844 if (FLAG_enable_embedded_constant_pool) {
1845 // Read the caller's constant pool from the previous frame.
1846 const intptr_t caller_cp =
1847 is_bottommost ? caller_constant_pool_
1848 : output_[frame_index - 1]->GetConstantPool();
1849 frame_writer.PushCallerConstantPool(caller_cp);
1850 }
1851
1852 // A marker value is used in place of the context.
1853 const intptr_t marker =
1854 StackFrame::TypeToMarker(BuiltinContinuationModeToFrameType(mode));
1855 frame_writer.PushRawValue(marker,
1856 "context (builtin continuation sentinel)\n");
1857
1858 if (BuiltinContinuationModeIsJavaScript(mode)) {
1859 frame_writer.PushRawValue(maybe_function, "JSFunction\n");
1860 } else {
1861 frame_writer.PushRawValue(0, "unused\n");
1862 }
1863
1864 // The delta from the SP to the FP; used to reconstruct SP in
1865 // Isolate::UnwindAndFindHandler.
1866 frame_writer.PushRawObject(Smi::FromInt(output_frame_size_above_fp),
1867 "frame height at deoptimization\n");
1868
1869 // The context even if this is a stub contininuation frame. We can't use the
1870 // usual context slot, because we must store the frame marker there.
1871 frame_writer.PushTranslatedValue(context_register_value,
1872 "builtin JavaScript context\n");
1873
1874 // The builtin to continue to.
1875 frame_writer.PushRawObject(Smi::FromInt(builtin_name), "builtin index\n");
1876
1877 const int allocatable_register_count =
1878 config->num_allocatable_general_registers();
1879 for (int i = 0; i < allocatable_register_count; ++i) {
1880 int code = config->GetAllocatableGeneralCode(i);
1881 ScopedVector<char> str(128);
1882 if (verbose_tracing_enabled()) {
1883 if (BuiltinContinuationModeIsJavaScript(mode) &&
1884 code == kJavaScriptCallArgCountRegister.code()) {
1885 SNPrintF(
1886 str,
1887 "tagged argument count %s (will be untagged by continuation)\n",
1888 RegisterName(Register::from_code(code)));
1889 } else {
1890 SNPrintF(str, "builtin register argument %s\n",
1891 RegisterName(Register::from_code(code)));
1892 }
1893 }
1894 frame_writer.PushTranslatedValue(
1895 register_values[code], verbose_tracing_enabled() ? str.begin() : "");
1896 }
1897
1898 // Some architectures must pad the stack frame with extra stack slots
1899 // to ensure the stack frame is aligned.
1900 const int padding_slot_count =
1901 BuiltinContinuationFrameConstants::PaddingSlotCount(
1902 allocatable_register_count);
1903 for (int i = 0; i < padding_slot_count; ++i) {
1904 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1905 }
1906
1907 if (is_topmost) {
1908 if (kPadArguments) {
1909 frame_writer.PushRawObject(roots.the_hole_value(), "padding\n");
1910 }
1911
1912 // Ensure the result is restored back when we return to the stub.
1913 if (frame_info.frame_has_result_stack_slot()) {
1914 Register result_reg = kReturnRegister0;
1915 frame_writer.PushRawValue(input_->GetRegister(result_reg.code()),
1916 "callback result\n");
1917 } else {
1918 frame_writer.PushRawObject(roots.undefined_value(), "callback result\n");
1919 }
1920 }
1921
1922 CHECK_EQ(translated_frame->end(), value_iterator);
1923 CHECK_EQ(0u, frame_writer.top_offset());
1924
1925 // Clear the context register. The context might be a de-materialized object
1926 // and will be materialized by {Runtime_NotifyDeoptimized}. For additional
1927 // safety we use Smi(0) instead of the potential {arguments_marker} here.
1928 if (is_topmost) {
1929 intptr_t context_value = static_cast<intptr_t>(Smi::zero().ptr());
1930 Register context_reg = JavaScriptFrame::context_register();
1931 output_frame->SetRegister(context_reg.code(), context_value);
1932 }
1933
1934 // Ensure the frame pointer register points to the callee's frame. The builtin
1935 // will build its own frame once we continue to it.
1936 Register fp_reg = JavaScriptFrame::fp_register();
1937 output_frame->SetRegister(fp_reg.code(), fp_value);
1938
1939 Code continue_to_builtin =
1940 isolate()->builtins()->builtin(TrampolineForBuiltinContinuation(
1941 mode, frame_info.frame_has_result_stack_slot()));
1942 if (is_topmost) {
1943 // Only the pc of the topmost frame needs to be signed since it is
1944 // authenticated at the end of the DeoptimizationEntry builtin.
1945 const intptr_t top_most_pc = PointerAuthentication::SignAndCheckPC(
1946 static_cast<intptr_t>(continue_to_builtin.InstructionStart()),
1947 frame_writer.frame()->GetTop());
1948 output_frame->SetPc(top_most_pc);
1949 } else {
1950 output_frame->SetPc(
1951 static_cast<intptr_t>(continue_to_builtin.InstructionStart()));
1952 }
1953
1954 Code continuation =
1955 isolate()->builtins()->builtin(Builtins::kNotifyDeoptimized);
1956 output_frame->SetContinuation(
1957 static_cast<intptr_t>(continuation.InstructionStart()));
1958 }
1959
MaterializeHeapObjects()1960 void Deoptimizer::MaterializeHeapObjects() {
1961 translated_state_.Prepare(static_cast<Address>(stack_fp_));
1962 if (FLAG_deopt_every_n_times > 0) {
1963 // Doing a GC here will find problems with the deoptimized frames.
1964 isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags,
1965 GarbageCollectionReason::kTesting);
1966 }
1967
1968 for (auto& materialization : values_to_materialize_) {
1969 Handle<Object> value = materialization.value_->GetValue();
1970
1971 if (verbose_tracing_enabled()) {
1972 PrintF(trace_scope()->file(),
1973 "Materialization [" V8PRIxPTR_FMT "] <- " V8PRIxPTR_FMT " ; ",
1974 static_cast<intptr_t>(materialization.output_slot_address_),
1975 value->ptr());
1976 value->ShortPrint(trace_scope()->file());
1977 PrintF(trace_scope()->file(), "\n");
1978 }
1979
1980 *(reinterpret_cast<Address*>(materialization.output_slot_address_)) =
1981 value->ptr();
1982 }
1983
1984 translated_state_.VerifyMaterializedObjects();
1985
1986 bool feedback_updated = translated_state_.DoUpdateFeedback();
1987 if (verbose_tracing_enabled() && feedback_updated) {
1988 FILE* file = trace_scope()->file();
1989 Deoptimizer::DeoptInfo info =
1990 Deoptimizer::GetDeoptInfo(compiled_code_, from_);
1991 PrintF(file, "Feedback updated from deoptimization at ");
1992 OFStream outstr(file);
1993 info.position.Print(outstr, compiled_code_);
1994 PrintF(file, ", %s\n", DeoptimizeReasonToString(info.deopt_reason));
1995 }
1996
1997 isolate_->materialized_object_store()->Remove(
1998 static_cast<Address>(stack_fp_));
1999 }
2000
QueueValueForMaterialization(Address output_address,Object obj,const TranslatedFrame::iterator & iterator)2001 void Deoptimizer::QueueValueForMaterialization(
2002 Address output_address, Object obj,
2003 const TranslatedFrame::iterator& iterator) {
2004 if (obj == ReadOnlyRoots(isolate_).arguments_marker()) {
2005 values_to_materialize_.push_back({output_address, iterator});
2006 }
2007 }
2008
ComputeInputFrameAboveFpFixedSize() const2009 unsigned Deoptimizer::ComputeInputFrameAboveFpFixedSize() const {
2010 unsigned fixed_size = CommonFrameConstants::kFixedFrameSizeAboveFp;
2011 // TODO(jkummerow): If {function_->IsSmi()} can indeed be true, then
2012 // {function_} should not have type {JSFunction}.
2013 if (!function_.IsSmi()) {
2014 fixed_size += ComputeIncomingArgumentSize(function_.shared());
2015 }
2016 return fixed_size;
2017 }
2018
ComputeInputFrameSize() const2019 unsigned Deoptimizer::ComputeInputFrameSize() const {
2020 // The fp-to-sp delta already takes the context, constant pool pointer and the
2021 // function into account so we have to avoid double counting them.
2022 unsigned fixed_size_above_fp = ComputeInputFrameAboveFpFixedSize();
2023 unsigned result = fixed_size_above_fp + fp_to_sp_delta_;
2024 DCHECK(CodeKindCanDeoptimize(compiled_code_.kind()));
2025 unsigned stack_slots = compiled_code_.stack_slots();
2026 unsigned outgoing_size = 0;
2027 // ComputeOutgoingArgumentSize(compiled_code_, bailout_id_);
2028 CHECK_EQ(fixed_size_above_fp + (stack_slots * kSystemPointerSize) -
2029 CommonFrameConstants::kFixedFrameSizeAboveFp + outgoing_size,
2030 result);
2031 return result;
2032 }
2033
2034 // static
ComputeIncomingArgumentSize(SharedFunctionInfo shared)2035 unsigned Deoptimizer::ComputeIncomingArgumentSize(SharedFunctionInfo shared) {
2036 int parameter_slots = InternalFormalParameterCountWithReceiver(shared);
2037 #ifndef V8_NO_ARGUMENTS_ADAPTOR
2038 if (ShouldPadArguments(parameter_slots)) parameter_slots++;
2039 #endif
2040 return parameter_slots * kSystemPointerSize;
2041 }
2042
FrameDescription(uint32_t frame_size,int parameter_count)2043 FrameDescription::FrameDescription(uint32_t frame_size, int parameter_count)
2044 : frame_size_(frame_size),
2045 parameter_count_(parameter_count),
2046 top_(kZapUint32),
2047 pc_(kZapUint32),
2048 fp_(kZapUint32),
2049 context_(kZapUint32),
2050 constant_pool_(kZapUint32) {
2051 // Zap all the registers.
2052 for (int r = 0; r < Register::kNumRegisters; r++) {
2053 // TODO(jbramley): It isn't safe to use kZapUint32 here. If the register
2054 // isn't used before the next safepoint, the GC will try to scan it as a
2055 // tagged value. kZapUint32 looks like a valid tagged pointer, but it isn't.
2056 #if defined(V8_OS_WIN) && defined(V8_TARGET_ARCH_ARM64)
2057 // x18 is reserved as platform register on Windows arm64 platform
2058 const int kPlatformRegister = 18;
2059 if (r != kPlatformRegister) {
2060 SetRegister(r, kZapUint32);
2061 }
2062 #else
2063 SetRegister(r, kZapUint32);
2064 #endif
2065 }
2066
2067 // Zap all the slots.
2068 for (unsigned o = 0; o < frame_size; o += kSystemPointerSize) {
2069 SetFrameSlot(o, kZapUint32);
2070 }
2071 }
2072
Add(int32_t value)2073 void TranslationBuffer::Add(int32_t value) {
2074 // This wouldn't handle kMinInt correctly if it ever encountered it.
2075 DCHECK_NE(value, kMinInt);
2076 // Encode the sign bit in the least significant bit.
2077 bool is_negative = (value < 0);
2078 uint32_t bits = (static_cast<uint32_t>(is_negative ? -value : value) << 1) |
2079 static_cast<uint32_t>(is_negative);
2080 // Encode the individual bytes using the least significant bit of
2081 // each byte to indicate whether or not more bytes follow.
2082 do {
2083 uint32_t next = bits >> 7;
2084 contents_.push_back(((bits << 1) & 0xFF) | (next != 0));
2085 bits = next;
2086 } while (bits != 0);
2087 }
2088
TranslationIterator(ByteArray buffer,int index)2089 TranslationIterator::TranslationIterator(ByteArray buffer, int index)
2090 : buffer_(buffer), index_(index) {
2091 DCHECK(index >= 0 && index < buffer.length());
2092 }
2093
Next()2094 int32_t TranslationIterator::Next() {
2095 // Run through the bytes until we reach one with a least significant
2096 // bit of zero (marks the end).
2097 uint32_t bits = 0;
2098 for (int i = 0; true; i += 7) {
2099 DCHECK(HasNext());
2100 uint8_t next = buffer_.get(index_++);
2101 bits |= (next >> 1) << i;
2102 if ((next & 1) == 0) break;
2103 }
2104 // The bits encode the sign in the least significant bit.
2105 bool is_negative = (bits & 1) == 1;
2106 int32_t result = bits >> 1;
2107 return is_negative ? -result : result;
2108 }
2109
HasNext() const2110 bool TranslationIterator::HasNext() const { return index_ < buffer_.length(); }
2111
CreateByteArray(Factory * factory)2112 Handle<ByteArray> TranslationBuffer::CreateByteArray(Factory* factory) {
2113 Handle<ByteArray> result =
2114 factory->NewByteArray(CurrentIndex(), AllocationType::kOld);
2115 contents_.CopyTo(result->GetDataStartAddress());
2116 return result;
2117 }
2118
BeginBuiltinContinuationFrame(BailoutId bailout_id,int literal_id,unsigned height)2119 void Translation::BeginBuiltinContinuationFrame(BailoutId bailout_id,
2120 int literal_id,
2121 unsigned height) {
2122 buffer_->Add(BUILTIN_CONTINUATION_FRAME);
2123 buffer_->Add(bailout_id.ToInt());
2124 buffer_->Add(literal_id);
2125 buffer_->Add(height);
2126 }
2127
BeginJavaScriptBuiltinContinuationFrame(BailoutId bailout_id,int literal_id,unsigned height)2128 void Translation::BeginJavaScriptBuiltinContinuationFrame(BailoutId bailout_id,
2129 int literal_id,
2130 unsigned height) {
2131 buffer_->Add(JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME);
2132 buffer_->Add(bailout_id.ToInt());
2133 buffer_->Add(literal_id);
2134 buffer_->Add(height);
2135 }
2136
BeginJavaScriptBuiltinContinuationWithCatchFrame(BailoutId bailout_id,int literal_id,unsigned height)2137 void Translation::BeginJavaScriptBuiltinContinuationWithCatchFrame(
2138 BailoutId bailout_id, int literal_id, unsigned height) {
2139 buffer_->Add(JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME);
2140 buffer_->Add(bailout_id.ToInt());
2141 buffer_->Add(literal_id);
2142 buffer_->Add(height);
2143 }
2144
BeginConstructStubFrame(BailoutId bailout_id,int literal_id,unsigned height)2145 void Translation::BeginConstructStubFrame(BailoutId bailout_id, int literal_id,
2146 unsigned height) {
2147 buffer_->Add(CONSTRUCT_STUB_FRAME);
2148 buffer_->Add(bailout_id.ToInt());
2149 buffer_->Add(literal_id);
2150 buffer_->Add(height);
2151 }
2152
BeginArgumentsAdaptorFrame(int literal_id,unsigned height)2153 void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) {
2154 buffer_->Add(ARGUMENTS_ADAPTOR_FRAME);
2155 buffer_->Add(literal_id);
2156 buffer_->Add(height);
2157 }
2158
BeginInterpretedFrame(BailoutId bytecode_offset,int literal_id,unsigned height,int return_value_offset,int return_value_count)2159 void Translation::BeginInterpretedFrame(BailoutId bytecode_offset,
2160 int literal_id, unsigned height,
2161 int return_value_offset,
2162 int return_value_count) {
2163 buffer_->Add(INTERPRETED_FRAME);
2164 buffer_->Add(bytecode_offset.ToInt());
2165 buffer_->Add(literal_id);
2166 buffer_->Add(height);
2167 buffer_->Add(return_value_offset);
2168 buffer_->Add(return_value_count);
2169 }
2170
ArgumentsElements(CreateArgumentsType type)2171 void Translation::ArgumentsElements(CreateArgumentsType type) {
2172 buffer_->Add(ARGUMENTS_ELEMENTS);
2173 buffer_->Add(static_cast<uint8_t>(type));
2174 }
2175
ArgumentsLength()2176 void Translation::ArgumentsLength() { buffer_->Add(ARGUMENTS_LENGTH); }
2177
BeginCapturedObject(int length)2178 void Translation::BeginCapturedObject(int length) {
2179 buffer_->Add(CAPTURED_OBJECT);
2180 buffer_->Add(length);
2181 }
2182
DuplicateObject(int object_index)2183 void Translation::DuplicateObject(int object_index) {
2184 buffer_->Add(DUPLICATED_OBJECT);
2185 buffer_->Add(object_index);
2186 }
2187
StoreRegister(Register reg)2188 void Translation::StoreRegister(Register reg) {
2189 buffer_->Add(REGISTER);
2190 buffer_->Add(reg.code());
2191 }
2192
StoreInt32Register(Register reg)2193 void Translation::StoreInt32Register(Register reg) {
2194 buffer_->Add(INT32_REGISTER);
2195 buffer_->Add(reg.code());
2196 }
2197
StoreInt64Register(Register reg)2198 void Translation::StoreInt64Register(Register reg) {
2199 buffer_->Add(INT64_REGISTER);
2200 buffer_->Add(reg.code());
2201 }
2202
StoreUint32Register(Register reg)2203 void Translation::StoreUint32Register(Register reg) {
2204 buffer_->Add(UINT32_REGISTER);
2205 buffer_->Add(reg.code());
2206 }
2207
StoreBoolRegister(Register reg)2208 void Translation::StoreBoolRegister(Register reg) {
2209 buffer_->Add(BOOL_REGISTER);
2210 buffer_->Add(reg.code());
2211 }
2212
StoreFloatRegister(FloatRegister reg)2213 void Translation::StoreFloatRegister(FloatRegister reg) {
2214 buffer_->Add(FLOAT_REGISTER);
2215 buffer_->Add(reg.code());
2216 }
2217
StoreDoubleRegister(DoubleRegister reg)2218 void Translation::StoreDoubleRegister(DoubleRegister reg) {
2219 buffer_->Add(DOUBLE_REGISTER);
2220 buffer_->Add(reg.code());
2221 }
2222
StoreStackSlot(int index)2223 void Translation::StoreStackSlot(int index) {
2224 buffer_->Add(STACK_SLOT);
2225 buffer_->Add(index);
2226 }
2227
StoreInt32StackSlot(int index)2228 void Translation::StoreInt32StackSlot(int index) {
2229 buffer_->Add(INT32_STACK_SLOT);
2230 buffer_->Add(index);
2231 }
2232
StoreInt64StackSlot(int index)2233 void Translation::StoreInt64StackSlot(int index) {
2234 buffer_->Add(INT64_STACK_SLOT);
2235 buffer_->Add(index);
2236 }
2237
StoreUint32StackSlot(int index)2238 void Translation::StoreUint32StackSlot(int index) {
2239 buffer_->Add(UINT32_STACK_SLOT);
2240 buffer_->Add(index);
2241 }
2242
StoreBoolStackSlot(int index)2243 void Translation::StoreBoolStackSlot(int index) {
2244 buffer_->Add(BOOL_STACK_SLOT);
2245 buffer_->Add(index);
2246 }
2247
StoreFloatStackSlot(int index)2248 void Translation::StoreFloatStackSlot(int index) {
2249 buffer_->Add(FLOAT_STACK_SLOT);
2250 buffer_->Add(index);
2251 }
2252
StoreDoubleStackSlot(int index)2253 void Translation::StoreDoubleStackSlot(int index) {
2254 buffer_->Add(DOUBLE_STACK_SLOT);
2255 buffer_->Add(index);
2256 }
2257
StoreLiteral(int literal_id)2258 void Translation::StoreLiteral(int literal_id) {
2259 buffer_->Add(LITERAL);
2260 buffer_->Add(literal_id);
2261 }
2262
AddUpdateFeedback(int vector_literal,int slot)2263 void Translation::AddUpdateFeedback(int vector_literal, int slot) {
2264 buffer_->Add(UPDATE_FEEDBACK);
2265 buffer_->Add(vector_literal);
2266 buffer_->Add(slot);
2267 }
2268
StoreJSFrameFunction()2269 void Translation::StoreJSFrameFunction() {
2270 StoreStackSlot((StandardFrameConstants::kCallerPCOffset -
2271 StandardFrameConstants::kFunctionOffset) /
2272 kSystemPointerSize);
2273 }
2274
NumberOfOperandsFor(Opcode opcode)2275 int Translation::NumberOfOperandsFor(Opcode opcode) {
2276 switch (opcode) {
2277 case ARGUMENTS_LENGTH:
2278 return 0;
2279 case DUPLICATED_OBJECT:
2280 case ARGUMENTS_ELEMENTS:
2281 case CAPTURED_OBJECT:
2282 case REGISTER:
2283 case INT32_REGISTER:
2284 case INT64_REGISTER:
2285 case UINT32_REGISTER:
2286 case BOOL_REGISTER:
2287 case FLOAT_REGISTER:
2288 case DOUBLE_REGISTER:
2289 case STACK_SLOT:
2290 case INT32_STACK_SLOT:
2291 case INT64_STACK_SLOT:
2292 case UINT32_STACK_SLOT:
2293 case BOOL_STACK_SLOT:
2294 case FLOAT_STACK_SLOT:
2295 case DOUBLE_STACK_SLOT:
2296 case LITERAL:
2297 return 1;
2298 case ARGUMENTS_ADAPTOR_FRAME:
2299 case UPDATE_FEEDBACK:
2300 return 2;
2301 case BEGIN:
2302 case CONSTRUCT_STUB_FRAME:
2303 case BUILTIN_CONTINUATION_FRAME:
2304 case JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME:
2305 case JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME:
2306 return 3;
2307 case INTERPRETED_FRAME:
2308 return 5;
2309 }
2310 FATAL("Unexpected translation type");
2311 return -1;
2312 }
2313
2314 #if defined(OBJECT_PRINT) || defined(ENABLE_DISASSEMBLER)
2315
StringFor(Opcode opcode)2316 const char* Translation::StringFor(Opcode opcode) {
2317 #define TRANSLATION_OPCODE_CASE(item) \
2318 case item: \
2319 return #item;
2320 switch (opcode) { TRANSLATION_OPCODE_LIST(TRANSLATION_OPCODE_CASE) }
2321 #undef TRANSLATION_OPCODE_CASE
2322 UNREACHABLE();
2323 }
2324
2325 #endif
2326
Get(Address fp)2327 Handle<FixedArray> MaterializedObjectStore::Get(Address fp) {
2328 int index = StackIdToIndex(fp);
2329 if (index == -1) {
2330 return Handle<FixedArray>::null();
2331 }
2332 Handle<FixedArray> array = GetStackEntries();
2333 CHECK_GT(array->length(), index);
2334 return Handle<FixedArray>::cast(Handle<Object>(array->get(index), isolate()));
2335 }
2336
Set(Address fp,Handle<FixedArray> materialized_objects)2337 void MaterializedObjectStore::Set(Address fp,
2338 Handle<FixedArray> materialized_objects) {
2339 int index = StackIdToIndex(fp);
2340 if (index == -1) {
2341 index = static_cast<int>(frame_fps_.size());
2342 frame_fps_.push_back(fp);
2343 }
2344
2345 Handle<FixedArray> array = EnsureStackEntries(index + 1);
2346 array->set(index, *materialized_objects);
2347 }
2348
Remove(Address fp)2349 bool MaterializedObjectStore::Remove(Address fp) {
2350 auto it = std::find(frame_fps_.begin(), frame_fps_.end(), fp);
2351 if (it == frame_fps_.end()) return false;
2352 int index = static_cast<int>(std::distance(frame_fps_.begin(), it));
2353
2354 frame_fps_.erase(it);
2355 FixedArray array = isolate()->heap()->materialized_objects();
2356
2357 CHECK_LT(index, array.length());
2358 int fps_size = static_cast<int>(frame_fps_.size());
2359 for (int i = index; i < fps_size; i++) {
2360 array.set(i, array.get(i + 1));
2361 }
2362 array.set(fps_size, ReadOnlyRoots(isolate()).undefined_value());
2363 return true;
2364 }
2365
StackIdToIndex(Address fp)2366 int MaterializedObjectStore::StackIdToIndex(Address fp) {
2367 auto it = std::find(frame_fps_.begin(), frame_fps_.end(), fp);
2368 return it == frame_fps_.end()
2369 ? -1
2370 : static_cast<int>(std::distance(frame_fps_.begin(), it));
2371 }
2372
GetStackEntries()2373 Handle<FixedArray> MaterializedObjectStore::GetStackEntries() {
2374 return Handle<FixedArray>(isolate()->heap()->materialized_objects(),
2375 isolate());
2376 }
2377
EnsureStackEntries(int length)2378 Handle<FixedArray> MaterializedObjectStore::EnsureStackEntries(int length) {
2379 Handle<FixedArray> array = GetStackEntries();
2380 if (array->length() >= length) {
2381 return array;
2382 }
2383
2384 int new_length = length > 10 ? length : 10;
2385 if (new_length < 2 * array->length()) {
2386 new_length = 2 * array->length();
2387 }
2388
2389 Handle<FixedArray> new_array =
2390 isolate()->factory()->NewFixedArray(new_length, AllocationType::kOld);
2391 for (int i = 0; i < array->length(); i++) {
2392 new_array->set(i, array->get(i));
2393 }
2394 HeapObject undefined_value = ReadOnlyRoots(isolate()).undefined_value();
2395 for (int i = array->length(); i < length; i++) {
2396 new_array->set(i, undefined_value);
2397 }
2398 isolate()->heap()->SetRootMaterializedObjects(*new_array);
2399 return new_array;
2400 }
2401
2402 namespace {
2403
GetValueForDebugger(TranslatedFrame::iterator it,Isolate * isolate)2404 Handle<Object> GetValueForDebugger(TranslatedFrame::iterator it,
2405 Isolate* isolate) {
2406 if (it->GetRawValue() == ReadOnlyRoots(isolate).arguments_marker()) {
2407 if (!it->IsMaterializableByDebugger()) {
2408 return isolate->factory()->optimized_out();
2409 }
2410 }
2411 return it->GetValue();
2412 }
2413
2414 } // namespace
2415
DeoptimizedFrameInfo(TranslatedState * state,TranslatedState::iterator frame_it,Isolate * isolate)2416 DeoptimizedFrameInfo::DeoptimizedFrameInfo(TranslatedState* state,
2417 TranslatedState::iterator frame_it,
2418 Isolate* isolate) {
2419 int parameter_count =
2420 frame_it->shared_info()->internal_formal_parameter_count();
2421 TranslatedFrame::iterator stack_it = frame_it->begin();
2422
2423 // Get the function. Note that this might materialize the function.
2424 // In case the debugger mutates this value, we should deoptimize
2425 // the function and remember the value in the materialized value store.
2426 function_ = Handle<JSFunction>::cast(stack_it->GetValue());
2427 stack_it++; // Skip the function.
2428 stack_it++; // Skip the receiver.
2429
2430 DCHECK_EQ(TranslatedFrame::kInterpretedFunction, frame_it->kind());
2431 source_position_ = Deoptimizer::ComputeSourcePositionFromBytecodeArray(
2432 *frame_it->shared_info(), frame_it->node_id());
2433
2434 DCHECK_EQ(parameter_count,
2435 function_->shared().internal_formal_parameter_count());
2436
2437 parameters_.resize(static_cast<size_t>(parameter_count));
2438 for (int i = 0; i < parameter_count; i++) {
2439 Handle<Object> parameter = GetValueForDebugger(stack_it, isolate);
2440 SetParameter(i, parameter);
2441 stack_it++;
2442 }
2443
2444 // Get the context.
2445 context_ = GetValueForDebugger(stack_it, isolate);
2446 stack_it++;
2447
2448 // Get the expression stack.
2449 DCHECK_EQ(TranslatedFrame::kInterpretedFunction, frame_it->kind());
2450 const int stack_height = frame_it->height(); // Accumulator *not* included.
2451
2452 expression_stack_.resize(static_cast<size_t>(stack_height));
2453 for (int i = 0; i < stack_height; i++) {
2454 Handle<Object> expression = GetValueForDebugger(stack_it, isolate);
2455 SetExpression(i, expression);
2456 stack_it++;
2457 }
2458
2459 DCHECK_EQ(TranslatedFrame::kInterpretedFunction, frame_it->kind());
2460 stack_it++; // Skip the accumulator.
2461
2462 CHECK(stack_it == frame_it->end());
2463 }
2464
GetDeoptInfo(Code code,Address pc)2465 Deoptimizer::DeoptInfo Deoptimizer::GetDeoptInfo(Code code, Address pc) {
2466 CHECK(code.InstructionStart() <= pc && pc <= code.InstructionEnd());
2467 SourcePosition last_position = SourcePosition::Unknown();
2468 DeoptimizeReason last_reason = DeoptimizeReason::kUnknown;
2469 int last_deopt_id = kNoDeoptimizationId;
2470 int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_REASON) |
2471 RelocInfo::ModeMask(RelocInfo::DEOPT_ID) |
2472 RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) |
2473 RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID);
2474 for (RelocIterator it(code, mask); !it.done(); it.next()) {
2475 RelocInfo* info = it.rinfo();
2476 if (info->pc() >= pc) break;
2477 if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
2478 int script_offset = static_cast<int>(info->data());
2479 it.next();
2480 DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID);
2481 int inlining_id = static_cast<int>(it.rinfo()->data());
2482 last_position = SourcePosition(script_offset, inlining_id);
2483 } else if (info->rmode() == RelocInfo::DEOPT_ID) {
2484 last_deopt_id = static_cast<int>(info->data());
2485 } else if (info->rmode() == RelocInfo::DEOPT_REASON) {
2486 last_reason = static_cast<DeoptimizeReason>(info->data());
2487 }
2488 }
2489 return DeoptInfo(last_position, last_reason, last_deopt_id);
2490 }
2491
2492 // static
ComputeSourcePositionFromBytecodeArray(SharedFunctionInfo shared,BailoutId node_id)2493 int Deoptimizer::ComputeSourcePositionFromBytecodeArray(
2494 SharedFunctionInfo shared, BailoutId node_id) {
2495 DCHECK(shared.HasBytecodeArray());
2496 return AbstractCode::cast(shared.GetBytecodeArray())
2497 .SourcePosition(node_id.ToInt());
2498 }
2499
2500 // static
NewDeferredObject(TranslatedState * container,int length,int object_index)2501 TranslatedValue TranslatedValue::NewDeferredObject(TranslatedState* container,
2502 int length,
2503 int object_index) {
2504 TranslatedValue slot(container, kCapturedObject);
2505 slot.materialization_info_ = {object_index, length};
2506 return slot;
2507 }
2508
2509 // static
NewDuplicateObject(TranslatedState * container,int id)2510 TranslatedValue TranslatedValue::NewDuplicateObject(TranslatedState* container,
2511 int id) {
2512 TranslatedValue slot(container, kDuplicatedObject);
2513 slot.materialization_info_ = {id, -1};
2514 return slot;
2515 }
2516
2517 // static
NewFloat(TranslatedState * container,Float32 value)2518 TranslatedValue TranslatedValue::NewFloat(TranslatedState* container,
2519 Float32 value) {
2520 TranslatedValue slot(container, kFloat);
2521 slot.float_value_ = value;
2522 return slot;
2523 }
2524
2525 // static
NewDouble(TranslatedState * container,Float64 value)2526 TranslatedValue TranslatedValue::NewDouble(TranslatedState* container,
2527 Float64 value) {
2528 TranslatedValue slot(container, kDouble);
2529 slot.double_value_ = value;
2530 return slot;
2531 }
2532
2533 // static
NewInt32(TranslatedState * container,int32_t value)2534 TranslatedValue TranslatedValue::NewInt32(TranslatedState* container,
2535 int32_t value) {
2536 TranslatedValue slot(container, kInt32);
2537 slot.int32_value_ = value;
2538 return slot;
2539 }
2540
2541 // static
NewInt64(TranslatedState * container,int64_t value)2542 TranslatedValue TranslatedValue::NewInt64(TranslatedState* container,
2543 int64_t value) {
2544 TranslatedValue slot(container, kInt64);
2545 slot.int64_value_ = value;
2546 return slot;
2547 }
2548
2549 // static
NewUInt32(TranslatedState * container,uint32_t value)2550 TranslatedValue TranslatedValue::NewUInt32(TranslatedState* container,
2551 uint32_t value) {
2552 TranslatedValue slot(container, kUInt32);
2553 slot.uint32_value_ = value;
2554 return slot;
2555 }
2556
2557 // static
NewBool(TranslatedState * container,uint32_t value)2558 TranslatedValue TranslatedValue::NewBool(TranslatedState* container,
2559 uint32_t value) {
2560 TranslatedValue slot(container, kBoolBit);
2561 slot.uint32_value_ = value;
2562 return slot;
2563 }
2564
2565 // static
NewTagged(TranslatedState * container,Object literal)2566 TranslatedValue TranslatedValue::NewTagged(TranslatedState* container,
2567 Object literal) {
2568 TranslatedValue slot(container, kTagged);
2569 slot.raw_literal_ = literal;
2570 return slot;
2571 }
2572
2573 // static
NewInvalid(TranslatedState * container)2574 TranslatedValue TranslatedValue::NewInvalid(TranslatedState* container) {
2575 return TranslatedValue(container, kInvalid);
2576 }
2577
isolate() const2578 Isolate* TranslatedValue::isolate() const { return container_->isolate(); }
2579
raw_literal() const2580 Object TranslatedValue::raw_literal() const {
2581 DCHECK_EQ(kTagged, kind());
2582 return raw_literal_;
2583 }
2584
int32_value() const2585 int32_t TranslatedValue::int32_value() const {
2586 DCHECK_EQ(kInt32, kind());
2587 return int32_value_;
2588 }
2589
int64_value() const2590 int64_t TranslatedValue::int64_value() const {
2591 DCHECK_EQ(kInt64, kind());
2592 return int64_value_;
2593 }
2594
uint32_value() const2595 uint32_t TranslatedValue::uint32_value() const {
2596 DCHECK(kind() == kUInt32 || kind() == kBoolBit);
2597 return uint32_value_;
2598 }
2599
float_value() const2600 Float32 TranslatedValue::float_value() const {
2601 DCHECK_EQ(kFloat, kind());
2602 return float_value_;
2603 }
2604
double_value() const2605 Float64 TranslatedValue::double_value() const {
2606 DCHECK_EQ(kDouble, kind());
2607 return double_value_;
2608 }
2609
object_length() const2610 int TranslatedValue::object_length() const {
2611 DCHECK_EQ(kind(), kCapturedObject);
2612 return materialization_info_.length_;
2613 }
2614
object_index() const2615 int TranslatedValue::object_index() const {
2616 DCHECK(kind() == kCapturedObject || kind() == kDuplicatedObject);
2617 return materialization_info_.id_;
2618 }
2619
GetRawValue() const2620 Object TranslatedValue::GetRawValue() const {
2621 // If we have a value, return it.
2622 if (materialization_state() == kFinished) {
2623 int smi;
2624 if (storage_->IsHeapNumber() &&
2625 DoubleToSmiInteger(storage_->Number(), &smi)) {
2626 return Smi::FromInt(smi);
2627 }
2628 return *storage_;
2629 }
2630
2631 // Otherwise, do a best effort to get the value without allocation.
2632 switch (kind()) {
2633 case kTagged:
2634 return raw_literal();
2635
2636 case kInt32: {
2637 bool is_smi = Smi::IsValid(int32_value());
2638 if (is_smi) {
2639 return Smi::FromInt(int32_value());
2640 }
2641 break;
2642 }
2643
2644 case kInt64: {
2645 bool is_smi = (int64_value() >= static_cast<int64_t>(Smi::kMinValue) &&
2646 int64_value() <= static_cast<int64_t>(Smi::kMaxValue));
2647 if (is_smi) {
2648 return Smi::FromIntptr(static_cast<intptr_t>(int64_value()));
2649 }
2650 break;
2651 }
2652
2653 case kUInt32: {
2654 bool is_smi = (uint32_value() <= static_cast<uintptr_t>(Smi::kMaxValue));
2655 if (is_smi) {
2656 return Smi::FromInt(static_cast<int32_t>(uint32_value()));
2657 }
2658 break;
2659 }
2660
2661 case kBoolBit: {
2662 if (uint32_value() == 0) {
2663 return ReadOnlyRoots(isolate()).false_value();
2664 } else {
2665 CHECK_EQ(1U, uint32_value());
2666 return ReadOnlyRoots(isolate()).true_value();
2667 }
2668 }
2669
2670 case kFloat: {
2671 int smi;
2672 if (DoubleToSmiInteger(float_value().get_scalar(), &smi)) {
2673 return Smi::FromInt(smi);
2674 }
2675 break;
2676 }
2677
2678 case kDouble: {
2679 int smi;
2680 if (DoubleToSmiInteger(double_value().get_scalar(), &smi)) {
2681 return Smi::FromInt(smi);
2682 }
2683 break;
2684 }
2685
2686 default:
2687 break;
2688 }
2689
2690 // If we could not get the value without allocation, return the arguments
2691 // marker.
2692 return ReadOnlyRoots(isolate()).arguments_marker();
2693 }
2694
set_initialized_storage(Handle<HeapObject> storage)2695 void TranslatedValue::set_initialized_storage(Handle<HeapObject> storage) {
2696 DCHECK_EQ(kUninitialized, materialization_state());
2697 storage_ = storage;
2698 materialization_state_ = kFinished;
2699 }
2700
GetValue()2701 Handle<Object> TranslatedValue::GetValue() {
2702 Handle<Object> value(GetRawValue(), isolate());
2703 if (materialization_state() == kFinished) return value;
2704
2705 if (value->IsSmi()) {
2706 // Even though stored as a Smi, this number might instead be needed as a
2707 // HeapNumber when materializing a JSObject with a field of HeapObject
2708 // representation. Since we don't have this information available here, we
2709 // just always allocate a HeapNumber and later extract the Smi again if we
2710 // don't need a HeapObject.
2711 set_initialized_storage(
2712 isolate()->factory()->NewHeapNumber(value->Number()));
2713 return value;
2714 }
2715
2716 if (*value != ReadOnlyRoots(isolate()).arguments_marker()) {
2717 set_initialized_storage(Handle<HeapObject>::cast(value));
2718 return storage_;
2719 }
2720
2721 // Otherwise we have to materialize.
2722
2723 if (kind() == TranslatedValue::kCapturedObject ||
2724 kind() == TranslatedValue::kDuplicatedObject) {
2725 // We need to materialize the object (or possibly even object graphs).
2726 // To make the object verifier happy, we materialize in two steps.
2727
2728 // 1. Allocate storage for reachable objects. This makes sure that for
2729 // each object we have allocated space on heap. The space will be
2730 // a byte array that will be later initialized, or a fully
2731 // initialized object if it is safe to allocate one that will
2732 // pass the verifier.
2733 container_->EnsureObjectAllocatedAt(this);
2734
2735 // 2. Initialize the objects. If we have allocated only byte arrays
2736 // for some objects, we now overwrite the byte arrays with the
2737 // correct object fields. Note that this phase does not allocate
2738 // any new objects, so it does not trigger the object verifier.
2739 return container_->InitializeObjectAt(this);
2740 }
2741
2742 double number;
2743 switch (kind()) {
2744 case TranslatedValue::kInt32:
2745 number = int32_value();
2746 break;
2747 case TranslatedValue::kInt64:
2748 number = int64_value();
2749 break;
2750 case TranslatedValue::kUInt32:
2751 number = uint32_value();
2752 break;
2753 case TranslatedValue::kFloat:
2754 number = float_value().get_scalar();
2755 break;
2756 case TranslatedValue::kDouble:
2757 number = double_value().get_scalar();
2758 break;
2759 default:
2760 UNREACHABLE();
2761 }
2762 DCHECK(!IsSmiDouble(number));
2763 set_initialized_storage(isolate()->factory()->NewHeapNumber(number));
2764 return storage_;
2765 }
2766
IsMaterializedObject() const2767 bool TranslatedValue::IsMaterializedObject() const {
2768 switch (kind()) {
2769 case kCapturedObject:
2770 case kDuplicatedObject:
2771 return true;
2772 default:
2773 return false;
2774 }
2775 }
2776
IsMaterializableByDebugger() const2777 bool TranslatedValue::IsMaterializableByDebugger() const {
2778 // At the moment, we only allow materialization of doubles.
2779 return (kind() == kDouble);
2780 }
2781
GetChildrenCount() const2782 int TranslatedValue::GetChildrenCount() const {
2783 if (kind() == kCapturedObject) {
2784 return object_length();
2785 } else {
2786 return 0;
2787 }
2788 }
2789
GetUInt64Slot(Address fp,int slot_offset)2790 uint64_t TranslatedState::GetUInt64Slot(Address fp, int slot_offset) {
2791 #if V8_TARGET_ARCH_32_BIT
2792 return ReadUnalignedValue<uint64_t>(fp + slot_offset);
2793 #else
2794 return Memory<uint64_t>(fp + slot_offset);
2795 #endif
2796 }
2797
GetUInt32Slot(Address fp,int slot_offset)2798 uint32_t TranslatedState::GetUInt32Slot(Address fp, int slot_offset) {
2799 Address address = fp + slot_offset;
2800 #if V8_TARGET_BIG_ENDIAN && V8_HOST_ARCH_64_BIT
2801 return Memory<uint32_t>(address + kIntSize);
2802 #else
2803 return Memory<uint32_t>(address);
2804 #endif
2805 }
2806
GetFloatSlot(Address fp,int slot_offset)2807 Float32 TranslatedState::GetFloatSlot(Address fp, int slot_offset) {
2808 #if !V8_TARGET_ARCH_S390X && !V8_TARGET_ARCH_PPC64
2809 return Float32::FromBits(GetUInt32Slot(fp, slot_offset));
2810 #else
2811 return Float32::FromBits(Memory<uint32_t>(fp + slot_offset));
2812 #endif
2813 }
2814
GetDoubleSlot(Address fp,int slot_offset)2815 Float64 TranslatedState::GetDoubleSlot(Address fp, int slot_offset) {
2816 return Float64::FromBits(GetUInt64Slot(fp, slot_offset));
2817 }
2818
Handlify()2819 void TranslatedValue::Handlify() {
2820 if (kind() == kTagged && raw_literal().IsHeapObject()) {
2821 set_initialized_storage(
2822 Handle<HeapObject>(HeapObject::cast(raw_literal()), isolate()));
2823 raw_literal_ = Object();
2824 }
2825 }
2826
InterpretedFrame(BailoutId bytecode_offset,SharedFunctionInfo shared_info,int height,int return_value_offset,int return_value_count)2827 TranslatedFrame TranslatedFrame::InterpretedFrame(
2828 BailoutId bytecode_offset, SharedFunctionInfo shared_info, int height,
2829 int return_value_offset, int return_value_count) {
2830 TranslatedFrame frame(kInterpretedFunction, shared_info, height,
2831 return_value_offset, return_value_count);
2832 frame.node_id_ = bytecode_offset;
2833 return frame;
2834 }
2835
ArgumentsAdaptorFrame(SharedFunctionInfo shared_info,int height)2836 TranslatedFrame TranslatedFrame::ArgumentsAdaptorFrame(
2837 SharedFunctionInfo shared_info, int height) {
2838 return TranslatedFrame(kArgumentsAdaptor, shared_info, height);
2839 }
2840
ConstructStubFrame(BailoutId bailout_id,SharedFunctionInfo shared_info,int height)2841 TranslatedFrame TranslatedFrame::ConstructStubFrame(
2842 BailoutId bailout_id, SharedFunctionInfo shared_info, int height) {
2843 TranslatedFrame frame(kConstructStub, shared_info, height);
2844 frame.node_id_ = bailout_id;
2845 return frame;
2846 }
2847
BuiltinContinuationFrame(BailoutId bailout_id,SharedFunctionInfo shared_info,int height)2848 TranslatedFrame TranslatedFrame::BuiltinContinuationFrame(
2849 BailoutId bailout_id, SharedFunctionInfo shared_info, int height) {
2850 TranslatedFrame frame(kBuiltinContinuation, shared_info, height);
2851 frame.node_id_ = bailout_id;
2852 return frame;
2853 }
2854
JavaScriptBuiltinContinuationFrame(BailoutId bailout_id,SharedFunctionInfo shared_info,int height)2855 TranslatedFrame TranslatedFrame::JavaScriptBuiltinContinuationFrame(
2856 BailoutId bailout_id, SharedFunctionInfo shared_info, int height) {
2857 TranslatedFrame frame(kJavaScriptBuiltinContinuation, shared_info, height);
2858 frame.node_id_ = bailout_id;
2859 return frame;
2860 }
2861
JavaScriptBuiltinContinuationWithCatchFrame(BailoutId bailout_id,SharedFunctionInfo shared_info,int height)2862 TranslatedFrame TranslatedFrame::JavaScriptBuiltinContinuationWithCatchFrame(
2863 BailoutId bailout_id, SharedFunctionInfo shared_info, int height) {
2864 TranslatedFrame frame(kJavaScriptBuiltinContinuationWithCatch, shared_info,
2865 height);
2866 frame.node_id_ = bailout_id;
2867 return frame;
2868 }
2869
GetValueCount()2870 int TranslatedFrame::GetValueCount() {
2871 // The function is added to all frame state descriptors in
2872 // InstructionSelector::AddInputsToFrameStateDescriptor.
2873 static constexpr int kTheFunction = 1;
2874
2875 switch (kind()) {
2876 case kInterpretedFunction: {
2877 int parameter_count =
2878 InternalFormalParameterCountWithReceiver(raw_shared_info_);
2879 static constexpr int kTheContext = 1;
2880 static constexpr int kTheAccumulator = 1;
2881 return height() + parameter_count + kTheContext + kTheFunction +
2882 kTheAccumulator;
2883 }
2884
2885 case kArgumentsAdaptor:
2886 return height() + kTheFunction;
2887
2888 case kConstructStub:
2889 case kBuiltinContinuation:
2890 case kJavaScriptBuiltinContinuation:
2891 case kJavaScriptBuiltinContinuationWithCatch: {
2892 static constexpr int kTheContext = 1;
2893 return height() + kTheContext + kTheFunction;
2894 }
2895
2896 case kInvalid:
2897 UNREACHABLE();
2898 }
2899 UNREACHABLE();
2900 }
2901
Handlify()2902 void TranslatedFrame::Handlify() {
2903 if (!raw_shared_info_.is_null()) {
2904 shared_info_ = Handle<SharedFunctionInfo>(raw_shared_info_,
2905 raw_shared_info_.GetIsolate());
2906 raw_shared_info_ = SharedFunctionInfo();
2907 }
2908 for (auto& value : values_) {
2909 value.Handlify();
2910 }
2911 }
2912
CreateNextTranslatedFrame(TranslationIterator * iterator,FixedArray literal_array,Address fp,FILE * trace_file)2913 TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
2914 TranslationIterator* iterator, FixedArray literal_array, Address fp,
2915 FILE* trace_file) {
2916 Translation::Opcode opcode =
2917 static_cast<Translation::Opcode>(iterator->Next());
2918 switch (opcode) {
2919 case Translation::INTERPRETED_FRAME: {
2920 BailoutId bytecode_offset = BailoutId(iterator->Next());
2921 SharedFunctionInfo shared_info =
2922 SharedFunctionInfo::cast(literal_array.get(iterator->Next()));
2923 int height = iterator->Next();
2924 int return_value_offset = iterator->Next();
2925 int return_value_count = iterator->Next();
2926 if (trace_file != nullptr) {
2927 std::unique_ptr<char[]> name = shared_info.DebugName().ToCString();
2928 PrintF(trace_file, " reading input frame %s", name.get());
2929 int arg_count = InternalFormalParameterCountWithReceiver(shared_info);
2930 PrintF(trace_file,
2931 " => bytecode_offset=%d, args=%d, height=%d, retval=%i(#%i); "
2932 "inputs:\n",
2933 bytecode_offset.ToInt(), arg_count, height, return_value_offset,
2934 return_value_count);
2935 }
2936 return TranslatedFrame::InterpretedFrame(bytecode_offset, shared_info,
2937 height, return_value_offset,
2938 return_value_count);
2939 }
2940
2941 case Translation::ARGUMENTS_ADAPTOR_FRAME: {
2942 SharedFunctionInfo shared_info =
2943 SharedFunctionInfo::cast(literal_array.get(iterator->Next()));
2944 int height = iterator->Next();
2945 if (trace_file != nullptr) {
2946 std::unique_ptr<char[]> name = shared_info.DebugName().ToCString();
2947 PrintF(trace_file, " reading arguments adaptor frame %s", name.get());
2948 PrintF(trace_file, " => height=%d; inputs:\n", height);
2949 }
2950 return TranslatedFrame::ArgumentsAdaptorFrame(shared_info, height);
2951 }
2952
2953 case Translation::CONSTRUCT_STUB_FRAME: {
2954 BailoutId bailout_id = BailoutId(iterator->Next());
2955 SharedFunctionInfo shared_info =
2956 SharedFunctionInfo::cast(literal_array.get(iterator->Next()));
2957 int height = iterator->Next();
2958 if (trace_file != nullptr) {
2959 std::unique_ptr<char[]> name = shared_info.DebugName().ToCString();
2960 PrintF(trace_file, " reading construct stub frame %s", name.get());
2961 PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n",
2962 bailout_id.ToInt(), height);
2963 }
2964 return TranslatedFrame::ConstructStubFrame(bailout_id, shared_info,
2965 height);
2966 }
2967
2968 case Translation::BUILTIN_CONTINUATION_FRAME: {
2969 BailoutId bailout_id = BailoutId(iterator->Next());
2970 SharedFunctionInfo shared_info =
2971 SharedFunctionInfo::cast(literal_array.get(iterator->Next()));
2972 int height = iterator->Next();
2973 if (trace_file != nullptr) {
2974 std::unique_ptr<char[]> name = shared_info.DebugName().ToCString();
2975 PrintF(trace_file, " reading builtin continuation frame %s",
2976 name.get());
2977 PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n",
2978 bailout_id.ToInt(), height);
2979 }
2980 return TranslatedFrame::BuiltinContinuationFrame(bailout_id, shared_info,
2981 height);
2982 }
2983
2984 case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME: {
2985 BailoutId bailout_id = BailoutId(iterator->Next());
2986 SharedFunctionInfo shared_info =
2987 SharedFunctionInfo::cast(literal_array.get(iterator->Next()));
2988 int height = iterator->Next();
2989 if (trace_file != nullptr) {
2990 std::unique_ptr<char[]> name = shared_info.DebugName().ToCString();
2991 PrintF(trace_file, " reading JavaScript builtin continuation frame %s",
2992 name.get());
2993 PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n",
2994 bailout_id.ToInt(), height);
2995 }
2996 return TranslatedFrame::JavaScriptBuiltinContinuationFrame(
2997 bailout_id, shared_info, height);
2998 }
2999 case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME: {
3000 BailoutId bailout_id = BailoutId(iterator->Next());
3001 SharedFunctionInfo shared_info =
3002 SharedFunctionInfo::cast(literal_array.get(iterator->Next()));
3003 int height = iterator->Next();
3004 if (trace_file != nullptr) {
3005 std::unique_ptr<char[]> name = shared_info.DebugName().ToCString();
3006 PrintF(trace_file,
3007 " reading JavaScript builtin continuation frame with catch %s",
3008 name.get());
3009 PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n",
3010 bailout_id.ToInt(), height);
3011 }
3012 return TranslatedFrame::JavaScriptBuiltinContinuationWithCatchFrame(
3013 bailout_id, shared_info, height);
3014 }
3015 case Translation::UPDATE_FEEDBACK:
3016 case Translation::BEGIN:
3017 case Translation::DUPLICATED_OBJECT:
3018 case Translation::ARGUMENTS_ELEMENTS:
3019 case Translation::ARGUMENTS_LENGTH:
3020 case Translation::CAPTURED_OBJECT:
3021 case Translation::REGISTER:
3022 case Translation::INT32_REGISTER:
3023 case Translation::INT64_REGISTER:
3024 case Translation::UINT32_REGISTER:
3025 case Translation::BOOL_REGISTER:
3026 case Translation::FLOAT_REGISTER:
3027 case Translation::DOUBLE_REGISTER:
3028 case Translation::STACK_SLOT:
3029 case Translation::INT32_STACK_SLOT:
3030 case Translation::INT64_STACK_SLOT:
3031 case Translation::UINT32_STACK_SLOT:
3032 case Translation::BOOL_STACK_SLOT:
3033 case Translation::FLOAT_STACK_SLOT:
3034 case Translation::DOUBLE_STACK_SLOT:
3035 case Translation::LITERAL:
3036 break;
3037 }
3038 FATAL("We should never get here - unexpected deopt info.");
3039 return TranslatedFrame::InvalidFrame();
3040 }
3041
3042 // static
AdvanceIterator(std::deque<TranslatedValue>::iterator * iter)3043 void TranslatedFrame::AdvanceIterator(
3044 std::deque<TranslatedValue>::iterator* iter) {
3045 int values_to_skip = 1;
3046 while (values_to_skip > 0) {
3047 // Consume the current element.
3048 values_to_skip--;
3049 // Add all the children.
3050 values_to_skip += (*iter)->GetChildrenCount();
3051
3052 (*iter)++;
3053 }
3054 }
3055
ComputeArgumentsPosition(Address input_frame_pointer,int * length)3056 Address TranslatedState::ComputeArgumentsPosition(Address input_frame_pointer,
3057 int* length) {
3058 Address parent_frame_pointer = *reinterpret_cast<Address*>(
3059 input_frame_pointer + StandardFrameConstants::kCallerFPOffset);
3060 intptr_t parent_frame_type = Memory<intptr_t>(
3061 parent_frame_pointer + CommonFrameConstants::kContextOrFrameTypeOffset);
3062
3063 Address arguments_frame;
3064 if (parent_frame_type ==
3065 StackFrame::TypeToMarker(StackFrame::ARGUMENTS_ADAPTOR)) {
3066 if (length)
3067 *length = Smi::cast(*FullObjectSlot(
3068 parent_frame_pointer +
3069 ArgumentsAdaptorFrameConstants::kLengthOffset))
3070 .value();
3071 arguments_frame = parent_frame_pointer;
3072 } else {
3073 if (length) *length = formal_parameter_count_;
3074 arguments_frame = input_frame_pointer;
3075 }
3076
3077 return arguments_frame;
3078 }
3079
3080 // Creates translated values for an arguments backing store, or the backing
3081 // store for rest parameters depending on the given {type}. The TranslatedValue
3082 // objects for the fields are not read from the TranslationIterator, but instead
3083 // created on-the-fly based on dynamic information in the optimized frame.
CreateArgumentsElementsTranslatedValues(int frame_index,Address input_frame_pointer,CreateArgumentsType type,FILE * trace_file)3084 void TranslatedState::CreateArgumentsElementsTranslatedValues(
3085 int frame_index, Address input_frame_pointer, CreateArgumentsType type,
3086 FILE* trace_file) {
3087 TranslatedFrame& frame = frames_[frame_index];
3088
3089 #ifdef V8_NO_ARGUMENTS_ADAPTOR
3090 int arguments_length = actual_argument_count_;
3091 #else
3092 int arguments_length;
3093 Address arguments_frame =
3094 ComputeArgumentsPosition(input_frame_pointer, &arguments_length);
3095 #endif
3096
3097 int length = type == CreateArgumentsType::kRestParameter
3098 ? std::max(0, arguments_length - formal_parameter_count_)
3099 : arguments_length;
3100
3101 int object_index = static_cast<int>(object_positions_.size());
3102 int value_index = static_cast<int>(frame.values_.size());
3103 if (trace_file != nullptr) {
3104 PrintF(trace_file, "arguments elements object #%d (type = %d, length = %d)",
3105 object_index, static_cast<uint8_t>(type), length);
3106 }
3107
3108 object_positions_.push_back({frame_index, value_index});
3109 frame.Add(TranslatedValue::NewDeferredObject(
3110 this, length + FixedArray::kHeaderSize / kTaggedSize, object_index));
3111
3112 ReadOnlyRoots roots(isolate_);
3113 frame.Add(TranslatedValue::NewTagged(this, roots.fixed_array_map()));
3114 frame.Add(TranslatedValue::NewInt32(this, length));
3115
3116 int number_of_holes = 0;
3117 if (type == CreateArgumentsType::kMappedArguments) {
3118 // If the actual number of arguments is less than the number of formal
3119 // parameters, we have fewer holes to fill to not overshoot the length.
3120 number_of_holes = std::min(formal_parameter_count_, length);
3121 }
3122 for (int i = 0; i < number_of_holes; ++i) {
3123 frame.Add(TranslatedValue::NewTagged(this, roots.the_hole_value()));
3124 }
3125 int argc = length - number_of_holes;
3126 int start_index = number_of_holes;
3127 if (type == CreateArgumentsType::kRestParameter) {
3128 start_index = std::max(0, formal_parameter_count_);
3129 }
3130 for (int i = 0; i < argc; i++) {
3131 // Skip the receiver.
3132 int offset = i + start_index + 1;
3133 #ifdef V8_NO_ARGUMENTS_ADAPTOR
3134 Address arguments_frame = offset > formal_parameter_count_
3135 ? stack_frame_pointer_
3136 : input_frame_pointer;
3137 #endif
3138 Address argument_slot = arguments_frame +
3139 CommonFrameConstants::kFixedFrameSizeAboveFp +
3140 offset * kSystemPointerSize;
3141
3142 frame.Add(TranslatedValue::NewTagged(this, *FullObjectSlot(argument_slot)));
3143 }
3144 }
3145
3146 // We can't intermix stack decoding and allocations because the deoptimization
3147 // infrastracture is not GC safe.
3148 // Thus we build a temporary structure in malloced space.
3149 // The TranslatedValue objects created correspond to the static translation
3150 // instructions from the TranslationIterator, except for
3151 // Translation::ARGUMENTS_ELEMENTS, where the number and values of the
3152 // FixedArray elements depend on dynamic information from the optimized frame.
3153 // Returns the number of expected nested translations from the
3154 // TranslationIterator.
CreateNextTranslatedValue(int frame_index,TranslationIterator * iterator,FixedArray literal_array,Address fp,RegisterValues * registers,FILE * trace_file)3155 int TranslatedState::CreateNextTranslatedValue(
3156 int frame_index, TranslationIterator* iterator, FixedArray literal_array,
3157 Address fp, RegisterValues* registers, FILE* trace_file) {
3158 disasm::NameConverter converter;
3159
3160 TranslatedFrame& frame = frames_[frame_index];
3161 int value_index = static_cast<int>(frame.values_.size());
3162
3163 Translation::Opcode opcode =
3164 static_cast<Translation::Opcode>(iterator->Next());
3165 switch (opcode) {
3166 case Translation::BEGIN:
3167 case Translation::INTERPRETED_FRAME:
3168 case Translation::ARGUMENTS_ADAPTOR_FRAME:
3169 case Translation::CONSTRUCT_STUB_FRAME:
3170 case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME:
3171 case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME:
3172 case Translation::BUILTIN_CONTINUATION_FRAME:
3173 case Translation::UPDATE_FEEDBACK:
3174 // Peeled off before getting here.
3175 break;
3176
3177 case Translation::DUPLICATED_OBJECT: {
3178 int object_id = iterator->Next();
3179 if (trace_file != nullptr) {
3180 PrintF(trace_file, "duplicated object #%d", object_id);
3181 }
3182 object_positions_.push_back(object_positions_[object_id]);
3183 TranslatedValue translated_value =
3184 TranslatedValue::NewDuplicateObject(this, object_id);
3185 frame.Add(translated_value);
3186 return translated_value.GetChildrenCount();
3187 }
3188
3189 case Translation::ARGUMENTS_ELEMENTS: {
3190 CreateArgumentsType arguments_type =
3191 static_cast<CreateArgumentsType>(iterator->Next());
3192 CreateArgumentsElementsTranslatedValues(frame_index, fp, arguments_type,
3193 trace_file);
3194 return 0;
3195 }
3196
3197 case Translation::ARGUMENTS_LENGTH: {
3198 #ifdef V8_NO_ARGUMENTS_ADAPTOR
3199 int arguments_length = actual_argument_count_;
3200 #else
3201 int arguments_length;
3202 ComputeArgumentsPosition(fp, &arguments_length);
3203 #endif
3204 if (trace_file != nullptr) {
3205 PrintF(trace_file, "arguments length field (length = %d)",
3206 arguments_length);
3207 }
3208 frame.Add(TranslatedValue::NewInt32(this, arguments_length));
3209 return 0;
3210 }
3211
3212 case Translation::CAPTURED_OBJECT: {
3213 int field_count = iterator->Next();
3214 int object_index = static_cast<int>(object_positions_.size());
3215 if (trace_file != nullptr) {
3216 PrintF(trace_file, "captured object #%d (length = %d)", object_index,
3217 field_count);
3218 }
3219 object_positions_.push_back({frame_index, value_index});
3220 TranslatedValue translated_value =
3221 TranslatedValue::NewDeferredObject(this, field_count, object_index);
3222 frame.Add(translated_value);
3223 return translated_value.GetChildrenCount();
3224 }
3225
3226 case Translation::REGISTER: {
3227 int input_reg = iterator->Next();
3228 if (registers == nullptr) {
3229 TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
3230 frame.Add(translated_value);
3231 return translated_value.GetChildrenCount();
3232 }
3233 intptr_t value = registers->GetRegister(input_reg);
3234 Address uncompressed_value = DecompressIfNeeded(value);
3235 if (trace_file != nullptr) {
3236 PrintF(trace_file, V8PRIxPTR_FMT " ; %s ", uncompressed_value,
3237 converter.NameOfCPURegister(input_reg));
3238 Object(uncompressed_value).ShortPrint(trace_file);
3239 }
3240 TranslatedValue translated_value =
3241 TranslatedValue::NewTagged(this, Object(uncompressed_value));
3242 frame.Add(translated_value);
3243 return translated_value.GetChildrenCount();
3244 }
3245
3246 case Translation::INT32_REGISTER: {
3247 int input_reg = iterator->Next();
3248 if (registers == nullptr) {
3249 TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
3250 frame.Add(translated_value);
3251 return translated_value.GetChildrenCount();
3252 }
3253 intptr_t value = registers->GetRegister(input_reg);
3254 if (trace_file != nullptr) {
3255 PrintF(trace_file, "%" V8PRIdPTR " ; %s (int32)", value,
3256 converter.NameOfCPURegister(input_reg));
3257 }
3258 TranslatedValue translated_value =
3259 TranslatedValue::NewInt32(this, static_cast<int32_t>(value));
3260 frame.Add(translated_value);
3261 return translated_value.GetChildrenCount();
3262 }
3263
3264 case Translation::INT64_REGISTER: {
3265 int input_reg = iterator->Next();
3266 if (registers == nullptr) {
3267 TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
3268 frame.Add(translated_value);
3269 return translated_value.GetChildrenCount();
3270 }
3271 intptr_t value = registers->GetRegister(input_reg);
3272 if (trace_file != nullptr) {
3273 PrintF(trace_file, "%" V8PRIdPTR " ; %s (int64)", value,
3274 converter.NameOfCPURegister(input_reg));
3275 }
3276 TranslatedValue translated_value =
3277 TranslatedValue::NewInt64(this, static_cast<int64_t>(value));
3278 frame.Add(translated_value);
3279 return translated_value.GetChildrenCount();
3280 }
3281
3282 case Translation::UINT32_REGISTER: {
3283 int input_reg = iterator->Next();
3284 if (registers == nullptr) {
3285 TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
3286 frame.Add(translated_value);
3287 return translated_value.GetChildrenCount();
3288 }
3289 intptr_t value = registers->GetRegister(input_reg);
3290 if (trace_file != nullptr) {
3291 PrintF(trace_file, "%" V8PRIuPTR " ; %s (uint32)", value,
3292 converter.NameOfCPURegister(input_reg));
3293 }
3294 TranslatedValue translated_value =
3295 TranslatedValue::NewUInt32(this, static_cast<uint32_t>(value));
3296 frame.Add(translated_value);
3297 return translated_value.GetChildrenCount();
3298 }
3299
3300 case Translation::BOOL_REGISTER: {
3301 int input_reg = iterator->Next();
3302 if (registers == nullptr) {
3303 TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
3304 frame.Add(translated_value);
3305 return translated_value.GetChildrenCount();
3306 }
3307 intptr_t value = registers->GetRegister(input_reg);
3308 if (trace_file != nullptr) {
3309 PrintF(trace_file, "%" V8PRIdPTR " ; %s (bool)", value,
3310 converter.NameOfCPURegister(input_reg));
3311 }
3312 TranslatedValue translated_value =
3313 TranslatedValue::NewBool(this, static_cast<uint32_t>(value));
3314 frame.Add(translated_value);
3315 return translated_value.GetChildrenCount();
3316 }
3317
3318 case Translation::FLOAT_REGISTER: {
3319 int input_reg = iterator->Next();
3320 if (registers == nullptr) {
3321 TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
3322 frame.Add(translated_value);
3323 return translated_value.GetChildrenCount();
3324 }
3325 Float32 value = registers->GetFloatRegister(input_reg);
3326 if (trace_file != nullptr) {
3327 PrintF(trace_file, "%e ; %s (float)", value.get_scalar(),
3328 RegisterName(FloatRegister::from_code(input_reg)));
3329 }
3330 TranslatedValue translated_value = TranslatedValue::NewFloat(this, value);
3331 frame.Add(translated_value);
3332 return translated_value.GetChildrenCount();
3333 }
3334
3335 case Translation::DOUBLE_REGISTER: {
3336 int input_reg = iterator->Next();
3337 if (registers == nullptr) {
3338 TranslatedValue translated_value = TranslatedValue::NewInvalid(this);
3339 frame.Add(translated_value);
3340 return translated_value.GetChildrenCount();
3341 }
3342 Float64 value = registers->GetDoubleRegister(input_reg);
3343 if (trace_file != nullptr) {
3344 PrintF(trace_file, "%e ; %s (double)", value.get_scalar(),
3345 RegisterName(DoubleRegister::from_code(input_reg)));
3346 }
3347 TranslatedValue translated_value =
3348 TranslatedValue::NewDouble(this, value);
3349 frame.Add(translated_value);
3350 return translated_value.GetChildrenCount();
3351 }
3352
3353 case Translation::STACK_SLOT: {
3354 int slot_offset =
3355 OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
3356 intptr_t value = *(reinterpret_cast<intptr_t*>(fp + slot_offset));
3357 Address uncompressed_value = DecompressIfNeeded(value);
3358 if (trace_file != nullptr) {
3359 PrintF(trace_file, V8PRIxPTR_FMT " ; [fp %c %3d] ",
3360 uncompressed_value, slot_offset < 0 ? '-' : '+',
3361 std::abs(slot_offset));
3362 Object(uncompressed_value).ShortPrint(trace_file);
3363 }
3364 TranslatedValue translated_value =
3365 TranslatedValue::NewTagged(this, Object(uncompressed_value));
3366 frame.Add(translated_value);
3367 return translated_value.GetChildrenCount();
3368 }
3369
3370 case Translation::INT32_STACK_SLOT: {
3371 int slot_offset =
3372 OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
3373 uint32_t value = GetUInt32Slot(fp, slot_offset);
3374 if (trace_file != nullptr) {
3375 PrintF(trace_file, "%d ; (int32) [fp %c %3d] ",
3376 static_cast<int32_t>(value), slot_offset < 0 ? '-' : '+',
3377 std::abs(slot_offset));
3378 }
3379 TranslatedValue translated_value = TranslatedValue::NewInt32(this, value);
3380 frame.Add(translated_value);
3381 return translated_value.GetChildrenCount();
3382 }
3383
3384 case Translation::INT64_STACK_SLOT: {
3385 int slot_offset =
3386 OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
3387 uint64_t value = GetUInt64Slot(fp, slot_offset);
3388 if (trace_file != nullptr) {
3389 PrintF(trace_file, "%" V8PRIdPTR " ; (int64) [fp %c %3d] ",
3390 static_cast<intptr_t>(value), slot_offset < 0 ? '-' : '+',
3391 std::abs(slot_offset));
3392 }
3393 TranslatedValue translated_value = TranslatedValue::NewInt64(this, value);
3394 frame.Add(translated_value);
3395 return translated_value.GetChildrenCount();
3396 }
3397
3398 case Translation::UINT32_STACK_SLOT: {
3399 int slot_offset =
3400 OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
3401 uint32_t value = GetUInt32Slot(fp, slot_offset);
3402 if (trace_file != nullptr) {
3403 PrintF(trace_file, "%u ; (uint32) [fp %c %3d] ", value,
3404 slot_offset < 0 ? '-' : '+', std::abs(slot_offset));
3405 }
3406 TranslatedValue translated_value =
3407 TranslatedValue::NewUInt32(this, value);
3408 frame.Add(translated_value);
3409 return translated_value.GetChildrenCount();
3410 }
3411
3412 case Translation::BOOL_STACK_SLOT: {
3413 int slot_offset =
3414 OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
3415 uint32_t value = GetUInt32Slot(fp, slot_offset);
3416 if (trace_file != nullptr) {
3417 PrintF(trace_file, "%u ; (bool) [fp %c %3d] ", value,
3418 slot_offset < 0 ? '-' : '+', std::abs(slot_offset));
3419 }
3420 TranslatedValue translated_value = TranslatedValue::NewBool(this, value);
3421 frame.Add(translated_value);
3422 return translated_value.GetChildrenCount();
3423 }
3424
3425 case Translation::FLOAT_STACK_SLOT: {
3426 int slot_offset =
3427 OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
3428 Float32 value = GetFloatSlot(fp, slot_offset);
3429 if (trace_file != nullptr) {
3430 PrintF(trace_file, "%e ; (float) [fp %c %3d] ", value.get_scalar(),
3431 slot_offset < 0 ? '-' : '+', std::abs(slot_offset));
3432 }
3433 TranslatedValue translated_value = TranslatedValue::NewFloat(this, value);
3434 frame.Add(translated_value);
3435 return translated_value.GetChildrenCount();
3436 }
3437
3438 case Translation::DOUBLE_STACK_SLOT: {
3439 int slot_offset =
3440 OptimizedFrame::StackSlotOffsetRelativeToFp(iterator->Next());
3441 Float64 value = GetDoubleSlot(fp, slot_offset);
3442 if (trace_file != nullptr) {
3443 PrintF(trace_file, "%e ; (double) [fp %c %d] ", value.get_scalar(),
3444 slot_offset < 0 ? '-' : '+', std::abs(slot_offset));
3445 }
3446 TranslatedValue translated_value =
3447 TranslatedValue::NewDouble(this, value);
3448 frame.Add(translated_value);
3449 return translated_value.GetChildrenCount();
3450 }
3451
3452 case Translation::LITERAL: {
3453 int literal_index = iterator->Next();
3454 Object value = literal_array.get(literal_index);
3455 if (trace_file != nullptr) {
3456 PrintF(trace_file, V8PRIxPTR_FMT " ; (literal %2d) ", value.ptr(),
3457 literal_index);
3458 value.ShortPrint(trace_file);
3459 }
3460
3461 TranslatedValue translated_value =
3462 TranslatedValue::NewTagged(this, value);
3463 frame.Add(translated_value);
3464 return translated_value.GetChildrenCount();
3465 }
3466 }
3467
3468 FATAL("We should never get here - unexpected deopt info.");
3469 }
3470
DecompressIfNeeded(intptr_t value)3471 Address TranslatedState::DecompressIfNeeded(intptr_t value) {
3472 if (COMPRESS_POINTERS_BOOL) {
3473 return DecompressTaggedAny(isolate()->isolate_root(),
3474 static_cast<uint32_t>(value));
3475 } else {
3476 return value;
3477 }
3478 }
3479
TranslatedState(const JavaScriptFrame * frame)3480 TranslatedState::TranslatedState(const JavaScriptFrame* frame) {
3481 int deopt_index = Safepoint::kNoDeoptimizationIndex;
3482 DeoptimizationData data =
3483 static_cast<const OptimizedFrame*>(frame)->GetDeoptimizationData(
3484 &deopt_index);
3485 DCHECK(!data.is_null() && deopt_index != Safepoint::kNoDeoptimizationIndex);
3486 TranslationIterator it(data.TranslationByteArray(),
3487 data.TranslationIndex(deopt_index).value());
3488 #ifdef V8_NO_ARGUMENTS_ADAPTOR
3489 int actual_argc = frame->GetActualArgumentCount();
3490 #else
3491 int actual_argc = 0;
3492 #endif
3493 Init(frame->isolate(), frame->fp(), frame->fp(), &it, data.LiteralArray(),
3494 nullptr /* registers */, nullptr /* trace file */,
3495 frame->function().shared().internal_formal_parameter_count(),
3496 actual_argc);
3497 }
3498
Init(Isolate * isolate,Address input_frame_pointer,Address stack_frame_pointer,TranslationIterator * iterator,FixedArray literal_array,RegisterValues * registers,FILE * trace_file,int formal_parameter_count,int actual_argument_count)3499 void TranslatedState::Init(Isolate* isolate, Address input_frame_pointer,
3500 Address stack_frame_pointer,
3501 TranslationIterator* iterator,
3502 FixedArray literal_array, RegisterValues* registers,
3503 FILE* trace_file, int formal_parameter_count,
3504 int actual_argument_count) {
3505 DCHECK(frames_.empty());
3506
3507 stack_frame_pointer_ = stack_frame_pointer;
3508 formal_parameter_count_ = formal_parameter_count;
3509 actual_argument_count_ = actual_argument_count;
3510 isolate_ = isolate;
3511
3512 // Read out the 'header' translation.
3513 Translation::Opcode opcode =
3514 static_cast<Translation::Opcode>(iterator->Next());
3515 CHECK(opcode == Translation::BEGIN);
3516
3517 int count = iterator->Next();
3518 frames_.reserve(count);
3519 iterator->Next(); // Drop JS frames count.
3520 int update_feedback_count = iterator->Next();
3521 CHECK_GE(update_feedback_count, 0);
3522 CHECK_LE(update_feedback_count, 1);
3523
3524 if (update_feedback_count == 1) {
3525 ReadUpdateFeedback(iterator, literal_array, trace_file);
3526 }
3527
3528 std::stack<int> nested_counts;
3529
3530 // Read the frames
3531 for (int frame_index = 0; frame_index < count; frame_index++) {
3532 // Read the frame descriptor.
3533 frames_.push_back(CreateNextTranslatedFrame(
3534 iterator, literal_array, input_frame_pointer, trace_file));
3535 TranslatedFrame& frame = frames_.back();
3536
3537 // Read the values.
3538 int values_to_process = frame.GetValueCount();
3539 while (values_to_process > 0 || !nested_counts.empty()) {
3540 if (trace_file != nullptr) {
3541 if (nested_counts.empty()) {
3542 // For top level values, print the value number.
3543 PrintF(trace_file,
3544 " %3i: ", frame.GetValueCount() - values_to_process);
3545 } else {
3546 // Take care of indenting for nested values.
3547 PrintF(trace_file, " ");
3548 for (size_t j = 0; j < nested_counts.size(); j++) {
3549 PrintF(trace_file, " ");
3550 }
3551 }
3552 }
3553
3554 int nested_count =
3555 CreateNextTranslatedValue(frame_index, iterator, literal_array,
3556 input_frame_pointer, registers, trace_file);
3557
3558 if (trace_file != nullptr) {
3559 PrintF(trace_file, "\n");
3560 }
3561
3562 // Update the value count and resolve the nesting.
3563 values_to_process--;
3564 if (nested_count > 0) {
3565 nested_counts.push(values_to_process);
3566 values_to_process = nested_count;
3567 } else {
3568 while (values_to_process == 0 && !nested_counts.empty()) {
3569 values_to_process = nested_counts.top();
3570 nested_counts.pop();
3571 }
3572 }
3573 }
3574 }
3575
3576 CHECK(!iterator->HasNext() || static_cast<Translation::Opcode>(
3577 iterator->Next()) == Translation::BEGIN);
3578 }
3579
Prepare(Address stack_frame_pointer)3580 void TranslatedState::Prepare(Address stack_frame_pointer) {
3581 for (auto& frame : frames_) frame.Handlify();
3582
3583 if (!feedback_vector_.is_null()) {
3584 feedback_vector_handle_ =
3585 Handle<FeedbackVector>(feedback_vector_, isolate());
3586 feedback_vector_ = FeedbackVector();
3587 }
3588 stack_frame_pointer_ = stack_frame_pointer;
3589
3590 UpdateFromPreviouslyMaterializedObjects();
3591 }
3592
GetValueByObjectIndex(int object_index)3593 TranslatedValue* TranslatedState::GetValueByObjectIndex(int object_index) {
3594 CHECK_LT(static_cast<size_t>(object_index), object_positions_.size());
3595 TranslatedState::ObjectPosition pos = object_positions_[object_index];
3596 return &(frames_[pos.frame_index_].values_[pos.value_index_]);
3597 }
3598
InitializeObjectAt(TranslatedValue * slot)3599 Handle<HeapObject> TranslatedState::InitializeObjectAt(TranslatedValue* slot) {
3600 slot = ResolveCapturedObject(slot);
3601
3602 DisallowHeapAllocation no_allocation;
3603 if (slot->materialization_state() != TranslatedValue::kFinished) {
3604 std::stack<int> worklist;
3605 worklist.push(slot->object_index());
3606 slot->mark_finished();
3607
3608 while (!worklist.empty()) {
3609 int index = worklist.top();
3610 worklist.pop();
3611 InitializeCapturedObjectAt(index, &worklist, no_allocation);
3612 }
3613 }
3614 return slot->storage();
3615 }
3616
InitializeCapturedObjectAt(int object_index,std::stack<int> * worklist,const DisallowHeapAllocation & no_allocation)3617 void TranslatedState::InitializeCapturedObjectAt(
3618 int object_index, std::stack<int>* worklist,
3619 const DisallowHeapAllocation& no_allocation) {
3620 CHECK_LT(static_cast<size_t>(object_index), object_positions_.size());
3621 TranslatedState::ObjectPosition pos = object_positions_[object_index];
3622 int value_index = pos.value_index_;
3623
3624 TranslatedFrame* frame = &(frames_[pos.frame_index_]);
3625 TranslatedValue* slot = &(frame->values_[value_index]);
3626 value_index++;
3627
3628 CHECK_EQ(TranslatedValue::kFinished, slot->materialization_state());
3629 CHECK_EQ(TranslatedValue::kCapturedObject, slot->kind());
3630
3631 // Ensure all fields are initialized.
3632 int children_init_index = value_index;
3633 for (int i = 0; i < slot->GetChildrenCount(); i++) {
3634 // If the field is an object that has not been initialized yet, queue it
3635 // for initialization (and mark it as such).
3636 TranslatedValue* child_slot = frame->ValueAt(children_init_index);
3637 if (child_slot->kind() == TranslatedValue::kCapturedObject ||
3638 child_slot->kind() == TranslatedValue::kDuplicatedObject) {
3639 child_slot = ResolveCapturedObject(child_slot);
3640 if (child_slot->materialization_state() != TranslatedValue::kFinished) {
3641 DCHECK_EQ(TranslatedValue::kAllocated,
3642 child_slot->materialization_state());
3643 worklist->push(child_slot->object_index());
3644 child_slot->mark_finished();
3645 }
3646 }
3647 SkipSlots(1, frame, &children_init_index);
3648 }
3649
3650 // Read the map.
3651 // The map should never be materialized, so let us check we already have
3652 // an existing object here.
3653 CHECK_EQ(frame->values_[value_index].kind(), TranslatedValue::kTagged);
3654 Handle<Map> map = Handle<Map>::cast(frame->values_[value_index].GetValue());
3655 CHECK(map->IsMap());
3656 value_index++;
3657
3658 // Handle the special cases.
3659 switch (map->instance_type()) {
3660 case HEAP_NUMBER_TYPE:
3661 case FIXED_DOUBLE_ARRAY_TYPE:
3662 return;
3663
3664 case FIXED_ARRAY_TYPE:
3665 case AWAIT_CONTEXT_TYPE:
3666 case BLOCK_CONTEXT_TYPE:
3667 case CATCH_CONTEXT_TYPE:
3668 case DEBUG_EVALUATE_CONTEXT_TYPE:
3669 case EVAL_CONTEXT_TYPE:
3670 case FUNCTION_CONTEXT_TYPE:
3671 case MODULE_CONTEXT_TYPE:
3672 case NATIVE_CONTEXT_TYPE:
3673 case SCRIPT_CONTEXT_TYPE:
3674 case WITH_CONTEXT_TYPE:
3675 case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
3676 case HASH_TABLE_TYPE:
3677 case ORDERED_HASH_MAP_TYPE:
3678 case ORDERED_HASH_SET_TYPE:
3679 case NAME_DICTIONARY_TYPE:
3680 case GLOBAL_DICTIONARY_TYPE:
3681 case NUMBER_DICTIONARY_TYPE:
3682 case SIMPLE_NUMBER_DICTIONARY_TYPE:
3683 case PROPERTY_ARRAY_TYPE:
3684 case SCRIPT_CONTEXT_TABLE_TYPE:
3685 case SLOPPY_ARGUMENTS_ELEMENTS_TYPE:
3686 InitializeObjectWithTaggedFieldsAt(frame, &value_index, slot, map,
3687 no_allocation);
3688 break;
3689
3690 default:
3691 CHECK(map->IsJSObjectMap());
3692 InitializeJSObjectAt(frame, &value_index, slot, map, no_allocation);
3693 break;
3694 }
3695 CHECK_EQ(value_index, children_init_index);
3696 }
3697
EnsureObjectAllocatedAt(TranslatedValue * slot)3698 void TranslatedState::EnsureObjectAllocatedAt(TranslatedValue* slot) {
3699 slot = ResolveCapturedObject(slot);
3700
3701 if (slot->materialization_state() == TranslatedValue::kUninitialized) {
3702 std::stack<int> worklist;
3703 worklist.push(slot->object_index());
3704 slot->mark_allocated();
3705
3706 while (!worklist.empty()) {
3707 int index = worklist.top();
3708 worklist.pop();
3709 EnsureCapturedObjectAllocatedAt(index, &worklist);
3710 }
3711 }
3712 }
3713
GetSmiValue() const3714 int TranslatedValue::GetSmiValue() const {
3715 Object value = GetRawValue();
3716 CHECK(value.IsSmi());
3717 return Smi::cast(value).value();
3718 }
3719
MaterializeFixedDoubleArray(TranslatedFrame * frame,int * value_index,TranslatedValue * slot,Handle<Map> map)3720 void TranslatedState::MaterializeFixedDoubleArray(TranslatedFrame* frame,
3721 int* value_index,
3722 TranslatedValue* slot,
3723 Handle<Map> map) {
3724 int length = frame->values_[*value_index].GetSmiValue();
3725 (*value_index)++;
3726 Handle<FixedDoubleArray> array = Handle<FixedDoubleArray>::cast(
3727 isolate()->factory()->NewFixedDoubleArray(length));
3728 CHECK_GT(length, 0);
3729 for (int i = 0; i < length; i++) {
3730 CHECK_NE(TranslatedValue::kCapturedObject,
3731 frame->values_[*value_index].kind());
3732 Handle<Object> value = frame->values_[*value_index].GetValue();
3733 if (value->IsNumber()) {
3734 array->set(i, value->Number());
3735 } else {
3736 CHECK(value.is_identical_to(isolate()->factory()->the_hole_value()));
3737 array->set_the_hole(isolate(), i);
3738 }
3739 (*value_index)++;
3740 }
3741 slot->set_storage(array);
3742 }
3743
MaterializeHeapNumber(TranslatedFrame * frame,int * value_index,TranslatedValue * slot)3744 void TranslatedState::MaterializeHeapNumber(TranslatedFrame* frame,
3745 int* value_index,
3746 TranslatedValue* slot) {
3747 CHECK_NE(TranslatedValue::kCapturedObject,
3748 frame->values_[*value_index].kind());
3749 Handle<Object> value = frame->values_[*value_index].GetValue();
3750 CHECK(value->IsNumber());
3751 Handle<HeapNumber> box = isolate()->factory()->NewHeapNumber(value->Number());
3752 (*value_index)++;
3753 slot->set_storage(box);
3754 }
3755
3756 namespace {
3757
3758 enum StorageKind : uint8_t {
3759 kStoreTagged,
3760 kStoreUnboxedDouble,
3761 kStoreHeapObject
3762 };
3763
3764 } // namespace
3765
SkipSlots(int slots_to_skip,TranslatedFrame * frame,int * value_index)3766 void TranslatedState::SkipSlots(int slots_to_skip, TranslatedFrame* frame,
3767 int* value_index) {
3768 while (slots_to_skip > 0) {
3769 TranslatedValue* slot = &(frame->values_[*value_index]);
3770 (*value_index)++;
3771 slots_to_skip--;
3772
3773 if (slot->kind() == TranslatedValue::kCapturedObject) {
3774 slots_to_skip += slot->GetChildrenCount();
3775 }
3776 }
3777 }
3778
EnsureCapturedObjectAllocatedAt(int object_index,std::stack<int> * worklist)3779 void TranslatedState::EnsureCapturedObjectAllocatedAt(
3780 int object_index, std::stack<int>* worklist) {
3781 CHECK_LT(static_cast<size_t>(object_index), object_positions_.size());
3782 TranslatedState::ObjectPosition pos = object_positions_[object_index];
3783 int value_index = pos.value_index_;
3784
3785 TranslatedFrame* frame = &(frames_[pos.frame_index_]);
3786 TranslatedValue* slot = &(frame->values_[value_index]);
3787 value_index++;
3788
3789 CHECK_EQ(TranslatedValue::kAllocated, slot->materialization_state());
3790 CHECK_EQ(TranslatedValue::kCapturedObject, slot->kind());
3791
3792 // Read the map.
3793 // The map should never be materialized, so let us check we already have
3794 // an existing object here.
3795 CHECK_EQ(frame->values_[value_index].kind(), TranslatedValue::kTagged);
3796 Handle<Map> map = Handle<Map>::cast(frame->values_[value_index].GetValue());
3797 CHECK(map->IsMap());
3798 value_index++;
3799
3800 // Handle the special cases.
3801 switch (map->instance_type()) {
3802 case FIXED_DOUBLE_ARRAY_TYPE:
3803 // Materialize (i.e. allocate&initialize) the array and return since
3804 // there is no need to process the children.
3805 return MaterializeFixedDoubleArray(frame, &value_index, slot, map);
3806
3807 case HEAP_NUMBER_TYPE:
3808 // Materialize (i.e. allocate&initialize) the heap number and return.
3809 // There is no need to process the children.
3810 return MaterializeHeapNumber(frame, &value_index, slot);
3811
3812 case FIXED_ARRAY_TYPE:
3813 case SCRIPT_CONTEXT_TABLE_TYPE:
3814 case AWAIT_CONTEXT_TYPE:
3815 case BLOCK_CONTEXT_TYPE:
3816 case CATCH_CONTEXT_TYPE:
3817 case DEBUG_EVALUATE_CONTEXT_TYPE:
3818 case EVAL_CONTEXT_TYPE:
3819 case FUNCTION_CONTEXT_TYPE:
3820 case MODULE_CONTEXT_TYPE:
3821 case NATIVE_CONTEXT_TYPE:
3822 case SCRIPT_CONTEXT_TYPE:
3823 case WITH_CONTEXT_TYPE:
3824 case HASH_TABLE_TYPE:
3825 case ORDERED_HASH_MAP_TYPE:
3826 case ORDERED_HASH_SET_TYPE:
3827 case NAME_DICTIONARY_TYPE:
3828 case GLOBAL_DICTIONARY_TYPE:
3829 case NUMBER_DICTIONARY_TYPE:
3830 case SIMPLE_NUMBER_DICTIONARY_TYPE: {
3831 // Check we have the right size.
3832 int array_length = frame->values_[value_index].GetSmiValue();
3833 int instance_size = FixedArray::SizeFor(array_length);
3834 CHECK_EQ(instance_size, slot->GetChildrenCount() * kTaggedSize);
3835
3836 // Canonicalize empty fixed array.
3837 if (*map == ReadOnlyRoots(isolate()).empty_fixed_array().map() &&
3838 array_length == 0) {
3839 slot->set_storage(isolate()->factory()->empty_fixed_array());
3840 } else {
3841 slot->set_storage(AllocateStorageFor(slot));
3842 }
3843
3844 // Make sure all the remaining children (after the map) are allocated.
3845 return EnsureChildrenAllocated(slot->GetChildrenCount() - 1, frame,
3846 &value_index, worklist);
3847 }
3848
3849 case SLOPPY_ARGUMENTS_ELEMENTS_TYPE: {
3850 // Verify that the arguments size is correct.
3851 int args_length = frame->values_[value_index].GetSmiValue();
3852 int args_size = SloppyArgumentsElements::SizeFor(args_length);
3853 CHECK_EQ(args_size, slot->GetChildrenCount() * kTaggedSize);
3854
3855 slot->set_storage(AllocateStorageFor(slot));
3856
3857 // Make sure all the remaining children (after the map) are allocated.
3858 return EnsureChildrenAllocated(slot->GetChildrenCount() - 1, frame,
3859 &value_index, worklist);
3860 }
3861
3862 case PROPERTY_ARRAY_TYPE: {
3863 // Check we have the right size.
3864 int length_or_hash = frame->values_[value_index].GetSmiValue();
3865 int array_length = PropertyArray::LengthField::decode(length_or_hash);
3866 int instance_size = PropertyArray::SizeFor(array_length);
3867 CHECK_EQ(instance_size, slot->GetChildrenCount() * kTaggedSize);
3868
3869 slot->set_storage(AllocateStorageFor(slot));
3870
3871 // Make sure all the remaining children (after the map) are allocated.
3872 return EnsureChildrenAllocated(slot->GetChildrenCount() - 1, frame,
3873 &value_index, worklist);
3874 }
3875
3876 default:
3877 CHECK(map->IsJSObjectMap());
3878 EnsureJSObjectAllocated(slot, map);
3879 TranslatedValue* properties_slot = &(frame->values_[value_index]);
3880 value_index++;
3881 if (properties_slot->kind() == TranslatedValue::kCapturedObject) {
3882 // If we are materializing the property array, make sure we put
3883 // the mutable heap numbers at the right places.
3884 EnsurePropertiesAllocatedAndMarked(properties_slot, map);
3885 EnsureChildrenAllocated(properties_slot->GetChildrenCount(), frame,
3886 &value_index, worklist);
3887 }
3888 // Make sure all the remaining children (after the map and properties) are
3889 // allocated.
3890 return EnsureChildrenAllocated(slot->GetChildrenCount() - 2, frame,
3891 &value_index, worklist);
3892 }
3893 UNREACHABLE();
3894 }
3895
EnsureChildrenAllocated(int count,TranslatedFrame * frame,int * value_index,std::stack<int> * worklist)3896 void TranslatedState::EnsureChildrenAllocated(int count, TranslatedFrame* frame,
3897 int* value_index,
3898 std::stack<int>* worklist) {
3899 // Ensure all children are allocated.
3900 for (int i = 0; i < count; i++) {
3901 // If the field is an object that has not been allocated yet, queue it
3902 // for initialization (and mark it as such).
3903 TranslatedValue* child_slot = frame->ValueAt(*value_index);
3904 if (child_slot->kind() == TranslatedValue::kCapturedObject ||
3905 child_slot->kind() == TranslatedValue::kDuplicatedObject) {
3906 child_slot = ResolveCapturedObject(child_slot);
3907 if (child_slot->materialization_state() ==
3908 TranslatedValue::kUninitialized) {
3909 worklist->push(child_slot->object_index());
3910 child_slot->mark_allocated();
3911 }
3912 } else {
3913 // Make sure the simple values (heap numbers, etc.) are properly
3914 // initialized.
3915 child_slot->GetValue();
3916 }
3917 SkipSlots(1, frame, value_index);
3918 }
3919 }
3920
EnsurePropertiesAllocatedAndMarked(TranslatedValue * properties_slot,Handle<Map> map)3921 void TranslatedState::EnsurePropertiesAllocatedAndMarked(
3922 TranslatedValue* properties_slot, Handle<Map> map) {
3923 CHECK_EQ(TranslatedValue::kUninitialized,
3924 properties_slot->materialization_state());
3925
3926 Handle<ByteArray> object_storage = AllocateStorageFor(properties_slot);
3927 properties_slot->mark_allocated();
3928 properties_slot->set_storage(object_storage);
3929
3930 // Set markers for out-of-object properties.
3931 Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad),
3932 isolate());
3933 for (InternalIndex i : map->IterateOwnDescriptors()) {
3934 FieldIndex index = FieldIndex::ForDescriptor(*map, i);
3935 Representation representation = descriptors->GetDetails(i).representation();
3936 if (!index.is_inobject() &&
3937 (representation.IsDouble() || representation.IsHeapObject())) {
3938 CHECK(!map->IsUnboxedDoubleField(index));
3939 int outobject_index = index.outobject_array_index();
3940 int array_index = outobject_index * kTaggedSize;
3941 object_storage->set(array_index, kStoreHeapObject);
3942 }
3943 }
3944 }
3945
AllocateStorageFor(TranslatedValue * slot)3946 Handle<ByteArray> TranslatedState::AllocateStorageFor(TranslatedValue* slot) {
3947 int allocate_size =
3948 ByteArray::LengthFor(slot->GetChildrenCount() * kTaggedSize);
3949 // It is important to allocate all the objects tenured so that the marker
3950 // does not visit them.
3951 Handle<ByteArray> object_storage =
3952 isolate()->factory()->NewByteArray(allocate_size, AllocationType::kOld);
3953 for (int i = 0; i < object_storage->length(); i++) {
3954 object_storage->set(i, kStoreTagged);
3955 }
3956 return object_storage;
3957 }
3958
EnsureJSObjectAllocated(TranslatedValue * slot,Handle<Map> map)3959 void TranslatedState::EnsureJSObjectAllocated(TranslatedValue* slot,
3960 Handle<Map> map) {
3961 CHECK_EQ(map->instance_size(), slot->GetChildrenCount() * kTaggedSize);
3962
3963 Handle<ByteArray> object_storage = AllocateStorageFor(slot);
3964 // Now we handle the interesting (JSObject) case.
3965 Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad),
3966 isolate());
3967
3968 // Set markers for in-object properties.
3969 for (InternalIndex i : map->IterateOwnDescriptors()) {
3970 FieldIndex index = FieldIndex::ForDescriptor(*map, i);
3971 Representation representation = descriptors->GetDetails(i).representation();
3972 if (index.is_inobject() &&
3973 (representation.IsDouble() || representation.IsHeapObject())) {
3974 CHECK_GE(index.index(), FixedArray::kHeaderSize / kTaggedSize);
3975 int array_index = index.index() * kTaggedSize - FixedArray::kHeaderSize;
3976 uint8_t marker = map->IsUnboxedDoubleField(index) ? kStoreUnboxedDouble
3977 : kStoreHeapObject;
3978 object_storage->set(array_index, marker);
3979 }
3980 }
3981 slot->set_storage(object_storage);
3982 }
3983
GetResolvedSlot(TranslatedFrame * frame,int value_index)3984 TranslatedValue* TranslatedState::GetResolvedSlot(TranslatedFrame* frame,
3985 int value_index) {
3986 TranslatedValue* slot = frame->ValueAt(value_index);
3987 if (slot->kind() == TranslatedValue::kDuplicatedObject) {
3988 slot = ResolveCapturedObject(slot);
3989 }
3990 CHECK_NE(slot->materialization_state(), TranslatedValue::kUninitialized);
3991 return slot;
3992 }
3993
GetResolvedSlotAndAdvance(TranslatedFrame * frame,int * value_index)3994 TranslatedValue* TranslatedState::GetResolvedSlotAndAdvance(
3995 TranslatedFrame* frame, int* value_index) {
3996 TranslatedValue* slot = GetResolvedSlot(frame, *value_index);
3997 SkipSlots(1, frame, value_index);
3998 return slot;
3999 }
4000
GetValueAndAdvance(TranslatedFrame * frame,int * value_index)4001 Handle<Object> TranslatedState::GetValueAndAdvance(TranslatedFrame* frame,
4002 int* value_index) {
4003 TranslatedValue* slot = GetResolvedSlot(frame, *value_index);
4004 SkipSlots(1, frame, value_index);
4005 return slot->GetValue();
4006 }
4007
InitializeJSObjectAt(TranslatedFrame * frame,int * value_index,TranslatedValue * slot,Handle<Map> map,const DisallowHeapAllocation & no_allocation)4008 void TranslatedState::InitializeJSObjectAt(
4009 TranslatedFrame* frame, int* value_index, TranslatedValue* slot,
4010 Handle<Map> map, const DisallowHeapAllocation& no_allocation) {
4011 Handle<HeapObject> object_storage = Handle<HeapObject>::cast(slot->storage_);
4012 DCHECK_EQ(TranslatedValue::kCapturedObject, slot->kind());
4013
4014 // The object should have at least a map and some payload.
4015 CHECK_GE(slot->GetChildrenCount(), 2);
4016
4017 // Notify the concurrent marker about the layout change.
4018 isolate()->heap()->NotifyObjectLayoutChange(*object_storage, no_allocation);
4019
4020 // Fill the property array field.
4021 {
4022 Handle<Object> properties = GetValueAndAdvance(frame, value_index);
4023 WRITE_FIELD(*object_storage, JSObject::kPropertiesOrHashOffset,
4024 *properties);
4025 WRITE_BARRIER(*object_storage, JSObject::kPropertiesOrHashOffset,
4026 *properties);
4027 }
4028
4029 // For all the other fields we first look at the fixed array and check the
4030 // marker to see if we store an unboxed double.
4031 DCHECK_EQ(kTaggedSize, JSObject::kPropertiesOrHashOffset);
4032 for (int i = 2; i < slot->GetChildrenCount(); i++) {
4033 TranslatedValue* slot = GetResolvedSlotAndAdvance(frame, value_index);
4034 // Read out the marker and ensure the field is consistent with
4035 // what the markers in the storage say (note that all heap numbers
4036 // should be fully initialized by now).
4037 int offset = i * kTaggedSize;
4038 uint8_t marker = object_storage->ReadField<uint8_t>(offset);
4039 if (marker == kStoreUnboxedDouble) {
4040 Handle<HeapObject> field_value = slot->storage();
4041 CHECK(field_value->IsHeapNumber());
4042 object_storage->WriteField<double>(offset, field_value->Number());
4043 } else if (marker == kStoreHeapObject) {
4044 Handle<HeapObject> field_value = slot->storage();
4045 WRITE_FIELD(*object_storage, offset, *field_value);
4046 WRITE_BARRIER(*object_storage, offset, *field_value);
4047 } else {
4048 CHECK_EQ(kStoreTagged, marker);
4049 Handle<Object> field_value = slot->GetValue();
4050 DCHECK_IMPLIES(field_value->IsHeapNumber(),
4051 !IsSmiDouble(field_value->Number()));
4052 WRITE_FIELD(*object_storage, offset, *field_value);
4053 WRITE_BARRIER(*object_storage, offset, *field_value);
4054 }
4055 }
4056 object_storage->synchronized_set_map(*map);
4057 }
4058
InitializeObjectWithTaggedFieldsAt(TranslatedFrame * frame,int * value_index,TranslatedValue * slot,Handle<Map> map,const DisallowHeapAllocation & no_allocation)4059 void TranslatedState::InitializeObjectWithTaggedFieldsAt(
4060 TranslatedFrame* frame, int* value_index, TranslatedValue* slot,
4061 Handle<Map> map, const DisallowHeapAllocation& no_allocation) {
4062 Handle<HeapObject> object_storage = Handle<HeapObject>::cast(slot->storage_);
4063
4064 // Skip the writes if we already have the canonical empty fixed array.
4065 if (*object_storage == ReadOnlyRoots(isolate()).empty_fixed_array()) {
4066 CHECK_EQ(2, slot->GetChildrenCount());
4067 Handle<Object> length_value = GetValueAndAdvance(frame, value_index);
4068 CHECK_EQ(*length_value, Smi::FromInt(0));
4069 return;
4070 }
4071
4072 // Notify the concurrent marker about the layout change.
4073 isolate()->heap()->NotifyObjectLayoutChange(*object_storage, no_allocation);
4074
4075 // Write the fields to the object.
4076 for (int i = 1; i < slot->GetChildrenCount(); i++) {
4077 TranslatedValue* slot = GetResolvedSlotAndAdvance(frame, value_index);
4078 int offset = i * kTaggedSize;
4079 uint8_t marker = object_storage->ReadField<uint8_t>(offset);
4080 Handle<Object> field_value;
4081 if (i > 1 && marker == kStoreHeapObject) {
4082 field_value = slot->storage();
4083 } else {
4084 CHECK(marker == kStoreTagged || i == 1);
4085 field_value = slot->GetValue();
4086 DCHECK_IMPLIES(field_value->IsHeapNumber(),
4087 !IsSmiDouble(field_value->Number()));
4088 }
4089 WRITE_FIELD(*object_storage, offset, *field_value);
4090 WRITE_BARRIER(*object_storage, offset, *field_value);
4091 }
4092
4093 object_storage->synchronized_set_map(*map);
4094 }
4095
ResolveCapturedObject(TranslatedValue * slot)4096 TranslatedValue* TranslatedState::ResolveCapturedObject(TranslatedValue* slot) {
4097 while (slot->kind() == TranslatedValue::kDuplicatedObject) {
4098 slot = GetValueByObjectIndex(slot->object_index());
4099 }
4100 CHECK_EQ(TranslatedValue::kCapturedObject, slot->kind());
4101 return slot;
4102 }
4103
GetFrameFromJSFrameIndex(int jsframe_index)4104 TranslatedFrame* TranslatedState::GetFrameFromJSFrameIndex(int jsframe_index) {
4105 for (size_t i = 0; i < frames_.size(); i++) {
4106 if (frames_[i].kind() == TranslatedFrame::kInterpretedFunction ||
4107 frames_[i].kind() == TranslatedFrame::kJavaScriptBuiltinContinuation ||
4108 frames_[i].kind() ==
4109 TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch) {
4110 if (jsframe_index > 0) {
4111 jsframe_index--;
4112 } else {
4113 return &(frames_[i]);
4114 }
4115 }
4116 }
4117 return nullptr;
4118 }
4119
GetArgumentsInfoFromJSFrameIndex(int jsframe_index,int * args_count)4120 TranslatedFrame* TranslatedState::GetArgumentsInfoFromJSFrameIndex(
4121 int jsframe_index, int* args_count) {
4122 for (size_t i = 0; i < frames_.size(); i++) {
4123 if (frames_[i].kind() == TranslatedFrame::kInterpretedFunction ||
4124 frames_[i].kind() == TranslatedFrame::kJavaScriptBuiltinContinuation ||
4125 frames_[i].kind() ==
4126 TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch) {
4127 if (jsframe_index > 0) {
4128 jsframe_index--;
4129 } else {
4130 // We have the JS function frame, now check if it has arguments
4131 // adaptor.
4132 if (i > 0 &&
4133 frames_[i - 1].kind() == TranslatedFrame::kArgumentsAdaptor) {
4134 *args_count = frames_[i - 1].height();
4135 return &(frames_[i - 1]);
4136 }
4137
4138 // JavaScriptBuiltinContinuation frames that are not preceeded by
4139 // a arguments adapter frame are currently only used by C++ API calls
4140 // from TurboFan. Calls to C++ API functions from TurboFan need
4141 // a special marker frame state, otherwise the API call wouldn't
4142 // be shown in a stack trace.
4143 if (frames_[i].kind() ==
4144 TranslatedFrame::kJavaScriptBuiltinContinuation &&
4145 frames_[i].shared_info()->internal_formal_parameter_count() ==
4146 kDontAdaptArgumentsSentinel) {
4147 DCHECK(frames_[i].shared_info()->IsApiFunction());
4148
4149 // The argument count for this special case is always the second
4150 // to last value in the TranslatedFrame. It should also always be
4151 // {1}, as the GenericLazyDeoptContinuation builtin only has one
4152 // argument (the receiver).
4153 static constexpr int kTheContext = 1;
4154 const int height = frames_[i].height() + kTheContext;
4155 *args_count = frames_[i].ValueAt(height - 1)->GetSmiValue();
4156 DCHECK_EQ(*args_count, 1);
4157 } else {
4158 *args_count = InternalFormalParameterCountWithReceiver(
4159 *frames_[i].shared_info());
4160 }
4161 return &(frames_[i]);
4162 }
4163 }
4164 }
4165 return nullptr;
4166 }
4167
StoreMaterializedValuesAndDeopt(JavaScriptFrame * frame)4168 void TranslatedState::StoreMaterializedValuesAndDeopt(JavaScriptFrame* frame) {
4169 MaterializedObjectStore* materialized_store =
4170 isolate_->materialized_object_store();
4171 Handle<FixedArray> previously_materialized_objects =
4172 materialized_store->Get(stack_frame_pointer_);
4173
4174 Handle<Object> marker = isolate_->factory()->arguments_marker();
4175
4176 int length = static_cast<int>(object_positions_.size());
4177 bool new_store = false;
4178 if (previously_materialized_objects.is_null()) {
4179 previously_materialized_objects =
4180 isolate_->factory()->NewFixedArray(length, AllocationType::kOld);
4181 for (int i = 0; i < length; i++) {
4182 previously_materialized_objects->set(i, *marker);
4183 }
4184 new_store = true;
4185 }
4186
4187 CHECK_EQ(length, previously_materialized_objects->length());
4188
4189 bool value_changed = false;
4190 for (int i = 0; i < length; i++) {
4191 TranslatedState::ObjectPosition pos = object_positions_[i];
4192 TranslatedValue* value_info =
4193 &(frames_[pos.frame_index_].values_[pos.value_index_]);
4194
4195 CHECK(value_info->IsMaterializedObject());
4196
4197 // Skip duplicate objects (i.e., those that point to some other object id).
4198 if (value_info->object_index() != i) continue;
4199
4200 Handle<Object> previous_value(previously_materialized_objects->get(i),
4201 isolate_);
4202 Handle<Object> value(value_info->GetRawValue(), isolate_);
4203
4204 if (value.is_identical_to(marker)) {
4205 DCHECK_EQ(*previous_value, *marker);
4206 } else {
4207 if (*previous_value == *marker) {
4208 if (value->IsSmi()) {
4209 value = isolate()->factory()->NewHeapNumber(value->Number());
4210 }
4211 previously_materialized_objects->set(i, *value);
4212 value_changed = true;
4213 } else {
4214 CHECK(*previous_value == *value ||
4215 (previous_value->IsHeapNumber() && value->IsSmi() &&
4216 previous_value->Number() == value->Number()));
4217 }
4218 }
4219 }
4220
4221 if (new_store && value_changed) {
4222 materialized_store->Set(stack_frame_pointer_,
4223 previously_materialized_objects);
4224 CHECK_EQ(frames_[0].kind(), TranslatedFrame::kInterpretedFunction);
4225 CHECK_EQ(frame->function(), frames_[0].front().GetRawValue());
4226 Deoptimizer::DeoptimizeFunction(frame->function(), frame->LookupCode());
4227 }
4228 }
4229
UpdateFromPreviouslyMaterializedObjects()4230 void TranslatedState::UpdateFromPreviouslyMaterializedObjects() {
4231 MaterializedObjectStore* materialized_store =
4232 isolate_->materialized_object_store();
4233 Handle<FixedArray> previously_materialized_objects =
4234 materialized_store->Get(stack_frame_pointer_);
4235
4236 // If we have no previously materialized objects, there is nothing to do.
4237 if (previously_materialized_objects.is_null()) return;
4238
4239 Handle<Object> marker = isolate_->factory()->arguments_marker();
4240
4241 int length = static_cast<int>(object_positions_.size());
4242 CHECK_EQ(length, previously_materialized_objects->length());
4243
4244 for (int i = 0; i < length; i++) {
4245 // For a previously materialized objects, inject their value into the
4246 // translated values.
4247 if (previously_materialized_objects->get(i) != *marker) {
4248 TranslatedState::ObjectPosition pos = object_positions_[i];
4249 TranslatedValue* value_info =
4250 &(frames_[pos.frame_index_].values_[pos.value_index_]);
4251 CHECK(value_info->IsMaterializedObject());
4252
4253 if (value_info->kind() == TranslatedValue::kCapturedObject) {
4254 Handle<Object> object(previously_materialized_objects->get(i),
4255 isolate_);
4256 CHECK(object->IsHeapObject());
4257 value_info->set_initialized_storage(Handle<HeapObject>::cast(object));
4258 }
4259 }
4260 }
4261 }
4262
VerifyMaterializedObjects()4263 void TranslatedState::VerifyMaterializedObjects() {
4264 #if VERIFY_HEAP
4265 int length = static_cast<int>(object_positions_.size());
4266 for (int i = 0; i < length; i++) {
4267 TranslatedValue* slot = GetValueByObjectIndex(i);
4268 if (slot->kind() == TranslatedValue::kCapturedObject) {
4269 CHECK_EQ(slot, GetValueByObjectIndex(slot->object_index()));
4270 if (slot->materialization_state() == TranslatedValue::kFinished) {
4271 slot->storage()->ObjectVerify(isolate());
4272 } else {
4273 CHECK_EQ(slot->materialization_state(),
4274 TranslatedValue::kUninitialized);
4275 }
4276 }
4277 }
4278 #endif
4279 }
4280
DoUpdateFeedback()4281 bool TranslatedState::DoUpdateFeedback() {
4282 if (!feedback_vector_handle_.is_null()) {
4283 CHECK(!feedback_slot_.IsInvalid());
4284 isolate()->CountUsage(v8::Isolate::kDeoptimizerDisableSpeculation);
4285 FeedbackNexus nexus(feedback_vector_handle_, feedback_slot_);
4286 nexus.SetSpeculationMode(SpeculationMode::kDisallowSpeculation);
4287 return true;
4288 }
4289 return false;
4290 }
4291
ReadUpdateFeedback(TranslationIterator * iterator,FixedArray literal_array,FILE * trace_file)4292 void TranslatedState::ReadUpdateFeedback(TranslationIterator* iterator,
4293 FixedArray literal_array,
4294 FILE* trace_file) {
4295 CHECK_EQ(Translation::UPDATE_FEEDBACK, iterator->Next());
4296 feedback_vector_ = FeedbackVector::cast(literal_array.get(iterator->Next()));
4297 feedback_slot_ = FeedbackSlot(iterator->Next());
4298 if (trace_file != nullptr) {
4299 PrintF(trace_file, " reading FeedbackVector (slot %d)\n",
4300 feedback_slot_.ToInt());
4301 }
4302 }
4303
4304 } // namespace internal
4305 } // namespace v8
4306
4307 // Undefine the heap manipulation macros.
4308 #include "src/objects/object-macros-undef.h"
4309