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
current_bytecode_size() const56 int BytecodeArrayIterator::current_bytecode_size() const {
57 return prefix_size_ + current_bytecode_size_without_prefix();
58 }
59
current_bytecode_size_without_prefix() const60 int BytecodeArrayIterator::current_bytecode_size_without_prefix() const {
61 return Bytecodes::Size(current_bytecode(), current_operand_scale());
62 }
63
GetUnsignedOperand(int operand_index,OperandType operand_type) const64 uint32_t BytecodeArrayIterator::GetUnsignedOperand(
65 int operand_index, OperandType operand_type) const {
66 DCHECK_GE(operand_index, 0);
67 DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
68 DCHECK_EQ(operand_type,
69 Bytecodes::GetOperandType(current_bytecode(), operand_index));
70 DCHECK(Bytecodes::IsUnsignedOperandType(operand_type));
71 Address operand_start =
72 reinterpret_cast<Address>(cursor_) +
73 Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
74 current_operand_scale());
75 return BytecodeDecoder::DecodeUnsignedOperand(operand_start, operand_type,
76 current_operand_scale());
77 }
78
GetSignedOperand(int operand_index,OperandType operand_type) const79 int32_t BytecodeArrayIterator::GetSignedOperand(
80 int operand_index, OperandType operand_type) const {
81 DCHECK_GE(operand_index, 0);
82 DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
83 DCHECK_EQ(operand_type,
84 Bytecodes::GetOperandType(current_bytecode(), operand_index));
85 DCHECK(!Bytecodes::IsUnsignedOperandType(operand_type));
86 Address operand_start =
87 reinterpret_cast<Address>(cursor_) +
88 Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
89 current_operand_scale());
90 return BytecodeDecoder::DecodeSignedOperand(operand_start, operand_type,
91 current_operand_scale());
92 }
93
GetFlagOperand(int operand_index) const94 uint32_t BytecodeArrayIterator::GetFlagOperand(int operand_index) const {
95 DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
96 OperandType::kFlag8);
97 return GetUnsignedOperand(operand_index, OperandType::kFlag8);
98 }
99
GetUnsignedImmediateOperand(int operand_index) const100 uint32_t BytecodeArrayIterator::GetUnsignedImmediateOperand(
101 int operand_index) const {
102 DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
103 OperandType::kUImm);
104 return GetUnsignedOperand(operand_index, OperandType::kUImm);
105 }
106
GetImmediateOperand(int operand_index) const107 int32_t BytecodeArrayIterator::GetImmediateOperand(int operand_index) const {
108 DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
109 OperandType::kImm);
110 return GetSignedOperand(operand_index, OperandType::kImm);
111 }
112
GetRegisterCountOperand(int operand_index) const113 uint32_t BytecodeArrayIterator::GetRegisterCountOperand(
114 int operand_index) const {
115 DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
116 OperandType::kRegCount);
117 return GetUnsignedOperand(operand_index, OperandType::kRegCount);
118 }
119
GetIndexOperand(int operand_index) const120 uint32_t BytecodeArrayIterator::GetIndexOperand(int operand_index) const {
121 OperandType operand_type =
122 Bytecodes::GetOperandType(current_bytecode(), operand_index);
123 DCHECK_EQ(operand_type, OperandType::kIdx);
124 return GetUnsignedOperand(operand_index, operand_type);
125 }
126
GetSlotOperand(int operand_index) const127 FeedbackSlot BytecodeArrayIterator::GetSlotOperand(int operand_index) const {
128 int index = GetIndexOperand(operand_index);
129 return FeedbackVector::ToSlot(index);
130 }
131
GetReceiver() const132 Register BytecodeArrayIterator::GetReceiver() const {
133 return Register::FromParameterIndex(0, bytecode_array()->parameter_count());
134 }
135
GetParameter(int parameter_index) const136 Register BytecodeArrayIterator::GetParameter(int parameter_index) const {
137 DCHECK_GE(parameter_index, 0);
138 // The parameter indices are shifted by 1 (receiver is the
139 // first entry).
140 return Register::FromParameterIndex(parameter_index + 1,
141 bytecode_array()->parameter_count());
142 }
143
GetRegisterOperand(int operand_index) const144 Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
145 OperandType operand_type =
146 Bytecodes::GetOperandType(current_bytecode(), operand_index);
147 Address operand_start =
148 reinterpret_cast<Address>(cursor_) +
149 Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
150 current_operand_scale());
151 return BytecodeDecoder::DecodeRegisterOperand(operand_start, operand_type,
152 current_operand_scale());
153 }
154
GetRegisterPairOperand(int operand_index) const155 std::pair<Register, Register> BytecodeArrayIterator::GetRegisterPairOperand(
156 int operand_index) const {
157 Register first = GetRegisterOperand(operand_index);
158 Register second(first.index() + 1);
159 return std::make_pair(first, second);
160 }
161
GetRegisterListOperand(int operand_index) const162 RegisterList BytecodeArrayIterator::GetRegisterListOperand(
163 int operand_index) const {
164 Register first = GetRegisterOperand(operand_index);
165 uint32_t count = GetRegisterCountOperand(operand_index + 1);
166 return RegisterList(first.index(), count);
167 }
168
GetRegisterOperandRange(int operand_index) const169 int BytecodeArrayIterator::GetRegisterOperandRange(int operand_index) const {
170 DCHECK_LE(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
171 const OperandType* operand_types =
172 Bytecodes::GetOperandTypes(current_bytecode());
173 OperandType operand_type = operand_types[operand_index];
174 DCHECK(Bytecodes::IsRegisterOperandType(operand_type));
175 if (operand_type == OperandType::kRegList ||
176 operand_type == OperandType::kRegOutList) {
177 return GetRegisterCountOperand(operand_index + 1);
178 } else {
179 return Bytecodes::GetNumberOfRegistersRepresentedBy(operand_type);
180 }
181 }
182
GetRuntimeIdOperand(int operand_index) const183 Runtime::FunctionId BytecodeArrayIterator::GetRuntimeIdOperand(
184 int operand_index) const {
185 OperandType operand_type =
186 Bytecodes::GetOperandType(current_bytecode(), operand_index);
187 DCHECK_EQ(operand_type, OperandType::kRuntimeId);
188 uint32_t raw_id = GetUnsignedOperand(operand_index, operand_type);
189 return static_cast<Runtime::FunctionId>(raw_id);
190 }
191
GetNativeContextIndexOperand(int operand_index) const192 uint32_t BytecodeArrayIterator::GetNativeContextIndexOperand(
193 int operand_index) const {
194 OperandType operand_type =
195 Bytecodes::GetOperandType(current_bytecode(), operand_index);
196 DCHECK_EQ(operand_type, OperandType::kNativeContextIndex);
197 return GetUnsignedOperand(operand_index, operand_type);
198 }
199
GetIntrinsicIdOperand(int operand_index) const200 Runtime::FunctionId BytecodeArrayIterator::GetIntrinsicIdOperand(
201 int operand_index) const {
202 OperandType operand_type =
203 Bytecodes::GetOperandType(current_bytecode(), operand_index);
204 DCHECK_EQ(operand_type, OperandType::kIntrinsicId);
205 uint32_t raw_id = GetUnsignedOperand(operand_index, operand_type);
206 return IntrinsicsHelper::ToRuntimeId(
207 static_cast<IntrinsicsHelper::IntrinsicId>(raw_id));
208 }
209
210 template <typename IsolateT>
GetConstantAtIndex(int index,IsolateT * isolate) const211 Handle<Object> BytecodeArrayIterator::GetConstantAtIndex(
212 int index, IsolateT* isolate) const {
213 return handle(bytecode_array()->constant_pool().get(index), isolate);
214 }
215
IsConstantAtIndexSmi(int index) const216 bool BytecodeArrayIterator::IsConstantAtIndexSmi(int index) const {
217 return bytecode_array()->constant_pool().get(index).IsSmi();
218 }
219
GetConstantAtIndexAsSmi(int index) const220 Smi BytecodeArrayIterator::GetConstantAtIndexAsSmi(int index) const {
221 return Smi::cast(bytecode_array()->constant_pool().get(index));
222 }
223
224 template <typename IsolateT>
GetConstantForIndexOperand(int operand_index,IsolateT * isolate) const225 Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
226 int operand_index, IsolateT* isolate) const {
227 return GetConstantAtIndex(GetIndexOperand(operand_index), isolate);
228 }
229
230 template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
231 Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
232 int operand_index, Isolate* isolate) const;
233 template Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
234 int operand_index, LocalIsolate* isolate) const;
235
GetRelativeJumpTargetOffset() const236 int BytecodeArrayIterator::GetRelativeJumpTargetOffset() const {
237 Bytecode bytecode = current_bytecode();
238 if (interpreter::Bytecodes::IsJumpImmediate(bytecode)) {
239 int relative_offset = GetUnsignedImmediateOperand(0);
240 if (bytecode == Bytecode::kJumpLoop) {
241 relative_offset = -relative_offset;
242 }
243 return relative_offset;
244 } else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
245 Smi smi = GetConstantAtIndexAsSmi(GetIndexOperand(0));
246 return smi.value();
247 } else {
248 UNREACHABLE();
249 }
250 }
251
GetJumpTargetOffset() const252 int BytecodeArrayIterator::GetJumpTargetOffset() const {
253 return GetAbsoluteOffset(GetRelativeJumpTargetOffset());
254 }
255
GetJumpTableTargetOffsets() const256 JumpTableTargetOffsets BytecodeArrayIterator::GetJumpTableTargetOffsets()
257 const {
258 uint32_t table_start, table_size;
259 int32_t case_value_base;
260 if (current_bytecode() == Bytecode::kSwitchOnGeneratorState) {
261 table_start = GetIndexOperand(1);
262 table_size = GetUnsignedImmediateOperand(2);
263 case_value_base = 0;
264 } else {
265 DCHECK_EQ(current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
266 table_start = GetIndexOperand(0);
267 table_size = GetUnsignedImmediateOperand(1);
268 case_value_base = GetImmediateOperand(2);
269 }
270 return JumpTableTargetOffsets(this, table_start, table_size, case_value_base);
271 }
272
GetAbsoluteOffset(int relative_offset) const273 int BytecodeArrayIterator::GetAbsoluteOffset(int relative_offset) const {
274 return current_offset() + relative_offset + prefix_size_;
275 }
276
PrintTo(std::ostream & os) const277 std::ostream& BytecodeArrayIterator::PrintTo(std::ostream& os) const {
278 return BytecodeDecoder::Decode(os, cursor_ - prefix_size_,
279 bytecode_array()->parameter_count());
280 }
281
UpdatePointers()282 void BytecodeArrayIterator::UpdatePointers() {
283 DisallowGarbageCollection no_gc;
284 uint8_t* start =
285 reinterpret_cast<uint8_t*>(bytecode_array_->GetFirstBytecodeAddress());
286 if (start != start_) {
287 start_ = start;
288 uint8_t* end = start + bytecode_array_->length();
289 size_t distance_to_end = end_ - cursor_;
290 cursor_ = end - distance_to_end;
291 end_ = end;
292 }
293 }
294
JumpTableTargetOffsets(const BytecodeArrayIterator * iterator,int table_start,int table_size,int case_value_base)295 JumpTableTargetOffsets::JumpTableTargetOffsets(
296 const BytecodeArrayIterator* iterator, int table_start, int table_size,
297 int case_value_base)
298 : iterator_(iterator),
299 table_start_(table_start),
300 table_size_(table_size),
301 case_value_base_(case_value_base) {}
302
begin() const303 JumpTableTargetOffsets::iterator JumpTableTargetOffsets::begin() const {
304 return iterator(case_value_base_, table_start_, table_start_ + table_size_,
305 iterator_);
306 }
end() const307 JumpTableTargetOffsets::iterator JumpTableTargetOffsets::end() const {
308 return iterator(case_value_base_ + table_size_, table_start_ + table_size_,
309 table_start_ + table_size_, iterator_);
310 }
size() const311 int JumpTableTargetOffsets::size() const {
312 int ret = 0;
313 // TODO(leszeks): Is there a more efficient way of doing this than iterating?
314 for (JumpTableTargetOffset entry : *this) {
315 USE(entry);
316 ret++;
317 }
318 return ret;
319 }
320
iterator(int case_value,int table_offset,int table_end,const BytecodeArrayIterator * iterator)321 JumpTableTargetOffsets::iterator::iterator(
322 int case_value, int table_offset, int table_end,
323 const BytecodeArrayIterator* iterator)
324 : iterator_(iterator),
325 current_(Smi::zero()),
326 index_(case_value),
327 table_offset_(table_offset),
328 table_end_(table_end) {
329 UpdateAndAdvanceToValid();
330 }
331
operator *()332 JumpTableTargetOffset JumpTableTargetOffsets::iterator::operator*() {
333 DCHECK_LT(table_offset_, table_end_);
334 return {index_, iterator_->GetAbsoluteOffset(Smi::ToInt(current_))};
335 }
336
337 JumpTableTargetOffsets::iterator&
operator ++()338 JumpTableTargetOffsets::iterator::operator++() {
339 DCHECK_LT(table_offset_, table_end_);
340 ++table_offset_;
341 ++index_;
342 UpdateAndAdvanceToValid();
343 return *this;
344 }
345
operator !=(const JumpTableTargetOffsets::iterator & other)346 bool JumpTableTargetOffsets::iterator::operator!=(
347 const JumpTableTargetOffsets::iterator& other) {
348 DCHECK_EQ(iterator_, other.iterator_);
349 DCHECK_EQ(table_end_, other.table_end_);
350 DCHECK_EQ(index_ - other.index_, table_offset_ - other.table_offset_);
351 return index_ != other.index_;
352 }
353
UpdateAndAdvanceToValid()354 void JumpTableTargetOffsets::iterator::UpdateAndAdvanceToValid() {
355 while (table_offset_ < table_end_ &&
356 !iterator_->IsConstantAtIndexSmi(table_offset_)) {
357 ++table_offset_;
358 ++index_;
359 }
360
361 // Make sure we haven't reached the end of the table with a hole in current.
362 if (table_offset_ < table_end_) {
363 DCHECK(iterator_->IsConstantAtIndexSmi(table_offset_));
364 current_ = iterator_->GetConstantAtIndexAsSmi(table_offset_);
365 }
366 }
367
368 } // namespace interpreter
369 } // namespace internal
370 } // namespace v8
371