blob: fd27f391aa93636829fd1d689e98ca79aa15b7c0 [file] [log] [blame]
// 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/bytecodes.h"
#include <iomanip>
#include "src/frames.h"
#include "src/interpreter/bytecode-traits.h"
#include "src/interpreter/interpreter.h"
namespace v8 {
namespace internal {
namespace interpreter {
// static
const char* Bytecodes::ToString(Bytecode bytecode) {
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
return #Name;
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return "";
}
// static
std::string Bytecodes::ToString(Bytecode bytecode, OperandScale operand_scale) {
static const char kSeparator = '.';
std::string value(ToString(bytecode));
if (operand_scale > OperandScale::kSingle) {
Bytecode prefix_bytecode = OperandScaleToPrefixBytecode(operand_scale);
std::string suffix = ToString(prefix_bytecode);
return value.append(1, kSeparator).append(suffix);
} else {
return value;
}
}
// static
const char* Bytecodes::AccumulatorUseToString(AccumulatorUse accumulator_use) {
switch (accumulator_use) {
case AccumulatorUse::kNone:
return "None";
case AccumulatorUse::kRead:
return "Read";
case AccumulatorUse::kWrite:
return "Write";
case AccumulatorUse::kReadWrite:
return "ReadWrite";
}
UNREACHABLE();
return "";
}
// static
const char* Bytecodes::OperandTypeToString(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return #Name;
OPERAND_TYPE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return "";
}
// static
const char* Bytecodes::OperandScaleToString(OperandScale operand_scale) {
switch (operand_scale) {
case OperandScale::kSingle:
return "Single";
case OperandScale::kDouble:
return "Double";
case OperandScale::kQuadruple:
return "Quadruple";
case OperandScale::kInvalid:
UNREACHABLE();
}
return "";
}
// static
const char* Bytecodes::OperandSizeToString(OperandSize operand_size) {
switch (operand_size) {
case OperandSize::kNone:
return "None";
case OperandSize::kByte:
return "Byte";
case OperandSize::kShort:
return "Short";
case OperandSize::kQuad:
return "Quad";
}
UNREACHABLE();
return "";
}
// static
uint8_t Bytecodes::ToByte(Bytecode bytecode) {
DCHECK(bytecode <= Bytecode::kLast);
return static_cast<uint8_t>(bytecode);
}
// static
Bytecode Bytecodes::FromByte(uint8_t value) {
Bytecode bytecode = static_cast<Bytecode>(value);
DCHECK(bytecode <= Bytecode::kLast);
return bytecode;
}
// static
Bytecode Bytecodes::GetDebugBreak(Bytecode bytecode) {
DCHECK(!IsDebugBreak(bytecode));
if (bytecode == Bytecode::kWide) {
return Bytecode::kDebugBreakWide;
}
if (bytecode == Bytecode::kExtraWide) {
return Bytecode::kDebugBreakExtraWide;
}
int bytecode_size = Size(bytecode, OperandScale::kSingle);
#define RETURN_IF_DEBUG_BREAK_SIZE_MATCHES(Name, ...) \
if (bytecode_size == Size(Bytecode::k##Name, OperandScale::kSingle)) { \
return Bytecode::k##Name; \
}
DEBUG_BREAK_PLAIN_BYTECODE_LIST(RETURN_IF_DEBUG_BREAK_SIZE_MATCHES)
#undef RETURN_IF_DEBUG_BREAK_SIZE_MATCHES
UNREACHABLE();
return Bytecode::kIllegal;
}
// static
int Bytecodes::Size(Bytecode bytecode, OperandScale operand_scale) {
int size = 1;
for (int i = 0; i < NumberOfOperands(bytecode); i++) {
OperandSize operand_size = GetOperandSize(bytecode, i, operand_scale);
int delta = static_cast<int>(operand_size);
DCHECK(base::bits::IsPowerOfTwo32(static_cast<uint32_t>(delta)));
size += delta;
}
return size;
}
// static
int Bytecodes::NumberOfOperands(Bytecode bytecode) {
DCHECK(bytecode <= Bytecode::kLast);
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
return BytecodeTraits<__VA_ARGS__>::kOperandCount;
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return 0;
}
// static
int Bytecodes::NumberOfRegisterOperands(Bytecode bytecode) {
DCHECK(bytecode <= Bytecode::kLast);
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
typedef BytecodeTraits<__VA_ARGS__> Name##Trait; \
return Name##Trait::kRegisterOperandCount;
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return false;
}
// static
Bytecode Bytecodes::OperandScaleToPrefixBytecode(OperandScale operand_scale) {
switch (operand_scale) {
case OperandScale::kQuadruple:
return Bytecode::kExtraWide;
case OperandScale::kDouble:
return Bytecode::kWide;
default:
UNREACHABLE();
return Bytecode::kIllegal;
}
}
// static
bool Bytecodes::OperandScaleRequiresPrefixBytecode(OperandScale operand_scale) {
return operand_scale != OperandScale::kSingle;
}
// static
OperandScale Bytecodes::PrefixBytecodeToOperandScale(Bytecode bytecode) {
switch (bytecode) {
case Bytecode::kExtraWide:
case Bytecode::kDebugBreakExtraWide:
return OperandScale::kQuadruple;
case Bytecode::kWide:
case Bytecode::kDebugBreakWide:
return OperandScale::kDouble;
default:
UNREACHABLE();
return OperandScale::kSingle;
}
}
// static
AccumulatorUse Bytecodes::GetAccumulatorUse(Bytecode bytecode) {
DCHECK(bytecode <= Bytecode::kLast);
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
return BytecodeTraits<__VA_ARGS__>::kAccumulatorUse;
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return AccumulatorUse::kNone;
}
// static
bool Bytecodes::ReadsAccumulator(Bytecode bytecode) {
return (GetAccumulatorUse(bytecode) & AccumulatorUse::kRead) ==
AccumulatorUse::kRead;
}
// static
bool Bytecodes::WritesAccumulator(Bytecode bytecode) {
return (GetAccumulatorUse(bytecode) & AccumulatorUse::kWrite) ==
AccumulatorUse::kWrite;
}
// static
OperandType Bytecodes::GetOperandType(Bytecode bytecode, int i) {
DCHECK(bytecode <= Bytecode::kLast);
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
return BytecodeTraits<__VA_ARGS__>::GetOperandType(i);
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return OperandType::kNone;
}
// static
OperandSize Bytecodes::GetOperandSize(Bytecode bytecode, int i,
OperandScale operand_scale) {
OperandType op_type = GetOperandType(bytecode, i);
return ScaledOperandSize(op_type, operand_scale);
}
// static
int Bytecodes::GetRegisterOperandBitmap(Bytecode bytecode) {
DCHECK(bytecode <= Bytecode::kLast);
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
typedef BytecodeTraits<__VA_ARGS__> Name##Trait; \
return Name##Trait::kRegisterOperandBitmap;
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return false;
}
// static
int Bytecodes::GetOperandOffset(Bytecode bytecode, int i,
OperandScale operand_scale) {
// TODO(oth): restore this to a statically determined constant.
int offset = 1;
for (int operand_index = 0; operand_index < i; ++operand_index) {
OperandSize operand_size =
GetOperandSize(bytecode, operand_index, operand_scale);
offset += static_cast<int>(operand_size);
}
return offset;
}
// static
OperandSize Bytecodes::SizeOfOperand(OperandType operand_type,
OperandScale operand_scale) {
return static_cast<OperandSize>(
ScaledOperandSize(operand_type, operand_scale));
}
// static
bool Bytecodes::IsConditionalJumpImmediate(Bytecode bytecode) {
return bytecode == Bytecode::kJumpIfTrue ||
bytecode == Bytecode::kJumpIfFalse ||
bytecode == Bytecode::kJumpIfToBooleanTrue ||
bytecode == Bytecode::kJumpIfToBooleanFalse ||
bytecode == Bytecode::kJumpIfNotHole ||
bytecode == Bytecode::kJumpIfNull ||
bytecode == Bytecode::kJumpIfUndefined;
}
// static
bool Bytecodes::IsConditionalJumpConstant(Bytecode bytecode) {
return bytecode == Bytecode::kJumpIfTrueConstant ||
bytecode == Bytecode::kJumpIfFalseConstant ||
bytecode == Bytecode::kJumpIfToBooleanTrueConstant ||
bytecode == Bytecode::kJumpIfToBooleanFalseConstant ||
bytecode == Bytecode::kJumpIfNotHoleConstant ||
bytecode == Bytecode::kJumpIfNullConstant ||
bytecode == Bytecode::kJumpIfUndefinedConstant;
}
// static
bool Bytecodes::IsConditionalJump(Bytecode bytecode) {
return IsConditionalJumpImmediate(bytecode) ||
IsConditionalJumpConstant(bytecode);
}
// static
bool Bytecodes::IsJumpImmediate(Bytecode bytecode) {
return bytecode == Bytecode::kJump || IsConditionalJumpImmediate(bytecode);
}
// static
bool Bytecodes::IsJumpConstant(Bytecode bytecode) {
return bytecode == Bytecode::kJumpConstant ||
IsConditionalJumpConstant(bytecode);
}
// static
bool Bytecodes::IsJump(Bytecode bytecode) {
return IsJumpImmediate(bytecode) || IsJumpConstant(bytecode);
}
// static
bool Bytecodes::IsCallOrNew(Bytecode bytecode) {
return bytecode == Bytecode::kCall || bytecode == Bytecode::kTailCall ||
bytecode == Bytecode::kNew;
}
// static
bool Bytecodes::IsCallRuntime(Bytecode bytecode) {
return bytecode == Bytecode::kCallRuntime ||
bytecode == Bytecode::kCallRuntimeForPair ||
bytecode == Bytecode::kInvokeIntrinsic;
}
// static
bool Bytecodes::IsDebugBreak(Bytecode bytecode) {
switch (bytecode) {
#define CASE(Name, ...) case Bytecode::k##Name:
DEBUG_BREAK_BYTECODE_LIST(CASE);
#undef CASE
return true;
default:
break;
}
return false;
}
// static
bool Bytecodes::IsBytecodeWithScalableOperands(Bytecode bytecode) {
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
typedef BytecodeTraits<__VA_ARGS__> Name##Trait; \
return Name##Trait::IsScalable();
BYTECODE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return false;
}
// static
bool Bytecodes::IsPrefixScalingBytecode(Bytecode bytecode) {
switch (bytecode) {
case Bytecode::kExtraWide:
case Bytecode::kDebugBreakExtraWide:
case Bytecode::kWide:
case Bytecode::kDebugBreakWide:
return true;
default:
return false;
}
}
// static
bool Bytecodes::IsJumpOrReturn(Bytecode bytecode) {
return bytecode == Bytecode::kReturn || IsJump(bytecode);
}
// static
bool Bytecodes::IsMaybeRegisterOperandType(OperandType operand_type) {
return operand_type == OperandType::kMaybeReg;
}
// static
bool Bytecodes::IsRegisterOperandType(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return true;
REGISTER_OPERAND_TYPE_LIST(CASE)
#undef CASE
#define CASE(Name, _) \
case OperandType::k##Name: \
break;
NON_REGISTER_OPERAND_TYPE_LIST(CASE)
#undef CASE
}
return false;
}
// static
bool Bytecodes::IsRegisterInputOperandType(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return true;
REGISTER_INPUT_OPERAND_TYPE_LIST(CASE)
#undef CASE
#define CASE(Name, _) \
case OperandType::k##Name: \
break;
NON_REGISTER_OPERAND_TYPE_LIST(CASE)
REGISTER_OUTPUT_OPERAND_TYPE_LIST(CASE)
#undef CASE
}
return false;
}
// static
bool Bytecodes::IsRegisterOutputOperandType(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return true;
REGISTER_OUTPUT_OPERAND_TYPE_LIST(CASE)
#undef CASE
#define CASE(Name, _) \
case OperandType::k##Name: \
break;
NON_REGISTER_OPERAND_TYPE_LIST(CASE)
REGISTER_INPUT_OPERAND_TYPE_LIST(CASE)
#undef CASE
}
return false;
}
// static
bool Bytecodes::IsUnsignedOperandType(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return OperandTraits<OperandType::k##Name>::TypeInfo::kIsUnsigned;
OPERAND_TYPE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
return false;
}
// static
OperandScale Bytecodes::NextOperandScale(OperandScale operand_scale) {
DCHECK(operand_scale >= OperandScale::kSingle &&
operand_scale <= OperandScale::kMaxValid);
return static_cast<OperandScale>(2 * static_cast<int>(operand_scale));
}
// static
Register Bytecodes::DecodeRegisterOperand(const uint8_t* 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
int32_t Bytecodes::DecodeSignedOperand(const uint8_t* operand_start,
OperandType operand_type,
OperandScale operand_scale) {
DCHECK(!Bytecodes::IsUnsignedOperandType(operand_type));
switch (Bytecodes::SizeOfOperand(operand_type, operand_scale)) {
case OperandSize::kByte:
return static_cast<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 Bytecodes::DecodeUnsignedOperand(const uint8_t* operand_start,
OperandType operand_type,
OperandScale operand_scale) {
DCHECK(Bytecodes::IsUnsignedOperandType(operand_type));
switch (Bytecodes::SizeOfOperand(operand_type, operand_scale)) {
case OperandSize::kByte:
return *operand_start;
case OperandSize::kShort:
return ReadUnalignedUInt16(operand_start);
case OperandSize::kQuad:
return ReadUnalignedUInt32(operand_start);
case OperandSize::kNone:
UNREACHABLE();
}
return 0;
}
// static
std::ostream& Bytecodes::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 (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 (IsDebugBreak(bytecode)) return os;
int number_of_operands = NumberOfOperands(bytecode);
int range = 0;
for (int i = 0; i < number_of_operands; i++) {
OperandType op_type = GetOperandType(bytecode, i);
const uint8_t* operand_start =
&bytecode_start[prefix_offset +
GetOperandOffset(bytecode, i, operand_scale)];
switch (op_type) {
case interpreter::OperandType::kRegCount:
os << "#"
<< DecodeUnsignedOperand(operand_start, op_type, operand_scale);
break;
case interpreter::OperandType::kIdx:
case interpreter::OperandType::kRuntimeId:
os << "["
<< 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::kMaybeReg:
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:
range += 1;
case interpreter::OperandType::kRegOutPair:
case interpreter::OperandType::kRegPair: {
range += 1;
Register first_reg =
DecodeRegisterOperand(operand_start, op_type, operand_scale);
Register last_reg = Register(first_reg.index() + range);
os << first_reg.ToString(parameter_count) << "-"
<< last_reg.ToString(parameter_count);
break;
}
case interpreter::OperandType::kNone:
UNREACHABLE();
break;
}
if (i != number_of_operands - 1) {
os << ", ";
}
}
return os;
}
// static
bool Bytecodes::BytecodeHasHandler(Bytecode bytecode,
OperandScale operand_scale) {
return operand_scale == OperandScale::kSingle ||
Bytecodes::IsBytecodeWithScalableOperands(bytecode);
}
std::ostream& operator<<(std::ostream& os, const Bytecode& bytecode) {
return os << Bytecodes::ToString(bytecode);
}
std::ostream& operator<<(std::ostream& os, const AccumulatorUse& use) {
return os << Bytecodes::AccumulatorUseToString(use);
}
std::ostream& operator<<(std::ostream& os, const OperandSize& operand_size) {
return os << Bytecodes::OperandSizeToString(operand_size);
}
std::ostream& operator<<(std::ostream& os, const OperandScale& operand_scale) {
return os << Bytecodes::OperandScaleToString(operand_scale);
}
std::ostream& operator<<(std::ostream& os, const OperandType& operand_type) {
return os << Bytecodes::OperandTypeToString(operand_type);
}
static const int kLastParamRegisterIndex =
-InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
static const int kFunctionClosureRegisterIndex =
-InterpreterFrameConstants::kFunctionFromRegisterPointer / kPointerSize;
static const int kCurrentContextRegisterIndex =
-InterpreterFrameConstants::kContextFromRegisterPointer / kPointerSize;
static const int kNewTargetRegisterIndex =
-InterpreterFrameConstants::kNewTargetFromRegisterPointer / kPointerSize;
bool Register::is_byte_operand() const {
return index_ >= -kMaxInt8 && index_ <= -kMinInt8;
}
bool Register::is_short_operand() const {
return index_ >= -kMaxInt16 && index_ <= -kMinInt16;
}
Register Register::FromParameterIndex(int index, int parameter_count) {
DCHECK_GE(index, 0);
DCHECK_LT(index, parameter_count);
int register_index = kLastParamRegisterIndex - parameter_count + index + 1;
DCHECK_LT(register_index, 0);
return Register(register_index);
}
int Register::ToParameterIndex(int parameter_count) const {
DCHECK(is_parameter());
return index() - kLastParamRegisterIndex + parameter_count - 1;
}
Register Register::function_closure() {
return Register(kFunctionClosureRegisterIndex);
}
bool Register::is_function_closure() const {
return index() == kFunctionClosureRegisterIndex;
}
Register Register::current_context() {
return Register(kCurrentContextRegisterIndex);
}
bool Register::is_current_context() const {
return index() == kCurrentContextRegisterIndex;
}
Register Register::new_target() { return Register(kNewTargetRegisterIndex); }
bool Register::is_new_target() const {
return index() == kNewTargetRegisterIndex;
}
bool Register::AreContiguous(Register reg1, Register reg2, Register reg3,
Register reg4, Register reg5) {
if (reg1.index() + 1 != reg2.index()) {
return false;
}
if (reg3.is_valid() && reg2.index() + 1 != reg3.index()) {
return false;
}
if (reg4.is_valid() && reg3.index() + 1 != reg4.index()) {
return false;
}
if (reg5.is_valid() && reg4.index() + 1 != reg5.index()) {
return false;
}
return true;
}
std::string Register::ToString(int parameter_count) {
if (is_current_context()) {
return std::string("<context>");
} else if (is_function_closure()) {
return std::string("<closure>");
} else if (is_new_target()) {
return std::string("<new.target>");
} else if (is_parameter()) {
int parameter_index = ToParameterIndex(parameter_count);
if (parameter_index == 0) {
return std::string("<this>");
} else {
std::ostringstream s;
s << "a" << parameter_index - 1;
return s.str();
}
} else {
std::ostringstream s;
s << "r" << index();
return s.str();
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8