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-iterator.h"
6
7#include "src/interpreter/bytecode-decoder.h"
8#include "src/interpreter/interpreter-intrinsics.h"
9#include "src/objects/code-inl.h"
10#include "src/objects/feedback-vector.h"
11#include "src/objects/objects-inl.h"
12
13namespace v8 {
14namespace internal {
15namespace interpreter {
16
17BytecodeArrayIterator::BytecodeArrayIterator(
18    Handle<BytecodeArray> bytecode_array, int initial_offset)
19    : bytecode_array_(bytecode_array),
20      start_(reinterpret_cast<uint8_t*>(
21          bytecode_array_->GetFirstBytecodeAddress())),
22      end_(start_ + bytecode_array_->length()),
23      cursor_(start_ + initial_offset),
24      operand_scale_(OperandScale::kSingle),
25      prefix_size_(0),
26      local_heap_(LocalHeap::Current()
27                      ? LocalHeap::Current()
28                      : Isolate::Current()->main_thread_local_heap()) {
29  local_heap_->AddGCEpilogueCallback(UpdatePointersCallback, this);
30  UpdateOperandScale();
31}
32
33BytecodeArrayIterator::~BytecodeArrayIterator() {
34  local_heap_->RemoveGCEpilogueCallback(UpdatePointersCallback, this);
35}
36
37void BytecodeArrayIterator::SetOffset(int offset) {
38  if (offset < 0) return;
39  cursor_ = reinterpret_cast<uint8_t*>(
40      bytecode_array()->GetFirstBytecodeAddress() + offset);
41  UpdateOperandScale();
42}
43
44void BytecodeArrayIterator::ApplyDebugBreak() {
45  // Get the raw bytecode from the bytecode array. This may give us a
46  // scaling prefix, which we can patch with the matching debug-break
47  // variant.
48  uint8_t* cursor = cursor_ - prefix_size_;
49  interpreter::Bytecode bytecode = interpreter::Bytecodes::FromByte(*cursor);
50  if (interpreter::Bytecodes::IsDebugBreak(bytecode)) return;
51  interpreter::Bytecode debugbreak =
52      interpreter::Bytecodes::GetDebugBreak(bytecode);
53  *cursor = interpreter::Bytecodes::ToByte(debugbreak);
54}
55
56uint32_t BytecodeArrayIterator::GetUnsignedOperand(
57    int operand_index, OperandType operand_type) const {
58  DCHECK_GE(operand_index, 0);
59  DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
60  DCHECK_EQ(operand_type,
61            Bytecodes::GetOperandType(current_bytecode(), operand_index));
62  DCHECK(Bytecodes::IsUnsignedOperandType(operand_type));
63  Address operand_start =
64      reinterpret_cast<Address>(cursor_) +
65      Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
66                                  current_operand_scale());
67  return BytecodeDecoder::DecodeUnsignedOperand(operand_start, operand_type,
68                                                current_operand_scale());
69}
70
71int32_t BytecodeArrayIterator::GetSignedOperand(
72    int operand_index, OperandType operand_type) const {
73  DCHECK_GE(operand_index, 0);
74  DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
75  DCHECK_EQ(operand_type,
76            Bytecodes::GetOperandType(current_bytecode(), operand_index));
77  DCHECK(!Bytecodes::IsUnsignedOperandType(operand_type));
78  Address operand_start =
79      reinterpret_cast<Address>(cursor_) +
80      Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
81                                  current_operand_scale());
82  return BytecodeDecoder::DecodeSignedOperand(operand_start, operand_type,
83                                              current_operand_scale());
84}
85
86uint32_t BytecodeArrayIterator::GetFlagOperand(int operand_index) const {
87  DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
88            OperandType::kFlag8);
89  return GetUnsignedOperand(operand_index, OperandType::kFlag8);
90}
91
92uint32_t BytecodeArrayIterator::GetUnsignedImmediateOperand(
93    int operand_index) const {
94  DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
95            OperandType::kUImm);
96  return GetUnsignedOperand(operand_index, OperandType::kUImm);
97}
98
99int32_t BytecodeArrayIterator::GetImmediateOperand(int operand_index) const {
100  DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
101            OperandType::kImm);
102  return GetSignedOperand(operand_index, OperandType::kImm);
103}
104
105uint32_t BytecodeArrayIterator::GetRegisterCountOperand(
106    int operand_index) const {
107  DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
108            OperandType::kRegCount);
109  return GetUnsignedOperand(operand_index, OperandType::kRegCount);
110}
111
112uint32_t BytecodeArrayIterator::GetIndexOperand(int operand_index) const {
113  OperandType operand_type =
114      Bytecodes::GetOperandType(current_bytecode(), operand_index);
115  DCHECK_EQ(operand_type, OperandType::kIdx);
116  return GetUnsignedOperand(operand_index, operand_type);
117}
118
119FeedbackSlot BytecodeArrayIterator::GetSlotOperand(int operand_index) const {
120  int index = GetIndexOperand(operand_index);
121  return FeedbackVector::ToSlot(index);
122}
123
124Register BytecodeArrayIterator::GetReceiver() const {
125  return Register::FromParameterIndex(0);
126}
127
128Register BytecodeArrayIterator::GetParameter(int parameter_index) const {
129  DCHECK_GE(parameter_index, 0);
130  // The parameter indices are shifted by 1 (receiver is the
131  // first entry).
132  return Register::FromParameterIndex(parameter_index + 1);
133}
134
135Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
136  OperandType operand_type =
137      Bytecodes::GetOperandType(current_bytecode(), operand_index);
138  Address operand_start =
139      reinterpret_cast<Address>(cursor_) +
140      Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
141                                  current_operand_scale());
142  return BytecodeDecoder::DecodeRegisterOperand(operand_start, operand_type,
143                                                current_operand_scale());
144}
145
146std::pair<Register, Register> BytecodeArrayIterator::GetRegisterPairOperand(
147    int operand_index) const {
148  Register first = GetRegisterOperand(operand_index);
149  Register second(first.index() + 1);
150  return std::make_pair(first, second);
151}
152
153RegisterList BytecodeArrayIterator::GetRegisterListOperand(
154    int operand_index) const {
155  Register first = GetRegisterOperand(operand_index);
156  uint32_t count = GetRegisterCountOperand(operand_index + 1);
157  return RegisterList(first.index(), count);
158}
159
160int BytecodeArrayIterator::GetRegisterOperandRange(int operand_index) const {
161  DCHECK_LE(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
162  const OperandType* operand_types =
163      Bytecodes::GetOperandTypes(current_bytecode());
164  OperandType operand_type = operand_types[operand_index];
165  DCHECK(Bytecodes::IsRegisterOperandType(operand_type));
166  if (operand_type == OperandType::kRegList ||
167      operand_type == OperandType::kRegOutList) {
168    return GetRegisterCountOperand(operand_index + 1);
169  } else {
170    return Bytecodes::GetNumberOfRegistersRepresentedBy(operand_type);
171  }
172}
173
174Runtime::FunctionId BytecodeArrayIterator::GetRuntimeIdOperand(
175    int operand_index) const {
176  OperandType operand_type =
177      Bytecodes::GetOperandType(current_bytecode(), operand_index);
178  DCHECK_EQ(operand_type, OperandType::kRuntimeId);
179  uint32_t raw_id = GetUnsignedOperand(operand_index, operand_type);
180  return static_cast<Runtime::FunctionId>(raw_id);
181}
182
183uint32_t BytecodeArrayIterator::GetNativeContextIndexOperand(
184    int operand_index) const {
185  OperandType operand_type =
186      Bytecodes::GetOperandType(current_bytecode(), operand_index);
187  DCHECK_EQ(operand_type, OperandType::kNativeContextIndex);
188  return GetUnsignedOperand(operand_index, operand_type);
189}
190
191Runtime::FunctionId BytecodeArrayIterator::GetIntrinsicIdOperand(
192    int operand_index) const {
193  OperandType operand_type =
194      Bytecodes::GetOperandType(current_bytecode(), operand_index);
195  DCHECK_EQ(operand_type, OperandType::kIntrinsicId);
196  uint32_t raw_id = GetUnsignedOperand(operand_index, operand_type);
197  return IntrinsicsHelper::ToRuntimeId(
198      static_cast<IntrinsicsHelper::IntrinsicId>(raw_id));
199}
200
201template <typename IsolateT>
202Handle<Object> BytecodeArrayIterator::GetConstantAtIndex(
203    int index, IsolateT* isolate) const {
204  return handle(bytecode_array()->constant_pool().get(index), isolate);
205}
206
207bool BytecodeArrayIterator::IsConstantAtIndexSmi(int index) const {
208  return bytecode_array()->constant_pool().get(index).IsSmi();
209}
210
211Smi BytecodeArrayIterator::GetConstantAtIndexAsSmi(int index) const {
212  return Smi::cast(bytecode_array()->constant_pool().get(index));
213}
214
215template <typename IsolateT>
216Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
217    int operand_index, IsolateT* isolate) const {
218  return GetConstantAtIndex(GetIndexOperand(operand_index), isolate);
219}
220
221template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
222    Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
223        int operand_index, Isolate* isolate) const;
224template Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
225    int operand_index, LocalIsolate* isolate) const;
226
227int BytecodeArrayIterator::GetRelativeJumpTargetOffset() const {
228  Bytecode bytecode = current_bytecode();
229  if (interpreter::Bytecodes::IsJumpImmediate(bytecode)) {
230    int relative_offset = GetUnsignedImmediateOperand(0);
231    if (bytecode == Bytecode::kJumpLoop) {
232      relative_offset = -relative_offset;
233    }
234    return relative_offset;
235  } else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
236    Smi smi = GetConstantAtIndexAsSmi(GetIndexOperand(0));
237    return smi.value();
238  } else {
239    UNREACHABLE();
240  }
241}
242
243int BytecodeArrayIterator::GetJumpTargetOffset() const {
244  return GetAbsoluteOffset(GetRelativeJumpTargetOffset());
245}
246
247JumpTableTargetOffsets BytecodeArrayIterator::GetJumpTableTargetOffsets()
248    const {
249  uint32_t table_start, table_size;
250  int32_t case_value_base;
251  if (current_bytecode() == Bytecode::kSwitchOnGeneratorState) {
252    table_start = GetIndexOperand(1);
253    table_size = GetUnsignedImmediateOperand(2);
254    case_value_base = 0;
255  } else {
256    DCHECK_EQ(current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
257    table_start = GetIndexOperand(0);
258    table_size = GetUnsignedImmediateOperand(1);
259    case_value_base = GetImmediateOperand(2);
260  }
261  return JumpTableTargetOffsets(this, table_start, table_size, case_value_base);
262}
263
264int BytecodeArrayIterator::GetAbsoluteOffset(int relative_offset) const {
265  return current_offset() + relative_offset + prefix_size_;
266}
267
268std::ostream& BytecodeArrayIterator::PrintTo(std::ostream& os) const {
269  return BytecodeDecoder::Decode(os, cursor_ - prefix_size_);
270}
271
272void BytecodeArrayIterator::UpdatePointers() {
273  DisallowGarbageCollection no_gc;
274  uint8_t* start =
275      reinterpret_cast<uint8_t*>(bytecode_array_->GetFirstBytecodeAddress());
276  if (start != start_) {
277    start_ = start;
278    uint8_t* end = start + bytecode_array_->length();
279    size_t distance_to_end = end_ - cursor_;
280    cursor_ = end - distance_to_end;
281    end_ = end;
282  }
283}
284
285JumpTableTargetOffsets::JumpTableTargetOffsets(
286    const BytecodeArrayIterator* iterator, int table_start, int table_size,
287    int case_value_base)
288    : iterator_(iterator),
289      table_start_(table_start),
290      table_size_(table_size),
291      case_value_base_(case_value_base) {}
292
293JumpTableTargetOffsets::iterator JumpTableTargetOffsets::begin() const {
294  return iterator(case_value_base_, table_start_, table_start_ + table_size_,
295                  iterator_);
296}
297JumpTableTargetOffsets::iterator JumpTableTargetOffsets::end() const {
298  return iterator(case_value_base_ + table_size_, table_start_ + table_size_,
299                  table_start_ + table_size_, iterator_);
300}
301int JumpTableTargetOffsets::size() const {
302  int ret = 0;
303  // TODO(leszeks): Is there a more efficient way of doing this than iterating?
304  for (JumpTableTargetOffset entry : *this) {
305    USE(entry);
306    ret++;
307  }
308  return ret;
309}
310
311JumpTableTargetOffsets::iterator::iterator(
312    int case_value, int table_offset, int table_end,
313    const BytecodeArrayIterator* iterator)
314    : iterator_(iterator),
315      current_(Smi::zero()),
316      index_(case_value),
317      table_offset_(table_offset),
318      table_end_(table_end) {
319  UpdateAndAdvanceToValid();
320}
321
322JumpTableTargetOffset JumpTableTargetOffsets::iterator::operator*() {
323  DCHECK_LT(table_offset_, table_end_);
324  return {index_, iterator_->GetAbsoluteOffset(Smi::ToInt(current_))};
325}
326
327JumpTableTargetOffsets::iterator&
328JumpTableTargetOffsets::iterator::operator++() {
329  DCHECK_LT(table_offset_, table_end_);
330  ++table_offset_;
331  ++index_;
332  UpdateAndAdvanceToValid();
333  return *this;
334}
335
336bool JumpTableTargetOffsets::iterator::operator!=(
337    const JumpTableTargetOffsets::iterator& other) {
338  DCHECK_EQ(iterator_, other.iterator_);
339  DCHECK_EQ(table_end_, other.table_end_);
340  DCHECK_EQ(index_ - other.index_, table_offset_ - other.table_offset_);
341  return index_ != other.index_;
342}
343
344void JumpTableTargetOffsets::iterator::UpdateAndAdvanceToValid() {
345  while (table_offset_ < table_end_ &&
346         !iterator_->IsConstantAtIndexSmi(table_offset_)) {
347    ++table_offset_;
348    ++index_;
349  }
350
351  // Make sure we haven't reached the end of the table with a hole in current.
352  if (table_offset_ < table_end_) {
353    DCHECK(iterator_->IsConstantAtIndexSmi(table_offset_));
354    current_ = iterator_->GetConstantAtIndexAsSmi(table_offset_);
355  }
356}
357
358}  // namespace interpreter
359}  // namespace internal
360}  // namespace v8
361