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