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