v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
frame-translation-builder.cc
Go to the documentation of this file.
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
6
7#include <optional>
8
9#include "src/base/vlq.h"
12
13#ifdef V8_USE_ZLIB
14#include "third_party/zlib/google/compression_utils_portable.h"
15#endif // V8_USE_ZLIB
16
17namespace v8 {
18namespace internal {
19
20namespace {
21
22class OperandBase {
23 public:
24 explicit OperandBase(uint32_t value) : value_(value) {}
25 uint32_t value() const { return value_; }
26
27 private:
28 uint32_t value_;
29};
30
31class SmallUnsignedOperand : public OperandBase {
32 public:
33 explicit SmallUnsignedOperand(uint32_t value) : OperandBase(value) {
35 }
36 void WriteVLQ(ZoneVector<uint8_t>* buffer) { buffer->push_back(value()); }
37 bool IsSigned() const { return false; }
38};
39
40class UnsignedOperand : public OperandBase {
41 public:
42 explicit UnsignedOperand(int32_t value)
43 : UnsignedOperand(static_cast<uint32_t>(value)) {
44 DCHECK_GE(value, 0);
45 }
46 explicit UnsignedOperand(uint32_t value) : OperandBase(value) {}
47 void WriteVLQ(ZoneVector<uint8_t>* buffer) {
49 [buffer](uint8_t value) { buffer->push_back(value); }, value());
50 }
51 bool IsSigned() const { return false; }
52};
53
54class SignedOperand : public OperandBase {
55 public:
56 explicit SignedOperand(int32_t value) : OperandBase(value) {}
57 // Use UnsignedOperand for unsigned values.
58 explicit SignedOperand(uint32_t value) = delete;
59 void WriteVLQ(ZoneVector<uint8_t>* buffer) {
61 [buffer](uint8_t value) {
62 buffer->push_back(value);
63 return &buffer->back();
64 },
65 value());
66 }
67 bool IsSigned() const { return true; }
68};
69
70template <typename... T>
71inline bool OperandsEqual(uint32_t* expected_operands, T... operands) {
72 return (... && (*(expected_operands++) == operands.value()));
73}
74
75} // namespace
76
77template <typename... T>
79 T... operands) {
80 DCHECK_EQ(sizeof...(T), TranslationOpcodeOperandCount(opcode));
81 DCHECK(!v8_flags.turbo_compress_frame_translations);
82 contents_.push_back(static_cast<uint8_t>(opcode));
83 (..., operands.WriteVLQ(&contents_));
84}
85
86template <typename... T>
88 TranslationOpcode opcode, T... operands) {
89 DCHECK_EQ(sizeof...(T), TranslationOpcodeOperandCount(opcode));
90 DCHECK(v8_flags.turbo_compress_frame_translations);
91 contents_for_compression_.push_back(static_cast<uint8_t>(opcode));
92 (..., contents_for_compression_.push_back(operands.value()));
93}
94
95template <typename... T>
96void FrameTranslationBuilder::AddRawBegin(bool update_feedback, T... operands) {
97 auto opcode = update_feedback ? TranslationOpcode::BEGIN_WITH_FEEDBACK
98 : TranslationOpcode::BEGIN_WITHOUT_FEEDBACK;
99 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
100 AddRawToContentsForCompression(opcode, operands...);
101 } else {
102 AddRawToContents(opcode, operands...);
103#ifdef ENABLE_SLOW_DCHECKS
104 if (v8_flags.enable_slow_asserts) {
105 all_instructions_.emplace_back(opcode, operands...);
106 }
107#endif
108 }
109}
110
112 int jsframe_count,
113 bool update_feedback) {
115 int start_index = Size();
116 int distance_from_last_start = 0;
117
118 // We should reuse an existing basis translation if:
119 // - we just finished writing the basis translation
120 // (match_previous_allowed_ is false), or
121 // - the translation we just finished was moderately successful at reusing
122 // instructions from the basis translation. We'll define "moderately
123 // successful" as reusing more than 3/4 of the basis instructions.
124 // Otherwise we should reset and write a new basis translation. At the
125 // beginning, match_previous_allowed_ is initialized to true so that this
126 // logic decides to start a new basis translation.
130 // Use the existing basis translation.
131 distance_from_last_start = start_index - index_of_basis_translation_start_;
133 } else {
134 // Abandon the existing basis translation and write a new one.
135 basis_instructions_.clear();
138 }
139
142
143 // BEGIN instructions can't be replaced by MATCH_PREVIOUS_TRANSLATION, so
144 // use a special helper function rather than calling Add().
145 AddRawBegin(update_feedback, UnsignedOperand(distance_from_last_start),
146 SignedOperand(frame_count), SignedOperand(jsframe_count));
147 return start_index;
148}
149
154
155 // There is a short form for the MATCH_PREVIOUS_TRANSLATION instruction
156 // because it's the most common opcode: rather than spending a byte on the
157 // opcode and a second byte on the operand, we can use only a single byte
158 // which doesn't match any valid opcode.
159 const int kMaxShortenableOperand =
160 std::numeric_limits<uint8_t>::max() - kNumTranslationOpcodes;
161 if (matching_instructions_count_ <= kMaxShortenableOperand) {
164 } else {
165 // The operand didn't fit in the opcode byte, so encode it normally.
167 TranslationOpcode::MATCH_PREVIOUS_TRANSLATION,
168 UnsignedOperand(static_cast<uint32_t>(matching_instructions_count_)));
169 }
171 }
172}
173
174template <typename... T>
176 DCHECK_EQ(sizeof...(T), TranslationOpcodeOperandCount(opcode));
177 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
178 AddRawToContentsForCompression(opcode, operands...);
179 return;
180 }
181#ifdef ENABLE_SLOW_DCHECKS
182 if (v8_flags.enable_slow_asserts) {
183 all_instructions_.emplace_back(opcode, operands...);
184 }
185#endif
188 opcode ==
190 OperandsEqual(
192 operands...)) {
194 } else {
196 AddRawToContents(opcode, operands...);
198 // Include this instruction in basis_instructions_ so that future
199 // translations can check whether they match with it.
202 basis_instructions_.emplace_back(opcode, operands...);
203 }
204 }
206}
207
210#ifdef V8_USE_ZLIB
211 if (V8_UNLIKELY(v8_flags.turbo_compress_frame_translations)) {
212 const int input_size = SizeInBytes();
213 uLongf compressed_data_size = compressBound(input_size);
214
215 ZoneVector<uint8_t> compressed_data(compressed_data_size, zone());
216
217 CHECK_EQ(
218 zlib_internal::CompressHelper(
219 zlib_internal::ZRAW, compressed_data.data(), &compressed_data_size,
220 reinterpret_cast<const Bytef*>(contents_for_compression_.data()),
221 input_size, Z_DEFAULT_COMPRESSION, nullptr, nullptr),
222 Z_OK);
223
224 const int translation_array_size =
225 static_cast<int>(compressed_data_size) +
226 DeoptimizationFrameTranslation::kUncompressedSizeSize;
228 factory->NewDeoptimizationFrameTranslation(translation_array_size);
229
230 result->set_int(DeoptimizationFrameTranslation::kUncompressedSizeOffset,
231 Size());
232 std::memcpy(
233 result->begin() + DeoptimizationFrameTranslation::kCompressedDataOffset,
234 compressed_data.data(), compressed_data_size);
235
236 return result;
237 }
238#endif
239 DCHECK(!v8_flags.turbo_compress_frame_translations);
243 if (SizeInBytes() == 0) return result;
244 memcpy(result->begin(), contents_.data(), contents_.size() * sizeof(uint8_t));
245#ifdef ENABLE_SLOW_DCHECKS
247 ValidateBytes(iter);
248#endif
249 return result;
250}
251
253 DCHECK(!v8_flags.turbo_compress_frame_translations);
256#ifdef ENABLE_SLOW_DCHECKS
258 ValidateBytes(iter);
259#endif
260 return result;
261}
262
264 DeoptTranslationIterator& iter) const {
265#ifdef ENABLE_SLOW_DCHECKS
266 if (v8_flags.enable_slow_asserts) {
267 // Check that we can read back all of the same content we intended to write.
268 for (size_t i = 0; i < all_instructions_.size(); ++i) {
269 CHECK(iter.HasNextOpcode());
270 const Instruction& instruction = all_instructions_[i];
271 CHECK_EQ(instruction.opcode, iter.NextOpcode());
272 for (int j = 0; j < TranslationOpcodeOperandCount(instruction.opcode);
273 ++j) {
274 uint32_t operand = instruction.is_operand_signed[j]
275 ? iter.NextOperand()
276 : iter.NextOperandUnsigned();
277 CHECK_EQ(instruction.operands[j], operand);
278 }
279 }
280 }
281#endif
282}
283
285 BytecodeOffset bytecode_offset, int literal_id, unsigned height) {
286 auto opcode = TranslationOpcode::BUILTIN_CONTINUATION_FRAME;
287 Add(opcode, SignedOperand(bytecode_offset.ToInt()), SignedOperand(literal_id),
288 UnsignedOperand(height));
289}
290
291#if V8_ENABLE_WEBASSEMBLY
292void FrameTranslationBuilder::BeginJSToWasmBuiltinContinuationFrame(
293 BytecodeOffset bytecode_offset, int literal_id, unsigned height,
294 std::optional<wasm::ValueKind> return_kind) {
295 auto opcode = TranslationOpcode::JS_TO_WASM_BUILTIN_CONTINUATION_FRAME;
296 Add(opcode, SignedOperand(bytecode_offset.ToInt()), SignedOperand(literal_id),
297 UnsignedOperand(height),
298 SignedOperand(return_kind ? static_cast<int>(return_kind.value())
300}
301
302void FrameTranslationBuilder::BeginWasmInlinedIntoJSFrame(
303 BytecodeOffset bailout_id, int literal_id, unsigned height) {
304 auto opcode = TranslationOpcode::WASM_INLINED_INTO_JS_FRAME;
305 Add(opcode, SignedOperand(bailout_id.ToInt()), SignedOperand(literal_id),
306 UnsignedOperand(height));
307}
308
309void FrameTranslationBuilder::BeginLiftoffFrame(BytecodeOffset bailout_id,
310 unsigned height,
311 uint32_t wasm_function_index) {
312 auto opcode = TranslationOpcode::LIFTOFF_FRAME;
313 Add(opcode, SignedOperand(bailout_id.ToInt()), UnsignedOperand(height),
314 UnsignedOperand(wasm_function_index));
315}
316#endif // V8_ENABLE_WEBASSEMBLY
317
319 BytecodeOffset bytecode_offset, int literal_id, unsigned height) {
320 auto opcode = TranslationOpcode::JAVASCRIPT_BUILTIN_CONTINUATION_FRAME;
321 Add(opcode, SignedOperand(bytecode_offset.ToInt()), SignedOperand(literal_id),
322 UnsignedOperand(height));
323}
324
326 BytecodeOffset bytecode_offset, int literal_id, unsigned height) {
327 auto opcode =
328 TranslationOpcode::JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME;
329 Add(opcode, SignedOperand(bytecode_offset.ToInt()), SignedOperand(literal_id),
330 UnsignedOperand(height));
331}
332
334 unsigned height) {
335 auto opcode = TranslationOpcode::CONSTRUCT_CREATE_STUB_FRAME;
336 Add(opcode, SignedOperand(literal_id), UnsignedOperand(height));
337}
338
340 auto opcode = TranslationOpcode::CONSTRUCT_INVOKE_STUB_FRAME;
341 Add(opcode, SignedOperand(literal_id));
342}
343
345 int literal_id, unsigned height, uint32_t parameter_count) {
346 auto opcode = TranslationOpcode::INLINED_EXTRA_ARGUMENTS;
347 Add(opcode, SignedOperand(literal_id), UnsignedOperand(height),
348 UnsignedOperand(parameter_count));
349}
350
352 BytecodeOffset bytecode_offset, int literal_id, int bytecode_array_id,
353 unsigned height, int return_value_offset, int return_value_count) {
354 if (return_value_count == 0) {
355 auto opcode = TranslationOpcode::INTERPRETED_FRAME_WITHOUT_RETURN;
356 Add(opcode, SignedOperand(bytecode_offset.ToInt()),
357 SignedOperand(literal_id), SignedOperand(bytecode_array_id),
358 UnsignedOperand(height));
359 } else {
360 auto opcode = TranslationOpcode::INTERPRETED_FRAME_WITH_RETURN;
361 Add(opcode, SignedOperand(bytecode_offset.ToInt()),
362 SignedOperand(literal_id), SignedOperand(bytecode_array_id),
363 UnsignedOperand(height), SignedOperand(return_value_offset),
364 SignedOperand(return_value_count));
365 }
366}
367
369 auto opcode = TranslationOpcode::ARGUMENTS_ELEMENTS;
370 Add(opcode, SignedOperand(static_cast<uint8_t>(type)));
371}
372
374 auto opcode = TranslationOpcode::ARGUMENTS_LENGTH;
375 Add(opcode);
376}
377
379 auto opcode = TranslationOpcode::REST_LENGTH;
380 Add(opcode);
381}
382
384 auto opcode = TranslationOpcode::CAPTURED_OBJECT;
385 Add(opcode, SignedOperand(length));
386}
387
389 auto opcode = TranslationOpcode::DUPLICATED_OBJECT;
390 Add(opcode, SignedOperand(object_index));
391}
392
394 auto opcode = TranslationOpcode::STRING_CONCAT;
395 Add(opcode);
396}
397
399 Register reg) {
400 static_assert(Register::kNumRegisters - 1 <= base::kDataMask);
401 Add(opcode, SmallUnsignedOperand(static_cast<uint8_t>(reg.code())));
402}
403
405 auto opcode = TranslationOpcode::REGISTER;
406 StoreRegister(opcode, reg);
407}
408
410 auto opcode = TranslationOpcode::INT32_REGISTER;
411 StoreRegister(opcode, reg);
412}
413
415 auto opcode = (kSystemPointerSize == 4) ? TranslationOpcode::INT32_REGISTER
416 : TranslationOpcode::INT64_REGISTER;
417 StoreRegister(opcode, reg);
418}
419
421 auto opcode = TranslationOpcode::INT64_REGISTER;
422 StoreRegister(opcode, reg);
423}
424
426 auto opcode = TranslationOpcode::SIGNED_BIGINT64_REGISTER;
427 StoreRegister(opcode, reg);
428}
429
431 auto opcode = TranslationOpcode::UNSIGNED_BIGINT64_REGISTER;
432 StoreRegister(opcode, reg);
433}
434
436 auto opcode = TranslationOpcode::UINT32_REGISTER;
437 StoreRegister(opcode, reg);
438}
439
441 auto opcode = TranslationOpcode::BOOL_REGISTER;
442 StoreRegister(opcode, reg);
443}
444
446 static_assert(FloatRegister::kNumRegisters - 1 <= base::kDataMask);
447 auto opcode = TranslationOpcode::FLOAT_REGISTER;
448 Add(opcode, SmallUnsignedOperand(static_cast<uint8_t>(reg.code())));
449}
450
453 auto opcode = TranslationOpcode::DOUBLE_REGISTER;
454 Add(opcode, SmallUnsignedOperand(static_cast<uint8_t>(reg.code())));
455}
456
459 auto opcode = TranslationOpcode::HOLEY_DOUBLE_REGISTER;
460 Add(opcode, SmallUnsignedOperand(static_cast<uint8_t>(reg.code())));
461}
462
465 auto opcode = TranslationOpcode::SIMD128_REGISTER;
466 Add(opcode, SmallUnsignedOperand(static_cast<uint8_t>(reg.code())));
467}
468
470 auto opcode = TranslationOpcode::TAGGED_STACK_SLOT;
471 Add(opcode, SignedOperand(index));
472}
473
475 auto opcode = TranslationOpcode::INT32_STACK_SLOT;
476 Add(opcode, SignedOperand(index));
477}
478
480 auto opcode = (kSystemPointerSize == 4) ? TranslationOpcode::INT32_STACK_SLOT
481 : TranslationOpcode::INT64_STACK_SLOT;
482 Add(opcode, SignedOperand(index));
483}
484
486 auto opcode = TranslationOpcode::INT64_STACK_SLOT;
487 Add(opcode, SignedOperand(index));
488}
489
491 auto opcode = TranslationOpcode::SIGNED_BIGINT64_STACK_SLOT;
492 Add(opcode, SignedOperand(index));
493}
494
496 auto opcode = TranslationOpcode::UNSIGNED_BIGINT64_STACK_SLOT;
497 Add(opcode, SignedOperand(index));
498}
499
501 auto opcode = TranslationOpcode::UINT32_STACK_SLOT;
502 Add(opcode, SignedOperand(index));
503}
504
506 auto opcode = TranslationOpcode::BOOL_STACK_SLOT;
507 Add(opcode, SignedOperand(index));
508}
509
511 auto opcode = TranslationOpcode::FLOAT_STACK_SLOT;
512 Add(opcode, SignedOperand(index));
513}
514
516 auto opcode = TranslationOpcode::DOUBLE_STACK_SLOT;
517 Add(opcode, SignedOperand(index));
518}
519
521 auto opcode = TranslationOpcode::SIMD128_STACK_SLOT;
522 Add(opcode, SignedOperand(index));
523}
524
526 auto opcode = TranslationOpcode::HOLEY_DOUBLE_STACK_SLOT;
527 Add(opcode, SignedOperand(index));
528}
529
531 auto opcode = TranslationOpcode::LITERAL;
532 DCHECK_GE(literal_id, 0);
533 Add(opcode, SignedOperand(literal_id));
534}
535
537 auto opcode = TranslationOpcode::OPTIMIZED_OUT;
538 Add(opcode);
539}
540
541void FrameTranslationBuilder::AddUpdateFeedback(int vector_literal, int slot) {
542 auto opcode = TranslationOpcode::UPDATE_FEEDBACK;
543 Add(opcode, SignedOperand(vector_literal), SignedOperand(slot));
544}
545
551
552} // namespace internal
553} // namespace v8
int16_t parameter_count
Definition builtins.cc:67
constexpr int ToInt() const
Definition utils.h:673
DirectHandle< DeoptimizationFrameTranslation > NewDeoptimizationFrameTranslation(int length)
int BeginTranslation(int frame_count, int jsframe_count, bool update_feedback)
void ValidateBytes(DeoptTranslationIterator &iter) const
void BeginJavaScriptBuiltinContinuationFrame(BytecodeOffset bailout_id, int literal_id, unsigned height)
void AddRawToContentsForCompression(TranslationOpcode opcode, T... operands)
base::Vector< const uint8_t > ToFrameTranslationWasm()
void StoreRegister(TranslationOpcode opcode, Register reg)
void BeginJavaScriptBuiltinContinuationWithCatchFrame(BytecodeOffset bailout_id, int literal_id, unsigned height)
void BeginInlinedExtraArguments(int literal_id, unsigned height, uint32_t parameter_count)
void ArgumentsElements(CreateArgumentsType type)
void AddUpdateFeedback(int vector_literal, int slot)
void BeginConstructCreateStubFrame(int literal_id, unsigned height)
void BeginBuiltinContinuationFrame(BytecodeOffset bailout_id, int literal_id, unsigned height)
void BeginInterpretedFrame(BytecodeOffset bytecode_offset, int literal_id, int bytecode_array_id, unsigned height, int return_value_offset, int return_value_count)
DirectHandle< DeoptimizationFrameTranslation > ToFrameTranslation(LocalFactory *factory)
void AddRawBegin(bool update_feedback, T... operands)
void Add(TranslationOpcode opcode, T... operands)
void AddRawToContents(TranslationOpcode opcode, T... operands)
void push_back(const T &value)
Register const value_
ZoneVector< RpoNumber > & result
LiftoffRegister reg
void VLQEncode(Function &&process_byte, int32_t value)
Definition vlq.h:60
static constexpr uint32_t kDataMask
Definition vlq.h:18
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
void VLQEncodeUnsigned(Function &&process_byte, uint32_t value)
Definition vlq.h:23
const int kNoWasmReturnKind
int TranslationOpcodeOperandCount(TranslationOpcode o)
static constexpr int kNumTranslationOpcodes
constexpr int kSystemPointerSize
Definition globals.h:410
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_UNLIKELY(condition)
Definition v8config.h:660