| // 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 |