blob: 4a1602a1d6ea6c3749cc74b30111c53a4cd1e730 [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/bytecode-array-builder.h"
namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeArrayBuilder::PreviousBytecodeHelper BASE_EMBEDDED {
public:
explicit PreviousBytecodeHelper(const BytecodeArrayBuilder& array_builder)
: array_builder_(array_builder),
previous_bytecode_start_(array_builder_.last_bytecode_start_) {
// This helper is expected to be instantiated only when the last bytecode is
// in the same basic block.
DCHECK(array_builder_.LastBytecodeInSameBlock());
}
// Returns the previous bytecode in the same basic block.
MUST_USE_RESULT Bytecode GetBytecode() const {
DCHECK_EQ(array_builder_.last_bytecode_start_, previous_bytecode_start_);
return Bytecodes::FromByte(
array_builder_.bytecodes()->at(previous_bytecode_start_));
}
// Returns the operand at operand_index for the previous bytecode in the
// same basic block.
MUST_USE_RESULT uint32_t GetOperand(int operand_index) const {
DCHECK_EQ(array_builder_.last_bytecode_start_, previous_bytecode_start_);
Bytecode bytecode = GetBytecode();
DCHECK_GE(operand_index, 0);
DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(bytecode));
size_t operand_offset =
previous_bytecode_start_ +
Bytecodes::GetOperandOffset(bytecode, operand_index);
OperandSize size = Bytecodes::GetOperandSize(bytecode, operand_index);
switch (size) {
case OperandSize::kNone:
UNREACHABLE();
break;
case OperandSize::kByte:
return static_cast<uint32_t>(
array_builder_.bytecodes()->at(operand_offset));
case OperandSize::kShort:
uint16_t operand =
(array_builder_.bytecodes()->at(operand_offset) << 8) +
array_builder_.bytecodes()->at(operand_offset + 1);
return static_cast<uint32_t>(operand);
}
return 0;
}
Handle<Object> GetConstantForIndexOperand(int operand_index) const {
return array_builder_.constant_array_builder()->At(
GetOperand(operand_index));
}
private:
const BytecodeArrayBuilder& array_builder_;
size_t previous_bytecode_start_;
DISALLOW_COPY_AND_ASSIGN(PreviousBytecodeHelper);
};
BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
: isolate_(isolate),
zone_(zone),
bytecodes_(zone),
bytecode_generated_(false),
constant_array_builder_(isolate, zone),
handler_table_builder_(isolate, zone),
last_block_end_(0),
last_bytecode_start_(~0),
exit_seen_in_block_(false),
unbound_jumps_(0),
parameter_count_(-1),
local_register_count_(-1),
context_register_count_(-1),
temporary_register_count_(0),
free_temporaries_(zone),
register_translator_(this) {}
BytecodeArrayBuilder::~BytecodeArrayBuilder() { DCHECK_EQ(0, unbound_jumps_); }
void BytecodeArrayBuilder::set_locals_count(int number_of_locals) {
local_register_count_ = number_of_locals;
DCHECK_LE(context_register_count_, 0);
}
void BytecodeArrayBuilder::set_parameter_count(int number_of_parameters) {
parameter_count_ = number_of_parameters;
}
void BytecodeArrayBuilder::set_context_count(int number_of_contexts) {
context_register_count_ = number_of_contexts;
DCHECK_GE(local_register_count_, 0);
}
Register BytecodeArrayBuilder::first_context_register() const {
DCHECK_GT(context_register_count_, 0);
return Register(local_register_count_);
}
Register BytecodeArrayBuilder::last_context_register() const {
DCHECK_GT(context_register_count_, 0);
return Register(local_register_count_ + context_register_count_ - 1);
}
Register BytecodeArrayBuilder::first_temporary_register() const {
DCHECK_GT(temporary_register_count_, 0);
return Register(fixed_register_count());
}
Register BytecodeArrayBuilder::last_temporary_register() const {
DCHECK_GT(temporary_register_count_, 0);
return Register(fixed_register_count() + temporary_register_count_ - 1);
}
Register BytecodeArrayBuilder::Parameter(int parameter_index) const {
DCHECK_GE(parameter_index, 0);
return Register::FromParameterIndex(parameter_index, parameter_count());
}
bool BytecodeArrayBuilder::RegisterIsParameterOrLocal(Register reg) const {
return reg.is_parameter() || reg.index() < locals_count();
}
bool BytecodeArrayBuilder::RegisterIsTemporary(Register reg) const {
return temporary_register_count_ > 0 && first_temporary_register() <= reg &&
reg <= last_temporary_register();
}
Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() {
DCHECK_EQ(bytecode_generated_, false);
EnsureReturn();
int bytecode_size = static_cast<int>(bytecodes_.size());
int register_count =
fixed_and_temporary_register_count() + translation_register_count();
int frame_size = register_count * kPointerSize;
Handle<FixedArray> constant_pool = constant_array_builder()->ToFixedArray();
Handle<FixedArray> handler_table = handler_table_builder()->ToHandlerTable();
Handle<BytecodeArray> output = isolate_->factory()->NewBytecodeArray(
bytecode_size, &bytecodes_.front(), frame_size, parameter_count(),
constant_pool);
output->set_handler_table(*handler_table);
bytecode_generated_ = true;
return output;
}
template <size_t N>
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t(&operands)[N]) {
// Don't output dead code.
if (exit_seen_in_block_) return;
int operand_count = static_cast<int>(N);
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count);
int register_operand_count = Bytecodes::NumberOfRegisterOperands(bytecode);
if (register_operand_count > 0) {
register_translator()->TranslateInputRegisters(bytecode, operands,
operand_count);
}
last_bytecode_start_ = bytecodes()->size();
bytecodes()->push_back(Bytecodes::ToByte(bytecode));
for (int i = 0; i < operand_count; i++) {
DCHECK(OperandIsValid(bytecode, i, operands[i]));
switch (Bytecodes::GetOperandSize(bytecode, i)) {
case OperandSize::kNone:
UNREACHABLE();
break;
case OperandSize::kByte:
bytecodes()->push_back(static_cast<uint8_t>(operands[i]));
break;
case OperandSize::kShort: {
uint8_t operand_bytes[2];
WriteUnalignedUInt16(operand_bytes, operands[i]);
bytecodes()->insert(bytecodes()->end(), operand_bytes,
operand_bytes + 2);
break;
}
}
}
if (register_operand_count > 0) {
register_translator()->TranslateOutputRegisters();
}
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2,
uint32_t operand3) {
uint32_t operands[] = {operand0, operand1, operand2, operand3};
Output(bytecode, operands);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2) {
uint32_t operands[] = {operand0, operand1, operand2};
Output(bytecode, operands);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0,
uint32_t operand1) {
uint32_t operands[] = {operand0, operand1};
Output(bytecode, operands);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0) {
uint32_t operands[] = {operand0};
Output(bytecode, operands);
}
void BytecodeArrayBuilder::Output(Bytecode bytecode) {
// Don't output dead code.
if (exit_seen_in_block_) return;
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 0);
last_bytecode_start_ = bytecodes()->size();
bytecodes()->push_back(Bytecodes::ToByte(bytecode));
}
BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op,
Register reg,
Strength strength) {
if (is_strong(strength)) {
UNIMPLEMENTED();
}
Output(BytecodeForBinaryOperation(op), reg.ToRawOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op,
Strength strength) {
if (is_strong(strength)) {
UNIMPLEMENTED();
}
Output(BytecodeForCountOperation(op));
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() {
Output(Bytecode::kLogicalNot);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() {
Output(Bytecode::kTypeOf);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(
Token::Value op, Register reg, Strength strength) {
if (is_strong(strength)) {
UNIMPLEMENTED();
}
Output(BytecodeForCompareOperation(op), reg.ToRawOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
v8::internal::Smi* smi) {
int32_t raw_smi = smi->value();
if (raw_smi == 0) {
Output(Bytecode::kLdaZero);
} else if (raw_smi >= -128 && raw_smi <= 127) {
Output(Bytecode::kLdaSmi8, static_cast<uint8_t>(raw_smi));
} else {
LoadLiteral(Handle<Object>(smi, isolate_));
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) {
size_t entry = GetConstantPoolEntry(object);
if (FitsInIdx8Operand(entry)) {
Output(Bytecode::kLdaConstant, static_cast<uint8_t>(entry));
} else if (FitsInIdx16Operand(entry)) {
Output(Bytecode::kLdaConstantWide, static_cast<uint16_t>(entry));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadUndefined() {
Output(Bytecode::kLdaUndefined);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNull() {
Output(Bytecode::kLdaNull);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTheHole() {
Output(Bytecode::kLdaTheHole);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTrue() {
Output(Bytecode::kLdaTrue);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadFalse() {
Output(Bytecode::kLdaFalse);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadBooleanConstant(bool value) {
if (value) {
LoadTrue();
} else {
LoadFalse();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
Register reg) {
if (!IsRegisterInAccumulator(reg)) {
Output(Bytecode::kLdar, reg.ToRawOperand());
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
Register reg) {
if (!IsRegisterInAccumulator(reg)) {
Output(Bytecode::kStar, reg.ToRawOperand());
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from,
Register to) {
DCHECK(from != to);
if (FitsInReg8Operand(from) && FitsInReg8Operand(to)) {
Output(Bytecode::kMov, from.ToRawOperand(), to.ToRawOperand());
} else if (FitsInReg16Operand(from) && FitsInReg16Operand(to)) {
Output(Bytecode::kMovWide, from.ToRawOperand(), to.ToRawOperand());
} else {
UNIMPLEMENTED();
}
return *this;
}
void BytecodeArrayBuilder::MoveRegisterUntranslated(Register from,
Register to) {
// Move bytecodes modify the stack. Checking validity is an
// essential mitigation against corrupting the stack.
if (FitsInReg8OperandUntranslated(from)) {
CHECK(RegisterIsValid(from, OperandType::kReg8) &&
RegisterIsValid(to, OperandType::kReg16));
} else if (FitsInReg8OperandUntranslated(to)) {
CHECK(RegisterIsValid(from, OperandType::kReg16) &&
RegisterIsValid(to, OperandType::kReg8));
} else {
UNIMPLEMENTED();
}
Output(Bytecode::kMovWide, from.ToRawOperand(), to.ToRawOperand());
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(
const Handle<String> name, int feedback_slot, LanguageMode language_mode,
TypeofMode typeof_mode) {
// TODO(rmcilroy): Potentially store language and typeof information in an
// operand rather than having extra bytecodes.
Bytecode bytecode = BytecodeForLoadGlobal(language_mode, typeof_mode);
size_t name_index = GetConstantPoolEntry(name);
if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) {
Output(bytecode, static_cast<uint8_t>(name_index),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInIdx16Operand(name_index) &&
FitsInIdx16Operand(feedback_slot)) {
Output(BytecodeForWideOperands(bytecode), static_cast<uint16_t>(name_index),
static_cast<uint16_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal(
const Handle<String> name, int feedback_slot, LanguageMode language_mode) {
Bytecode bytecode = BytecodeForStoreGlobal(language_mode);
size_t name_index = GetConstantPoolEntry(name);
if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) {
Output(bytecode, static_cast<uint8_t>(name_index),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInIdx16Operand(name_index) &&
FitsInIdx16Operand(feedback_slot)) {
Output(BytecodeForWideOperands(bytecode), static_cast<uint16_t>(name_index),
static_cast<uint16_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(Register context,
int slot_index) {
DCHECK(slot_index >= 0);
if (FitsInIdx8Operand(slot_index)) {
Output(Bytecode::kLdaContextSlot, context.ToRawOperand(),
static_cast<uint8_t>(slot_index));
} else if (FitsInIdx16Operand(slot_index)) {
Output(Bytecode::kLdaContextSlotWide, context.ToRawOperand(),
static_cast<uint16_t>(slot_index));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreContextSlot(Register context,
int slot_index) {
DCHECK(slot_index >= 0);
if (FitsInIdx8Operand(slot_index)) {
Output(Bytecode::kStaContextSlot, context.ToRawOperand(),
static_cast<uint8_t>(slot_index));
} else if (FitsInIdx16Operand(slot_index)) {
Output(Bytecode::kStaContextSlotWide, context.ToRawOperand(),
static_cast<uint16_t>(slot_index));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupSlot(
const Handle<String> name, TypeofMode typeof_mode) {
Bytecode bytecode = (typeof_mode == INSIDE_TYPEOF)
? Bytecode::kLdaLookupSlotInsideTypeof
: Bytecode::kLdaLookupSlot;
size_t name_index = GetConstantPoolEntry(name);
if (FitsInIdx8Operand(name_index)) {
Output(bytecode, static_cast<uint8_t>(name_index));
} else if (FitsInIdx16Operand(name_index)) {
Output(BytecodeForWideOperands(bytecode),
static_cast<uint16_t>(name_index));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreLookupSlot(
const Handle<String> name, LanguageMode language_mode) {
Bytecode bytecode = BytecodeForStoreLookupSlot(language_mode);
size_t name_index = GetConstantPoolEntry(name);
if (FitsInIdx8Operand(name_index)) {
Output(bytecode, static_cast<uint8_t>(name_index));
} else if (FitsInIdx16Operand(name_index)) {
Output(BytecodeForWideOperands(bytecode),
static_cast<uint16_t>(name_index));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
Register object, const Handle<String> name, int feedback_slot,
LanguageMode language_mode) {
Bytecode bytecode = BytecodeForLoadIC(language_mode);
size_t name_index = GetConstantPoolEntry(name);
if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) {
Output(bytecode, object.ToRawOperand(), static_cast<uint8_t>(name_index),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInIdx16Operand(name_index) &&
FitsInIdx16Operand(feedback_slot)) {
Output(BytecodeForWideOperands(bytecode), object.ToRawOperand(),
static_cast<uint16_t>(name_index),
static_cast<uint16_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty(
Register object, int feedback_slot, LanguageMode language_mode) {
Bytecode bytecode = BytecodeForKeyedLoadIC(language_mode);
if (FitsInIdx8Operand(feedback_slot)) {
Output(bytecode, object.ToRawOperand(),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInIdx16Operand(feedback_slot)) {
Output(BytecodeForWideOperands(bytecode), object.ToRawOperand(),
static_cast<uint16_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
Register object, const Handle<String> name, int feedback_slot,
LanguageMode language_mode) {
Bytecode bytecode = BytecodeForStoreIC(language_mode);
size_t name_index = GetConstantPoolEntry(name);
if (FitsInIdx8Operand(name_index) && FitsInIdx8Operand(feedback_slot)) {
Output(bytecode, object.ToRawOperand(), static_cast<uint8_t>(name_index),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInIdx16Operand(name_index) &&
FitsInIdx16Operand(feedback_slot)) {
Output(BytecodeForWideOperands(bytecode), object.ToRawOperand(),
static_cast<uint16_t>(name_index),
static_cast<uint16_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
Register object, Register key, int feedback_slot,
LanguageMode language_mode) {
Bytecode bytecode = BytecodeForKeyedStoreIC(language_mode);
if (FitsInIdx8Operand(feedback_slot)) {
Output(bytecode, object.ToRawOperand(), key.ToRawOperand(),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInIdx16Operand(feedback_slot)) {
Output(BytecodeForWideOperands(bytecode), object.ToRawOperand(),
key.ToRawOperand(), static_cast<uint16_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(
Handle<SharedFunctionInfo> shared_info, PretenureFlag tenured) {
size_t entry = GetConstantPoolEntry(shared_info);
DCHECK(FitsInImm8Operand(tenured));
if (FitsInIdx8Operand(entry)) {
Output(Bytecode::kCreateClosure, static_cast<uint8_t>(entry),
static_cast<uint8_t>(tenured));
} else if (FitsInIdx16Operand(entry)) {
Output(Bytecode::kCreateClosureWide, static_cast<uint16_t>(entry),
static_cast<uint8_t>(tenured));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
CreateArgumentsType type) {
// TODO(rmcilroy): Consider passing the type as a bytecode operand rather
// than having two different bytecodes once we have better support for
// branches in the InterpreterAssembler.
Bytecode bytecode = BytecodeForCreateArguments(type);
Output(bytecode);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
Handle<String> pattern, int literal_index, int flags) {
DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bits.
size_t pattern_entry = GetConstantPoolEntry(pattern);
if (FitsInIdx8Operand(literal_index) && FitsInIdx8Operand(pattern_entry)) {
Output(Bytecode::kCreateRegExpLiteral, static_cast<uint8_t>(pattern_entry),
static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags));
} else if (FitsInIdx16Operand(literal_index) &&
FitsInIdx16Operand(pattern_entry)) {
Output(Bytecode::kCreateRegExpLiteralWide,
static_cast<uint16_t>(pattern_entry),
static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
Handle<FixedArray> constant_elements, int literal_index, int flags) {
DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bits.
size_t constant_elements_entry = GetConstantPoolEntry(constant_elements);
if (FitsInIdx8Operand(literal_index) &&
FitsInIdx8Operand(constant_elements_entry)) {
Output(Bytecode::kCreateArrayLiteral,
static_cast<uint8_t>(constant_elements_entry),
static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags));
} else if (FitsInIdx16Operand(literal_index) &&
FitsInIdx16Operand(constant_elements_entry)) {
Output(Bytecode::kCreateArrayLiteralWide,
static_cast<uint16_t>(constant_elements_entry),
static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
Handle<FixedArray> constant_properties, int literal_index, int flags) {
DCHECK(FitsInImm8Operand(flags)); // Flags should fit in 8 bits.
size_t constant_properties_entry = GetConstantPoolEntry(constant_properties);
if (FitsInIdx8Operand(literal_index) &&
FitsInIdx8Operand(constant_properties_entry)) {
Output(Bytecode::kCreateObjectLiteral,
static_cast<uint8_t>(constant_properties_entry),
static_cast<uint8_t>(literal_index), static_cast<uint8_t>(flags));
} else if (FitsInIdx16Operand(literal_index) &&
FitsInIdx16Operand(constant_properties_entry)) {
Output(Bytecode::kCreateObjectLiteralWide,
static_cast<uint16_t>(constant_properties_entry),
static_cast<uint16_t>(literal_index), static_cast<uint8_t>(flags));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
Output(Bytecode::kPushContext, context.ToRawOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
Output(Bytecode::kPopContext, context.ToRawOperand());
return *this;
}
bool BytecodeArrayBuilder::NeedToBooleanCast() {
if (!LastBytecodeInSameBlock()) {
return true;
}
PreviousBytecodeHelper previous_bytecode(*this);
switch (previous_bytecode.GetBytecode()) {
// If the previous bytecode puts a boolean in the accumulator return true.
case Bytecode::kLdaTrue:
case Bytecode::kLdaFalse:
case Bytecode::kLogicalNot:
case Bytecode::kTestEqual:
case Bytecode::kTestNotEqual:
case Bytecode::kTestEqualStrict:
case Bytecode::kTestNotEqualStrict:
case Bytecode::kTestLessThan:
case Bytecode::kTestLessThanOrEqual:
case Bytecode::kTestGreaterThan:
case Bytecode::kTestGreaterThanOrEqual:
case Bytecode::kTestInstanceOf:
case Bytecode::kTestIn:
case Bytecode::kForInDone:
return false;
default:
return true;
}
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToJSObject() {
Output(Bytecode::kToObject);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() {
if (LastBytecodeInSameBlock()) {
PreviousBytecodeHelper previous_bytecode(*this);
switch (previous_bytecode.GetBytecode()) {
case Bytecode::kToName:
case Bytecode::kTypeOf:
return *this;
case Bytecode::kLdaConstantWide:
case Bytecode::kLdaConstant: {
Handle<Object> object = previous_bytecode.GetConstantForIndexOperand(0);
if (object->IsName()) return *this;
break;
}
default:
break;
}
}
Output(Bytecode::kToName);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToNumber() {
// TODO(rmcilroy): consider omitting if the preceeding bytecode always returns
// a number.
Output(Bytecode::kToNumber);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
if (label->is_forward_target()) {
// An earlier jump instruction refers to this label. Update it's location.
PatchJump(bytecodes()->end(), bytecodes()->begin() + label->offset());
// Now treat as if the label will only be back referred to.
}
label->bind_to(bytecodes()->size());
LeaveBasicBlock();
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target,
BytecodeLabel* label) {
DCHECK(!label->is_bound());
DCHECK(target.is_bound());
PatchJump(bytecodes()->begin() + target.offset(),
bytecodes()->begin() + label->offset());
label->bind_to(target.offset());
LeaveBasicBlock();
return *this;
}
// static
Bytecode BytecodeArrayBuilder::GetJumpWithConstantOperand(
Bytecode jump_bytecode) {
switch (jump_bytecode) {
case Bytecode::kJump:
return Bytecode::kJumpConstant;
case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfTrueConstant;
case Bytecode::kJumpIfFalse:
return Bytecode::kJumpIfFalseConstant;
case Bytecode::kJumpIfToBooleanTrue:
return Bytecode::kJumpIfToBooleanTrueConstant;
case Bytecode::kJumpIfToBooleanFalse:
return Bytecode::kJumpIfToBooleanFalseConstant;
case Bytecode::kJumpIfNull:
return Bytecode::kJumpIfNullConstant;
case Bytecode::kJumpIfUndefined:
return Bytecode::kJumpIfUndefinedConstant;
default:
UNREACHABLE();
return static_cast<Bytecode>(-1);
}
}
// static
Bytecode BytecodeArrayBuilder::GetJumpWithConstantWideOperand(
Bytecode jump_bytecode) {
switch (jump_bytecode) {
case Bytecode::kJump:
return Bytecode::kJumpConstantWide;
case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfTrueConstantWide;
case Bytecode::kJumpIfFalse:
return Bytecode::kJumpIfFalseConstantWide;
case Bytecode::kJumpIfToBooleanTrue:
return Bytecode::kJumpIfToBooleanTrueConstantWide;
case Bytecode::kJumpIfToBooleanFalse:
return Bytecode::kJumpIfToBooleanFalseConstantWide;
case Bytecode::kJumpIfNull:
return Bytecode::kJumpIfNullConstantWide;
case Bytecode::kJumpIfUndefined:
return Bytecode::kJumpIfUndefinedConstantWide;
default:
UNREACHABLE();
return static_cast<Bytecode>(-1);
}
}
// static
Bytecode BytecodeArrayBuilder::GetJumpWithToBoolean(Bytecode jump_bytecode) {
switch (jump_bytecode) {
case Bytecode::kJump:
case Bytecode::kJumpIfNull:
case Bytecode::kJumpIfUndefined:
return jump_bytecode;
case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfToBooleanTrue;
case Bytecode::kJumpIfFalse:
return Bytecode::kJumpIfToBooleanFalse;
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
void BytecodeArrayBuilder::PatchIndirectJumpWith8BitOperand(
const ZoneVector<uint8_t>::iterator& jump_location, int delta) {
Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location);
DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
ZoneVector<uint8_t>::iterator operand_location = jump_location + 1;
DCHECK_EQ(*operand_location, 0);
if (FitsInImm8Operand(delta)) {
// The jump fits within the range of an Imm8 operand, so cancel
// the reservation and jump directly.
constant_array_builder()->DiscardReservedEntry(OperandSize::kByte);
*operand_location = static_cast<uint8_t>(delta);
} else {
// The jump does not fit within the range of an Imm8 operand, so
// commit reservation putting the offset into the constant pool,
// and update the jump instruction and operand.
size_t entry = constant_array_builder()->CommitReservedEntry(
OperandSize::kByte, handle(Smi::FromInt(delta), isolate()));
DCHECK(FitsInIdx8Operand(entry));
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
*jump_location = Bytecodes::ToByte(jump_bytecode);
*operand_location = static_cast<uint8_t>(entry);
}
}
void BytecodeArrayBuilder::PatchIndirectJumpWith16BitOperand(
const ZoneVector<uint8_t>::iterator& jump_location, int delta) {
DCHECK(Bytecodes::IsJumpConstantWide(Bytecodes::FromByte(*jump_location)));
ZoneVector<uint8_t>::iterator operand_location = jump_location + 1;
size_t entry = constant_array_builder()->CommitReservedEntry(
OperandSize::kShort, handle(Smi::FromInt(delta), isolate()));
DCHECK(FitsInIdx16Operand(entry));
uint8_t operand_bytes[2];
WriteUnalignedUInt16(operand_bytes, static_cast<uint16_t>(entry));
DCHECK(*operand_location == 0 && *(operand_location + 1) == 0);
*operand_location++ = operand_bytes[0];
*operand_location = operand_bytes[1];
}
void BytecodeArrayBuilder::PatchJump(
const ZoneVector<uint8_t>::iterator& jump_target,
const ZoneVector<uint8_t>::iterator& jump_location) {
Bytecode jump_bytecode = Bytecodes::FromByte(*jump_location);
int delta = static_cast<int>(jump_target - jump_location);
DCHECK(Bytecodes::IsJump(jump_bytecode));
switch (Bytecodes::GetOperandSize(jump_bytecode, 0)) {
case OperandSize::kByte:
PatchIndirectJumpWith8BitOperand(jump_location, delta);
break;
case OperandSize::kShort:
PatchIndirectJumpWith16BitOperand(jump_location, delta);
break;
case OperandSize::kNone:
UNREACHABLE();
}
unbound_jumps_--;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
BytecodeLabel* label) {
// Don't emit dead code.
if (exit_seen_in_block_) return *this;
// Check if the value in accumulator is boolean, if not choose an
// appropriate JumpIfToBoolean bytecode.
if (NeedToBooleanCast()) {
jump_bytecode = GetJumpWithToBoolean(jump_bytecode);
}
if (label->is_bound()) {
// Label has been bound already so this is a backwards jump.
CHECK_GE(bytecodes()->size(), label->offset());
CHECK_LE(bytecodes()->size(), static_cast<size_t>(kMaxInt));
size_t abs_delta = bytecodes()->size() - label->offset();
int delta = -static_cast<int>(abs_delta);
if (FitsInImm8Operand(delta)) {
Output(jump_bytecode, static_cast<uint8_t>(delta));
} else {
size_t entry =
GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate()));
if (FitsInIdx8Operand(entry)) {
Output(GetJumpWithConstantOperand(jump_bytecode),
static_cast<uint8_t>(entry));
} else if (FitsInIdx16Operand(entry)) {
Output(GetJumpWithConstantWideOperand(jump_bytecode),
static_cast<uint16_t>(entry));
} else {
UNREACHABLE();
}
}
} else {
// The label has not yet been bound so this is a forward reference
// that will be patched when the label is bound. We create a
// reservation in the constant pool so the jump can be patched
// when the label is bound. The reservation means the maximum size
// of the operand for the constant is known and the jump can
// be emitted into the bytecode stream with space for the operand.
label->set_referrer(bytecodes()->size());
unbound_jumps_++;
OperandSize reserved_operand_size =
constant_array_builder()->CreateReservedEntry();
switch (reserved_operand_size) {
case OperandSize::kByte:
Output(jump_bytecode, 0);
break;
case OperandSize::kShort:
Output(GetJumpWithConstantWideOperand(jump_bytecode), 0);
break;
case OperandSize::kNone:
UNREACHABLE();
}
}
LeaveBasicBlock();
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) {
return OutputJump(Bytecode::kJump, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfTrue(BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfTrue, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfFalse, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfNull, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined(
BytecodeLabel* label) {
return OutputJump(Bytecode::kJumpIfUndefined, label);
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() {
Output(Bytecode::kThrow);
exit_seen_in_block_ = true;
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ReThrow() {
Output(Bytecode::kReThrow);
exit_seen_in_block_ = true;
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
Output(Bytecode::kReturn);
exit_seen_in_block_ = true;
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare(
Register cache_info_triple) {
if (FitsInReg8Operand(cache_info_triple)) {
Output(Bytecode::kForInPrepare, cache_info_triple.ToRawOperand());
} else if (FitsInReg16Operand(cache_info_triple)) {
Output(Bytecode::kForInPrepareWide, cache_info_triple.ToRawOperand());
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register index,
Register cache_length) {
Output(Bytecode::kForInDone, index.ToRawOperand(),
cache_length.ToRawOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(
Register receiver, Register index, Register cache_type_array_pair) {
if (FitsInReg8Operand(receiver) && FitsInReg8Operand(index) &&
FitsInReg8Operand(cache_type_array_pair)) {
Output(Bytecode::kForInNext, receiver.ToRawOperand(), index.ToRawOperand(),
cache_type_array_pair.ToRawOperand());
} else if (FitsInReg16Operand(receiver) && FitsInReg16Operand(index) &&
FitsInReg16Operand(cache_type_array_pair)) {
Output(Bytecode::kForInNextWide, receiver.ToRawOperand(),
index.ToRawOperand(), cache_type_array_pair.ToRawOperand());
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) {
Output(Bytecode::kForInStep, index.ToRawOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkHandler(int handler_id,
bool will_catch) {
handler_table_builder()->SetHandlerTarget(handler_id, bytecodes()->size());
handler_table_builder()->SetPrediction(handler_id, will_catch);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryBegin(int handler_id,
Register context) {
handler_table_builder()->SetTryRegionStart(handler_id, bytecodes()->size());
handler_table_builder()->SetContextRegister(handler_id, context);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryEnd(int handler_id) {
handler_table_builder()->SetTryRegionEnd(handler_id, bytecodes()->size());
return *this;
}
void BytecodeArrayBuilder::LeaveBasicBlock() {
last_block_end_ = bytecodes()->size();
exit_seen_in_block_ = false;
}
void BytecodeArrayBuilder::EnsureReturn() {
if (!exit_seen_in_block_) {
LoadUndefined();
Return();
}
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
Register receiver,
size_t arg_count,
int feedback_slot) {
if (FitsInReg8Operand(callable) && FitsInReg8Operand(receiver) &&
FitsInIdx8Operand(arg_count) && FitsInIdx8Operand(feedback_slot)) {
Output(Bytecode::kCall, callable.ToRawOperand(), receiver.ToRawOperand(),
static_cast<uint8_t>(arg_count),
static_cast<uint8_t>(feedback_slot));
} else if (FitsInReg16Operand(callable) && FitsInReg16Operand(receiver) &&
FitsInIdx16Operand(arg_count) &&
FitsInIdx16Operand(feedback_slot)) {
Output(Bytecode::kCallWide, callable.ToRawOperand(),
receiver.ToRawOperand(), static_cast<uint16_t>(arg_count),
static_cast<uint16_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::New(Register constructor,
Register first_arg,
size_t arg_count) {
if (!first_arg.is_valid()) {
DCHECK_EQ(0u, arg_count);
first_arg = Register(0);
}
if (FitsInReg8Operand(constructor) && FitsInReg8Operand(first_arg) &&
FitsInIdx8Operand(arg_count)) {
Output(Bytecode::kNew, constructor.ToRawOperand(), first_arg.ToRawOperand(),
static_cast<uint8_t>(arg_count));
} else if (FitsInReg16Operand(constructor) && FitsInReg16Operand(first_arg) &&
FitsInIdx16Operand(arg_count)) {
Output(Bytecode::kNewWide, constructor.ToRawOperand(),
first_arg.ToRawOperand(), static_cast<uint16_t>(arg_count));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count) {
DCHECK_EQ(1, Runtime::FunctionForId(function_id)->result_size);
DCHECK(FitsInIdx16Operand(function_id));
if (!first_arg.is_valid()) {
DCHECK_EQ(0u, arg_count);
first_arg = Register(0);
}
if (FitsInReg8Operand(first_arg) && FitsInIdx8Operand(arg_count)) {
Output(Bytecode::kCallRuntime, static_cast<uint16_t>(function_id),
first_arg.ToRawOperand(), static_cast<uint8_t>(arg_count));
} else if (FitsInReg16Operand(first_arg) && FitsInIdx16Operand(arg_count)) {
Output(Bytecode::kCallRuntimeWide, static_cast<uint16_t>(function_id),
first_arg.ToRawOperand(), static_cast<uint16_t>(arg_count));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count,
Register first_return) {
DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size);
DCHECK(FitsInIdx16Operand(function_id));
if (!first_arg.is_valid()) {
DCHECK_EQ(0u, arg_count);
first_arg = Register(0);
}
if (FitsInReg8Operand(first_arg) && FitsInIdx8Operand(arg_count) &&
FitsInReg8Operand(first_return)) {
Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id),
first_arg.ToRawOperand(), static_cast<uint8_t>(arg_count),
first_return.ToRawOperand());
} else if (FitsInReg16Operand(first_arg) && FitsInIdx16Operand(arg_count) &&
FitsInReg16Operand(first_return)) {
Output(Bytecode::kCallRuntimeForPairWide,
static_cast<uint16_t>(function_id), first_arg.ToRawOperand(),
static_cast<uint16_t>(arg_count), first_return.ToRawOperand());
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index,
Register receiver,
size_t arg_count) {
DCHECK(FitsInIdx16Operand(context_index));
if (FitsInReg8Operand(receiver) && FitsInIdx8Operand(arg_count)) {
Output(Bytecode::kCallJSRuntime, static_cast<uint16_t>(context_index),
receiver.ToRawOperand(), static_cast<uint8_t>(arg_count));
} else if (FitsInReg16Operand(receiver) && FitsInIdx16Operand(arg_count)) {
Output(Bytecode::kCallJSRuntimeWide, static_cast<uint16_t>(context_index),
receiver.ToRawOperand(), static_cast<uint16_t>(arg_count));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object,
LanguageMode language_mode) {
Output(BytecodeForDelete(language_mode), object.ToRawOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::DeleteLookupSlot() {
Output(Bytecode::kDeleteLookupSlot);
return *this;
}
size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
return constant_array_builder()->Insert(object);
}
void BytecodeArrayBuilder::ForgeTemporaryRegister() {
temporary_register_count_++;
}
int BytecodeArrayBuilder::BorrowTemporaryRegister() {
if (free_temporaries_.empty()) {
ForgeTemporaryRegister();
return last_temporary_register().index();
} else {
auto pos = free_temporaries_.begin();
int retval = *pos;
free_temporaries_.erase(pos);
return retval;
}
}
int BytecodeArrayBuilder::BorrowTemporaryRegisterNotInRange(int start_index,
int end_index) {
auto index = free_temporaries_.lower_bound(start_index);
if (index == free_temporaries_.begin()) {
// If start_index is the first free register, check for a register
// greater than end_index.
index = free_temporaries_.upper_bound(end_index);
if (index == free_temporaries_.end()) {
ForgeTemporaryRegister();
return last_temporary_register().index();
}
} else {
// If there is a free register < start_index
index--;
}
int retval = *index;
free_temporaries_.erase(index);
return retval;
}
void BytecodeArrayBuilder::BorrowConsecutiveTemporaryRegister(int reg_index) {
DCHECK(free_temporaries_.find(reg_index) != free_temporaries_.end());
free_temporaries_.erase(reg_index);
}
void BytecodeArrayBuilder::ReturnTemporaryRegister(int reg_index) {
DCHECK(free_temporaries_.find(reg_index) == free_temporaries_.end());
free_temporaries_.insert(reg_index);
}
int BytecodeArrayBuilder::PrepareForConsecutiveTemporaryRegisters(
size_t count) {
if (count == 0) {
return -1;
}
// TODO(oth): replace use of set<> here for free_temporaries with a
// more efficient structure. And/or partition into two searches -
// one before the translation window and one after.
// A run will require at least |count| free temporaries.
while (free_temporaries_.size() < count) {
ForgeTemporaryRegister();
free_temporaries_.insert(last_temporary_register().index());
}
// Search within existing temporaries for a run.
auto start = free_temporaries_.begin();
size_t run_length = 0;
for (auto run_end = start; run_end != free_temporaries_.end(); run_end++) {
int expected = *start + static_cast<int>(run_length);
if (*run_end != expected) {
start = run_end;
run_length = 0;
}
Register reg_start(*start);
Register reg_expected(expected);
if (RegisterTranslator::DistanceToTranslationWindow(reg_start) > 0 &&
RegisterTranslator::DistanceToTranslationWindow(reg_expected) <= 0) {
// Run straddles the lower edge of the translation window. Registers
// after the start of this boundary are displaced by the register
// translator to provide a hole for translation. Runs either side
// of the boundary are fine.
start = run_end;
run_length = 0;
}
if (++run_length == count) {
return *start;
}
}
// Continue run if possible across existing last temporary.
if (temporary_register_count_ > 0 &&
(start == free_temporaries_.end() ||
*start + static_cast<int>(run_length) !=
last_temporary_register().index() + 1)) {
run_length = 0;
}
// Pad temporaries if extended run would cross translation boundary.
Register reg_first(*start);
Register reg_last(*start + static_cast<int>(count) - 1);
DCHECK_GT(RegisterTranslator::DistanceToTranslationWindow(reg_first),
RegisterTranslator::DistanceToTranslationWindow(reg_last));
while (RegisterTranslator::DistanceToTranslationWindow(reg_first) > 0 &&
RegisterTranslator::DistanceToTranslationWindow(reg_last) <= 0) {
ForgeTemporaryRegister();
free_temporaries_.insert(last_temporary_register().index());
start = --free_temporaries_.end();
reg_first = Register(*start);
reg_last = Register(*start + static_cast<int>(count) - 1);
run_length = 0;
}
// Ensure enough registers for run.
while (run_length++ < count) {
ForgeTemporaryRegister();
free_temporaries_.insert(last_temporary_register().index());
}
int run_start =
last_temporary_register().index() - static_cast<int>(count) + 1;
DCHECK(RegisterTranslator::DistanceToTranslationWindow(Register(run_start)) <=
0 ||
RegisterTranslator::DistanceToTranslationWindow(
Register(run_start + static_cast<int>(count) - 1)) > 0);
return run_start;
}
bool BytecodeArrayBuilder::TemporaryRegisterIsLive(Register reg) const {
if (temporary_register_count_ > 0) {
DCHECK(reg.index() >= first_temporary_register().index() &&
reg.index() <= last_temporary_register().index());
return free_temporaries_.find(reg.index()) == free_temporaries_.end();
} else {
return false;
}
}
bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
uint32_t operand_value) const {
OperandType operand_type = Bytecodes::GetOperandType(bytecode, operand_index);
switch (operand_type) {
case OperandType::kNone:
return false;
case OperandType::kRegCount16: {
// Expect kRegCount16 is part of a range previous operand is a
// valid operand to start a range.
if (operand_index > 0) {
OperandType previous_operand_type =
Bytecodes::GetOperandType(bytecode, operand_index - 1);
return ((previous_operand_type == OperandType::kMaybeReg16 ||
previous_operand_type == OperandType::kReg16) &&
static_cast<uint16_t>(operand_value) == operand_value);
} else {
return false;
}
}
case OperandType::kRegCount8: {
// Expect kRegCount8 is part of a range previous operand is a
// valid operand to start a range.
if (operand_index > 0) {
OperandType previous_operand_type =
Bytecodes::GetOperandType(bytecode, operand_index - 1);
return ((previous_operand_type == OperandType::kMaybeReg8 ||
previous_operand_type == OperandType::kReg8 ||
previous_operand_type == OperandType::kMaybeReg16) &&
static_cast<uint8_t>(operand_value) == operand_value);
} else {
return false;
}
}
case OperandType::kIdx16:
return static_cast<uint16_t>(operand_value) == operand_value;
case OperandType::kImm8:
case OperandType::kIdx8:
return static_cast<uint8_t>(operand_value) == operand_value;
case OperandType::kMaybeReg8:
if (operand_value == 0) {
return true;
}
// Fall-through to kReg8 case.
case OperandType::kReg8:
case OperandType::kRegOut8:
return RegisterIsValid(Register::FromRawOperand(operand_value),
operand_type);
case OperandType::kRegOutPair8:
case OperandType::kRegOutPair16:
case OperandType::kRegPair8:
case OperandType::kRegPair16: {
Register reg0 = Register::FromRawOperand(operand_value);
Register reg1 = Register(reg0.index() + 1);
return RegisterIsValid(reg0, operand_type) &&
RegisterIsValid(reg1, operand_type);
}
case OperandType::kRegOutTriple8:
case OperandType::kRegOutTriple16: {
Register reg0 = Register::FromRawOperand(operand_value);
Register reg1 = Register(reg0.index() + 1);
Register reg2 = Register(reg0.index() + 2);
return RegisterIsValid(reg0, operand_type) &&
RegisterIsValid(reg1, operand_type) &&
RegisterIsValid(reg2, operand_type);
}
case OperandType::kMaybeReg16:
if (operand_value == 0) {
return true;
}
// Fall-through to kReg16 case.
case OperandType::kReg16:
case OperandType::kRegOut16: {
Register reg = Register::FromRawOperand(operand_value);
return RegisterIsValid(reg, operand_type);
}
}
UNREACHABLE();
return false;
}
bool BytecodeArrayBuilder::RegisterIsValid(Register reg,
OperandType reg_type) const {
if (!reg.is_valid()) {
return false;
}
switch (Bytecodes::SizeOfOperand(reg_type)) {
case OperandSize::kByte:
if (!FitsInReg8OperandUntranslated(reg)) {
return false;
}
break;
case OperandSize::kShort:
if (!FitsInReg16OperandUntranslated(reg)) {
return false;
}
break;
case OperandSize::kNone:
UNREACHABLE();
return false;
}
if (reg.is_current_context() || reg.is_function_closure() ||
reg.is_new_target()) {
return true;
} else if (reg.is_parameter()) {
int parameter_index = reg.ToParameterIndex(parameter_count());
return parameter_index >= 0 && parameter_index < parameter_count();
} else if (RegisterTranslator::InTranslationWindow(reg)) {
return translation_register_count() > 0;
} else {
reg = RegisterTranslator::UntranslateRegister(reg);
if (reg.index() < fixed_register_count()) {
return true;
} else {
return TemporaryRegisterIsLive(reg);
}
}
}
bool BytecodeArrayBuilder::LastBytecodeInSameBlock() const {
return last_bytecode_start_ < bytecodes()->size() &&
last_bytecode_start_ >= last_block_end_;
}
bool BytecodeArrayBuilder::IsRegisterInAccumulator(Register reg) {
if (LastBytecodeInSameBlock()) {
PreviousBytecodeHelper previous_bytecode(*this);
Bytecode bytecode = previous_bytecode.GetBytecode();
if (bytecode == Bytecode::kLdar || bytecode == Bytecode::kStar) {
Register previous_reg =
Register::FromOperand(previous_bytecode.GetOperand(0));
return previous_reg == reg;
}
}
return false;
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
switch (op) {
case Token::Value::ADD:
return Bytecode::kAdd;
case Token::Value::SUB:
return Bytecode::kSub;
case Token::Value::MUL:
return Bytecode::kMul;
case Token::Value::DIV:
return Bytecode::kDiv;
case Token::Value::MOD:
return Bytecode::kMod;
case Token::Value::BIT_OR:
return Bytecode::kBitwiseOr;
case Token::Value::BIT_XOR:
return Bytecode::kBitwiseXor;
case Token::Value::BIT_AND:
return Bytecode::kBitwiseAnd;
case Token::Value::SHL:
return Bytecode::kShiftLeft;
case Token::Value::SAR:
return Bytecode::kShiftRight;
case Token::Value::SHR:
return Bytecode::kShiftRightLogical;
default:
UNREACHABLE();
return static_cast<Bytecode>(-1);
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCountOperation(Token::Value op) {
switch (op) {
case Token::Value::ADD:
return Bytecode::kInc;
case Token::Value::SUB:
return Bytecode::kDec;
default:
UNREACHABLE();
return static_cast<Bytecode>(-1);
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
switch (op) {
case Token::Value::EQ:
return Bytecode::kTestEqual;
case Token::Value::NE:
return Bytecode::kTestNotEqual;
case Token::Value::EQ_STRICT:
return Bytecode::kTestEqualStrict;
case Token::Value::NE_STRICT:
return Bytecode::kTestNotEqualStrict;
case Token::Value::LT:
return Bytecode::kTestLessThan;
case Token::Value::GT:
return Bytecode::kTestGreaterThan;
case Token::Value::LTE:
return Bytecode::kTestLessThanOrEqual;
case Token::Value::GTE:
return Bytecode::kTestGreaterThanOrEqual;
case Token::Value::INSTANCEOF:
return Bytecode::kTestInstanceOf;
case Token::Value::IN:
return Bytecode::kTestIn;
default:
UNREACHABLE();
return static_cast<Bytecode>(-1);
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForWideOperands(Bytecode bytecode) {
switch (bytecode) {
case Bytecode::kLoadICSloppy:
return Bytecode::kLoadICSloppyWide;
case Bytecode::kLoadICStrict:
return Bytecode::kLoadICStrictWide;
case Bytecode::kKeyedLoadICSloppy:
return Bytecode::kKeyedLoadICSloppyWide;
case Bytecode::kKeyedLoadICStrict:
return Bytecode::kKeyedLoadICStrictWide;
case Bytecode::kStoreICSloppy:
return Bytecode::kStoreICSloppyWide;
case Bytecode::kStoreICStrict:
return Bytecode::kStoreICStrictWide;
case Bytecode::kKeyedStoreICSloppy:
return Bytecode::kKeyedStoreICSloppyWide;
case Bytecode::kKeyedStoreICStrict:
return Bytecode::kKeyedStoreICStrictWide;
case Bytecode::kLdaGlobalSloppy:
return Bytecode::kLdaGlobalSloppyWide;
case Bytecode::kLdaGlobalStrict:
return Bytecode::kLdaGlobalStrictWide;
case Bytecode::kLdaGlobalInsideTypeofSloppy:
return Bytecode::kLdaGlobalInsideTypeofSloppyWide;
case Bytecode::kLdaGlobalInsideTypeofStrict:
return Bytecode::kLdaGlobalInsideTypeofStrictWide;
case Bytecode::kStaGlobalSloppy:
return Bytecode::kStaGlobalSloppyWide;
case Bytecode::kStaGlobalStrict:
return Bytecode::kStaGlobalStrictWide;
case Bytecode::kLdaLookupSlot:
return Bytecode::kLdaLookupSlotWide;
case Bytecode::kLdaLookupSlotInsideTypeof:
return Bytecode::kLdaLookupSlotInsideTypeofWide;
case Bytecode::kStaLookupSlotStrict:
return Bytecode::kStaLookupSlotStrictWide;
case Bytecode::kStaLookupSlotSloppy:
return Bytecode::kStaLookupSlotSloppyWide;
default:
UNREACHABLE();
return static_cast<Bytecode>(-1);
}
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForLoadIC(LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kLoadICSloppy;
case STRICT:
return Bytecode::kLoadICStrict;
case STRONG:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForKeyedLoadIC(
LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kKeyedLoadICSloppy;
case STRICT:
return Bytecode::kKeyedLoadICStrict;
case STRONG:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreIC(LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kStoreICSloppy;
case STRICT:
return Bytecode::kStoreICStrict;
case STRONG:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForKeyedStoreIC(
LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kKeyedStoreICSloppy;
case STRICT:
return Bytecode::kKeyedStoreICStrict;
case STRONG:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForLoadGlobal(LanguageMode language_mode,
TypeofMode typeof_mode) {
switch (language_mode) {
case SLOPPY:
return typeof_mode == INSIDE_TYPEOF
? Bytecode::kLdaGlobalInsideTypeofSloppy
: Bytecode::kLdaGlobalSloppy;
case STRICT:
return typeof_mode == INSIDE_TYPEOF
? Bytecode::kLdaGlobalInsideTypeofStrict
: Bytecode::kLdaGlobalStrict;
case STRONG:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreGlobal(
LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kStaGlobalSloppy;
case STRICT:
return Bytecode::kStaGlobalStrict;
case STRONG:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForStoreLookupSlot(
LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kStaLookupSlotSloppy;
case STRICT:
return Bytecode::kStaLookupSlotStrict;
case STRONG:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForCreateArguments(
CreateArgumentsType type) {
switch (type) {
case CreateArgumentsType::kMappedArguments:
return Bytecode::kCreateMappedArguments;
case CreateArgumentsType::kUnmappedArguments:
return Bytecode::kCreateUnmappedArguments;
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
Bytecode BytecodeArrayBuilder::BytecodeForDelete(LanguageMode language_mode) {
switch (language_mode) {
case SLOPPY:
return Bytecode::kDeletePropertySloppy;
case STRICT:
return Bytecode::kDeletePropertyStrict;
case STRONG:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
return static_cast<Bytecode>(-1);
}
// static
bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) {
return kMinUInt8 <= value && value <= kMaxUInt8;
}
// static
bool BytecodeArrayBuilder::FitsInIdx8Operand(size_t value) {
return value <= static_cast<size_t>(kMaxUInt8);
}
// static
bool BytecodeArrayBuilder::FitsInImm8Operand(int value) {
return kMinInt8 <= value && value <= kMaxInt8;
}
// static
bool BytecodeArrayBuilder::FitsInIdx16Operand(int value) {
return kMinUInt16 <= value && value <= kMaxUInt16;
}
// static
bool BytecodeArrayBuilder::FitsInIdx16Operand(size_t value) {
return value <= static_cast<size_t>(kMaxUInt16);
}
// static
bool BytecodeArrayBuilder::FitsInReg8Operand(Register value) {
return RegisterTranslator::FitsInReg8Operand(value);
}
// static
bool BytecodeArrayBuilder::FitsInReg8OperandUntranslated(Register value) {
return value.is_byte_operand();
}
// static
bool BytecodeArrayBuilder::FitsInReg16Operand(Register value) {
return RegisterTranslator::FitsInReg16Operand(value);
}
// static
bool BytecodeArrayBuilder::FitsInReg16OperandUntranslated(Register value) {
return value.is_short_operand();
}
} // namespace interpreter
} // namespace internal
} // namespace v8