// 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/globals.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" #include "src/objects-inl.h" namespace v8 { namespace internal { namespace interpreter { BytecodeArrayBuilder::BytecodeArrayBuilder( Isolate* isolate, Zone* zone, int parameter_count, int context_count, int locals_count, FunctionLiteral* literal, SourcePositionTableBuilder::RecordingMode source_position_mode) : zone_(zone), literal_(literal), bytecode_generated_(false), constant_array_builder_(zone), handler_table_builder_(zone), return_seen_in_block_(false), parameter_count_(parameter_count), local_register_count_(locals_count), context_register_count_(context_count), register_allocator_(fixed_register_count()), bytecode_array_writer_(zone, &constant_array_builder_, source_position_mode), pipeline_(&bytecode_array_writer_), register_optimizer_(nullptr) { 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(pipeline_); } if (FLAG_ignition_reo) { register_optimizer_ = new (zone) BytecodeRegisterOptimizer( zone, ®ister_allocator_, fixed_register_count(), parameter_count, pipeline_); } return_position_ = literal ? literal->return_position() : kNoSourcePosition; } 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()); } Register BytecodeArrayBuilder::Local(int index) const { // TODO(marja): Make a DCHECK once crbug.com/706234 is fixed. CHECK_LT(index, locals_count()); return Register(index); } Handle BytecodeArrayBuilder::ToBytecodeArray(Isolate* isolate) { DCHECK(return_seen_in_block_); DCHECK(!bytecode_generated_); bytecode_generated_ = true; int register_count = total_register_count(); if (register_optimizer_) { register_optimizer_->Flush(); register_count = register_optimizer_->maxiumum_register_index() + 1; } Handle handler_table = handler_table_builder()->ToHandlerTable(isolate); return pipeline_->ToBytecodeArray(isolate, register_count, parameter_count(), handler_table); } BytecodeSourceInfo BytecodeArrayBuilder::CurrentSourcePosition( Bytecode bytecode) { BytecodeSourceInfo source_position; 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 (if expression position filtering is turned on). We only // invalidate the existing source position information if it is used. if (latest_source_info_.is_statement() || !FLAG_ignition_filter_expression_positions || !Bytecodes::IsWithoutExternalSideEffects(bytecode)) { source_position = latest_source_info_; latest_source_info_.set_invalid(); } } return source_position; } namespace { template class UnsignedOperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, size_t value)) { DCHECK(IsValid(value)); return static_cast(value); } INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, int value)) { DCHECK_GE(value, 0); return Convert(builder, static_cast(value)); } private: static bool IsValid(size_t value) { switch (type_info) { case OperandTypeInfo::kFixedUnsignedByte: return value <= kMaxUInt8; case OperandTypeInfo::kFixedUnsignedShort: return value <= kMaxUInt16; case OperandTypeInfo::kScalableUnsignedByte: return value <= kMaxUInt32; default: UNREACHABLE(); return false; } } }; template class OperandHelper {}; #define DEFINE_UNSIGNED_OPERAND_HELPER(Name, Type) \ template <> \ class OperandHelper \ : public UnsignedOperandHelper {}; UNSIGNED_FIXED_SCALAR_OPERAND_TYPE_LIST(DEFINE_UNSIGNED_OPERAND_HELPER) UNSIGNED_SCALABLE_SCALAR_OPERAND_TYPE_LIST(DEFINE_UNSIGNED_OPERAND_HELPER) #undef DEFINE_UNSIGNED_OPERAND_HELPER template <> class OperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, int value)) { return static_cast(value); } }; template <> class OperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, Register reg)) { return builder->GetInputRegisterOperand(reg); } }; template <> class OperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, RegisterList reg_list)) { return builder->GetInputRegisterListOperand(reg_list); } }; template <> class OperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, RegisterList reg_list)) { DCHECK_EQ(reg_list.register_count(), 2); return builder->GetInputRegisterListOperand(reg_list); } }; template <> class OperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, Register reg)) { return builder->GetOutputRegisterOperand(reg); } }; template <> class OperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, RegisterList reg_list)) { DCHECK_EQ(2, reg_list.register_count()); return builder->GetOutputRegisterListOperand(reg_list); } }; template <> class OperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, RegisterList reg_list)) { DCHECK_EQ(3, reg_list.register_count()); return builder->GetOutputRegisterListOperand(reg_list); } }; } // namespace template class BytecodeNodeBuilder { public: template INLINE(static BytecodeNode Make(BytecodeArrayBuilder* builder, BytecodeSourceInfo source_info, Operands... operands)) { builder->PrepareToOutputBytecode(); // The "OperandHelper::Convert(builder, operands)..." will // expand both the OperandType... and Operands... parameter packs e.g. for: // BytecodeNodeBuilder::Make< // Register, int>(..., Register reg, int immediate) // the code will expand into: // OperandHelper::Convert(builder, reg), // OperandHelper::Convert(builder, immediate), return BytecodeNode::Create( source_info, OperandHelper::Convert(builder, operands)...); } }; #define DEFINE_BYTECODE_OUTPUT(name, ...) \ template \ void BytecodeArrayBuilder::Output##name(Operands... operands) { \ static_assert(sizeof...(Operands) <= Bytecodes::kMaxOperands, \ "too many operands for bytecode"); \ BytecodeNode node( \ BytecodeNodeBuilder::Make< \ Operands...>(this, CurrentSourcePosition(Bytecode::k##name), \ operands...)); \ pipeline()->Write(&node); \ } \ \ template \ void BytecodeArrayBuilder::Output##name(BytecodeLabel* label, \ Operands... operands) { \ DCHECK(Bytecodes::IsJump(Bytecode::k##name)); \ BytecodeNode node( \ BytecodeNodeBuilder::Make< \ Operands...>(this, CurrentSourcePosition(Bytecode::k##name), \ operands...)); \ pipeline()->WriteJump(&node, label); \ LeaveBasicBlock(); \ } BYTECODE_LIST(DEFINE_BYTECODE_OUTPUT) #undef DEFINE_BYTECODE_OUTPUT BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op, Register reg, int feedback_slot) { switch (op) { case Token::Value::ADD: OutputAdd(reg, feedback_slot); break; case Token::Value::SUB: OutputSub(reg, feedback_slot); break; case Token::Value::MUL: OutputMul(reg, feedback_slot); break; case Token::Value::DIV: OutputDiv(reg, feedback_slot); break; case Token::Value::MOD: OutputMod(reg, feedback_slot); break; case Token::Value::BIT_OR: OutputBitwiseOr(reg, feedback_slot); break; case Token::Value::BIT_XOR: OutputBitwiseXor(reg, feedback_slot); break; case Token::Value::BIT_AND: OutputBitwiseAnd(reg, feedback_slot); break; case Token::Value::SHL: OutputShiftLeft(reg, feedback_slot); break; case Token::Value::SAR: OutputShiftRight(reg, feedback_slot); break; case Token::Value::SHR: OutputShiftRightLogical(reg, feedback_slot); break; default: UNREACHABLE(); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CountOperation(Token::Value op, int feedback_slot) { if (op == Token::Value::ADD) { OutputInc(feedback_slot); } else { DCHECK_EQ(op, Token::Value::SUB); OutputDec(feedback_slot); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot() { OutputToBooleanLogicalNot(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() { OutputTypeOf(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::GetSuperConstructor(Register out) { OutputGetSuperConstructor(out); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation( Token::Value op, Register reg, int feedback_slot) { switch (op) { case Token::Value::EQ: OutputTestEqual(reg, feedback_slot); break; case Token::Value::NE: OutputTestNotEqual(reg, feedback_slot); break; case Token::Value::EQ_STRICT: OutputTestEqualStrict(reg, feedback_slot); break; case Token::Value::LT: OutputTestLessThan(reg, feedback_slot); break; case Token::Value::GT: OutputTestGreaterThan(reg, feedback_slot); break; case Token::Value::LTE: OutputTestLessThanOrEqual(reg, feedback_slot); break; case Token::Value::GTE: OutputTestGreaterThanOrEqual(reg, feedback_slot); break; case Token::Value::INSTANCEOF: OutputTestInstanceOf(reg); break; case Token::Value::IN: OutputTestIn(reg); break; default: UNREACHABLE(); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadConstantPoolEntry( size_t entry) { OutputLdaConstant(entry); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral( v8::internal::Smi* smi) { int32_t raw_smi = smi->value(); if (raw_smi == 0) { OutputLdaZero(); } else { OutputLdaSmi(raw_smi); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral( const AstRawString* raw_string) { size_t entry = GetConstantPoolEntry(raw_string); OutputLdaConstant(entry); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(const Scope* scope) { size_t entry = GetConstantPoolEntry(scope); OutputLdaConstant(entry); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral( const AstValue* ast_value) { if (ast_value->IsSmi()) { return LoadLiteral(ast_value->AsSmi()); } else if (ast_value->IsUndefined()) { return LoadUndefined(); } else if (ast_value->IsTrue()) { return LoadTrue(); } else if (ast_value->IsFalse()) { return LoadFalse(); } else if (ast_value->IsNull()) { return LoadNull(); } else if (ast_value->IsTheHole()) { return LoadTheHole(); } else if (ast_value->IsString()) { return LoadLiteral(ast_value->AsString()); } else if (ast_value->IsHeapNumber()) { size_t entry = GetConstantPoolEntry(ast_value); OutputLdaConstant(entry); return *this; } else { // This should be the only ast value type left. DCHECK(ast_value->IsSymbol()); size_t entry; switch (ast_value->AsSymbol()) { case AstSymbol::kHomeObjectSymbol: entry = HomeObjectSymbolConstantPoolEntry(); break; // No default case so that we get a warning if AstSymbol changes } OutputLdaConstant(entry); return *this; } } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadUndefined() { OutputLdaUndefined(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNull() { OutputLdaNull(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTheHole() { OutputLdaTheHole(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTrue() { OutputLdaTrue(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadFalse() { OutputLdaFalse(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister( Register reg) { if (register_optimizer_) { register_optimizer_->DoLdar(reg, CurrentSourcePosition(Bytecode::kLdar)); } else { OutputLdar(reg); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister( Register reg) { if (register_optimizer_) { register_optimizer_->DoStar(reg, CurrentSourcePosition(Bytecode::kStar)); } else { OutputStar(reg); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from, Register to) { DCHECK(from != to); if (register_optimizer_) { register_optimizer_->DoMov(from, to, CurrentSourcePosition(Bytecode::kMov)); } else { OutputMov(from, to); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(const AstRawString* name, int feedback_slot, TypeofMode typeof_mode) { size_t name_index = GetConstantPoolEntry(name); // Ensure that typeof mode is in sync with the IC slot kind if the function // literal is available (not a unit test case). // TODO(ishell): check only in debug mode. if (literal_) { FeedbackSlot slot = FeedbackVector::ToSlot(feedback_slot); CHECK_EQ(GetTypeofModeFromSlotKind(feedback_vector_spec()->GetKind(slot)), typeof_mode); } if (typeof_mode == INSIDE_TYPEOF) { OutputLdaGlobalInsideTypeof(name_index, feedback_slot); } else { DCHECK_EQ(typeof_mode, NOT_INSIDE_TYPEOF); OutputLdaGlobal(name_index, feedback_slot); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal( const AstRawString* name, int feedback_slot, LanguageMode language_mode) { size_t name_index = GetConstantPoolEntry(name); if (language_mode == SLOPPY) { OutputStaGlobalSloppy(name_index, feedback_slot); } else { DCHECK_EQ(language_mode, STRICT); OutputStaGlobalStrict(name_index, feedback_slot); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot( Register context, int slot_index, int depth, ContextSlotMutability mutability) { if (context.is_current_context() && depth == 0) { if (mutability == kImmutableSlot) { OutputLdaImmutableCurrentContextSlot(slot_index); } else { DCHECK_EQ(kMutableSlot, mutability); OutputLdaCurrentContextSlot(slot_index); } } else if (mutability == kImmutableSlot) { OutputLdaImmutableContextSlot(context, slot_index, depth); } else { DCHECK_EQ(mutability, kMutableSlot); OutputLdaContextSlot(context, slot_index, depth); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreContextSlot(Register context, int slot_index, int depth) { if (context.is_current_context() && depth == 0) { OutputStaCurrentContextSlot(slot_index); } else { OutputStaContextSlot(context, slot_index, depth); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupSlot( const AstRawString* name, TypeofMode typeof_mode) { size_t name_index = GetConstantPoolEntry(name); if (typeof_mode == INSIDE_TYPEOF) { OutputLdaLookupSlotInsideTypeof(name_index); } else { DCHECK_EQ(typeof_mode, NOT_INSIDE_TYPEOF); OutputLdaLookupSlot(name_index); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupContextSlot( const AstRawString* name, TypeofMode typeof_mode, int slot_index, int depth) { size_t name_index = GetConstantPoolEntry(name); if (typeof_mode == INSIDE_TYPEOF) { OutputLdaLookupContextSlotInsideTypeof(name_index, slot_index, depth); } else { DCHECK(typeof_mode == NOT_INSIDE_TYPEOF); OutputLdaLookupContextSlot(name_index, slot_index, depth); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupGlobalSlot( const AstRawString* name, TypeofMode typeof_mode, int feedback_slot, int depth) { size_t name_index = GetConstantPoolEntry(name); if (typeof_mode == INSIDE_TYPEOF) { OutputLdaLookupGlobalSlotInsideTypeof(name_index, feedback_slot, depth); } else { DCHECK(typeof_mode == NOT_INSIDE_TYPEOF); OutputLdaLookupGlobalSlot(name_index, feedback_slot, depth); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreLookupSlot( const AstRawString* name, LanguageMode language_mode) { size_t name_index = GetConstantPoolEntry(name); if (language_mode == SLOPPY) { OutputStaLookupSlotSloppy(name_index); } else { DCHECK_EQ(language_mode, STRICT); OutputStaLookupSlotStrict(name_index); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty( Register object, const AstRawString* name, int feedback_slot) { size_t name_index = GetConstantPoolEntry(name); OutputLdaNamedProperty(object, name_index, feedback_slot); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty( Register object, int feedback_slot) { OutputLdaKeyedProperty(object, feedback_slot); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadIteratorProperty( Register object, int feedback_slot) { size_t name_index = IteratorSymbolConstantPoolEntry(); OutputLdaNamedProperty(object, name_index, feedback_slot); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAsyncIteratorProperty( Register object, int feedback_slot) { size_t name_index = AsyncIteratorSymbolConstantPoolEntry(); OutputLdaNamedProperty(object, name_index, feedback_slot); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreDataPropertyInLiteral( Register object, Register name, DataPropertyInLiteralFlags flags, int feedback_slot) { OutputStaDataPropertyInLiteral(object, name, flags, feedback_slot); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty( Register object, size_t name_index, int feedback_slot, LanguageMode language_mode) { // Ensure that language mode is in sync with the IC slot kind if the function // literal is available (not a unit test case). // TODO(ishell): check only in debug mode. if (literal_) { FeedbackSlot slot = FeedbackVector::ToSlot(feedback_slot); CHECK_EQ(GetLanguageModeFromSlotKind(feedback_vector_spec()->GetKind(slot)), language_mode); } if (language_mode == SLOPPY) { OutputStaNamedPropertySloppy(object, name_index, feedback_slot); } else { DCHECK_EQ(language_mode, STRICT); OutputStaNamedPropertyStrict(object, name_index, feedback_slot); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty( Register object, const AstRawString* name, int feedback_slot, LanguageMode language_mode) { size_t name_index = GetConstantPoolEntry(name); return StoreNamedProperty(object, name_index, feedback_slot, language_mode); } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedOwnProperty( Register object, const AstRawString* name, int feedback_slot) { size_t name_index = GetConstantPoolEntry(name); // Ensure that the store operation is in sync with the IC slot kind if // the function literal is available (not a unit test case). // TODO(ishell): check only in debug mode. if (literal_) { FeedbackSlot slot = FeedbackVector::ToSlot(feedback_slot); CHECK_EQ(FeedbackSlotKind::kStoreOwnNamed, feedback_vector_spec()->GetKind(slot)); } OutputStaNamedOwnProperty(object, name_index, feedback_slot); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty( Register object, Register key, int feedback_slot, LanguageMode language_mode) { // Ensure that language mode is in sync with the IC slot kind if the function // literal is available (not a unit test case). // TODO(ishell): check only in debug mode. if (literal_) { FeedbackSlot slot = FeedbackVector::ToSlot(feedback_slot); CHECK_EQ(GetLanguageModeFromSlotKind(feedback_vector_spec()->GetKind(slot)), language_mode); } if (language_mode == SLOPPY) { OutputStaKeyedPropertySloppy(object, key, feedback_slot); } else { DCHECK_EQ(language_mode, STRICT); OutputStaKeyedPropertyStrict(object, key, feedback_slot); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreHomeObjectProperty( Register object, int feedback_slot, LanguageMode language_mode) { size_t name_index = HomeObjectSymbolConstantPoolEntry(); return StoreNamedProperty(object, name_index, feedback_slot, language_mode); } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure( size_t shared_function_info_entry, int slot, int flags) { OutputCreateClosure(shared_function_info_entry, slot, flags); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateBlockContext( const Scope* scope) { size_t entry = GetConstantPoolEntry(scope); OutputCreateBlockContext(entry); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateCatchContext( Register exception, const AstRawString* name, const Scope* scope) { size_t name_index = GetConstantPoolEntry(name); size_t scope_index = GetConstantPoolEntry(scope); OutputCreateCatchContext(exception, name_index, scope_index); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateFunctionContext(int slots) { OutputCreateFunctionContext(slots); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateEvalContext(int slots) { OutputCreateEvalContext(slots); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateWithContext( Register object, const Scope* scope) { size_t scope_index = GetConstantPoolEntry(scope); OutputCreateWithContext(object, scope_index); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments( CreateArgumentsType type) { switch (type) { case CreateArgumentsType::kMappedArguments: OutputCreateMappedArguments(); break; case CreateArgumentsType::kUnmappedArguments: OutputCreateUnmappedArguments(); break; case CreateArgumentsType::kRestParameter: OutputCreateRestParameter(); break; default: UNREACHABLE(); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral( const AstRawString* pattern, int literal_index, int flags) { size_t pattern_entry = GetConstantPoolEntry(pattern); OutputCreateRegExpLiteral(pattern_entry, literal_index, flags); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral( size_t constant_elements_entry, int literal_index, int flags) { OutputCreateArrayLiteral(constant_elements_entry, literal_index, flags); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral( size_t constant_properties_entry, int literal_index, int flags, Register output) { OutputCreateObjectLiteral(constant_properties_entry, literal_index, flags, output); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) { OutputPushContext(context); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) { OutputPopContext(context); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ConvertAccumulatorToObject( Register out) { OutputToObject(out); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ConvertAccumulatorToName( Register out) { OutputToName(out); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ConvertAccumulatorToNumber( Register out) { OutputToNumber(out); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) { // Flush the register optimizer when binding a label to ensure all // expected registers are valid when jumping to this label. if (register_optimizer_) register_optimizer_->Flush(); pipeline_->BindLabel(label); LeaveBasicBlock(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target, BytecodeLabel* label) { pipeline_->BindLabel(target, label); LeaveBasicBlock(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) { DCHECK(!label->is_bound()); OutputJump(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfTrue(BytecodeLabel* label) { // The peephole optimizer attempts to simplify JumpIfToBooleanTrue // to JumpIfTrue. DCHECK(!label->is_bound()); OutputJumpIfToBooleanTrue(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) { DCHECK(!label->is_bound()); OutputJumpIfToBooleanFalse(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) { DCHECK(!label->is_bound()); OutputJumpIfNull(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined( BytecodeLabel* label) { DCHECK(!label->is_bound()); OutputJumpIfUndefined(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotHole( BytecodeLabel* label) { DCHECK(!label->is_bound()); OutputJumpIfNotHole(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfJSReceiver( BytecodeLabel* label) { DCHECK(!label->is_bound()); OutputJumpIfJSReceiver(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpLoop(BytecodeLabel* label, int loop_depth) { DCHECK(label->is_bound()); OutputJumpLoop(label, 0, loop_depth); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck(int position) { if (position != kNoSourcePosition) { // 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); } OutputStackCheck(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::SetPendingMessage() { OutputSetPendingMessage(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() { OutputThrow(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ReThrow() { OutputReThrow(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Return() { SetReturnPosition(); OutputReturn(); return_seen_in_block_ = true; return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Debugger() { OutputDebugger(); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare( Register receiver, RegisterList cache_info_triple) { DCHECK_EQ(3, cache_info_triple.register_count()); OutputForInPrepare(receiver, cache_info_triple); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInContinue( Register index, Register cache_length) { OutputForInContinue(index, cache_length); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext( Register receiver, Register index, RegisterList cache_type_array_pair, int feedback_slot) { DCHECK_EQ(2, cache_type_array_pair.register_count()); OutputForInNext(receiver, index, cache_type_array_pair, feedback_slot); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) { OutputForInStep(index); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreModuleVariable(int cell_index, int depth) { OutputStaModuleVariable(cell_index, depth); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::LoadModuleVariable(int cell_index, int depth) { OutputLdaModuleVariable(cell_index, depth); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::SuspendGenerator( Register generator) { OutputSuspendGenerator(generator); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ResumeGenerator( Register generator) { OutputResumeGenerator(generator); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::MarkHandler( int handler_id, HandlerTable::CatchPrediction catch_prediction) { BytecodeLabel handler; Bind(&handler); handler_table_builder()->SetHandlerTarget(handler_id, handler.offset()); handler_table_builder()->SetPrediction(handler_id, catch_prediction); 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; } BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable, RegisterList args, int feedback_slot, Call::CallType call_type, TailCallMode tail_call_mode) { if (tail_call_mode == TailCallMode::kDisallow) { if (call_type == Call::NAMED_PROPERTY_CALL || call_type == Call::KEYED_PROPERTY_CALL) { OutputCallProperty(callable, args, args.register_count(), feedback_slot); } else { OutputCall(callable, args, args.register_count(), feedback_slot); } } else { DCHECK(tail_call_mode == TailCallMode::kAllow); OutputTailCall(callable, args, args.register_count(), feedback_slot); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CallWithSpread(Register callable, RegisterList args) { OutputCallWithSpread(callable, args, args.register_count()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Construct(Register constructor, RegisterList args, int feedback_slot_id) { OutputConstruct(constructor, args, args.register_count(), feedback_slot_id); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::ConstructWithSpread( Register constructor, RegisterList args) { OutputConstructWithSpread(constructor, args, args.register_count()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime( Runtime::FunctionId function_id, RegisterList args) { DCHECK_EQ(1, Runtime::FunctionForId(function_id)->result_size); DCHECK(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort); if (IntrinsicsHelper::IsSupported(function_id)) { IntrinsicsHelper::IntrinsicId intrinsic_id = IntrinsicsHelper::FromRuntimeId(function_id); OutputInvokeIntrinsic(static_cast(intrinsic_id), args, args.register_count()); } else { OutputCallRuntime(static_cast(function_id), args, args.register_count()); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime( Runtime::FunctionId function_id, Register arg) { return CallRuntime(function_id, RegisterList(arg.index(), 1)); } BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime( Runtime::FunctionId function_id) { return CallRuntime(function_id, RegisterList()); } BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair( Runtime::FunctionId function_id, RegisterList args, RegisterList return_pair) { DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size); DCHECK(Bytecodes::SizeForUnsignedOperand(function_id) <= OperandSize::kShort); DCHECK_EQ(2, return_pair.register_count()); OutputCallRuntimeForPair(static_cast(function_id), args, args.register_count(), return_pair); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair( Runtime::FunctionId function_id, Register arg, RegisterList return_pair) { return CallRuntimeForPair(function_id, RegisterList(arg.index(), 1), return_pair); } BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index, RegisterList args) { OutputCallJSRuntime(context_index, args, args.register_count()); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object, LanguageMode language_mode) { if (language_mode == SLOPPY) { OutputDeletePropertySloppy(object); } else { DCHECK_EQ(language_mode, STRICT); OutputDeletePropertyStrict(object); } return *this; } size_t BytecodeArrayBuilder::GetConstantPoolEntry( const AstRawString* raw_string) { return constant_array_builder()->Insert(raw_string); } size_t BytecodeArrayBuilder::GetConstantPoolEntry(const AstValue* heap_number) { DCHECK(heap_number->IsHeapNumber()); return constant_array_builder()->Insert(heap_number); } size_t BytecodeArrayBuilder::GetConstantPoolEntry(const Scope* scope) { return constant_array_builder()->Insert(scope); } #define ENTRY_GETTER(NAME, ...) \ size_t BytecodeArrayBuilder::NAME##ConstantPoolEntry() { \ return constant_array_builder()->Insert##NAME(); \ } SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_GETTER) #undef ENTRY_GETTER size_t BytecodeArrayBuilder::AllocateDeferredConstantPoolEntry() { return constant_array_builder()->InsertDeferred(); } void BytecodeArrayBuilder::SetDeferredConstantPoolEntry(size_t entry, Handle object) { constant_array_builder()->SetDeferredAt(entry, object); } void BytecodeArrayBuilder::SetReturnPosition() { if (return_position_ == kNoSourcePosition) return; latest_source_info_.MakeStatementPosition(return_position_); } 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 register_allocator()->RegisterIsLive(reg); } } bool BytecodeArrayBuilder::RegisterListIsValid(RegisterList reg_list) const { if (reg_list.register_count() == 0) { return reg_list.first_register() == Register(0); } else { int first_reg_index = reg_list.first_register().index(); for (int i = 0; i < reg_list.register_count(); i++) { if (!RegisterIsValid(Register(first_reg_index + i))) { return false; } } return true; } } template void BytecodeArrayBuilder::PrepareToOutputBytecode() { if (register_optimizer_) register_optimizer_->PrepareForBytecode(); } uint32_t BytecodeArrayBuilder::GetInputRegisterOperand(Register reg) { DCHECK(RegisterIsValid(reg)); if (register_optimizer_) reg = register_optimizer_->GetInputRegister(reg); return static_cast(reg.ToOperand()); } uint32_t BytecodeArrayBuilder::GetOutputRegisterOperand(Register reg) { DCHECK(RegisterIsValid(reg)); if (register_optimizer_) register_optimizer_->PrepareOutputRegister(reg); return static_cast(reg.ToOperand()); } uint32_t BytecodeArrayBuilder::GetInputRegisterListOperand( RegisterList reg_list) { DCHECK(RegisterListIsValid(reg_list)); if (register_optimizer_) reg_list = register_optimizer_->GetInputRegisterList(reg_list); return static_cast(reg_list.first_register().ToOperand()); } uint32_t BytecodeArrayBuilder::GetOutputRegisterListOperand( RegisterList reg_list) { DCHECK(RegisterListIsValid(reg_list)); if (register_optimizer_) register_optimizer_->PrepareOutputRegisterList(reg_list); return static_cast(reg_list.first_register().ToOperand()); } } // namespace interpreter } // namespace internal } // namespace v8