• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 #ifndef V8_DEOPTIMIZER_TRANSLATED_STATE_H_
6 #define V8_DEOPTIMIZER_TRANSLATED_STATE_H_
7 
8 #include <stack>
9 #include <vector>
10 
11 #include "src/deoptimizer/translation-array.h"
12 #include "src/objects/feedback-vector.h"
13 #include "src/objects/heap-object.h"
14 #include "src/objects/shared-function-info.h"
15 #include "src/utils/boxed-float.h"
16 
17 #if V8_ENABLE_WEBASSEMBLY
18 #include "src/wasm/value-type.h"
19 #endif  // V8_ENABLE_WEBASSEMBLY
20 
21 namespace v8 {
22 namespace internal {
23 
24 class RegisterValues;
25 class TranslatedState;
26 
27 // TODO(jgruber): This duplicates decoding logic already present in
28 // TranslatedState/TranslatedFrame. Deduplicate into one class, e.g. by basing
29 // printing off TranslatedFrame.
30 void TranslationArrayPrintSingleFrame(std::ostream& os,
31                                       TranslationArray translation_array,
32                                       int translation_index,
33                                       DeoptimizationLiteralArray literal_array);
34 
35 // The Translated{Value,Frame,State} class hierarchy are a set of utility
36 // functions to work with the combination of translations (built from a
37 // TranslationArray) and the actual current CPU state (represented by
38 // RegisterValues).
39 //
40 // TranslatedState: describes the entire stack state of the current optimized
41 // frame, contains:
42 //
43 // TranslatedFrame: describes a single unoptimized frame, contains:
44 //
45 // TranslatedValue: the actual value of some location.
46 
47 class TranslatedValue {
48  public:
49   // Allocation-free getter of the value.
50   // Returns ReadOnlyRoots::arguments_marker() if allocation would be necessary
51   // to get the value. In the case of numbers, returns a Smi if possible.
52   Object GetRawValue() const;
53 
54   // Convenience wrapper around GetRawValue (checked).
55   int GetSmiValue() const;
56 
57   // Returns the value, possibly materializing it first (and the whole subgraph
58   // reachable from this value). In the case of numbers, returns a Smi if
59   // possible.
60   Handle<Object> GetValue();
61 
62   bool IsMaterializedObject() const;
63   bool IsMaterializableByDebugger() const;
64 
65  private:
66   friend class TranslatedState;
67   friend class TranslatedFrame;
68   friend class Deoptimizer;
69 
70   enum Kind : uint8_t {
71     kInvalid,
72     kTagged,
73     kInt32,
74     kInt64,
75     kInt64ToBigInt,
76     kUInt32,
77     kBoolBit,
78     kFloat,
79     kDouble,
80     kCapturedObject,   // Object captured by the escape analysis.
81                        // The number of nested objects can be obtained
82                        // with the DeferredObjectLength() method
83                        // (the values of the nested objects follow
84                        // this value in the depth-first order.)
85     kDuplicatedObject  // Duplicated object of a deferred object.
86   };
87 
88   enum MaterializationState : uint8_t {
89     kUninitialized,
90     kAllocated,  // Storage for the object has been allocated (or
91                  // enqueued for allocation).
92     kFinished,   // The object has been initialized (or enqueued for
93                  // initialization).
94   };
95 
TranslatedValue(TranslatedState * container,Kind kind)96   TranslatedValue(TranslatedState* container, Kind kind)
97       : kind_(kind), container_(container) {}
kind()98   Kind kind() const { return kind_; }
materialization_state()99   MaterializationState materialization_state() const {
100     return materialization_state_;
101   }
102   void Handlify();
103   int GetChildrenCount() const;
104 
105   static TranslatedValue NewDeferredObject(TranslatedState* container,
106                                            int length, int object_index);
107   static TranslatedValue NewDuplicateObject(TranslatedState* container, int id);
108   static TranslatedValue NewFloat(TranslatedState* container, Float32 value);
109   static TranslatedValue NewDouble(TranslatedState* container, Float64 value);
110   static TranslatedValue NewInt32(TranslatedState* container, int32_t value);
111   static TranslatedValue NewInt64(TranslatedState* container, int64_t value);
112   static TranslatedValue NewInt64ToBigInt(TranslatedState* container,
113                                           int64_t value);
114   static TranslatedValue NewUInt32(TranslatedState* container, uint32_t value);
115   static TranslatedValue NewBool(TranslatedState* container, uint32_t value);
116   static TranslatedValue NewTagged(TranslatedState* container, Object literal);
117   static TranslatedValue NewInvalid(TranslatedState* container);
118 
119   Isolate* isolate() const;
120 
set_storage(Handle<HeapObject> storage)121   void set_storage(Handle<HeapObject> storage) { storage_ = storage; }
122   void set_initialized_storage(Handle<HeapObject> storage);
mark_finished()123   void mark_finished() { materialization_state_ = kFinished; }
mark_allocated()124   void mark_allocated() { materialization_state_ = kAllocated; }
125 
storage()126   Handle<HeapObject> storage() {
127     DCHECK_NE(materialization_state(), kUninitialized);
128     return storage_;
129   }
130 
131   void ReplaceElementsArrayWithCopy();
132 
133   Kind kind_;
134   MaterializationState materialization_state_ = kUninitialized;
135   TranslatedState* container_;  // This is only needed for materialization of
136                                 // objects and constructing handles (to get
137                                 // to the isolate).
138 
139   Handle<HeapObject> storage_;  // Contains the materialized value or the
140                                 // byte-array that will be later morphed into
141                                 // the materialized object.
142 
143   struct MaterializedObjectInfo {
144     int id_;
145     int length_;  // Applies only to kCapturedObject kinds.
146   };
147 
148   union {
149     // kind kTagged. After handlification it is always nullptr.
150     Object raw_literal_;
151     // kind is kUInt32 or kBoolBit.
152     uint32_t uint32_value_;
153     // kind is kInt32.
154     int32_t int32_value_;
155     // kind is kInt64.
156     int64_t int64_value_;
157     // kind is kFloat
158     Float32 float_value_;
159     // kind is kDouble
160     Float64 double_value_;
161     // kind is kDuplicatedObject or kCapturedObject.
162     MaterializedObjectInfo materialization_info_;
163   };
164 
165   // Checked accessors for the union members.
166   Object raw_literal() const;
167   int32_t int32_value() const;
168   int64_t int64_value() const;
169   uint32_t uint32_value() const;
170   Float32 float_value() const;
171   Float64 double_value() const;
172   int object_length() const;
173   int object_index() const;
174 };
175 
176 class TranslatedFrame {
177  public:
178   enum Kind {
179     kUnoptimizedFunction,
180     kArgumentsAdaptor,
181     kConstructStub,
182     kBuiltinContinuation,
183 #if V8_ENABLE_WEBASSEMBLY
184     kJSToWasmBuiltinContinuation,
185 #endif  // V8_ENABLE_WEBASSEMBLY
186     kJavaScriptBuiltinContinuation,
187     kJavaScriptBuiltinContinuationWithCatch,
188     kInvalid
189   };
190 
191   int GetValueCount();
192 
kind()193   Kind kind() const { return kind_; }
bytecode_offset()194   BytecodeOffset bytecode_offset() const { return bytecode_offset_; }
shared_info()195   Handle<SharedFunctionInfo> shared_info() const { return shared_info_; }
196 
197   // TODO(jgruber): Simplify/clarify the semantics of this field. The name
198   // `height` is slightly misleading. Yes, this value is related to stack frame
199   // height, but must undergo additional mutations to arrive at the real stack
200   // frame height (e.g.: addition/subtraction of context, accumulator, fixed
201   // frame sizes, padding).
height()202   int height() const { return height_; }
203 
return_value_offset()204   int return_value_offset() const { return return_value_offset_; }
return_value_count()205   int return_value_count() const { return return_value_count_; }
206 
raw_shared_info()207   SharedFunctionInfo raw_shared_info() const {
208     CHECK(!raw_shared_info_.is_null());
209     return raw_shared_info_;
210   }
211 
212   class iterator {
213    public:
214     iterator& operator++() {
215       ++input_index_;
216       AdvanceIterator(&position_);
217       return *this;
218     }
219 
220     iterator operator++(int) {
221       iterator original(position_, input_index_);
222       ++input_index_;
223       AdvanceIterator(&position_);
224       return original;
225     }
226 
227     bool operator==(const iterator& other) const {
228       // Ignore {input_index_} for equality.
229       return position_ == other.position_;
230     }
231     bool operator!=(const iterator& other) const { return !(*this == other); }
232 
233     TranslatedValue& operator*() { return (*position_); }
234     TranslatedValue* operator->() { return &(*position_); }
235     const TranslatedValue& operator*() const { return (*position_); }
236     const TranslatedValue* operator->() const { return &(*position_); }
237 
input_index()238     int input_index() const { return input_index_; }
239 
240    private:
241     friend TranslatedFrame;
242 
243     explicit iterator(std::deque<TranslatedValue>::iterator position,
244                       int input_index = 0)
position_(position)245         : position_(position), input_index_(input_index) {}
246 
247     std::deque<TranslatedValue>::iterator position_;
248     int input_index_;
249   };
250 
251   using reference = TranslatedValue&;
252   using const_reference = TranslatedValue const&;
253 
begin()254   iterator begin() { return iterator(values_.begin()); }
end()255   iterator end() { return iterator(values_.end()); }
256 
front()257   reference front() { return values_.front(); }
front()258   const_reference front() const { return values_.front(); }
259 
260 #if V8_ENABLE_WEBASSEMBLY
261   // Only for Kind == kJSToWasmBuiltinContinuation
wasm_call_return_kind()262   base::Optional<wasm::ValueKind> wasm_call_return_kind() const {
263     DCHECK_EQ(kind(), kJSToWasmBuiltinContinuation);
264     return return_kind_;
265   }
266 #endif  // V8_ENABLE_WEBASSEMBLY
267 
268  private:
269   friend class TranslatedState;
270   friend class Deoptimizer;
271 
272   // Constructor static methods.
273   static TranslatedFrame UnoptimizedFrame(BytecodeOffset bytecode_offset,
274                                           SharedFunctionInfo shared_info,
275                                           int height, int return_value_offset,
276                                           int return_value_count);
277   static TranslatedFrame AccessorFrame(Kind kind,
278                                        SharedFunctionInfo shared_info);
279   static TranslatedFrame ArgumentsAdaptorFrame(SharedFunctionInfo shared_info,
280                                                int height);
281   static TranslatedFrame ConstructStubFrame(BytecodeOffset bailout_id,
282                                             SharedFunctionInfo shared_info,
283                                             int height);
284   static TranslatedFrame BuiltinContinuationFrame(
285       BytecodeOffset bailout_id, SharedFunctionInfo shared_info, int height);
286 #if V8_ENABLE_WEBASSEMBLY
287   static TranslatedFrame JSToWasmBuiltinContinuationFrame(
288       BytecodeOffset bailout_id, SharedFunctionInfo shared_info, int height,
289       base::Optional<wasm::ValueKind> return_type);
290 #endif  // V8_ENABLE_WEBASSEMBLY
291   static TranslatedFrame JavaScriptBuiltinContinuationFrame(
292       BytecodeOffset bailout_id, SharedFunctionInfo shared_info, int height);
293   static TranslatedFrame JavaScriptBuiltinContinuationWithCatchFrame(
294       BytecodeOffset bailout_id, SharedFunctionInfo shared_info, int height);
InvalidFrame()295   static TranslatedFrame InvalidFrame() {
296     return TranslatedFrame(kInvalid, SharedFunctionInfo());
297   }
298 
299   static void AdvanceIterator(std::deque<TranslatedValue>::iterator* iter);
300 
301   TranslatedFrame(Kind kind,
302                   SharedFunctionInfo shared_info = SharedFunctionInfo(),
303                   int height = 0, int return_value_offset = 0,
304                   int return_value_count = 0)
kind_(kind)305       : kind_(kind),
306         bytecode_offset_(BytecodeOffset::None()),
307         raw_shared_info_(shared_info),
308         height_(height),
309         return_value_offset_(return_value_offset),
310         return_value_count_(return_value_count) {}
311 
Add(const TranslatedValue & value)312   void Add(const TranslatedValue& value) { values_.push_back(value); }
ValueAt(int index)313   TranslatedValue* ValueAt(int index) { return &(values_[index]); }
314   void Handlify();
315 
316   Kind kind_;
317   BytecodeOffset bytecode_offset_;
318   SharedFunctionInfo raw_shared_info_;
319   Handle<SharedFunctionInfo> shared_info_;
320   int height_;
321   int return_value_offset_;
322   int return_value_count_;
323 
324   using ValuesContainer = std::deque<TranslatedValue>;
325 
326   ValuesContainer values_;
327 
328 #if V8_ENABLE_WEBASSEMBLY
329   // Only for Kind == kJSToWasmBuiltinContinuation
330   base::Optional<wasm::ValueKind> return_kind_;
331 #endif  // V8_ENABLE_WEBASSEMBLY
332 };
333 
334 // Auxiliary class for translating deoptimization values.
335 // Typical usage sequence:
336 //
337 // 1. Construct the instance. This will involve reading out the translations
338 //    and resolving them to values using the supplied frame pointer and
339 //    machine state (registers). This phase is guaranteed not to allocate
340 //    and not to use any HandleScope. Any object pointers will be stored raw.
341 //
342 // 2. Handlify pointers. This will convert all the raw pointers to handles.
343 //
344 // 3. Reading out the frame values.
345 //
346 // Note: After the instance is constructed, it is possible to iterate over
347 // the values eagerly.
348 
349 class TranslatedState {
350  public:
351   // There are two constructors, each for a different purpose:
352 
353   // The default constructor is for the purpose of deoptimizing an optimized
354   // frame (replacing it with one or several unoptimized frames). It is used by
355   // the Deoptimizer.
TranslatedState()356   TranslatedState() : purpose_(kDeoptimization) {}
357 
358   // This constructor is for the purpose of merely inspecting an optimized
359   // frame. It is used by stack trace generation and various debugging features.
360   explicit TranslatedState(const JavaScriptFrame* frame);
361 
362   void Prepare(Address stack_frame_pointer);
363 
364   // Store newly materialized values into the isolate.
365   void StoreMaterializedValuesAndDeopt(JavaScriptFrame* frame);
366 
367   using iterator = std::vector<TranslatedFrame>::iterator;
begin()368   iterator begin() { return frames_.begin(); }
end()369   iterator end() { return frames_.end(); }
370 
371   using const_iterator = std::vector<TranslatedFrame>::const_iterator;
begin()372   const_iterator begin() const { return frames_.begin(); }
end()373   const_iterator end() const { return frames_.end(); }
374 
frames()375   std::vector<TranslatedFrame>& frames() { return frames_; }
376 
377   TranslatedFrame* GetFrameFromJSFrameIndex(int jsframe_index);
378   TranslatedFrame* GetArgumentsInfoFromJSFrameIndex(int jsframe_index,
379                                                     int* arguments_count);
380 
isolate()381   Isolate* isolate() { return isolate_; }
382 
383   void Init(Isolate* isolate, Address input_frame_pointer,
384             Address stack_frame_pointer, TranslationArrayIterator* iterator,
385             DeoptimizationLiteralArray literal_array, RegisterValues* registers,
386             FILE* trace_file, int parameter_count, int actual_argument_count);
387 
388   void VerifyMaterializedObjects();
389   bool DoUpdateFeedback();
390 
391  private:
392   friend TranslatedValue;
393 
394   // See the description of the constructors for an explanation of the two
395   // purposes. The only actual difference is that in the kFrameInspection case
396   // extra work is needed to not violate assumptions made by left-trimming.  For
397   // details, see the code around ReplaceElementsArrayWithCopy.
398   enum Purpose { kDeoptimization, kFrameInspection };
399 
400   TranslatedFrame CreateNextTranslatedFrame(
401       TranslationArrayIterator* iterator,
402       DeoptimizationLiteralArray literal_array, Address fp, FILE* trace_file);
403   int CreateNextTranslatedValue(int frame_index,
404                                 TranslationArrayIterator* iterator,
405                                 DeoptimizationLiteralArray literal_array,
406                                 Address fp, RegisterValues* registers,
407                                 FILE* trace_file);
408   Address DecompressIfNeeded(intptr_t value);
409   void CreateArgumentsElementsTranslatedValues(int frame_index,
410                                                Address input_frame_pointer,
411                                                CreateArgumentsType type,
412                                                FILE* trace_file);
413 
414   void UpdateFromPreviouslyMaterializedObjects();
415   void MaterializeFixedDoubleArray(TranslatedFrame* frame, int* value_index,
416                                    TranslatedValue* slot, Handle<Map> map);
417   void MaterializeHeapNumber(TranslatedFrame* frame, int* value_index,
418                              TranslatedValue* slot);
419 
420   void EnsureObjectAllocatedAt(TranslatedValue* slot);
421 
422   void SkipSlots(int slots_to_skip, TranslatedFrame* frame, int* value_index);
423 
424   Handle<ByteArray> AllocateStorageFor(TranslatedValue* slot);
425   void EnsureJSObjectAllocated(TranslatedValue* slot, Handle<Map> map);
426   void EnsurePropertiesAllocatedAndMarked(TranslatedValue* properties_slot,
427                                           Handle<Map> map);
428   void EnsureChildrenAllocated(int count, TranslatedFrame* frame,
429                                int* value_index, std::stack<int>* worklist);
430   void EnsureCapturedObjectAllocatedAt(int object_index,
431                                        std::stack<int>* worklist);
432   Handle<HeapObject> InitializeObjectAt(TranslatedValue* slot);
433   void InitializeCapturedObjectAt(int object_index, std::stack<int>* worklist,
434                                   const DisallowGarbageCollection& no_gc);
435   void InitializeJSObjectAt(TranslatedFrame* frame, int* value_index,
436                             TranslatedValue* slot, Handle<Map> map,
437                             const DisallowGarbageCollection& no_gc);
438   void InitializeObjectWithTaggedFieldsAt(
439       TranslatedFrame* frame, int* value_index, TranslatedValue* slot,
440       Handle<Map> map, const DisallowGarbageCollection& no_gc);
441 
442   void ReadUpdateFeedback(TranslationArrayIterator* iterator,
443                           DeoptimizationLiteralArray literal_array,
444                           FILE* trace_file);
445 
446   TranslatedValue* ResolveCapturedObject(TranslatedValue* slot);
447   TranslatedValue* GetValueByObjectIndex(int object_index);
448   Handle<Object> GetValueAndAdvance(TranslatedFrame* frame, int* value_index);
449   TranslatedValue* GetResolvedSlot(TranslatedFrame* frame, int value_index);
450   TranslatedValue* GetResolvedSlotAndAdvance(TranslatedFrame* frame,
451                                              int* value_index);
452 
453   static uint32_t GetUInt32Slot(Address fp, int slot_index);
454   static uint64_t GetUInt64Slot(Address fp, int slot_index);
455   static Float32 GetFloatSlot(Address fp, int slot_index);
456   static Float64 GetDoubleSlot(Address fp, int slot_index);
457 
458   Purpose const purpose_;
459   std::vector<TranslatedFrame> frames_;
460   Isolate* isolate_ = nullptr;
461   Address stack_frame_pointer_ = kNullAddress;
462   int formal_parameter_count_;
463   int actual_argument_count_;
464 
465   struct ObjectPosition {
466     int frame_index_;
467     int value_index_;
468   };
469   std::deque<ObjectPosition> object_positions_;
470   Handle<FeedbackVector> feedback_vector_handle_;
471   FeedbackVector feedback_vector_;
472   FeedbackSlot feedback_slot_;
473 };
474 
475 // Return kind encoding for a Wasm function returning void.
476 const int kNoWasmReturnKind = -1;
477 
478 }  // namespace internal
479 }  // namespace v8
480 
481 #endif  // V8_DEOPTIMIZER_TRANSLATED_STATE_H_
482