v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
deoptimization-data.cc
Go to the documentation of this file.
1// Copyright 2023 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
6
7#include <iomanip>
8
11#include "src/objects/casting.h"
12#include "src/objects/code.h"
15
16#ifdef V8_USE_ZLIB
17#include "third_party/zlib/google/compression_utils_portable.h"
18#endif // V8_USE_ZLIB
19
20namespace v8 {
21namespace internal {
22
24 Validate();
25 switch (kind_) {
27 return object_;
28 }
30 return isolate->factory()->NewNumber(number_);
31 }
33 return BigInt::FromInt64(isolate, int64_);
34 }
36 return BigInt::FromUint64(isolate, uint64_);
37 }
39 // Hole NaNs that made it to here represent the undefined value.
40 return isolate->factory()->undefined_value();
41 }
48 }
49 }
51}
52
54 int deopt_entry_count) {
56 isolate->factory()->NewProtectedFixedArray(LengthFor(deopt_entry_count)));
57}
58
60 int deopt_entry_count) {
62 isolate->factory()->NewProtectedFixedArray(LengthFor(deopt_entry_count)));
63}
64
67 isolate->factory()->empty_protected_fixed_array());
68}
69
72 isolate->factory()->empty_protected_fixed_array());
73}
74
76 if (index == -1) {
77 return GetSharedFunctionInfo();
78 } else {
79 return Cast<i::SharedFunctionInfo>(LiteralArray()->get(index));
80 }
81}
82
83#ifdef DEBUG
84void DeoptimizationData::Verify(Handle<BytecodeArray> bytecode) const {
85#ifdef V8_USE_ZLIB
86 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
87 return;
88 }
89#endif // V8_USE_ZLIB
90 for (int i = 0; i < DeoptCount(); ++i) {
91 // Check the frame count and identify the bailout id of the top compilation
92 // unit.
93 int idx = TranslationIndex(i).value();
94 DeoptimizationFrameTranslation::Iterator iterator(FrameTranslation(), idx);
95 auto [frame_count, jsframe_count] = iterator.EnterBeginOpcode();
96 DCHECK_GE(frame_count, jsframe_count);
97 BytecodeOffset bailout = BytecodeOffset::None();
98 bool first_frame = true;
99 while (frame_count > 0) {
100 TranslationOpcode frame = iterator.SeekNextFrame();
101 frame_count--;
102 if (IsTranslationJsFrameOpcode(frame)) {
103 jsframe_count--;
104 if (first_frame) {
105 bailout = BytecodeOffset(iterator.NextOperand());
106 first_frame = false;
107 iterator.SkipOperands(TranslationOpcodeOperandCount(frame) - 1);
108 continue;
109 }
110 }
111 iterator.SkipOperands(TranslationOpcodeOperandCount(frame));
112 }
113 CHECK_EQ(frame_count, 0);
114 CHECK_EQ(jsframe_count, 0);
115
116 // Check the bytecode offset exists in the bytecode array
117 if (bailout != BytecodeOffset::None()) {
118#ifdef ENABLE_SLOW_DCHECKS
119 interpreter::BytecodeArrayIterator bytecode_iterator(bytecode);
120 while (bytecode_iterator.current_offset() < bailout.ToInt()) {
121 bytecode_iterator.Advance();
122 DCHECK_LE(bytecode_iterator.current_offset(), bailout.ToInt());
123 }
124#else
125 DCHECK_GE(bailout.ToInt(), 0);
126 DCHECK_LT(bailout.ToInt(), bytecode->length());
127#endif // ENABLE_SLOW_DCHECKS
128 }
129 }
130}
131#endif // DEBUG
132
133#ifdef ENABLE_DISASSEMBLER
134
135namespace {
136void print_pc(std::ostream& os, int pc) {
137 if (pc == -1) {
138 os << "NA";
139 } else {
140 os << std::hex << pc << std::dec;
141 }
142}
143} // namespace
144
145void DeoptimizationData::PrintDeoptimizationData(std::ostream& os) const {
146 if (length() == 0) {
147 os << "Deoptimization Input Data invalidated by lazy deoptimization\n";
148 return;
149 }
150
151 int const inlined_function_count = InlinedFunctionCount().value();
152 os << "Inlined functions (count = " << inlined_function_count << ")\n";
153 for (int id = 0; id < inlined_function_count; ++id) {
154 Tagged<Object> info = LiteralArray()->get(id);
155 os << " " << Brief(Cast<i::SharedFunctionInfo>(info)) << "\n";
156 }
157 os << "\n";
158 int deopt_count = DeoptCount();
159 os << "Deoptimization Input Data (deopt points = " << deopt_count << ")\n";
160 if (0 != deopt_count) {
161#ifdef DEBUG
162 os << " index bytecode-offset node-id pc";
163#else // DEBUG
164 os << " index bytecode-offset pc";
165#endif // DEBUG
166 if (v8_flags.print_code_verbose) os << " commands";
167 os << "\n";
168 }
169 for (int i = 0; i < deopt_count; i++) {
170 os << std::setw(6) << i << " " << std::setw(15)
172#ifdef DEBUG
173 << std::setw(7) << NodeId(i).value() << " "
174#endif // DEBUG
175 << std::setw(4);
176 print_pc(os, Pc(i).value());
177 os << std::setw(2) << "\n";
178
179 if (v8_flags.print_code_verbose) {
180 FrameTranslation()->PrintFrameTranslation(os, TranslationIndex(i).value(),
181 ProtectedLiteralArray(),
182 LiteralArray());
183 }
184 }
185}
186
187#endif // ENABLE_DISASSEMBLER
188
190 base::Vector<const uint8_t> buffer, int index)
191 : buffer_(buffer), index_(index) {
192#ifdef V8_USE_ZLIB
193 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
194 const int size =
195 base::ReadUnalignedValue<uint32_t>(reinterpret_cast<Address>(
196 &buffer_[DeoptimizationFrameTranslation::kUncompressedSizeOffset]));
197 uncompressed_contents_.insert(uncompressed_contents_.begin(), size, 0);
198
199 uLongf uncompressed_size = size *
200 DeoptimizationFrameTranslation::
201 kDeoptimizationFrameTranslationElementSize;
202
203 CHECK_EQ(zlib_internal::UncompressHelper(
204 zlib_internal::ZRAW,
205 reinterpret_cast<Bytef*>(uncompressed_contents_.data()),
206 &uncompressed_size,
207 buffer_.begin() +
208 DeoptimizationFrameTranslation::kCompressedDataOffset,
209 buffer_.length()),
210 Z_OK);
211 DCHECK(index >= 0 && index < size);
212 return;
213 }
214#endif // V8_USE_ZLIB
215 DCHECK(!v8_flags.turbo_compress_frame_translations);
216 DCHECK(index >= 0 && index < buffer_.length());
217 // Starting at a location other than a BEGIN would make
218 // MATCH_PREVIOUS_TRANSLATION instructions not work.
219 DCHECK(
221}
222
226 base::Vector<uint8_t>(buffer->begin(), buffer->length()), index) {}
227
229 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
230 return uncompressed_contents_[index_++];
231 } else if (remaining_ops_to_use_from_previous_translation_) {
232 int32_t value = base::VLQDecode(buffer_.begin(), &previous_index_);
233 DCHECK_LT(previous_index_, index_);
234 return value;
235 } else {
236 int32_t value = base::VLQDecode(buffer_.begin(), &index_);
237 DCHECK_LE(index_, buffer_.length());
238 return value;
239 }
240}
241
243 TranslationOpcode opcode =
244 static_cast<TranslationOpcode>(buffer_[previous_index_++]);
245 DCHECK_LT(static_cast<uint32_t>(opcode), kNumTranslationOpcodes);
246 DCHECK_NE(opcode, TranslationOpcode::MATCH_PREVIOUS_TRANSLATION);
247 DCHECK_LT(previous_index_, index_);
248 return opcode;
249}
250
252 uint32_t value = base::VLQDecodeUnsigned(buffer_.begin(), &previous_index_);
253 DCHECK_LT(previous_index_, index_);
254 return value;
255}
256
258 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
259 return uncompressed_contents_[index_++];
260 } else if (remaining_ops_to_use_from_previous_translation_) {
261 return NextUnsignedOperandAtPreviousIndex();
262 } else {
263 uint32_t value = base::VLQDecodeUnsigned(buffer_.begin(), &index_);
264 DCHECK_LE(index_, buffer_.length());
265 return value;
266 }
267}
268
270 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
271 return static_cast<TranslationOpcode>(NextOperandUnsigned());
272 }
273 if (remaining_ops_to_use_from_previous_translation_) {
274 --remaining_ops_to_use_from_previous_translation_;
275 }
276 if (remaining_ops_to_use_from_previous_translation_) {
277 return NextOpcodeAtPreviousIndex();
278 }
279 CHECK_LT(index_, buffer_.length());
280 uint8_t opcode_byte = buffer_[index_++];
281
282 // If the opcode byte is greater than any valid opcode, then the opcode is
283 // implicitly MATCH_PREVIOUS_TRANSLATION and the operand is the opcode byte
284 // minus kNumTranslationOpcodes. This special-case encoding of the most common
285 // opcode saves some memory.
286 if (opcode_byte >= kNumTranslationOpcodes) {
287 remaining_ops_to_use_from_previous_translation_ =
288 opcode_byte - kNumTranslationOpcodes;
289 opcode_byte =
290 static_cast<uint8_t>(TranslationOpcode::MATCH_PREVIOUS_TRANSLATION);
291 } else if (opcode_byte ==
292 static_cast<uint8_t>(
293 TranslationOpcode::MATCH_PREVIOUS_TRANSLATION)) {
294 remaining_ops_to_use_from_previous_translation_ = NextOperandUnsigned();
295 }
296
297 TranslationOpcode opcode = static_cast<TranslationOpcode>(opcode_byte);
298 DCHECK_LE(index_, buffer_.length());
299 DCHECK_LT(static_cast<uint32_t>(opcode), kNumTranslationOpcodes);
300 if (TranslationOpcodeIsBegin(opcode)) {
301 int temp_index = index_;
302 // The first argument for BEGIN is the distance, in bytes, since the
303 // previous BEGIN, or zero to indicate that MATCH_PREVIOUS_TRANSLATION will
304 // not be used in this translation.
305 uint32_t lookback_distance =
306 base::VLQDecodeUnsigned(buffer_.begin(), &temp_index);
307 if (lookback_distance) {
308 previous_index_ = index_ - 1 - lookback_distance;
310 static_cast<TranslationOpcode>(buffer_[previous_index_])));
311 // The previous BEGIN should specify zero as its lookback distance,
312 // meaning it won't use MATCH_PREVIOUS_TRANSLATION.
313 DCHECK_EQ(buffer_[previous_index_ + 1], 0);
314 }
315 ops_since_previous_index_was_updated_ = 1;
316 } else if (opcode == TranslationOpcode::MATCH_PREVIOUS_TRANSLATION) {
317 for (int i = 0; i < ops_since_previous_index_was_updated_; ++i) {
318 SkipOpcodeAndItsOperandsAtPreviousIndex();
319 }
320 ops_since_previous_index_was_updated_ = 0;
321 opcode = NextOpcodeAtPreviousIndex();
322 } else {
323 ++ops_since_previous_index_was_updated_;
324 }
325 return opcode;
326}
327
330 TranslationOpcode opcode = NextOpcode();
332 USE(opcode);
333 NextOperand(); // Skip lookback distance.
334 int frame_count = NextOperand();
335 int jsframe_count = NextOperand();
336 return {frame_count, jsframe_count};
337}
338
340 while (HasNextOpcode()) {
341 TranslationOpcode opcode = NextOpcode();
343 if (IsTranslationJsFrameOpcode(opcode)) {
344 return opcode;
345 } else {
346 // Skip over operands to advance to the next opcode.
347 SkipOperands(TranslationOpcodeOperandCount(opcode));
348 }
349 }
350 UNREACHABLE();
351}
352
354 while (HasNextOpcode()) {
355 TranslationOpcode opcode = NextOpcode();
357 if (IsTranslationFrameOpcode(opcode)) {
358 return opcode;
359 } else {
360 // Skip over operands to advance to the next opcode.
361 SkipOperands(TranslationOpcodeOperandCount(opcode));
362 }
363 }
364 UNREACHABLE();
365}
366
368 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
369 return index_ < static_cast<int>(uncompressed_contents_.size());
370 } else {
371 return index_ < buffer_.length() ||
372 remaining_ops_to_use_from_previous_translation_ > 1;
373 }
374}
375
377 TranslationOpcode opcode = NextOpcodeAtPreviousIndex();
378 for (int count = TranslationOpcodeOperandCount(opcode); count != 0; --count) {
379 NextUnsignedOperandAtPreviousIndex();
380 }
381}
382
383#ifdef ENABLE_DISASSEMBLER
384
385void DeoptimizationFrameTranslation::PrintFrameTranslation(
386 std::ostream& os, int index,
387 Tagged<ProtectedDeoptimizationLiteralArray> protected_literal_array,
388 Tagged<DeoptimizationLiteralArray> literal_array) const {
389 DisallowGarbageCollection gc_oh_noes;
390
391 DeoptimizationFrameTranslation::Iterator iterator(this, index);
392 TranslationOpcode first_opcode = iterator.NextOpcode();
393 DCHECK(TranslationOpcodeIsBegin(first_opcode));
394 os << first_opcode << " ";
396 os, first_opcode, iterator, protected_literal_array, literal_array);
397 while (iterator.HasNextOpcode()) {
398 TranslationOpcode opcode = iterator.NextOpcode();
399 if (TranslationOpcodeIsBegin(opcode)) {
400 break;
401 }
402 os << opcode << " ";
404 os, opcode, iterator, protected_literal_array, literal_array);
405 }
406}
407
408#endif // ENABLE_DISASSEMBLER
409
410} // namespace internal
411} // namespace v8
static V8_EXPORT_PRIVATE Handle< BigInt > FromUint64(Isolate *isolate, uint64_t n)
Definition bigint.cc:1355
static V8_EXPORT_PRIVATE Handle< BigInt > FromInt64(Isolate *isolate, int64_t n)
Definition bigint.cc:1333
static constexpr BytecodeOffset None()
Definition utils.h:675
constexpr int ToInt() const
Definition utils.h:673
DeoptimizationFrameTranslation::FrameCount EnterBeginOpcode()
const base::Vector< const uint8_t > buffer_
DeoptTranslationIterator(base::Vector< const uint8_t > buffer, int index)
static int LengthFor(int entry_count)
Tagged< ProtectedDeoptimizationLiteralArray > Tagged< TrustedPodArray< InliningPosition > > Tagged< SharedFunctionInfo > GetSharedFunctionInfo() const
BytecodeOffset GetBytecodeOffsetOrBuiltinContinuationId(int i) const
static Handle< DeoptimizationData > New(Isolate *isolate, int deopt_entry_count)
Tagged< SharedFunctionInfo > GetInlinedFunction(int index)
static V8_EXPORT_PRIVATE Handle< DeoptimizationData > Empty(Isolate *isolate)
Iterator(Tagged< DeoptimizationFrameTranslation > buffer, int index)
DirectHandle< Object > Reify(Isolate *isolate) const
Register const index_
base::OwnedVector< uint8_t > buffer_
Definition assembler.cc:111
uint32_t count
uint32_t VLQDecodeUnsigned(GetNextFunction &&get_next)
Definition vlq.h:80
int32_t VLQDecode(const uint8_t *data_start, int *index)
Definition vlq.h:107
bool IsTranslationFrameOpcode(TranslationOpcode o)
bool TranslationOpcodeIsBegin(TranslationOpcode o)
int TranslationOpcodeOperandCount(TranslationOpcode o)
static constexpr int kNumTranslationOpcodes
bool IsTranslationJsFrameOpcode(TranslationOpcode o)
void DeoptimizationFrameTranslationPrintSingleOpcode(std::ostream &os, TranslationOpcode opcode, DeoptimizationFrameTranslation::Iterator &iterator, Tagged< ProtectedDeoptimizationLiteralArray > protected_literal_array, Tagged< DeoptimizationLiteralArray > literal_array)
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK_LT(lhs, rhs)
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
#define V8_UNLIKELY(condition)
Definition v8config.h:660