%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/objects/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/objects/deoptimization-data.cc |
// Copyright 2023 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/objects/deoptimization-data.h" #include <iomanip> #include "src/deoptimizer/translated-state.h" #include "src/interpreter/bytecode-array-iterator.h" #include "src/objects/code.h" #include "src/objects/deoptimization-data-inl.h" #include "src/objects/shared-function-info.h" #ifdef V8_USE_ZLIB #include "third_party/zlib/google/compression_utils_portable.h" #endif // V8_USE_ZLIB namespace v8 { namespace internal { Handle<DeoptimizationData> DeoptimizationData::New(Isolate* isolate, int deopt_entry_count, AllocationType allocation) { return Handle<DeoptimizationData>::cast(isolate->factory()->NewFixedArray( LengthFor(deopt_entry_count), allocation)); } Handle<DeoptimizationData> DeoptimizationData::New(LocalIsolate* isolate, int deopt_entry_count, AllocationType allocation) { return Handle<DeoptimizationData>::cast(isolate->factory()->NewFixedArray( LengthFor(deopt_entry_count), allocation)); } Handle<DeoptimizationData> DeoptimizationData::Empty(Isolate* isolate) { return Handle<DeoptimizationData>::cast( isolate->factory()->empty_fixed_array()); } Handle<DeoptimizationData> DeoptimizationData::Empty(LocalIsolate* isolate) { return Handle<DeoptimizationData>::cast( isolate->factory()->empty_fixed_array()); } Tagged<SharedFunctionInfo> DeoptimizationData::GetInlinedFunction(int index) { if (index == -1) { return SharedFunctionInfo::cast(SharedFunctionInfo()); } else { return SharedFunctionInfo::cast(LiteralArray()->get(index)); } } #ifdef DEBUG void DeoptimizationData::Verify(Handle<BytecodeArray> bytecode) const { #ifdef V8_USE_ZLIB if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) { return; } #endif // V8_USE_ZLIB for (int i = 0; i < DeoptCount(); ++i) { // Check the frame count and identify the bailout id of the top compilation // unit. int idx = TranslationIndex(i).value(); DeoptimizationFrameTranslation::Iterator iterator(FrameTranslation(), idx); auto [frame_count, jsframe_count] = iterator.EnterBeginOpcode(); DCHECK_GE(frame_count, jsframe_count); BytecodeOffset bailout = BytecodeOffset::None(); bool first_frame = true; while (frame_count > 0) { TranslationOpcode frame = iterator.SeekNextFrame(); frame_count--; if (IsTranslationJsFrameOpcode(frame)) { jsframe_count--; if (first_frame) { bailout = BytecodeOffset(iterator.NextOperand()); first_frame = false; iterator.SkipOperands(TranslationOpcodeOperandCount(frame) - 1); continue; } } iterator.SkipOperands(TranslationOpcodeOperandCount(frame)); } CHECK_EQ(frame_count, 0); CHECK_EQ(jsframe_count, 0); // Check the bytecode offset exists in the bytecode array if (bailout != BytecodeOffset::None()) { #ifdef ENABLE_SLOW_DCHECKS interpreter::BytecodeArrayIterator bytecode_iterator(bytecode); while (bytecode_iterator.current_offset() < bailout.ToInt()) { bytecode_iterator.Advance(); DCHECK_LE(bytecode_iterator.current_offset(), bailout.ToInt()); } #else DCHECK_GE(bailout.ToInt(), 0); DCHECK_LT(bailout.ToInt(), bytecode->length()); #endif // ENABLE_SLOW_DCHECKS } } } #endif // DEBUG #ifdef ENABLE_DISASSEMBLER namespace { void print_pc(std::ostream& os, int pc) { if (pc == -1) { os << "NA"; } else { os << std::hex << pc << std::dec; } } } // namespace void DeoptimizationData::PrintDeoptimizationData(std::ostream& os) const { if (length() == 0) { os << "Deoptimization Input Data invalidated by lazy deoptimization\n"; return; } int const inlined_function_count = InlinedFunctionCount().value(); os << "Inlined functions (count = " << inlined_function_count << ")\n"; for (int id = 0; id < inlined_function_count; ++id) { Tagged<Object> info = LiteralArray()->get(id); os << " " << Brief(SharedFunctionInfo::cast(info)) << "\n"; } os << "\n"; int deopt_count = DeoptCount(); os << "Deoptimization Input Data (deopt points = " << deopt_count << ")\n"; if (0 != deopt_count) { #ifdef DEBUG os << " index bytecode-offset node-id pc"; #else // DEBUG os << " index bytecode-offset pc"; #endif // DEBUG if (v8_flags.print_code_verbose) os << " commands"; os << "\n"; } for (int i = 0; i < deopt_count; i++) { os << std::setw(6) << i << " " << std::setw(15) << GetBytecodeOffsetOrBuiltinContinuationId(i).ToInt() << " " #ifdef DEBUG << std::setw(7) << NodeId(i).value() << " " #endif // DEBUG << std::setw(4); print_pc(os, Pc(i).value()); os << std::setw(2) << "\n"; if (v8_flags.print_code_verbose) { FrameTranslation()->PrintFrameTranslation(os, TranslationIndex(i).value(), LiteralArray()); } } } #endif // ENABLE_DISASSEMBLER DeoptimizationFrameTranslation::Iterator::Iterator( Tagged<DeoptimizationFrameTranslation> buffer, int index) : buffer_(buffer), index_(index) { #ifdef V8_USE_ZLIB if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) { const int size = buffer_->get_int(kUncompressedSizeOffset); uncompressed_contents_.insert(uncompressed_contents_.begin(), size, 0); uLongf uncompressed_size = size * kDeoptimizationFrameTranslationElementSize; CHECK_EQ(zlib_internal::UncompressHelper( zlib_internal::ZRAW, base::bit_cast<Bytef*>(uncompressed_contents_.data()), &uncompressed_size, buffer_->GetDataStartAddress() + kCompressedDataOffset, buffer_->DataSize()), Z_OK); DCHECK(index >= 0 && index < size); return; } #endif // V8_USE_ZLIB DCHECK(!v8_flags.turbo_compress_frame_translations); DCHECK(index >= 0 && index < buffer->length()); // Starting at a location other than a BEGIN would make // MATCH_PREVIOUS_TRANSLATION instructions not work. DCHECK(TranslationOpcodeIsBegin( static_cast<TranslationOpcode>(buffer_->GetDataStartAddress()[index]))); } int32_t DeoptimizationFrameTranslation::Iterator::NextOperand() { if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) { return uncompressed_contents_[index_++]; } else if (remaining_ops_to_use_from_previous_translation_) { int32_t value = base::VLQDecode(buffer_->GetDataStartAddress(), &previous_index_); DCHECK_LT(previous_index_, index_); return value; } else { int32_t value = base::VLQDecode(buffer_->GetDataStartAddress(), &index_); DCHECK_LE(index_, buffer_->length()); return value; } } TranslationOpcode DeoptimizationFrameTranslation::Iterator::NextOpcodeAtPreviousIndex() { TranslationOpcode opcode = static_cast<TranslationOpcode>(buffer_->get(previous_index_++)); DCHECK_LT(static_cast<uint32_t>(opcode), kNumTranslationOpcodes); DCHECK_NE(opcode, TranslationOpcode::MATCH_PREVIOUS_TRANSLATION); DCHECK_LT(previous_index_, index_); return opcode; } uint32_t DeoptimizationFrameTranslation::Iterator::NextUnsignedOperandAtPreviousIndex() { uint32_t value = base::VLQDecodeUnsigned(buffer_->GetDataStartAddress(), &previous_index_); DCHECK_LT(previous_index_, index_); return value; } uint32_t DeoptimizationFrameTranslation::Iterator::NextOperandUnsigned() { if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) { return uncompressed_contents_[index_++]; } else if (remaining_ops_to_use_from_previous_translation_) { return NextUnsignedOperandAtPreviousIndex(); } else { uint32_t value = base::VLQDecodeUnsigned(buffer_->GetDataStartAddress(), &index_); DCHECK_LE(index_, buffer_->length()); return value; } } TranslationOpcode DeoptimizationFrameTranslation::Iterator::NextOpcode() { if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) { return static_cast<TranslationOpcode>(NextOperandUnsigned()); } if (remaining_ops_to_use_from_previous_translation_) { --remaining_ops_to_use_from_previous_translation_; } if (remaining_ops_to_use_from_previous_translation_) { return NextOpcodeAtPreviousIndex(); } CHECK_LT(index_, buffer_->length()); uint8_t opcode_byte = buffer_->get(index_++); // If the opcode byte is greater than any valid opcode, then the opcode is // implicitly MATCH_PREVIOUS_TRANSLATION and the operand is the opcode byte // minus kNumTranslationOpcodes. This special-case encoding of the most common // opcode saves some memory. if (opcode_byte >= kNumTranslationOpcodes) { remaining_ops_to_use_from_previous_translation_ = opcode_byte - kNumTranslationOpcodes; opcode_byte = static_cast<uint8_t>(TranslationOpcode::MATCH_PREVIOUS_TRANSLATION); } else if (opcode_byte == static_cast<uint8_t>( TranslationOpcode::MATCH_PREVIOUS_TRANSLATION)) { remaining_ops_to_use_from_previous_translation_ = NextOperandUnsigned(); } TranslationOpcode opcode = static_cast<TranslationOpcode>(opcode_byte); DCHECK_LE(index_, buffer_->length()); DCHECK_LT(static_cast<uint32_t>(opcode), kNumTranslationOpcodes); if (TranslationOpcodeIsBegin(opcode)) { int temp_index = index_; // The first argument for BEGIN is the distance, in bytes, since the // previous BEGIN, or zero to indicate that MATCH_PREVIOUS_TRANSLATION will // not be used in this translation. uint32_t lookback_distance = base::VLQDecodeUnsigned(buffer_->GetDataStartAddress(), &temp_index); if (lookback_distance) { previous_index_ = index_ - 1 - lookback_distance; DCHECK(TranslationOpcodeIsBegin( static_cast<TranslationOpcode>(buffer_->get(previous_index_)))); // The previous BEGIN should specify zero as its lookback distance, // meaning it won't use MATCH_PREVIOUS_TRANSLATION. DCHECK_EQ(buffer_->get(previous_index_ + 1), 0); } ops_since_previous_index_was_updated_ = 1; } else if (opcode == TranslationOpcode::MATCH_PREVIOUS_TRANSLATION) { for (int i = 0; i < ops_since_previous_index_was_updated_; ++i) { SkipOpcodeAndItsOperandsAtPreviousIndex(); } ops_since_previous_index_was_updated_ = 0; opcode = NextOpcodeAtPreviousIndex(); } else { ++ops_since_previous_index_was_updated_; } return opcode; } DeoptimizationFrameTranslation::FrameCount DeoptimizationFrameTranslation::Iterator::EnterBeginOpcode() { TranslationOpcode opcode = NextOpcode(); DCHECK(TranslationOpcodeIsBegin(opcode)); USE(opcode); NextOperand(); // Skip lookback distance. int frame_count = NextOperand(); int jsframe_count = NextOperand(); return {frame_count, jsframe_count}; } TranslationOpcode DeoptimizationFrameTranslation::Iterator::SeekNextJSFrame() { while (HasNextOpcode()) { TranslationOpcode opcode = NextOpcode(); DCHECK(!TranslationOpcodeIsBegin(opcode)); if (IsTranslationJsFrameOpcode(opcode)) { return opcode; } else { // Skip over operands to advance to the next opcode. SkipOperands(TranslationOpcodeOperandCount(opcode)); } } UNREACHABLE(); } TranslationOpcode DeoptimizationFrameTranslation::Iterator::SeekNextFrame() { while (HasNextOpcode()) { TranslationOpcode opcode = NextOpcode(); DCHECK(!TranslationOpcodeIsBegin(opcode)); if (IsTranslationFrameOpcode(opcode)) { return opcode; } else { // Skip over operands to advance to the next opcode. SkipOperands(TranslationOpcodeOperandCount(opcode)); } } UNREACHABLE(); } bool DeoptimizationFrameTranslation::Iterator::HasNextOpcode() const { if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) { return index_ < static_cast<int>(uncompressed_contents_.size()); } else { return index_ < buffer_->length() || remaining_ops_to_use_from_previous_translation_ > 1; } } void DeoptimizationFrameTranslation::Iterator:: SkipOpcodeAndItsOperandsAtPreviousIndex() { TranslationOpcode opcode = NextOpcodeAtPreviousIndex(); for (int count = TranslationOpcodeOperandCount(opcode); count != 0; --count) { NextUnsignedOperandAtPreviousIndex(); } } #ifdef ENABLE_DISASSEMBLER void DeoptimizationFrameTranslation::PrintFrameTranslation( std::ostream& os, int index, Tagged<DeoptimizationLiteralArray> literal_array) const { DisallowGarbageCollection gc_oh_noes; DeoptimizationFrameTranslation::Iterator iterator(*this, index); TranslationOpcode opcode = iterator.NextOpcode(); DCHECK(TranslationOpcodeIsBegin(opcode)); os << opcode << " "; DeoptimizationFrameTranslationPrintSingleOpcode(os, opcode, iterator, literal_array); while (iterator.HasNextOpcode()) { TranslationOpcode opcode = iterator.NextOpcode(); if (TranslationOpcodeIsBegin(opcode)) { break; } os << opcode << " "; DeoptimizationFrameTranslationPrintSingleOpcode(os, opcode, iterator, literal_array); } } #endif // ENABLE_DISASSEMBLER } // namespace internal } // namespace v8