// 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" 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), bytecode_generated_(false), constant_array_builder_(zone, isolate->factory()->the_hole_value()), 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()); } Handle<BytecodeArray> 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<FixedArray> 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 <OperandTypeInfo type_info> class UnsignedOperandHelper { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, size_t value)) { DCHECK(IsValid(value)); return static_cast<uint32_t>(value); } INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, int value)) { DCHECK_GE(value, 0); return Convert(builder, static_cast<size_t>(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 <OperandType> class OperandHelper {}; #define DEFINE_UNSIGNED_OPERAND_HELPER(Name, Type) \ template <> \ class OperandHelper<OperandType::k##Name> \ : public UnsignedOperandHelper<Type> {}; UNSIGNED_SCALAR_OPERAND_TYPE_LIST(DEFINE_UNSIGNED_OPERAND_HELPER) #undef DEFINE_UNSIGNED_OPERAND_HELPER template <> class OperandHelper<OperandType::kImm> { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, int value)) { return static_cast<uint32_t>(value); } }; template <> class OperandHelper<OperandType::kReg> { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, Register reg)) { return builder->GetInputRegisterOperand(reg); } }; template <> class OperandHelper<OperandType::kRegList> { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, RegisterList reg_list)) { return builder->GetInputRegisterListOperand(reg_list); } }; template <> class OperandHelper<OperandType::kRegPair> { 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<OperandType::kRegOut> { public: INLINE(static uint32_t Convert(BytecodeArrayBuilder* builder, Register reg)) { return builder->GetOutputRegisterOperand(reg); } }; template <> class OperandHelper<OperandType::kRegOutPair> { 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<OperandType::kRegOutTriple> { 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 <OperandType... operand_types> class BytecodeNodeBuilder { public: template <typename... Operands> INLINE(static BytecodeNode Make(BytecodeArrayBuilder* builder, BytecodeSourceInfo source_info, Bytecode bytecode, Operands... operands)) { builder->PrepareToOutputBytecode(bytecode); // The "OperandHelper<operand_types>::Convert(builder, operands)..." will // expand both the OperandType... and Operands... parameter packs e.g. for: // BytecodeNodeBuilder<OperandType::kReg, OperandType::kImm>::Make< // Register, int>(..., Register reg, int immediate) // the code will expand into: // OperandHelper<OperandType::kReg>::Convert(builder, reg), // OperandHelper<OperandType::kImm>::Convert(builder, immediate), return BytecodeNode( bytecode, OperandHelper<operand_types>::Convert(builder, operands)..., source_info); } }; #define DEFINE_BYTECODE_OUTPUT(name, accumulator_use, ...) \ template <typename... Operands> \ void BytecodeArrayBuilder::Output##name(Operands... operands) { \ BytecodeNode node(BytecodeNodeBuilder<__VA_ARGS__>::Make<Operands...>( \ this, CurrentSourcePosition(Bytecode::k##name), Bytecode::k##name, \ operands...)); \ pipeline()->Write(&node); \ } \ \ template <typename... Operands> \ void BytecodeArrayBuilder::Output##name(BytecodeLabel* label, \ Operands... operands) { \ DCHECK(Bytecodes::IsJump(Bytecode::k##name)); \ BytecodeNode node(BytecodeNodeBuilder<__VA_ARGS__>::Make<Operands...>( \ this, CurrentSourcePosition(Bytecode::k##name), 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::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(Handle<Object> object) { size_t entry = GetConstantPoolEntry(object); 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(int feedback_slot, TypeofMode typeof_mode) { if (typeof_mode == INSIDE_TYPEOF) { OutputLdaGlobalInsideTypeof(feedback_slot); } else { DCHECK_EQ(typeof_mode, NOT_INSIDE_TYPEOF); OutputLdaGlobal(feedback_slot); } return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal( const Handle<String> 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) { if (context.is_current_context() && depth == 0) { OutputLdaCurrentContextSlot(slot_index); } else { 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 Handle<String> 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 Handle<String> 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 Handle<String> 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 Handle<String> 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 Handle<Name> 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::StoreNamedProperty( Register object, const Handle<Name> name, int feedback_slot, LanguageMode language_mode) { size_t name_index = GetConstantPoolEntry(name); 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::StoreKeyedProperty( Register object, Register key, int feedback_slot, LanguageMode 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::CreateClosure(size_t entry, int flags) { OutputCreateClosure(entry, flags); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateBlockContext( Handle<ScopeInfo> scope_info) { size_t entry = GetConstantPoolEntry(scope_info); OutputCreateBlockContext(entry); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateCatchContext( Register exception, Handle<String> name, Handle<ScopeInfo> scope_info) { size_t name_index = GetConstantPoolEntry(name); size_t scope_info_index = GetConstantPoolEntry(scope_info); OutputCreateCatchContext(exception, name_index, scope_info_index); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateFunctionContext(int slots) { OutputCreateFunctionContext(slots); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateWithContext( Register object, Handle<ScopeInfo> scope_info) { size_t scope_info_index = GetConstantPoolEntry(scope_info); OutputCreateWithContext(object, scope_info_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( Handle<String> pattern, int literal_index, int flags) { size_t pattern_entry = GetConstantPoolEntry(pattern); OutputCreateRegExpLiteral(pattern_entry, literal_index, flags); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral( Handle<FixedArray> constant_elements, int literal_index, int flags) { size_t constant_elements_entry = GetConstantPoolEntry(constant_elements); OutputCreateArrayLiteral(constant_elements_entry, literal_index, flags); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral( Handle<FixedArray> constant_properties, int literal_index, int flags, Register output) { size_t constant_properties_entry = GetConstantPoolEntry(constant_properties); 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) { OutputJump(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfTrue(BytecodeLabel* label) { // The peephole optimizer attempts to simplify JumpIfToBooleanTrue // to JumpIfTrue. OutputJumpIfToBooleanTrue(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(BytecodeLabel* label) { OutputJumpIfToBooleanFalse(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) { OutputJumpIfNull(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined( BytecodeLabel* label) { OutputJumpIfUndefined(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotHole( BytecodeLabel* label) { OutputJumpIfNotHole(label, 0); return *this; } BytecodeArrayBuilder& BytecodeArrayBuilder::JumpLoop(BytecodeLabel* label, int loop_depth) { 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::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::New(Register constructor, RegisterList args, int feedback_slot_id) { OutputNew(constructor, args, args.register_count(), feedback_slot_id); 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<int>(intrinsic_id), args, args.register_count()); } else { OutputCallRuntime(static_cast<int>(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<uint16_t>(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(Handle<Object> object) { return constant_array_builder()->Insert(object); } size_t BytecodeArrayBuilder::AllocateConstantPoolEntry() { return constant_array_builder()->AllocateEntry(); } void BytecodeArrayBuilder::InsertConstantPoolEntryAt(size_t entry, Handle<Object> object) { constant_array_builder()->InsertAllocatedEntry(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; } } void BytecodeArrayBuilder::PrepareToOutputBytecode(Bytecode bytecode) { if (register_optimizer_) register_optimizer_->PrepareForBytecode(bytecode); } uint32_t BytecodeArrayBuilder::GetInputRegisterOperand(Register reg) { DCHECK(RegisterIsValid(reg)); if (register_optimizer_) reg = register_optimizer_->GetInputRegister(reg); return static_cast<uint32_t>(reg.ToOperand()); } uint32_t BytecodeArrayBuilder::GetOutputRegisterOperand(Register reg) { DCHECK(RegisterIsValid(reg)); if (register_optimizer_) register_optimizer_->PrepareOutputRegister(reg); return static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(reg_list.first_register().ToOperand()); } } // namespace interpreter } // namespace internal } // namespace v8