| // Copyright 2015 the V8 project authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/interpreter/bytecode-decoder.h" | |
| #include <iomanip> | |
| #include "src/contexts.h" | |
| #include "src/interpreter/interpreter-intrinsics.h" | |
| #include "src/objects-inl.h" | |
| namespace v8 { | |
| namespace internal { | |
| namespace interpreter { | |
| // static | |
| Register BytecodeDecoder::DecodeRegisterOperand(Address operand_start, | |
| OperandType operand_type, | |
| OperandScale operand_scale) { | |
| DCHECK(Bytecodes::IsRegisterOperandType(operand_type)); | |
| int32_t operand = | |
| DecodeSignedOperand(operand_start, operand_type, operand_scale); | |
| return Register::FromOperand(operand); | |
| } | |
| // static | |
| RegisterList BytecodeDecoder::DecodeRegisterListOperand( | |
| Address operand_start, uint32_t count, OperandType operand_type, | |
| OperandScale operand_scale) { | |
| Register first_reg = | |
| DecodeRegisterOperand(operand_start, operand_type, operand_scale); | |
| return RegisterList(first_reg.index(), static_cast<int>(count)); | |
| } | |
| // static | |
| int32_t BytecodeDecoder::DecodeSignedOperand(Address operand_start, | |
| OperandType operand_type, | |
| OperandScale operand_scale) { | |
| DCHECK(!Bytecodes::IsUnsignedOperandType(operand_type)); | |
| switch (Bytecodes::SizeOfOperand(operand_type, operand_scale)) { | |
| case OperandSize::kByte: | |
| return *reinterpret_cast<const int8_t*>(operand_start); | |
| case OperandSize::kShort: | |
| return static_cast<int16_t>(ReadUnalignedUInt16(operand_start)); | |
| case OperandSize::kQuad: | |
| return static_cast<int32_t>(ReadUnalignedUInt32(operand_start)); | |
| case OperandSize::kNone: | |
| UNREACHABLE(); | |
| } | |
| return 0; | |
| } | |
| // static | |
| uint32_t BytecodeDecoder::DecodeUnsignedOperand(Address operand_start, | |
| OperandType operand_type, | |
| OperandScale operand_scale) { | |
| DCHECK(Bytecodes::IsUnsignedOperandType(operand_type)); | |
| switch (Bytecodes::SizeOfOperand(operand_type, operand_scale)) { | |
| case OperandSize::kByte: | |
| return *reinterpret_cast<const uint8_t*>(operand_start); | |
| case OperandSize::kShort: | |
| return ReadUnalignedUInt16(operand_start); | |
| case OperandSize::kQuad: | |
| return ReadUnalignedUInt32(operand_start); | |
| case OperandSize::kNone: | |
| UNREACHABLE(); | |
| } | |
| return 0; | |
| } | |
| namespace { | |
| const char* NameForRuntimeId(uint32_t idx) { | |
| switch (idx) { | |
| #define CASE(name, nargs, ressize) \ | |
| case Runtime::k##name: \ | |
| return #name; \ | |
| case Runtime::kInline##name: \ | |
| return "_" #name; | |
| FOR_EACH_INTRINSIC(CASE) | |
| #undef CASE | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| const char* NameForNativeContextIndex(uint32_t idx) { | |
| switch (idx) { | |
| #define CASE(index_name, type, name) \ | |
| case Context::index_name: \ | |
| return #name; | |
| NATIVE_CONTEXT_FIELDS(CASE) | |
| #undef CASE | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| } // anonymous namespace | |
| // static | |
| std::ostream& BytecodeDecoder::Decode(std::ostream& os, | |
| const uint8_t* bytecode_start, | |
| int parameter_count) { | |
| Bytecode bytecode = Bytecodes::FromByte(bytecode_start[0]); | |
| int prefix_offset = 0; | |
| OperandScale operand_scale = OperandScale::kSingle; | |
| if (Bytecodes::IsPrefixScalingBytecode(bytecode)) { | |
| prefix_offset = 1; | |
| operand_scale = Bytecodes::PrefixBytecodeToOperandScale(bytecode); | |
| bytecode = Bytecodes::FromByte(bytecode_start[1]); | |
| } | |
| // Prepare to print bytecode and operands as hex digits. | |
| std::ios saved_format(nullptr); | |
| saved_format.copyfmt(saved_format); | |
| os.fill('0'); | |
| os.flags(std::ios::hex); | |
| int bytecode_size = Bytecodes::Size(bytecode, operand_scale); | |
| for (int i = 0; i < prefix_offset + bytecode_size; i++) { | |
| os << std::setw(2) << static_cast<uint32_t>(bytecode_start[i]) << ' '; | |
| } | |
| os.copyfmt(saved_format); | |
| const int kBytecodeColumnSize = 6; | |
| for (int i = prefix_offset + bytecode_size; i < kBytecodeColumnSize; i++) { | |
| os << " "; | |
| } | |
| os << Bytecodes::ToString(bytecode, operand_scale) << " "; | |
| // Operands for the debug break are from the original instruction. | |
| if (Bytecodes::IsDebugBreak(bytecode)) return os; | |
| int number_of_operands = Bytecodes::NumberOfOperands(bytecode); | |
| for (int i = 0; i < number_of_operands; i++) { | |
| OperandType op_type = Bytecodes::GetOperandType(bytecode, i); | |
| int operand_offset = | |
| Bytecodes::GetOperandOffset(bytecode, i, operand_scale); | |
| Address operand_start = reinterpret_cast<Address>( | |
| &bytecode_start[prefix_offset + operand_offset]); | |
| switch (op_type) { | |
| case interpreter::OperandType::kIdx: | |
| case interpreter::OperandType::kUImm: | |
| os << "[" | |
| << DecodeUnsignedOperand(operand_start, op_type, operand_scale) | |
| << "]"; | |
| break; | |
| case interpreter::OperandType::kIntrinsicId: { | |
| auto id = static_cast<IntrinsicsHelper::IntrinsicId>( | |
| DecodeUnsignedOperand(operand_start, op_type, operand_scale)); | |
| os << "[" << NameForRuntimeId(IntrinsicsHelper::ToRuntimeId(id)) << "]"; | |
| break; | |
| } | |
| case interpreter::OperandType::kNativeContextIndex: { | |
| auto id = DecodeUnsignedOperand(operand_start, op_type, operand_scale); | |
| os << "[" << NameForNativeContextIndex(id) << "]"; | |
| break; | |
| } | |
| case interpreter::OperandType::kRuntimeId: | |
| os << "[" << NameForRuntimeId(DecodeUnsignedOperand( | |
| operand_start, op_type, operand_scale)) | |
| << "]"; | |
| break; | |
| case interpreter::OperandType::kImm: | |
| os << "[" << DecodeSignedOperand(operand_start, op_type, operand_scale) | |
| << "]"; | |
| break; | |
| case interpreter::OperandType::kFlag8: | |
| os << "#" | |
| << DecodeUnsignedOperand(operand_start, op_type, operand_scale); | |
| break; | |
| case interpreter::OperandType::kReg: | |
| case interpreter::OperandType::kRegOut: { | |
| Register reg = | |
| DecodeRegisterOperand(operand_start, op_type, operand_scale); | |
| os << reg.ToString(parameter_count); | |
| break; | |
| } | |
| case interpreter::OperandType::kRegOutTriple: { | |
| RegisterList reg_list = | |
| DecodeRegisterListOperand(operand_start, 3, op_type, operand_scale); | |
| os << reg_list.first_register().ToString(parameter_count) << "-" | |
| << reg_list.last_register().ToString(parameter_count); | |
| break; | |
| } | |
| case interpreter::OperandType::kRegOutPair: | |
| case interpreter::OperandType::kRegPair: { | |
| RegisterList reg_list = | |
| DecodeRegisterListOperand(operand_start, 2, op_type, operand_scale); | |
| os << reg_list.first_register().ToString(parameter_count) << "-" | |
| << reg_list.last_register().ToString(parameter_count); | |
| break; | |
| } | |
| case interpreter::OperandType::kRegOutList: | |
| case interpreter::OperandType::kRegList: { | |
| DCHECK_LT(i, number_of_operands - 1); | |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, i + 1), | |
| OperandType::kRegCount); | |
| int reg_count_offset = | |
| Bytecodes::GetOperandOffset(bytecode, i + 1, operand_scale); | |
| Address reg_count_operand = reinterpret_cast<Address>( | |
| &bytecode_start[prefix_offset + reg_count_offset]); | |
| uint32_t count = DecodeUnsignedOperand( | |
| reg_count_operand, OperandType::kRegCount, operand_scale); | |
| RegisterList reg_list = DecodeRegisterListOperand( | |
| operand_start, count, op_type, operand_scale); | |
| os << reg_list.first_register().ToString(parameter_count) << "-" | |
| << reg_list.last_register().ToString(parameter_count); | |
| i++; // Skip kRegCount. | |
| break; | |
| } | |
| case interpreter::OperandType::kNone: | |
| case interpreter::OperandType::kRegCount: // Dealt with in kRegList. | |
| UNREACHABLE(); | |
| break; | |
| } | |
| if (i != number_of_operands - 1) { | |
| os << ", "; | |
| } | |
| } | |
| return os; | |
| } | |
| } // namespace interpreter | |
| } // namespace internal | |
| } // namespace v8 |