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-accessor.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
17 namespace {
18
19 class OnHeapBytecodeArray final : public AbstractBytecodeArray {
20 public:
OnHeapBytecodeArray(Handle<BytecodeArray> bytecode_array)21 explicit OnHeapBytecodeArray(Handle<BytecodeArray> bytecode_array)
22 : array_(bytecode_array) {}
23
length() const24 int length() const override { return array_->length(); }
25
parameter_count() const26 int parameter_count() const override { return array_->parameter_count(); }
27
get(int index) const28 uint8_t get(int index) const override { return array_->get(index); }
29
set(int index,uint8_t value)30 void set(int index, uint8_t value) override {
31 return array_->set(index, value);
32 }
33
GetFirstBytecodeAddress() const34 Address GetFirstBytecodeAddress() const override {
35 return array_->GetFirstBytecodeAddress();
36 }
37
GetConstantAtIndex(int index,Isolate * isolate) const38 Handle<Object> GetConstantAtIndex(int index,
39 Isolate* isolate) const override {
40 return handle(array_->constant_pool().get(index), isolate);
41 }
42
IsConstantAtIndexSmi(int index) const43 bool IsConstantAtIndexSmi(int index) const override {
44 return array_->constant_pool().get(index).IsSmi();
45 }
46
GetConstantAtIndexAsSmi(int index) const47 Smi GetConstantAtIndexAsSmi(int index) const override {
48 return Smi::cast(array_->constant_pool().get(index));
49 }
50
51 private:
52 Handle<BytecodeArray> array_;
53 };
54
55 } // namespace
56
BytecodeArrayAccessor(std::unique_ptr<AbstractBytecodeArray> bytecode_array,int initial_offset)57 BytecodeArrayAccessor::BytecodeArrayAccessor(
58 std::unique_ptr<AbstractBytecodeArray> bytecode_array, int initial_offset)
59 : bytecode_array_(std::move(bytecode_array)),
60 bytecode_offset_(initial_offset),
61 operand_scale_(OperandScale::kSingle),
62 prefix_offset_(0) {
63 UpdateOperandScale();
64 }
65
BytecodeArrayAccessor(Handle<BytecodeArray> bytecode_array,int initial_offset)66 BytecodeArrayAccessor::BytecodeArrayAccessor(
67 Handle<BytecodeArray> bytecode_array, int initial_offset)
68 : BytecodeArrayAccessor(
69 std::make_unique<OnHeapBytecodeArray>(bytecode_array),
70 initial_offset) {}
71
SetOffset(int offset)72 void BytecodeArrayAccessor::SetOffset(int offset) {
73 bytecode_offset_ = offset;
74 UpdateOperandScale();
75 }
76
ApplyDebugBreak()77 void BytecodeArrayAccessor::ApplyDebugBreak() {
78 // Get the raw bytecode from the bytecode array. This may give us a
79 // scaling prefix, which we can patch with the matching debug-break
80 // variant.
81 interpreter::Bytecode bytecode =
82 interpreter::Bytecodes::FromByte(bytecode_array()->get(bytecode_offset_));
83 if (interpreter::Bytecodes::IsDebugBreak(bytecode)) return;
84 interpreter::Bytecode debugbreak =
85 interpreter::Bytecodes::GetDebugBreak(bytecode);
86 bytecode_array()->set(bytecode_offset_,
87 interpreter::Bytecodes::ToByte(debugbreak));
88 }
89
UpdateOperandScale()90 void BytecodeArrayAccessor::UpdateOperandScale() {
91 if (OffsetInBounds()) {
92 uint8_t current_byte = bytecode_array()->get(bytecode_offset_);
93 Bytecode current_bytecode = Bytecodes::FromByte(current_byte);
94 if (Bytecodes::IsPrefixScalingBytecode(current_bytecode)) {
95 operand_scale_ =
96 Bytecodes::PrefixBytecodeToOperandScale(current_bytecode);
97 prefix_offset_ = 1;
98 } else {
99 operand_scale_ = OperandScale::kSingle;
100 prefix_offset_ = 0;
101 }
102 }
103 }
104
OffsetInBounds() const105 bool BytecodeArrayAccessor::OffsetInBounds() const {
106 return bytecode_offset_ >= 0 && bytecode_offset_ < bytecode_array()->length();
107 }
108
current_bytecode() const109 Bytecode BytecodeArrayAccessor::current_bytecode() const {
110 DCHECK(OffsetInBounds());
111 uint8_t current_byte =
112 bytecode_array()->get(bytecode_offset_ + current_prefix_offset());
113 Bytecode current_bytecode = Bytecodes::FromByte(current_byte);
114 DCHECK(!Bytecodes::IsPrefixScalingBytecode(current_bytecode));
115 return current_bytecode;
116 }
117
current_bytecode_size() const118 int BytecodeArrayAccessor::current_bytecode_size() const {
119 return current_prefix_offset() +
120 Bytecodes::Size(current_bytecode(), current_operand_scale());
121 }
122
GetUnsignedOperand(int operand_index,OperandType operand_type) const123 uint32_t BytecodeArrayAccessor::GetUnsignedOperand(
124 int operand_index, OperandType operand_type) const {
125 DCHECK_GE(operand_index, 0);
126 DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
127 DCHECK_EQ(operand_type,
128 Bytecodes::GetOperandType(current_bytecode(), operand_index));
129 DCHECK(Bytecodes::IsUnsignedOperandType(operand_type));
130 Address operand_start =
131 bytecode_array()->GetFirstBytecodeAddress() + bytecode_offset_ +
132 current_prefix_offset() +
133 Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
134 current_operand_scale());
135 return BytecodeDecoder::DecodeUnsignedOperand(operand_start, operand_type,
136 current_operand_scale());
137 }
138
GetSignedOperand(int operand_index,OperandType operand_type) const139 int32_t BytecodeArrayAccessor::GetSignedOperand(
140 int operand_index, OperandType operand_type) const {
141 DCHECK_GE(operand_index, 0);
142 DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
143 DCHECK_EQ(operand_type,
144 Bytecodes::GetOperandType(current_bytecode(), operand_index));
145 DCHECK(!Bytecodes::IsUnsignedOperandType(operand_type));
146 Address operand_start =
147 bytecode_array()->GetFirstBytecodeAddress() + bytecode_offset_ +
148 current_prefix_offset() +
149 Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
150 current_operand_scale());
151 return BytecodeDecoder::DecodeSignedOperand(operand_start, operand_type,
152 current_operand_scale());
153 }
154
GetFlagOperand(int operand_index) const155 uint32_t BytecodeArrayAccessor::GetFlagOperand(int operand_index) const {
156 DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
157 OperandType::kFlag8);
158 return GetUnsignedOperand(operand_index, OperandType::kFlag8);
159 }
160
GetUnsignedImmediateOperand(int operand_index) const161 uint32_t BytecodeArrayAccessor::GetUnsignedImmediateOperand(
162 int operand_index) const {
163 DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
164 OperandType::kUImm);
165 return GetUnsignedOperand(operand_index, OperandType::kUImm);
166 }
167
GetImmediateOperand(int operand_index) const168 int32_t BytecodeArrayAccessor::GetImmediateOperand(int operand_index) const {
169 DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
170 OperandType::kImm);
171 return GetSignedOperand(operand_index, OperandType::kImm);
172 }
173
GetRegisterCountOperand(int operand_index) const174 uint32_t BytecodeArrayAccessor::GetRegisterCountOperand(
175 int operand_index) const {
176 DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
177 OperandType::kRegCount);
178 return GetUnsignedOperand(operand_index, OperandType::kRegCount);
179 }
180
GetIndexOperand(int operand_index) const181 uint32_t BytecodeArrayAccessor::GetIndexOperand(int operand_index) const {
182 OperandType operand_type =
183 Bytecodes::GetOperandType(current_bytecode(), operand_index);
184 DCHECK_EQ(operand_type, OperandType::kIdx);
185 return GetUnsignedOperand(operand_index, operand_type);
186 }
187
GetSlotOperand(int operand_index) const188 FeedbackSlot BytecodeArrayAccessor::GetSlotOperand(int operand_index) const {
189 int index = GetIndexOperand(operand_index);
190 return FeedbackVector::ToSlot(index);
191 }
192
GetReceiver() const193 Register BytecodeArrayAccessor::GetReceiver() const {
194 return Register::FromParameterIndex(0, bytecode_array()->parameter_count());
195 }
196
GetParameter(int parameter_index) const197 Register BytecodeArrayAccessor::GetParameter(int parameter_index) const {
198 DCHECK_GE(parameter_index, 0);
199 // The parameter indices are shifted by 1 (receiver is the
200 // first entry).
201 return Register::FromParameterIndex(parameter_index + 1,
202 bytecode_array()->parameter_count());
203 }
204
GetRegisterOperand(int operand_index) const205 Register BytecodeArrayAccessor::GetRegisterOperand(int operand_index) const {
206 OperandType operand_type =
207 Bytecodes::GetOperandType(current_bytecode(), operand_index);
208 Address operand_start =
209 bytecode_array()->GetFirstBytecodeAddress() + bytecode_offset_ +
210 current_prefix_offset() +
211 Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
212 current_operand_scale());
213 return BytecodeDecoder::DecodeRegisterOperand(operand_start, operand_type,
214 current_operand_scale());
215 }
216
GetRegisterOperandRange(int operand_index) const217 int BytecodeArrayAccessor::GetRegisterOperandRange(int operand_index) const {
218 DCHECK_LE(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
219 const OperandType* operand_types =
220 Bytecodes::GetOperandTypes(current_bytecode());
221 OperandType operand_type = operand_types[operand_index];
222 DCHECK(Bytecodes::IsRegisterOperandType(operand_type));
223 if (operand_type == OperandType::kRegList ||
224 operand_type == OperandType::kRegOutList) {
225 return GetRegisterCountOperand(operand_index + 1);
226 } else {
227 return Bytecodes::GetNumberOfRegistersRepresentedBy(operand_type);
228 }
229 }
230
GetRuntimeIdOperand(int operand_index) const231 Runtime::FunctionId BytecodeArrayAccessor::GetRuntimeIdOperand(
232 int operand_index) const {
233 OperandType operand_type =
234 Bytecodes::GetOperandType(current_bytecode(), operand_index);
235 DCHECK_EQ(operand_type, OperandType::kRuntimeId);
236 uint32_t raw_id = GetUnsignedOperand(operand_index, operand_type);
237 return static_cast<Runtime::FunctionId>(raw_id);
238 }
239
GetNativeContextIndexOperand(int operand_index) const240 uint32_t BytecodeArrayAccessor::GetNativeContextIndexOperand(
241 int operand_index) const {
242 OperandType operand_type =
243 Bytecodes::GetOperandType(current_bytecode(), operand_index);
244 DCHECK_EQ(operand_type, OperandType::kNativeContextIndex);
245 return GetUnsignedOperand(operand_index, operand_type);
246 }
247
GetIntrinsicIdOperand(int operand_index) const248 Runtime::FunctionId BytecodeArrayAccessor::GetIntrinsicIdOperand(
249 int operand_index) const {
250 OperandType operand_type =
251 Bytecodes::GetOperandType(current_bytecode(), operand_index);
252 DCHECK_EQ(operand_type, OperandType::kIntrinsicId);
253 uint32_t raw_id = GetUnsignedOperand(operand_index, operand_type);
254 return IntrinsicsHelper::ToRuntimeId(
255 static_cast<IntrinsicsHelper::IntrinsicId>(raw_id));
256 }
257
GetConstantAtIndex(int index,Isolate * isolate) const258 Handle<Object> BytecodeArrayAccessor::GetConstantAtIndex(
259 int index, Isolate* isolate) const {
260 return bytecode_array()->GetConstantAtIndex(index, isolate);
261 }
262
IsConstantAtIndexSmi(int index) const263 bool BytecodeArrayAccessor::IsConstantAtIndexSmi(int index) const {
264 return bytecode_array()->IsConstantAtIndexSmi(index);
265 }
266
GetConstantAtIndexAsSmi(int index) const267 Smi BytecodeArrayAccessor::GetConstantAtIndexAsSmi(int index) const {
268 return bytecode_array()->GetConstantAtIndexAsSmi(index);
269 }
270
GetConstantForIndexOperand(int operand_index,Isolate * isolate) const271 Handle<Object> BytecodeArrayAccessor::GetConstantForIndexOperand(
272 int operand_index, Isolate* isolate) const {
273 return GetConstantAtIndex(GetIndexOperand(operand_index), isolate);
274 }
275
GetRelativeJumpTargetOffset() const276 int BytecodeArrayAccessor::GetRelativeJumpTargetOffset() const {
277 Bytecode bytecode = current_bytecode();
278 if (interpreter::Bytecodes::IsJumpImmediate(bytecode)) {
279 int relative_offset = GetUnsignedImmediateOperand(0);
280 if (bytecode == Bytecode::kJumpLoop) {
281 relative_offset = -relative_offset;
282 }
283 return relative_offset;
284 } else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
285 Smi smi = GetConstantAtIndexAsSmi(GetIndexOperand(0));
286 return smi.value();
287 } else {
288 UNREACHABLE();
289 }
290 }
291
GetJumpTargetOffset() const292 int BytecodeArrayAccessor::GetJumpTargetOffset() const {
293 return GetAbsoluteOffset(GetRelativeJumpTargetOffset());
294 }
295
GetJumpTableTargetOffsets() const296 JumpTableTargetOffsets BytecodeArrayAccessor::GetJumpTableTargetOffsets()
297 const {
298 uint32_t table_start, table_size;
299 int32_t case_value_base;
300 if (current_bytecode() == Bytecode::kSwitchOnGeneratorState) {
301 table_start = GetIndexOperand(1);
302 table_size = GetUnsignedImmediateOperand(2);
303 case_value_base = 0;
304 } else {
305 DCHECK_EQ(current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
306 table_start = GetIndexOperand(0);
307 table_size = GetUnsignedImmediateOperand(1);
308 case_value_base = GetImmediateOperand(2);
309 }
310 return JumpTableTargetOffsets(this, table_start, table_size, case_value_base);
311 }
312
GetAbsoluteOffset(int relative_offset) const313 int BytecodeArrayAccessor::GetAbsoluteOffset(int relative_offset) const {
314 return current_offset() + relative_offset + current_prefix_offset();
315 }
316
OffsetWithinBytecode(int offset) const317 bool BytecodeArrayAccessor::OffsetWithinBytecode(int offset) const {
318 return current_offset() <= offset &&
319 offset < current_offset() + current_bytecode_size();
320 }
321
PrintTo(std::ostream & os) const322 std::ostream& BytecodeArrayAccessor::PrintTo(std::ostream& os) const {
323 const uint8_t* bytecode_addr = reinterpret_cast<const uint8_t*>(
324 bytecode_array()->GetFirstBytecodeAddress() + bytecode_offset_);
325 return BytecodeDecoder::Decode(os, bytecode_addr,
326 bytecode_array()->parameter_count());
327 }
328
JumpTableTargetOffsets(const BytecodeArrayAccessor * accessor,int table_start,int table_size,int case_value_base)329 JumpTableTargetOffsets::JumpTableTargetOffsets(
330 const BytecodeArrayAccessor* accessor, int table_start, int table_size,
331 int case_value_base)
332 : accessor_(accessor),
333 table_start_(table_start),
334 table_size_(table_size),
335 case_value_base_(case_value_base) {}
336
begin() const337 JumpTableTargetOffsets::iterator JumpTableTargetOffsets::begin() const {
338 return iterator(case_value_base_, table_start_, table_start_ + table_size_,
339 accessor_);
340 }
end() const341 JumpTableTargetOffsets::iterator JumpTableTargetOffsets::end() const {
342 return iterator(case_value_base_ + table_size_, table_start_ + table_size_,
343 table_start_ + table_size_, accessor_);
344 }
size() const345 int JumpTableTargetOffsets::size() const {
346 int ret = 0;
347 // TODO(leszeks): Is there a more efficient way of doing this than iterating?
348 for (const auto& entry : *this) {
349 USE(entry);
350 ret++;
351 }
352 return ret;
353 }
354
iterator(int case_value,int table_offset,int table_end,const BytecodeArrayAccessor * accessor)355 JumpTableTargetOffsets::iterator::iterator(
356 int case_value, int table_offset, int table_end,
357 const BytecodeArrayAccessor* accessor)
358 : accessor_(accessor),
359 current_(Smi::zero()),
360 index_(case_value),
361 table_offset_(table_offset),
362 table_end_(table_end) {
363 UpdateAndAdvanceToValid();
364 }
365
operator *()366 JumpTableTargetOffset JumpTableTargetOffsets::iterator::operator*() {
367 DCHECK_LT(table_offset_, table_end_);
368 return {index_, accessor_->GetAbsoluteOffset(Smi::ToInt(current_))};
369 }
370
371 JumpTableTargetOffsets::iterator& JumpTableTargetOffsets::iterator::
operator ++()372 operator++() {
373 DCHECK_LT(table_offset_, table_end_);
374 ++table_offset_;
375 ++index_;
376 UpdateAndAdvanceToValid();
377 return *this;
378 }
379
operator !=(const JumpTableTargetOffsets::iterator & other)380 bool JumpTableTargetOffsets::iterator::operator!=(
381 const JumpTableTargetOffsets::iterator& other) {
382 DCHECK_EQ(accessor_, other.accessor_);
383 DCHECK_EQ(table_end_, other.table_end_);
384 DCHECK_EQ(index_ - other.index_, table_offset_ - other.table_offset_);
385 return index_ != other.index_;
386 }
387
UpdateAndAdvanceToValid()388 void JumpTableTargetOffsets::iterator::UpdateAndAdvanceToValid() {
389 while (table_offset_ < table_end_ &&
390 !accessor_->IsConstantAtIndexSmi(table_offset_)) {
391 ++table_offset_;
392 ++index_;
393 }
394
395 // Make sure we haven't reached the end of the table with a hole in current.
396 if (table_offset_ < table_end_) {
397 DCHECK(accessor_->IsConstantAtIndexSmi(table_offset_));
398 current_ = accessor_->GetConstantAtIndexAsSmi(table_offset_);
399 }
400 }
401
402 } // namespace interpreter
403 } // namespace internal
404 } // namespace v8
405