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