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.h"
8#include "src/interpreter/bytecode-label.h"
9#include "src/interpreter/bytecode-register.h"
10#include "src/interpreter/constant-array-builder.h"
11#include "src/log.h"
12
13namespace v8 {
14namespace internal {
15namespace interpreter {
16
17STATIC_CONST_MEMBER_DEFINITION const size_t
18    BytecodeArrayWriter::kMaxSizeOfPackedBytecode;
19
20BytecodeArrayWriter::BytecodeArrayWriter(
21    Zone* zone, ConstantArrayBuilder* constant_array_builder,
22    SourcePositionTableBuilder::RecordingMode source_position_mode)
23    : bytecodes_(zone),
24      unbound_jumps_(0),
25      source_position_table_builder_(zone, source_position_mode),
26      constant_array_builder_(constant_array_builder) {
27  bytecodes_.reserve(512);  // Derived via experimentation.
28}
29
30// override
31BytecodeArrayWriter::~BytecodeArrayWriter() {}
32
33// override
34Handle<BytecodeArray> BytecodeArrayWriter::ToBytecodeArray(
35    Isolate* isolate, int register_count, int parameter_count,
36    Handle<FixedArray> handler_table) {
37  DCHECK_EQ(0, unbound_jumps_);
38
39  int bytecode_size = static_cast<int>(bytecodes()->size());
40  int frame_size = register_count * kPointerSize;
41  Handle<FixedArray> constant_pool =
42      constant_array_builder()->ToFixedArray(isolate);
43  Handle<BytecodeArray> bytecode_array = isolate->factory()->NewBytecodeArray(
44      bytecode_size, &bytecodes()->front(), frame_size, parameter_count,
45      constant_pool);
46  bytecode_array->set_handler_table(*handler_table);
47  Handle<ByteArray> source_position_table =
48      source_position_table_builder()->ToSourcePositionTable(
49          isolate, Handle<AbstractCode>::cast(bytecode_array));
50  bytecode_array->set_source_position_table(*source_position_table);
51  return bytecode_array;
52}
53
54// override
55void BytecodeArrayWriter::Write(BytecodeNode* node) {
56  DCHECK(!Bytecodes::IsJump(node->bytecode()));
57  UpdateSourcePositionTable(node);
58  EmitBytecode(node);
59}
60
61// override
62void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
63  DCHECK(Bytecodes::IsJump(node->bytecode()));
64  UpdateSourcePositionTable(node);
65  EmitJump(node, label);
66}
67
68// override
69void BytecodeArrayWriter::BindLabel(BytecodeLabel* label) {
70  size_t current_offset = bytecodes()->size();
71  if (label->is_forward_target()) {
72    // An earlier jump instruction refers to this label. Update it's location.
73    PatchJump(current_offset, label->offset());
74    // Now treat as if the label will only be back referred to.
75  }
76  label->bind_to(current_offset);
77}
78
79// override
80void BytecodeArrayWriter::BindLabel(const BytecodeLabel& target,
81                                    BytecodeLabel* label) {
82  DCHECK(!label->is_bound());
83  DCHECK(target.is_bound());
84  if (label->is_forward_target()) {
85    // An earlier jump instruction refers to this label. Update it's location.
86    PatchJump(target.offset(), label->offset());
87    // Now treat as if the label will only be back referred to.
88  }
89  label->bind_to(target.offset());
90}
91
92void BytecodeArrayWriter::UpdateSourcePositionTable(
93    const BytecodeNode* const node) {
94  int bytecode_offset = static_cast<int>(bytecodes()->size());
95  const BytecodeSourceInfo& source_info = node->source_info();
96  if (source_info.is_valid()) {
97    source_position_table_builder()->AddPosition(
98        bytecode_offset, SourcePosition(source_info.source_position()),
99        source_info.is_statement());
100  }
101}
102
103void BytecodeArrayWriter::EmitBytecode(const BytecodeNode* const node) {
104  DCHECK_NE(node->bytecode(), Bytecode::kIllegal);
105
106  Bytecode bytecode = node->bytecode();
107  OperandScale operand_scale = node->operand_scale();
108
109  if (operand_scale != OperandScale::kSingle) {
110    Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
111    bytecodes()->push_back(Bytecodes::ToByte(prefix));
112  }
113  bytecodes()->push_back(Bytecodes::ToByte(bytecode));
114
115  const uint32_t* const operands = node->operands();
116  const int operand_count = node->operand_count();
117  const OperandSize* operand_sizes =
118      Bytecodes::GetOperandSizes(bytecode, operand_scale);
119  for (int i = 0; i < operand_count; ++i) {
120    switch (operand_sizes[i]) {
121      case OperandSize::kNone:
122        UNREACHABLE();
123        break;
124      case OperandSize::kByte:
125        bytecodes()->push_back(static_cast<uint8_t>(operands[i]));
126        break;
127      case OperandSize::kShort: {
128        uint16_t operand = static_cast<uint16_t>(operands[i]);
129        const uint8_t* raw_operand = reinterpret_cast<const uint8_t*>(&operand);
130        bytecodes()->push_back(raw_operand[0]);
131        bytecodes()->push_back(raw_operand[1]);
132        break;
133      }
134      case OperandSize::kQuad: {
135        const uint8_t* raw_operand =
136            reinterpret_cast<const uint8_t*>(&operands[i]);
137        bytecodes()->push_back(raw_operand[0]);
138        bytecodes()->push_back(raw_operand[1]);
139        bytecodes()->push_back(raw_operand[2]);
140        bytecodes()->push_back(raw_operand[3]);
141        break;
142      }
143    }
144  }
145}
146
147// static
148Bytecode GetJumpWithConstantOperand(Bytecode jump_bytecode) {
149  switch (jump_bytecode) {
150    case Bytecode::kJump:
151      return Bytecode::kJumpConstant;
152    case Bytecode::kJumpIfTrue:
153      return Bytecode::kJumpIfTrueConstant;
154    case Bytecode::kJumpIfFalse:
155      return Bytecode::kJumpIfFalseConstant;
156    case Bytecode::kJumpIfToBooleanTrue:
157      return Bytecode::kJumpIfToBooleanTrueConstant;
158    case Bytecode::kJumpIfToBooleanFalse:
159      return Bytecode::kJumpIfToBooleanFalseConstant;
160    case Bytecode::kJumpIfNotHole:
161      return Bytecode::kJumpIfNotHoleConstant;
162    case Bytecode::kJumpIfNull:
163      return Bytecode::kJumpIfNullConstant;
164    case Bytecode::kJumpIfUndefined:
165      return Bytecode::kJumpIfUndefinedConstant;
166    default:
167      UNREACHABLE();
168      return Bytecode::kIllegal;
169  }
170}
171
172void BytecodeArrayWriter::PatchJumpWith8BitOperand(size_t jump_location,
173                                                   int delta) {
174  Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
175  DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
176  size_t operand_location = jump_location + 1;
177  DCHECK_EQ(bytecodes()->at(operand_location), k8BitJumpPlaceholder);
178  if (Bytecodes::ScaleForSignedOperand(delta) == OperandScale::kSingle) {
179    // The jump fits within the range of an Imm8 operand, so cancel
180    // the reservation and jump directly.
181    constant_array_builder()->DiscardReservedEntry(OperandSize::kByte);
182    bytecodes()->at(operand_location) = static_cast<uint8_t>(delta);
183  } else {
184    // The jump does not fit within the range of an Imm8 operand, so
185    // commit reservation putting the offset into the constant pool,
186    // and update the jump instruction and operand.
187    size_t entry = constant_array_builder()->CommitReservedEntry(
188        OperandSize::kByte, Smi::FromInt(delta));
189    DCHECK_EQ(Bytecodes::SizeForUnsignedOperand(static_cast<uint32_t>(entry)),
190              OperandSize::kByte);
191    jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
192    bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
193    bytecodes()->at(operand_location) = static_cast<uint8_t>(entry);
194  }
195}
196
197void BytecodeArrayWriter::PatchJumpWith16BitOperand(size_t jump_location,
198                                                    int delta) {
199  Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
200  DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
201  size_t operand_location = jump_location + 1;
202  uint8_t operand_bytes[2];
203  if (Bytecodes::ScaleForSignedOperand(delta) <= OperandScale::kDouble) {
204    // The jump fits within the range of an Imm16 operand, so cancel
205    // the reservation and jump directly.
206    constant_array_builder()->DiscardReservedEntry(OperandSize::kShort);
207    WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(delta));
208  } else {
209    // The jump does not fit within the range of an Imm16 operand, so
210    // commit reservation putting the offset into the constant pool,
211    // and update the jump instruction and operand.
212    size_t entry = constant_array_builder()->CommitReservedEntry(
213        OperandSize::kShort, Smi::FromInt(delta));
214    jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
215    bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
216    WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(entry));
217  }
218  DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder &&
219         bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder);
220  bytecodes()->at(operand_location++) = operand_bytes[0];
221  bytecodes()->at(operand_location) = operand_bytes[1];
222}
223
224void BytecodeArrayWriter::PatchJumpWith32BitOperand(size_t jump_location,
225                                                    int delta) {
226  DCHECK(Bytecodes::IsJumpImmediate(
227      Bytecodes::FromByte(bytecodes()->at(jump_location))));
228  constant_array_builder()->DiscardReservedEntry(OperandSize::kQuad);
229  uint8_t operand_bytes[4];
230  WriteUnalignedUInt32(operand_bytes, static_cast<uint32_t>(delta));
231  size_t operand_location = jump_location + 1;
232  DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder &&
233         bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder &&
234         bytecodes()->at(operand_location + 2) == k8BitJumpPlaceholder &&
235         bytecodes()->at(operand_location + 3) == k8BitJumpPlaceholder);
236  bytecodes()->at(operand_location++) = operand_bytes[0];
237  bytecodes()->at(operand_location++) = operand_bytes[1];
238  bytecodes()->at(operand_location++) = operand_bytes[2];
239  bytecodes()->at(operand_location) = operand_bytes[3];
240}
241
242void BytecodeArrayWriter::PatchJump(size_t jump_target, size_t jump_location) {
243  Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
244  int delta = static_cast<int>(jump_target - jump_location);
245  int prefix_offset = 0;
246  OperandScale operand_scale = OperandScale::kSingle;
247  if (Bytecodes::IsPrefixScalingBytecode(jump_bytecode)) {
248    // If a prefix scaling bytecode is emitted the target offset is one
249    // less than the case of no prefix scaling bytecode.
250    delta -= 1;
251    prefix_offset = 1;
252    operand_scale = Bytecodes::PrefixBytecodeToOperandScale(jump_bytecode);
253    jump_bytecode =
254        Bytecodes::FromByte(bytecodes()->at(jump_location + prefix_offset));
255  }
256
257  DCHECK(Bytecodes::IsJump(jump_bytecode));
258  switch (operand_scale) {
259    case OperandScale::kSingle:
260      PatchJumpWith8BitOperand(jump_location, delta);
261      break;
262    case OperandScale::kDouble:
263      PatchJumpWith16BitOperand(jump_location + prefix_offset, delta);
264      break;
265    case OperandScale::kQuadruple:
266      PatchJumpWith32BitOperand(jump_location + prefix_offset, delta);
267      break;
268    default:
269      UNREACHABLE();
270  }
271  unbound_jumps_--;
272}
273
274void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) {
275  DCHECK(Bytecodes::IsJump(node->bytecode()));
276  DCHECK_EQ(0u, node->operand(0));
277
278  size_t current_offset = bytecodes()->size();
279
280  if (label->is_bound()) {
281    CHECK_GE(current_offset, label->offset());
282    CHECK_LE(current_offset, static_cast<size_t>(kMaxInt));
283    // Label has been bound already so this is a backwards jump.
284    size_t abs_delta = current_offset - label->offset();
285    int delta = -static_cast<int>(abs_delta);
286    OperandScale operand_scale = Bytecodes::ScaleForSignedOperand(delta);
287    if (operand_scale > OperandScale::kSingle) {
288      // Adjust for scaling byte prefix for wide jump offset.
289      DCHECK_LE(delta, 0);
290      delta -= 1;
291    }
292    DCHECK_EQ(Bytecode::kJumpLoop, node->bytecode());
293    node->set_bytecode(node->bytecode(), delta, node->operand(1));
294  } else {
295    // The label has not yet been bound so this is a forward reference
296    // that will be patched when the label is bound. We create a
297    // reservation in the constant pool so the jump can be patched
298    // when the label is bound. The reservation means the maximum size
299    // of the operand for the constant is known and the jump can
300    // be emitted into the bytecode stream with space for the operand.
301    unbound_jumps_++;
302    label->set_referrer(current_offset);
303    OperandSize reserved_operand_size =
304        constant_array_builder()->CreateReservedEntry();
305    DCHECK_NE(Bytecode::kJumpLoop, node->bytecode());
306    switch (reserved_operand_size) {
307      case OperandSize::kNone:
308        UNREACHABLE();
309        break;
310      case OperandSize::kByte:
311        node->set_bytecode(node->bytecode(), k8BitJumpPlaceholder);
312        break;
313      case OperandSize::kShort:
314        node->set_bytecode(node->bytecode(), k16BitJumpPlaceholder);
315        break;
316      case OperandSize::kQuad:
317        node->set_bytecode(node->bytecode(), k32BitJumpPlaceholder);
318        break;
319    }
320  }
321  EmitBytecode(node);
322}
323
324}  // namespace interpreter
325}  // namespace internal
326}  // namespace v8
327