// 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 { 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) { default: case OperandSize::kNone: UNREACHABLE(); 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); } } 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), 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) {} 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_register_count() + temporary_register_count_; int frame_size = register_count * kPointerSize; Factory* factory = isolate_->factory(); Handle<FixedArray> constant_pool = constant_array_builder()->ToFixedArray(factory); Handle<BytecodeArray> output = factory->NewBytecodeArray(bytecode_size, &bytecodes_.front(), frame_size, parameter_count(), constant_pool); 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; DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), static_cast<int>(N)); last_bytecode_start_ = bytecodes()->size(); bytecodes()->push_back(Bytecodes::ToByte(bytecode)); for (int i = 0; i < static_cast<int>(N); i++) { DCHECK(OperandIsValid(bytecode, i, operands[i])); switch (Bytecodes::GetOperandSize(bytecode, i)) { case OperandSize::kNone: UNREACHABLE(); 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; } } } } 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.ToOperand()); 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.ToOperand()); 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.ToOperand()); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( Register reg) { // TODO(oth): Avoid storing the accumulator in the register if the // previous bytecode loaded the accumulator with the same register. // // TODO(oth): If the previous bytecode is a MOV into this register, // the previous instruction can be removed. The logic for determining // these redundant MOVs appears complex. Output(Bytecode::kStar, reg.ToOperand()); if (!IsRegisterInAccumulator(reg)) { Output(Bytecode::kStar, reg.ToOperand()); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from, Register to) { DCHECK(from != to); Output(Bytecode::kMov, from.ToOperand(), to.ToOperand()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ExchangeRegisters(Register reg0, Register reg1) { DCHECK(reg0 != reg1); if (FitsInReg8Operand(reg0)) { Output(Bytecode::kExchange, reg0.ToOperand(), reg1.ToWideOperand()); } else if (FitsInReg8Operand(reg1)) { Output(Bytecode::kExchange, reg1.ToOperand(), reg0.ToWideOperand()); } else { Output(Bytecode::kExchangeWide, reg0.ToWideOperand(), reg1.ToWideOperand()); } return *this; } 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.ToOperand(), static_cast<uint8_t>(slot_index)); } else if (FitsInIdx16Operand(slot_index)) { Output(Bytecode::kLdaContextSlotWide, context.ToOperand(), 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.ToOperand(), static_cast<uint8_t>(slot_index)); } else if (FitsInIdx16Operand(slot_index)) { Output(Bytecode::kStaContextSlotWide, context.ToOperand(), 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.ToOperand(), static_cast<uint8_t>(name_index), static_cast<uint8_t>(feedback_slot)); } else if (FitsInIdx16Operand(name_index) && FitsInIdx16Operand(feedback_slot)) { Output(BytecodeForWideOperands(bytecode), object.ToOperand(), 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.ToOperand(), static_cast<uint8_t>(feedback_slot)); } else if (FitsInIdx16Operand(feedback_slot)) { Output(BytecodeForWideOperands(bytecode), object.ToOperand(), 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.ToOperand(), static_cast<uint8_t>(name_index), static_cast<uint8_t>(feedback_slot)); } else if (FitsInIdx16Operand(name_index) && FitsInIdx16Operand(feedback_slot)) { Output(BytecodeForWideOperands(bytecode), object.ToOperand(), 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.ToOperand(), key.ToOperand(), static_cast<uint8_t>(feedback_slot)); } else if (FitsInIdx16Operand(feedback_slot)) { Output(BytecodeForWideOperands(bytecode), object.ToOperand(), key.ToOperand(), 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.ToOperand()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) { Output(Bytecode::kPopContext, context.ToOperand()); 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::Return() { Output(Bytecode::kReturn); exit_seen_in_block_ = true; return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare( Register cache_type, Register cache_array, Register cache_length) { Output(Bytecode::kForInPrepare, cache_type.ToOperand(), cache_array.ToOperand(), cache_length.ToOperand()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register index, Register cache_length) { Output(Bytecode::kForInDone, index.ToOperand(), cache_length.ToOperand()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(Register receiver, Register cache_type, Register cache_array, Register index) { Output(Bytecode::kForInNext, receiver.ToOperand(), cache_type.ToOperand(), cache_array.ToOperand(), index.ToOperand()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) { Output(Bytecode::kForInStep, index.ToOperand()); 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 (FitsInIdx8Operand(arg_count) && FitsInIdx8Operand(feedback_slot)) { Output(Bytecode::kCall, callable.ToOperand(), receiver.ToOperand(), static_cast<uint8_t>(arg_count), static_cast<uint8_t>(feedback_slot)); } else if (FitsInIdx16Operand(arg_count) && FitsInIdx16Operand(feedback_slot)) { Output(Bytecode::kCallWide, callable.ToOperand(), receiver.ToOperand(), 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); } DCHECK(FitsInIdx8Operand(arg_count)); Output(Bytecode::kNew, constructor.ToOperand(), first_arg.ToOperand(), static_cast<uint8_t>(arg_count)); 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)); DCHECK(FitsInIdx8Operand(arg_count)); if (!first_arg.is_valid()) { DCHECK_EQ(0u, arg_count); first_arg = Register(0); } Output(Bytecode::kCallRuntime, static_cast<uint16_t>(function_id), first_arg.ToOperand(), static_cast<uint8_t>(arg_count)); 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)); DCHECK(FitsInIdx8Operand(arg_count)); if (!first_arg.is_valid()) { DCHECK_EQ(0u, arg_count); first_arg = Register(0); } Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id), first_arg.ToOperand(), static_cast<uint8_t>(arg_count), first_return.ToOperand()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index, Register receiver, size_t arg_count) { DCHECK(FitsInIdx16Operand(context_index)); DCHECK(FitsInIdx8Operand(arg_count)); Output(Bytecode::kCallJSRuntime, static_cast<uint16_t>(context_index), receiver.ToOperand(), static_cast<uint8_t>(arg_count)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object, LanguageMode language_mode) { Output(BytecodeForDelete(language_mode), object.ToOperand()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::DeleteLookupSlot() { Output(Bytecode::kDeleteLookupSlot); return *this; } size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) { return constant_array_builder()->Insert(object); } int BytecodeArrayBuilder::BorrowTemporaryRegister() { if (free_temporaries_.empty()) { temporary_register_count_ += 1; 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()) { temporary_register_count_ += 1; 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; } // 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++) { if (*run_end != *start + static_cast<int>(run_length)) { 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; } // Ensure enough registers for run. while (run_length++ < count) { temporary_register_count_++; free_temporaries_.insert(last_temporary_register().index()); } return last_temporary_register().index() - static_cast<int>(count) + 1; } 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::RegisterIsValid(Register reg) const { if (reg.is_function_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 (reg.index() < fixed_register_count()) { return true; } else { return TemporaryRegisterIsLive(reg); } } 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::kCount16: case OperandType::kIdx16: return static_cast<uint16_t>(operand_value) == operand_value; case OperandType::kCount8: 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: return RegisterIsValid( Register::FromOperand(static_cast<uint8_t>(operand_value))); case OperandType::kRegPair8: { Register reg0 = Register::FromOperand(static_cast<uint8_t>(operand_value)); Register reg1 = Register(reg0.index() + 1); return RegisterIsValid(reg0) && RegisterIsValid(reg1); } case OperandType::kReg16: if (bytecode != Bytecode::kExchange && bytecode != Bytecode::kExchangeWide) { return false; } return RegisterIsValid( Register::FromWideOperand(static_cast<uint16_t>(operand_value))); } UNREACHABLE(); return false; } 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) && (reg == Register::FromOperand(previous_bytecode.GetOperand(0)))) { return true; } } 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 kMinInt8 <= value.index() && value.index() <= kMaxInt8; } // static bool BytecodeArrayBuilder::FitsInReg16Operand(Register value) { return kMinInt16 <= value.index() && value.index() <= kMaxInt16; } } // namespace interpreter } // namespace internal } // namespace v8