// 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" #include "src/compiler.h" #include "src/interpreter/bytecode-array-writer.h" #include "src/interpreter/bytecode-dead-code-optimizer.h" #include "src/interpreter/bytecode-label.h" #include "src/interpreter/bytecode-peephole-optimizer.h" #include "src/interpreter/bytecode-register-optimizer.h" #include "src/interpreter/interpreter-intrinsics.h" namespace v8 { namespace internal { namespace interpreter { BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone, int parameter_count, int context_count, int locals_count, FunctionLiteral* literal) : isolate_(isolate), zone_(zone), bytecode_generated_(false), constant_array_builder_(isolate, zone), handler_table_builder_(isolate, zone), return_seen_in_block_(false), parameter_count_(parameter_count), local_register_count_(locals_count), context_register_count_(context_count), temporary_allocator_(zone, fixed_register_count()), bytecode_array_writer_(isolate, zone, &constant_array_builder_), pipeline_(&bytecode_array_writer_) { DCHECK_GE(parameter_count_, 0); DCHECK_GE(context_register_count_, 0); DCHECK_GE(local_register_count_, 0); if (FLAG_ignition_deadcode) { pipeline_ = new (zone) BytecodeDeadCodeOptimizer(pipeline_); } if (FLAG_ignition_peephole) { pipeline_ = new (zone) BytecodePeepholeOptimizer(&constant_array_builder_, pipeline_); } if (FLAG_ignition_reo) { pipeline_ = new (zone) BytecodeRegisterOptimizer( zone, &temporary_allocator_, parameter_count, pipeline_); } return_position_ = literal ? std::max(literal->start_position(), literal->end_position() - 1) : RelocInfo::kNoPosition; } 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::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(); } Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray() { DCHECK(return_seen_in_block_); DCHECK(!bytecode_generated_); bytecode_generated_ = true; Handle<FixedArray> handler_table = handler_table_builder()->ToHandlerTable(); return pipeline_->ToBytecodeArray(fixed_register_count(), parameter_count(), handler_table); } namespace { static bool ExpressionPositionIsNeeded(Bytecode bytecode) { // An expression position is always needed if filtering is turned // off. Otherwise an expression is only needed if the bytecode has // external side effects. return !FLAG_ignition_filter_expression_positions || !Bytecodes::IsWithoutExternalSideEffects(bytecode); } } // namespace void BytecodeArrayBuilder::AttachSourceInfo(BytecodeNode* node) { if (latest_source_info_.is_valid()) { // Statement positions need to be emitted immediately. Expression // positions can be pushed back until a bytecode is found that can // throw. Hence we only invalidate the existing source position // information if it is used. if (latest_source_info_.is_statement() || ExpressionPositionIsNeeded(node->bytecode())) { node->source_info().Clone(latest_source_info_); latest_source_info_.set_invalid(); } } } void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1, uint32_t operand2, uint32_t operand3) { DCHECK(OperandsAreValid(bytecode, 4, operand0, operand1, operand2, operand3)); BytecodeNode node(bytecode, operand0, operand1, operand2, operand3); AttachSourceInfo(&node); pipeline()->Write(&node); } void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1, uint32_t operand2) { DCHECK(OperandsAreValid(bytecode, 3, operand0, operand1, operand2)); BytecodeNode node(bytecode, operand0, operand1, operand2); AttachSourceInfo(&node); pipeline()->Write(&node); } void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1) { DCHECK(OperandsAreValid(bytecode, 2, operand0, operand1)); BytecodeNode node(bytecode, operand0, operand1); AttachSourceInfo(&node); pipeline()->Write(&node); } void BytecodeArrayBuilder::Output(Bytecode bytecode, uint32_t operand0) { DCHECK(OperandsAreValid(bytecode, 1, operand0)); BytecodeNode node(bytecode, operand0); AttachSourceInfo(&node); pipeline()->Write(&node); } void BytecodeArrayBuilder::Output(Bytecode bytecode) { DCHECK(OperandsAreValid(bytecode, 0)); BytecodeNode node(bytecode); AttachSourceInfo(&node); pipeline()->Write(&node); } BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op, Register reg) { Output(BytecodeForBinaryOperation(op), RegisterOperand(reg)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op) { Output(BytecodeForCountOperation(op)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() { Output(Bytecode::kToBooleanLogicalNot); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() { Output(Bytecode::kTypeOf); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(Token::Value op, Register reg) { Output(BytecodeForCompareOperation(op), RegisterOperand(reg)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral( v8::internal::Smi* smi) { int32_t raw_smi = smi->value(); if (raw_smi == 0) { Output(Bytecode::kLdaZero); } else { Output(Bytecode::kLdaSmi, SignedOperand(raw_smi)); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) { size_t entry = GetConstantPoolEntry(object); Output(Bytecode::kLdaConstant, UnsignedOperand(entry)); 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::LoadAccumulatorWithRegister( Register reg) { Output(Bytecode::kLdar, RegisterOperand(reg)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( Register reg) { Output(Bytecode::kStar, RegisterOperand(reg)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from, Register to) { DCHECK(from != to); Output(Bytecode::kMov, RegisterOperand(from), RegisterOperand(to)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(int feedback_slot, TypeofMode typeof_mode) { // TODO(rmcilroy): Potentially store typeof information in an // operand rather than having extra bytecodes. Bytecode bytecode = BytecodeForLoadGlobal(typeof_mode); Output(bytecode, UnsignedOperand(feedback_slot)); 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); Output(bytecode, UnsignedOperand(name_index), UnsignedOperand(feedback_slot)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(Register context, int slot_index) { Output(Bytecode::kLdaContextSlot, RegisterOperand(context), UnsignedOperand(slot_index)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreContextSlot(Register context, int slot_index) { Output(Bytecode::kStaContextSlot, RegisterOperand(context), UnsignedOperand(slot_index)); 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); Output(bytecode, UnsignedOperand(name_index)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreLookupSlot( const Handle<String> name, LanguageMode language_mode) { Bytecode bytecode = BytecodeForStoreLookupSlot(language_mode); size_t name_index = GetConstantPoolEntry(name); Output(bytecode, UnsignedOperand(name_index)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty( Register object, const Handle<Name> name, int feedback_slot) { size_t name_index = GetConstantPoolEntry(name); Output(Bytecode::kLdaNamedProperty, RegisterOperand(object), UnsignedOperand(name_index), UnsignedOperand(feedback_slot)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty( Register object, int feedback_slot) { Output(Bytecode::kLdaKeyedProperty, RegisterOperand(object), UnsignedOperand(feedback_slot)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty( Register object, const Handle<Name> name, int feedback_slot, LanguageMode language_mode) { Bytecode bytecode = BytecodeForStoreNamedProperty(language_mode); size_t name_index = GetConstantPoolEntry(name); Output(bytecode, RegisterOperand(object), UnsignedOperand(name_index), UnsignedOperand(feedback_slot)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty( Register object, Register key, int feedback_slot, LanguageMode language_mode) { Bytecode bytecode = BytecodeForStoreKeyedProperty(language_mode); Output(bytecode, RegisterOperand(object), RegisterOperand(key), UnsignedOperand(feedback_slot)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure( Handle<SharedFunctionInfo> shared_info, PretenureFlag tenured) { size_t entry = GetConstantPoolEntry(shared_info); Output(Bytecode::kCreateClosure, UnsignedOperand(entry), UnsignedOperand(static_cast<size_t>(tenured))); 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) { size_t pattern_entry = GetConstantPoolEntry(pattern); Output(Bytecode::kCreateRegExpLiteral, UnsignedOperand(pattern_entry), UnsignedOperand(literal_index), UnsignedOperand(flags)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral( Handle<FixedArray> constant_elements, int literal_index, int flags) { size_t constant_elements_entry = GetConstantPoolEntry(constant_elements); Output(Bytecode::kCreateArrayLiteral, UnsignedOperand(constant_elements_entry), UnsignedOperand(literal_index), UnsignedOperand(flags)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral( Handle<FixedArray> constant_properties, int literal_index, int flags) { size_t constant_properties_entry = GetConstantPoolEntry(constant_properties); Output(Bytecode::kCreateObjectLiteral, UnsignedOperand(constant_properties_entry), UnsignedOperand(literal_index), UnsignedOperand(flags)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) { Output(Bytecode::kPushContext, RegisterOperand(context)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) { Output(Bytecode::kPopContext, RegisterOperand(context)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToJSObject() { Output(Bytecode::kToObject); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToName() { Output(Bytecode::kToName); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CastAccumulatorToNumber() { Output(Bytecode::kToNumber); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) { pipeline_->BindLabel(label); LeaveBasicBlock(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target, BytecodeLabel* label) { pipeline_->BindLabel(target, label); LeaveBasicBlock(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode, BytecodeLabel* label) { BytecodeNode node(jump_bytecode, 0); AttachSourceInfo(&node); pipeline_->WriteJump(&node, label); LeaveBasicBlock(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) { return OutputJump(Bytecode::kJump, label); } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfTrue(BytecodeLabel* label) { // The peephole optimizer attempts to simplify JumpIfToBooleanTrue // to JumpIfTrue. return OutputJump(Bytecode::kJumpIfToBooleanTrue, label); } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) { // The peephole optimizer attempts to simplify JumpIfToBooleanFalse // to JumpIfFalse. return OutputJump(Bytecode::kJumpIfToBooleanFalse, label); } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) { return OutputJump(Bytecode::kJumpIfNull, label); } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined( BytecodeLabel* label) { return OutputJump(Bytecode::kJumpIfUndefined, label); } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotHole( BytecodeLabel* label) { return OutputJump(Bytecode::kJumpIfNotHole, label); } BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck(int position) { if (position != RelocInfo::kNoPosition) { // We need to attach a non-breakable source position to a stack // check, so we simply add it as expression position. There can be // a prior statement position from constructs like: // // do var x; while (false); // // A Nop could be inserted for empty statements, but since no code // is associated with these positions, instead we force the stack // check's expression position which eliminates the empty // statement's position. latest_source_info_.ForceExpressionPosition(position); } Output(Bytecode::kStackCheck); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() { Output(Bytecode::kThrow); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ReThrow() { Output(Bytecode::kReThrow); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Return() { SetReturnPosition(); Output(Bytecode::kReturn); return_seen_in_block_ = true; return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Debugger() { Output(Bytecode::kDebugger); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare( Register cache_info_triple) { Output(Bytecode::kForInPrepare, RegisterOperand(cache_info_triple)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register index, Register cache_length) { Output(Bytecode::kForInDone, RegisterOperand(index), RegisterOperand(cache_length)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext( Register receiver, Register index, Register cache_type_array_pair, int feedback_slot) { Output(Bytecode::kForInNext, RegisterOperand(receiver), RegisterOperand(index), RegisterOperand(cache_type_array_pair), UnsignedOperand(feedback_slot)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) { Output(Bytecode::kForInStep, RegisterOperand(index)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::SuspendGenerator( Register generator) { Output(Bytecode::kSuspendGenerator, RegisterOperand(generator)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ResumeGenerator( Register generator) { Output(Bytecode::kResumeGenerator, RegisterOperand(generator)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::MarkHandler(int handler_id, bool will_catch) { BytecodeLabel handler; Bind(&handler); handler_table_builder()->SetHandlerTarget(handler_id, handler.offset()); handler_table_builder()->SetPrediction(handler_id, will_catch); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryBegin(int handler_id, Register context) { BytecodeLabel try_begin; Bind(&try_begin); handler_table_builder()->SetTryRegionStart(handler_id, try_begin.offset()); handler_table_builder()->SetContextRegister(handler_id, context); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryEnd(int handler_id) { BytecodeLabel try_end; Bind(&try_end); handler_table_builder()->SetTryRegionEnd(handler_id, try_end.offset()); return *this; } void BytecodeArrayBuilder::EnsureReturn() { if (!return_seen_in_block_) { LoadUndefined(); Return(); } DCHECK(return_seen_in_block_); } BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable, Register receiver_args, size_t receiver_args_count, int feedback_slot, TailCallMode tail_call_mode) { Bytecode bytecode = BytecodeForCall(tail_call_mode); Output(bytecode, RegisterOperand(callable), RegisterOperand(receiver_args), UnsignedOperand(receiver_args_count), UnsignedOperand(feedback_slot)); 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); } Output(Bytecode::kNew, RegisterOperand(constructor), RegisterOperand(first_arg), UnsignedOperand(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(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort); if (!first_arg.is_valid()) { DCHECK_EQ(0u, arg_count); first_arg = Register(0); } Bytecode bytecode; uint32_t id; if (IntrinsicsHelper::IsSupported(function_id)) { bytecode = Bytecode::kInvokeIntrinsic; id = static_cast<uint32_t>(IntrinsicsHelper::FromRuntimeId(function_id)); } else { bytecode = Bytecode::kCallRuntime; id = static_cast<uint32_t>(function_id); } Output(bytecode, id, RegisterOperand(first_arg), UnsignedOperand(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(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort); if (!first_arg.is_valid()) { DCHECK_EQ(0u, arg_count); first_arg = Register(0); } Output(Bytecode::kCallRuntimeForPair, static_cast<uint16_t>(function_id), RegisterOperand(first_arg), UnsignedOperand(arg_count), RegisterOperand(first_return)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime( int context_index, Register receiver_args, size_t receiver_args_count) { Output(Bytecode::kCallJSRuntime, UnsignedOperand(context_index), RegisterOperand(receiver_args), UnsignedOperand(receiver_args_count)); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object, LanguageMode language_mode) { Output(BytecodeForDelete(language_mode), RegisterOperand(object)); return *this; } size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) { return constant_array_builder()->Insert(object); } void BytecodeArrayBuilder::SetReturnPosition() { if (return_position_ == RelocInfo::kNoPosition) return; latest_source_info_.MakeStatementPosition(return_position_); } void BytecodeArrayBuilder::SetStatementPosition(Statement* stmt) { if (stmt->position() == RelocInfo::kNoPosition) return; latest_source_info_.MakeStatementPosition(stmt->position()); } void BytecodeArrayBuilder::SetExpressionPosition(Expression* expr) { if (expr->position() == RelocInfo::kNoPosition) return; if (!latest_source_info_.is_statement()) { // Ensure the current expression position is overwritten with the // latest value. latest_source_info_.MakeExpressionPosition(expr->position()); } } void BytecodeArrayBuilder::SetExpressionAsStatementPosition(Expression* expr) { if (expr->position() == RelocInfo::kNoPosition) return; latest_source_info_.MakeStatementPosition(expr->position()); } bool BytecodeArrayBuilder::TemporaryRegisterIsLive(Register reg) const { return temporary_register_allocator()->RegisterIsLive(reg); } bool BytecodeArrayBuilder::RegisterIsValid(Register reg) const { if (!reg.is_valid()) { 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 (reg.index() < fixed_register_count()) { return true; } else { return TemporaryRegisterIsLive(reg); } } bool BytecodeArrayBuilder::OperandsAreValid( Bytecode bytecode, int operand_count, uint32_t operand0, uint32_t operand1, uint32_t operand2, uint32_t operand3) const { if (Bytecodes::NumberOfOperands(bytecode) != operand_count) { return false; } uint32_t operands[] = {operand0, operand1, operand2, operand3}; const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode); for (int i = 0; i < operand_count; ++i) { switch (operand_types[i]) { case OperandType::kNone: return false; case OperandType::kRegCount: { CHECK_NE(i, 0); CHECK(operand_types[i - 1] == OperandType::kMaybeReg || operand_types[i - 1] == OperandType::kReg); if (i > 0 && operands[i] > 0) { Register start = Register::FromOperand(operands[i - 1]); Register end(start.index() + static_cast<int>(operands[i]) - 1); if (!RegisterIsValid(start) || !RegisterIsValid(end) || start > end) { return false; } } break; } case OperandType::kFlag8: case OperandType::kIntrinsicId: if (Bytecodes::SizeForUnsignedOperand(operands[i]) > OperandSize::kByte) { return false; } break; case OperandType::kRuntimeId: if (Bytecodes::SizeForUnsignedOperand(operands[i]) > OperandSize::kShort) { return false; } break; case OperandType::kIdx: // TODO(oth): Consider splitting OperandType::kIdx into two // operand types. One which is a constant pool index that can // be checked, and the other is an unsigned value. break; case OperandType::kImm: break; case OperandType::kMaybeReg: if (Register::FromOperand(operands[i]) == Register(0)) { break; } // Fall-through to kReg case. case OperandType::kReg: case OperandType::kRegOut: { Register reg = Register::FromOperand(operands[i]); if (!RegisterIsValid(reg)) { return false; } break; } case OperandType::kRegOutPair: case OperandType::kRegPair: { Register reg0 = Register::FromOperand(operands[i]); Register reg1 = Register(reg0.index() + 1); if (!RegisterIsValid(reg0) || !RegisterIsValid(reg1)) { return false; } break; } case OperandType::kRegOutTriple: { Register reg0 = Register::FromOperand(operands[i]); Register reg1 = Register(reg0.index() + 1); Register reg2 = Register(reg0.index() + 2); if (!RegisterIsValid(reg0) || !RegisterIsValid(reg1) || !RegisterIsValid(reg2)) { return false; } break; } } } return true; } // 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 Bytecode::kIllegal; } } // 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 Bytecode::kIllegal; } } // 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::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 Bytecode::kIllegal; } } // static Bytecode BytecodeArrayBuilder::BytecodeForStoreNamedProperty( LanguageMode language_mode) { switch (language_mode) { case SLOPPY: return Bytecode::kStaNamedPropertySloppy; case STRICT: return Bytecode::kStaNamedPropertyStrict; default: UNREACHABLE(); } return Bytecode::kIllegal; } // static Bytecode BytecodeArrayBuilder::BytecodeForStoreKeyedProperty( LanguageMode language_mode) { switch (language_mode) { case SLOPPY: return Bytecode::kStaKeyedPropertySloppy; case STRICT: return Bytecode::kStaKeyedPropertyStrict; default: UNREACHABLE(); } return Bytecode::kIllegal; } // static Bytecode BytecodeArrayBuilder::BytecodeForLoadGlobal(TypeofMode typeof_mode) { return typeof_mode == INSIDE_TYPEOF ? Bytecode::kLdaGlobalInsideTypeof : Bytecode::kLdaGlobal; } // static Bytecode BytecodeArrayBuilder::BytecodeForStoreGlobal( LanguageMode language_mode) { switch (language_mode) { case SLOPPY: return Bytecode::kStaGlobalSloppy; case STRICT: return Bytecode::kStaGlobalStrict; default: UNREACHABLE(); } return Bytecode::kIllegal; } // static Bytecode BytecodeArrayBuilder::BytecodeForStoreLookupSlot( LanguageMode language_mode) { switch (language_mode) { case SLOPPY: return Bytecode::kStaLookupSlotSloppy; case STRICT: return Bytecode::kStaLookupSlotStrict; default: UNREACHABLE(); } return Bytecode::kIllegal; } // static Bytecode BytecodeArrayBuilder::BytecodeForCreateArguments( CreateArgumentsType type) { switch (type) { case CreateArgumentsType::kMappedArguments: return Bytecode::kCreateMappedArguments; case CreateArgumentsType::kUnmappedArguments: return Bytecode::kCreateUnmappedArguments; case CreateArgumentsType::kRestParameter: return Bytecode::kCreateRestParameter; } UNREACHABLE(); return Bytecode::kIllegal; } // static Bytecode BytecodeArrayBuilder::BytecodeForDelete(LanguageMode language_mode) { switch (language_mode) { case SLOPPY: return Bytecode::kDeletePropertySloppy; case STRICT: return Bytecode::kDeletePropertyStrict; default: UNREACHABLE(); } return Bytecode::kIllegal; } // static Bytecode BytecodeArrayBuilder::BytecodeForCall(TailCallMode tail_call_mode) { switch (tail_call_mode) { case TailCallMode::kDisallow: return Bytecode::kCall; case TailCallMode::kAllow: return Bytecode::kTailCall; default: UNREACHABLE(); } return Bytecode::kIllegal; } } // namespace interpreter } // namespace internal } // namespace v8