V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
bytecode-array-writer.cc
1 // Copyright 2015 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 #include "src/interpreter/bytecode-array-writer.h"
6 
7 #include "src/api-inl.h"
8 #include "src/interpreter/bytecode-jump-table.h"
9 #include "src/interpreter/bytecode-label.h"
10 #include "src/interpreter/bytecode-node.h"
11 #include "src/interpreter/bytecode-register.h"
12 #include "src/interpreter/bytecode-source-info.h"
13 #include "src/interpreter/constant-array-builder.h"
14 #include "src/log.h"
15 #include "src/objects-inl.h"
16 
17 namespace v8 {
18 namespace internal {
19 namespace interpreter {
20 
21 STATIC_CONST_MEMBER_DEFINITION const size_t
22  BytecodeArrayWriter::kMaxSizeOfPackedBytecode;
23 
24 BytecodeArrayWriter::BytecodeArrayWriter(
25  Zone* zone, ConstantArrayBuilder* constant_array_builder,
26  SourcePositionTableBuilder::RecordingMode source_position_mode)
27  : bytecodes_(zone),
28  unbound_jumps_(0),
29  source_position_table_builder_(source_position_mode),
30  constant_array_builder_(constant_array_builder),
31  last_bytecode_(Bytecode::kIllegal),
32  last_bytecode_offset_(0),
33  last_bytecode_had_source_info_(false),
34  elide_noneffectful_bytecodes_(FLAG_ignition_elide_noneffectful_bytecodes),
35  exit_seen_in_block_(false) {
36  bytecodes_.reserve(512); // Derived via experimentation.
37 }
38 
39 Handle<BytecodeArray> BytecodeArrayWriter::ToBytecodeArray(
40  Isolate* isolate, int register_count, int parameter_count,
41  Handle<ByteArray> handler_table) {
42  DCHECK_EQ(0, unbound_jumps_);
43 
44  int bytecode_size = static_cast<int>(bytecodes()->size());
45  int frame_size = register_count * kPointerSize;
46  Handle<FixedArray> constant_pool =
47  constant_array_builder()->ToFixedArray(isolate);
48  Handle<ByteArray> source_position_table =
49  source_position_table_builder()->ToSourcePositionTable(isolate);
50  Handle<BytecodeArray> bytecode_array = isolate->factory()->NewBytecodeArray(
51  bytecode_size, &bytecodes()->front(), frame_size, parameter_count,
52  constant_pool);
53  bytecode_array->set_handler_table(*handler_table);
54  bytecode_array->set_source_position_table(*source_position_table);
55  LOG_CODE_EVENT(isolate, CodeLinePosInfoRecordEvent(
56  bytecode_array->GetFirstBytecodeAddress(),
57  *source_position_table));
58  return bytecode_array;
59 }
60 
61 void BytecodeArrayWriter::Write(BytecodeNode* node) {
62  DCHECK(!Bytecodes::IsJump(node->bytecode()));
63 
64  if (exit_seen_in_block_) return; // Don't emit dead code.
65  UpdateExitSeenInBlock(node->bytecode());
66  MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
67 
68  UpdateSourcePositionTable(node);
69  EmitBytecode(node);
70 }
71 
72 void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
73  DCHECK(Bytecodes::IsJump(node->bytecode()));
74 
75  // TODO(rmcilroy): For forward jumps we could also mark the label as dead,
76  // thereby avoiding emitting dead code when we bind the label.
77  if (exit_seen_in_block_) return; // Don't emit dead code.
78  UpdateExitSeenInBlock(node->bytecode());
79  MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
80 
81  UpdateSourcePositionTable(node);
82  EmitJump(node, label);
83 }
84 
85 void BytecodeArrayWriter::WriteSwitch(BytecodeNode* node,
86  BytecodeJumpTable* jump_table) {
87  DCHECK(Bytecodes::IsSwitch(node->bytecode()));
88 
89  // TODO(rmcilroy): For jump tables we could also mark the table as dead,
90  // thereby avoiding emitting dead code when we bind the entries.
91  if (exit_seen_in_block_) return; // Don't emit dead code.
92  UpdateExitSeenInBlock(node->bytecode());
93  MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
94 
95  UpdateSourcePositionTable(node);
96  EmitSwitch(node, jump_table);
97 }
98 
99 void BytecodeArrayWriter::BindLabel(BytecodeLabel* label) {
100  size_t current_offset = bytecodes()->size();
101  if (label->is_forward_target()) {
102  // An earlier jump instruction refers to this label. Update it's location.
103  PatchJump(current_offset, label->offset());
104  // Now treat as if the label will only be back referred to.
105  }
106  label->bind_to(current_offset);
107  InvalidateLastBytecode();
108  exit_seen_in_block_ = false; // Starting a new basic block.
109 }
110 
111 void BytecodeArrayWriter::BindLabel(const BytecodeLabel& target,
112  BytecodeLabel* label) {
113  DCHECK(!label->is_bound());
114  DCHECK(target.is_bound());
115  if (label->is_forward_target()) {
116  // An earlier jump instruction refers to this label. Update it's location.
117  PatchJump(target.offset(), label->offset());
118  // Now treat as if the label will only be back referred to.
119  }
120  label->bind_to(target.offset());
121  InvalidateLastBytecode();
122  // exit_seen_in_block_ was reset when target was bound, so shouldn't be
123  // changed here.
124 }
125 
126 void BytecodeArrayWriter::BindJumpTableEntry(BytecodeJumpTable* jump_table,
127  int case_value) {
128  DCHECK(!jump_table->is_bound(case_value));
129 
130  size_t current_offset = bytecodes()->size();
131  size_t relative_jump = current_offset - jump_table->switch_bytecode_offset();
132 
133  constant_array_builder()->SetJumpTableSmi(
134  jump_table->ConstantPoolEntryFor(case_value),
135  Smi::FromInt(static_cast<int>(relative_jump)));
136  jump_table->mark_bound(case_value);
137 
138  InvalidateLastBytecode();
139  exit_seen_in_block_ = false; // Starting a new basic block.
140 }
141 
142 void BytecodeArrayWriter::UpdateSourcePositionTable(
143  const BytecodeNode* const node) {
144  int bytecode_offset = static_cast<int>(bytecodes()->size());
145  const BytecodeSourceInfo& source_info = node->source_info();
146  if (source_info.is_valid()) {
147  source_position_table_builder()->AddPosition(
148  bytecode_offset, SourcePosition(source_info.source_position()),
149  source_info.is_statement());
150  }
151 }
152 
153 void BytecodeArrayWriter::UpdateExitSeenInBlock(Bytecode bytecode) {
154  switch (bytecode) {
155  case Bytecode::kReturn:
156  case Bytecode::kThrow:
157  case Bytecode::kReThrow:
158  case Bytecode::kAbort:
159  case Bytecode::kJump:
160  case Bytecode::kJumpConstant:
161  case Bytecode::kSuspendGenerator:
162  exit_seen_in_block_ = true;
163  break;
164  default:
165  break;
166  }
167 }
168 
169 void BytecodeArrayWriter::MaybeElideLastBytecode(Bytecode next_bytecode,
170  bool has_source_info) {
171  if (!elide_noneffectful_bytecodes_) return;
172 
173  // If the last bytecode loaded the accumulator without any external effect,
174  // and the next bytecode clobbers this load without reading the accumulator,
175  // then the previous bytecode can be elided as it has no effect.
176  if (Bytecodes::IsAccumulatorLoadWithoutEffects(last_bytecode_) &&
177  Bytecodes::GetAccumulatorUse(next_bytecode) == AccumulatorUse::kWrite &&
178  (!last_bytecode_had_source_info_ || !has_source_info)) {
179  DCHECK_GT(bytecodes()->size(), last_bytecode_offset_);
180  bytecodes()->resize(last_bytecode_offset_);
181  // If the last bytecode had source info we will transfer the source info
182  // to this bytecode.
183  has_source_info |= last_bytecode_had_source_info_;
184  }
185  last_bytecode_ = next_bytecode;
186  last_bytecode_had_source_info_ = has_source_info;
187  last_bytecode_offset_ = bytecodes()->size();
188 }
189 
190 void BytecodeArrayWriter::InvalidateLastBytecode() {
191  last_bytecode_ = Bytecode::kIllegal;
192 }
193 
194 void BytecodeArrayWriter::EmitBytecode(const BytecodeNode* const node) {
195  DCHECK_NE(node->bytecode(), Bytecode::kIllegal);
196 
197  Bytecode bytecode = node->bytecode();
198  OperandScale operand_scale = node->operand_scale();
199 
200  if (operand_scale != OperandScale::kSingle) {
201  Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
202  bytecodes()->push_back(Bytecodes::ToByte(prefix));
203  }
204  bytecodes()->push_back(Bytecodes::ToByte(bytecode));
205 
206  const uint32_t* const operands = node->operands();
207  const int operand_count = node->operand_count();
208  const OperandSize* operand_sizes =
209  Bytecodes::GetOperandSizes(bytecode, operand_scale);
210  for (int i = 0; i < operand_count; ++i) {
211  switch (operand_sizes[i]) {
212  case OperandSize::kNone:
213  UNREACHABLE();
214  break;
215  case OperandSize::kByte:
216  bytecodes()->push_back(static_cast<uint8_t>(operands[i]));
217  break;
218  case OperandSize::kShort: {
219  uint16_t operand = static_cast<uint16_t>(operands[i]);
220  const uint8_t* raw_operand = reinterpret_cast<const uint8_t*>(&operand);
221  bytecodes()->push_back(raw_operand[0]);
222  bytecodes()->push_back(raw_operand[1]);
223  break;
224  }
225  case OperandSize::kQuad: {
226  const uint8_t* raw_operand =
227  reinterpret_cast<const uint8_t*>(&operands[i]);
228  bytecodes()->push_back(raw_operand[0]);
229  bytecodes()->push_back(raw_operand[1]);
230  bytecodes()->push_back(raw_operand[2]);
231  bytecodes()->push_back(raw_operand[3]);
232  break;
233  }
234  }
235  }
236 }
237 
238 // static
239 Bytecode GetJumpWithConstantOperand(Bytecode jump_bytecode) {
240  switch (jump_bytecode) {
241  case Bytecode::kJump:
242  return Bytecode::kJumpConstant;
243  case Bytecode::kJumpIfTrue:
244  return Bytecode::kJumpIfTrueConstant;
245  case Bytecode::kJumpIfFalse:
246  return Bytecode::kJumpIfFalseConstant;
247  case Bytecode::kJumpIfToBooleanTrue:
248  return Bytecode::kJumpIfToBooleanTrueConstant;
249  case Bytecode::kJumpIfToBooleanFalse:
250  return Bytecode::kJumpIfToBooleanFalseConstant;
251  case Bytecode::kJumpIfNull:
252  return Bytecode::kJumpIfNullConstant;
253  case Bytecode::kJumpIfNotNull:
254  return Bytecode::kJumpIfNotNullConstant;
255  case Bytecode::kJumpIfUndefined:
256  return Bytecode::kJumpIfUndefinedConstant;
257  case Bytecode::kJumpIfNotUndefined:
258  return Bytecode::kJumpIfNotUndefinedConstant;
259  case Bytecode::kJumpIfJSReceiver:
260  return Bytecode::kJumpIfJSReceiverConstant;
261  default:
262  UNREACHABLE();
263  }
264 }
265 
266 void BytecodeArrayWriter::PatchJumpWith8BitOperand(size_t jump_location,
267  int delta) {
268  Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
269  DCHECK(Bytecodes::IsForwardJump(jump_bytecode));
270  DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
271  DCHECK_EQ(Bytecodes::GetOperandType(jump_bytecode, 0), OperandType::kUImm);
272  DCHECK_GT(delta, 0);
273  size_t operand_location = jump_location + 1;
274  DCHECK_EQ(bytecodes()->at(operand_location), k8BitJumpPlaceholder);
275  if (Bytecodes::ScaleForUnsignedOperand(delta) == OperandScale::kSingle) {
276  // The jump fits within the range of an UImm8 operand, so cancel
277  // the reservation and jump directly.
278  constant_array_builder()->DiscardReservedEntry(OperandSize::kByte);
279  bytecodes()->at(operand_location) = static_cast<uint8_t>(delta);
280  } else {
281  // The jump does not fit within the range of an UImm8 operand, so
282  // commit reservation putting the offset into the constant pool,
283  // and update the jump instruction and operand.
284  size_t entry = constant_array_builder()->CommitReservedEntry(
285  OperandSize::kByte, Smi::FromInt(delta));
286  DCHECK_EQ(Bytecodes::SizeForUnsignedOperand(static_cast<uint32_t>(entry)),
287  OperandSize::kByte);
288  jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
289  bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
290  bytecodes()->at(operand_location) = static_cast<uint8_t>(entry);
291  }
292 }
293 
294 void BytecodeArrayWriter::PatchJumpWith16BitOperand(size_t jump_location,
295  int delta) {
296  Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
297  DCHECK(Bytecodes::IsForwardJump(jump_bytecode));
298  DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
299  DCHECK_EQ(Bytecodes::GetOperandType(jump_bytecode, 0), OperandType::kUImm);
300  DCHECK_GT(delta, 0);
301  size_t operand_location = jump_location + 1;
302  uint8_t operand_bytes[2];
303  if (Bytecodes::ScaleForUnsignedOperand(delta) <= OperandScale::kDouble) {
304  // The jump fits within the range of an Imm16 operand, so cancel
305  // the reservation and jump directly.
306  constant_array_builder()->DiscardReservedEntry(OperandSize::kShort);
307  WriteUnalignedUInt16(reinterpret_cast<Address>(operand_bytes),
308  static_cast<uint16_t>(delta));
309  } else {
310  // The jump does not fit within the range of an Imm16 operand, so
311  // commit reservation putting the offset into the constant pool,
312  // and update the jump instruction and operand.
313  size_t entry = constant_array_builder()->CommitReservedEntry(
314  OperandSize::kShort, Smi::FromInt(delta));
315  jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
316  bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
317  WriteUnalignedUInt16(reinterpret_cast<Address>(operand_bytes),
318  static_cast<uint16_t>(entry));
319  }
320  DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder &&
321  bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder);
322  bytecodes()->at(operand_location++) = operand_bytes[0];
323  bytecodes()->at(operand_location) = operand_bytes[1];
324 }
325 
326 void BytecodeArrayWriter::PatchJumpWith32BitOperand(size_t jump_location,
327  int delta) {
328  DCHECK(Bytecodes::IsJumpImmediate(
329  Bytecodes::FromByte(bytecodes()->at(jump_location))));
330  constant_array_builder()->DiscardReservedEntry(OperandSize::kQuad);
331  uint8_t operand_bytes[4];
332  WriteUnalignedUInt32(reinterpret_cast<Address>(operand_bytes),
333  static_cast<uint32_t>(delta));
334  size_t operand_location = jump_location + 1;
335  DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder &&
336  bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder &&
337  bytecodes()->at(operand_location + 2) == k8BitJumpPlaceholder &&
338  bytecodes()->at(operand_location + 3) == k8BitJumpPlaceholder);
339  bytecodes()->at(operand_location++) = operand_bytes[0];
340  bytecodes()->at(operand_location++) = operand_bytes[1];
341  bytecodes()->at(operand_location++) = operand_bytes[2];
342  bytecodes()->at(operand_location) = operand_bytes[3];
343 }
344 
345 void BytecodeArrayWriter::PatchJump(size_t jump_target, size_t jump_location) {
346  Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
347  int delta = static_cast<int>(jump_target - jump_location);
348  int prefix_offset = 0;
349  OperandScale operand_scale = OperandScale::kSingle;
350  if (Bytecodes::IsPrefixScalingBytecode(jump_bytecode)) {
351  // If a prefix scaling bytecode is emitted the target offset is one
352  // less than the case of no prefix scaling bytecode.
353  delta -= 1;
354  prefix_offset = 1;
355  operand_scale = Bytecodes::PrefixBytecodeToOperandScale(jump_bytecode);
356  jump_bytecode =
357  Bytecodes::FromByte(bytecodes()->at(jump_location + prefix_offset));
358  }
359 
360  DCHECK(Bytecodes::IsJump(jump_bytecode));
361  switch (operand_scale) {
362  case OperandScale::kSingle:
363  PatchJumpWith8BitOperand(jump_location, delta);
364  break;
365  case OperandScale::kDouble:
366  PatchJumpWith16BitOperand(jump_location + prefix_offset, delta);
367  break;
368  case OperandScale::kQuadruple:
369  PatchJumpWith32BitOperand(jump_location + prefix_offset, delta);
370  break;
371  default:
372  UNREACHABLE();
373  }
374  unbound_jumps_--;
375 }
376 
377 void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) {
378  DCHECK(Bytecodes::IsJump(node->bytecode()));
379  DCHECK_EQ(0u, node->operand(0));
380 
381  size_t current_offset = bytecodes()->size();
382 
383  if (label->is_bound()) {
384  CHECK_GE(current_offset, label->offset());
385  CHECK_LE(current_offset, static_cast<size_t>(kMaxUInt32));
386  // Label has been bound already so this is a backwards jump.
387  uint32_t delta = static_cast<uint32_t>(current_offset - label->offset());
388  OperandScale operand_scale = Bytecodes::ScaleForUnsignedOperand(delta);
389  if (operand_scale > OperandScale::kSingle) {
390  // Adjust for scaling byte prefix for wide jump offset.
391  delta += 1;
392  }
393  DCHECK_EQ(Bytecode::kJumpLoop, node->bytecode());
394  node->update_operand0(delta);
395  } else {
396  // The label has not yet been bound so this is a forward reference
397  // that will be patched when the label is bound. We create a
398  // reservation in the constant pool so the jump can be patched
399  // when the label is bound. The reservation means the maximum size
400  // of the operand for the constant is known and the jump can
401  // be emitted into the bytecode stream with space for the operand.
402  unbound_jumps_++;
403  label->set_referrer(current_offset);
404  OperandSize reserved_operand_size =
405  constant_array_builder()->CreateReservedEntry();
406  DCHECK_NE(Bytecode::kJumpLoop, node->bytecode());
407  switch (reserved_operand_size) {
408  case OperandSize::kNone:
409  UNREACHABLE();
410  break;
411  case OperandSize::kByte:
412  node->update_operand0(k8BitJumpPlaceholder);
413  break;
414  case OperandSize::kShort:
415  node->update_operand0(k16BitJumpPlaceholder);
416  break;
417  case OperandSize::kQuad:
418  node->update_operand0(k32BitJumpPlaceholder);
419  break;
420  }
421  }
422  EmitBytecode(node);
423 }
424 
425 void BytecodeArrayWriter::EmitSwitch(BytecodeNode* node,
426  BytecodeJumpTable* jump_table) {
427  DCHECK(Bytecodes::IsSwitch(node->bytecode()));
428 
429  size_t current_offset = bytecodes()->size();
430  if (node->operand_scale() > OperandScale::kSingle) {
431  // Adjust for scaling byte prefix.
432  current_offset += 1;
433  }
434  jump_table->set_switch_bytecode_offset(current_offset);
435 
436  EmitBytecode(node);
437 }
438 
439 } // namespace interpreter
440 } // namespace internal
441 } // namespace v8
Definition: libplatform.h:13