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
13 namespace v8 {
14 namespace internal {
15 namespace interpreter {
16
BytecodeArrayIterator(Handle<BytecodeArray> bytecode_array,int initial_offset)17 BytecodeArrayIterator::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
~BytecodeArrayIterator()33 BytecodeArrayIterator::~BytecodeArrayIterator() {
34 local_heap_->RemoveGCEpilogueCallback(UpdatePointersCallback, this);
35 }
36
SetOffset(int offset)37 void BytecodeArrayIterator::SetOffset(int offset) {
38 if (offset < 0) return;
39 cursor_ = reinterpret_cast<uint8_t*>(
40 bytecode_array()->GetFirstBytecodeAddress() + offset);
41 UpdateOperandScale();
42 }
43
ApplyDebugBreak()44 void 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
GetUnsignedOperand(int operand_index,OperandType operand_type) const56 uint32_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
GetSignedOperand(int operand_index,OperandType operand_type) const71 int32_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
GetFlagOperand(int operand_index) const86 uint32_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
GetUnsignedImmediateOperand(int operand_index) const92 uint32_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
GetImmediateOperand(int operand_index) const99 int32_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
GetRegisterCountOperand(int operand_index) const105 uint32_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
GetIndexOperand(int operand_index) const112 uint32_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
GetSlotOperand(int operand_index) const119 FeedbackSlot BytecodeArrayIterator::GetSlotOperand(int operand_index) const {
120 int index = GetIndexOperand(operand_index);
121 return FeedbackVector::ToSlot(index);
122 }
123
GetReceiver() const124 Register BytecodeArrayIterator::GetReceiver() const {
125 return Register::FromParameterIndex(0);
126 }
127
GetParameter(int parameter_index) const128 Register 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
GetRegisterOperand(int operand_index) const135 Register 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
GetRegisterPairOperand(int operand_index) const146 std::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
GetRegisterListOperand(int operand_index) const153 RegisterList 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
GetRegisterOperandRange(int operand_index) const160 int 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
GetRuntimeIdOperand(int operand_index) const174 Runtime::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
GetNativeContextIndexOperand(int operand_index) const183 uint32_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
GetIntrinsicIdOperand(int operand_index) const191 Runtime::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
201 template <typename IsolateT>
GetConstantAtIndex(int index,IsolateT * isolate) const202 Handle<Object> BytecodeArrayIterator::GetConstantAtIndex(
203 int index, IsolateT* isolate) const {
204 return handle(bytecode_array()->constant_pool().get(index), isolate);
205 }
206
IsConstantAtIndexSmi(int index) const207 bool BytecodeArrayIterator::IsConstantAtIndexSmi(int index) const {
208 return bytecode_array()->constant_pool().get(index).IsSmi();
209 }
210
GetConstantAtIndexAsSmi(int index) const211 Smi BytecodeArrayIterator::GetConstantAtIndexAsSmi(int index) const {
212 return Smi::cast(bytecode_array()->constant_pool().get(index));
213 }
214
215 template <typename IsolateT>
GetConstantForIndexOperand(int operand_index,IsolateT * isolate) const216 Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
217 int operand_index, IsolateT* isolate) const {
218 return GetConstantAtIndex(GetIndexOperand(operand_index), isolate);
219 }
220
221 template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
222 Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
223 int operand_index, Isolate* isolate) const;
224 template Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
225 int operand_index, LocalIsolate* isolate) const;
226
GetRelativeJumpTargetOffset() const227 int 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
GetJumpTargetOffset() const243 int BytecodeArrayIterator::GetJumpTargetOffset() const {
244 return GetAbsoluteOffset(GetRelativeJumpTargetOffset());
245 }
246
GetJumpTableTargetOffsets() const247 JumpTableTargetOffsets 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
GetAbsoluteOffset(int relative_offset) const264 int BytecodeArrayIterator::GetAbsoluteOffset(int relative_offset) const {
265 return current_offset() + relative_offset + prefix_size_;
266 }
267
PrintTo(std::ostream & os) const268 std::ostream& BytecodeArrayIterator::PrintTo(std::ostream& os) const {
269 return BytecodeDecoder::Decode(os, cursor_ - prefix_size_);
270 }
271
UpdatePointers()272 void 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
JumpTableTargetOffsets(const BytecodeArrayIterator * iterator,int table_start,int table_size,int case_value_base)285 JumpTableTargetOffsets::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
begin() const293 JumpTableTargetOffsets::iterator JumpTableTargetOffsets::begin() const {
294 return iterator(case_value_base_, table_start_, table_start_ + table_size_,
295 iterator_);
296 }
end() const297 JumpTableTargetOffsets::iterator JumpTableTargetOffsets::end() const {
298 return iterator(case_value_base_ + table_size_, table_start_ + table_size_,
299 table_start_ + table_size_, iterator_);
300 }
size() const301 int 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
iterator(int case_value,int table_offset,int table_end,const BytecodeArrayIterator * iterator)311 JumpTableTargetOffsets::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
operator *()322 JumpTableTargetOffset JumpTableTargetOffsets::iterator::operator*() {
323 DCHECK_LT(table_offset_, table_end_);
324 return {index_, iterator_->GetAbsoluteOffset(Smi::ToInt(current_))};
325 }
326
327 JumpTableTargetOffsets::iterator&
operator ++()328 JumpTableTargetOffsets::iterator::operator++() {
329 DCHECK_LT(table_offset_, table_end_);
330 ++table_offset_;
331 ++index_;
332 UpdateAndAdvanceToValid();
333 return *this;
334 }
335
operator !=(const JumpTableTargetOffsets::iterator & other)336 bool 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
UpdateAndAdvanceToValid()344 void 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